下拉刷新XListView的简单分析
依照这篇博文里的思路分析和理解的
先要理解Scroller,看过的博文:
http://ipjmc.iteye.com/blog/1615828
http://blog.csdn.net/wangjinyu501/article/details/32339379
还要理解View的touch时间传递:
http://www.codekk.com/open-source-project-analysis/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8BView%20%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92
在实现中遇到的问题:
1、下拉时,下拉区域不会尾随下拉而变化,仅仅显示当中一部分。
图:
解决:採用设置下拉区域的paddind,实现尾随滚动效果。终于图:
2、当下拉超过极限高度后向上滑动时。listview会尾随滑动。
解决方法是通过在onTouchEvent推断这一情况推断这一情况,具体在代码里。
代码:
下拉区域布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" > <RelativeLayout
android:id="@+id/xlistview_header_content"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_marginBottom="2dp"
android:gravity="center_horizontal" > <TextView
android:id="@+id/xlistview_header_hint_textview"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="正在载入"
android:textColor="@android:color/black"
android:textSize="14sp" /> <ImageView
android:id="@+id/xlistview_header_image"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/xlistview_header_hint_textview"
android:src="@drawable/indicator_arrow" /> <ProgressBar
android:id="@+id/xlistview_header_progressbar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/xlistview_header_hint_textview"
android:visibility="invisible" />
</RelativeLayout> </LinearLayout>
下拉区域
package com.example.test; import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView; public class XListViewHeader extends LinearLayout { private static final String HINT_NORMAL = "下拉刷新";
private static final String HINT_READY = "松开刷新数据";
private static final String HINT_LOADING = "正在载入..."; // 正常状态,下拉未超过head高度
public final static int STATE_NORMAL = 0;
// 准备刷新状态,也就是箭头方向发生改变之后的状态,可是没有刷新
public final static int STATE_READY = 1;
// 刷新状态。箭头变成了progressBar,正在刷新
public final static int STATE_REFRESHING = 2;
// 布局容器,也就是根布局
private LinearLayout mContentLayout;
// 箭头图片
private ImageView mImageView;
// 刷新状态显示
private ProgressBar mProgressBar;
// 说明文本
private TextView mHintTextView;
// 记录当前的状态
private int mState = -1;
// 用于改变箭头的方向的动画
private Animation mRotateUpAnim;
private Animation mRotateDownAnim;
// 动画持续时间
private final int ROTATE_ANIM_DURATION = 180; private int headHeight;
private Context context; public XListViewHeader(Context context) {
super(context);
this.context = context;
init();
} private void init() {
LinearLayout.LayoutParams lp = new LayoutParams(
LayoutParams.MATCH_PARENT, 0);// 初始化高度为0
mContentLayout = (LinearLayout) LayoutInflater.from(context).inflate(
R.layout.xlistview_header, null);
mContentLayout.setLayoutParams(lp);
addView(mContentLayout); mImageView = (ImageView) mContentLayout
.findViewById(R.id.xlistview_header_image);// 箭头图片
mHintTextView = (TextView) mContentLayout
.findViewById(R.id.xlistview_header_hint_textview);// 提示文本
mProgressBar = (ProgressBar) mContentLayout
.findViewById(R.id.xlistview_header_progressbar);// 进度条
mRotateUpAnim = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);// 箭头向上旋转的动画
mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);// 动画持续时间
mRotateUpAnim.setFillAfter(true);// 动画终止时停留在最后,也就是保留动画以后的状态
mRotateDownAnim = new RotateAnimation(-180, 0,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
mRotateDownAnim.setFillAfter(true);
setState(STATE_NORMAL);// 初始化设置为正常模式
} public void setState(int state) {
if (state == mState) {
return;
}
if (state == STATE_REFRESHING) {// 设置为正在刷新状态时,清楚全部动画,箭头隐藏, 进度条显示
mImageView.clearAnimation();
mImageView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
} else {
mImageView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
switch (state) {
case STATE_NORMAL:
if (mState == STATE_READY) {// 由准备状态变为正常状态。开启向下动画
mImageView.startAnimation(mRotateDownAnim);
} else {
mImageView.clearAnimation();
}
mHintTextView.setText(HINT_NORMAL);
break;
case STATE_READY:
if (mState == STATE_NORMAL) {
mImageView.startAnimation(mRotateUpAnim);
}
mHintTextView.setText(HINT_READY);
break;
case STATE_REFRESHING:
mHintTextView.setText(HINT_LOADING);
break;
}
mState = state;
} @SuppressLint("NewApi")
public void setVisitHeight(int height) {
if (height < 0) {
height = 0;
}
LinearLayout.LayoutParams lp = (LayoutParams) mContentLayout
.getLayoutParams();
lp.height = height;
mContentLayout.setLayoutParams(lp);
mContentLayout.setPadding(mContentLayout.getPaddingLeft(), height
- headHeight, mContentLayout.getPaddingRight(),
mContentLayout.getPaddingBottom());// 设置padding是为了下拉时,head尾随着下拉。更好看
} public int getVisitHeight() {
return mContentLayout.getHeight();
} public void show() {
mContentLayout.setVisibility(View.VISIBLE);
} public void hide() {
mContentLayout.setVisibility(View.INVISIBLE);
} public int getHeadHeight() {
return headHeight;
} public void setHeadHeight(int headHeight) {
this.headHeight = headHeight;
} }
listview
package com.example.test; import android.content.Context;
import android.view.MotionEvent;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller; public class XListView extends ListView {
private Context context;
// 滑动时长
private final static int SCROLL_DURATION = 400;
// 滑动比例
private final static float OFFSET_RADIO = 2f;
// 记录按下点的y坐标
private float lastY;
// 用来回滚
private Scroller scroller;
private IXListViewListener mListViewListener;
private XListViewHeader headerView;
private RelativeLayout headerViewContent;
// header的高度
private int headerHeight;
// 是否可以刷新
private boolean enableRefresh = true;
// 是否正在刷新
private boolean isRefreashing = false;
// 记录当前手势是向上还是向下
private int TOUCH_UP = 0, TOUCH_DOWN = 1;
private int mTouch; public XListView(Context context) {
super(context);
this.context = context;
init();
} private void init() {
scroller = new Scroller(context, new DecelerateInterpolator());
headerView = new XListViewHeader(context);
headerViewContent = (RelativeLayout) headerView
.findViewById(R.id.xlistview_header_content);
// 获得head的高度
headerView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
headerHeight = headerViewContent.getHeight();
headerView.setHeadHeight(headerHeight);
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
addHeaderView(headerView);
} @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float t = ev.getRawY() - lastY;
lastY = ev.getRawY();
if (t > 0) {
mTouch = TOUCH_DOWN;
} else {
mTouch = TOUCH_UP;
}
// 当前是第一个item,且手势是向下,就显示下拉条,更新高度
if (getFirstVisiblePosition() == 0
&& (headerView.getVisitHeight() > 0 || t > 0)) {
updateHeaderViewHeight(t / OFFSET_RADIO);
}
if (!isRefreashing && mTouch == TOUCH_UP
&& headerView.getVisitHeight() > 0) {
return true;// 当下拉高度达到header高度时候,松开就可以刷新。若此刻向上滑,listview会尾随滑动,return
// true 代表消费这个事件,listview禁止滚动
}
break;
case MotionEvent.ACTION_UP:
if (getFirstVisiblePosition() == 0) {
if (enableRefresh && headerView.getVisitHeight() > headerHeight) {
isRefreashing = true;
headerView.setState(headerView.STATE_REFRESHING);
if (mListViewListener != null) {
mListViewListener.onRefresh();//刷新事件
}
}
}
resetHeaderHeight();
break;
}
return super.onTouchEvent(ev);
} public void updateHeaderViewHeight(float f) {
headerView.setVisitHeight((int) f + headerView.getVisitHeight());
// 未处于刷新状态,更新箭头
if (enableRefresh && !isRefreashing) {
if (headerView.getVisitHeight() > headerHeight) {
headerView.setState(XListViewHeader.STATE_READY);
}else{
headerView.setState(XListViewHeader.STATE_NORMAL);
}
}
} // 下拉条动态消失
public void resetHeaderHeight() {
int height = headerView.getVisitHeight();
int endheight = 0;
if (isRefreashing) {
endheight = headerHeight;
}
// y轴方向由 height 到 endheight 。第三个參数是增量,假设不是刷新则高度变为0,假设是,高度变为head原始高度
scroller.startScroll(0, height, 0, endheight - height, SCROLL_DURATION);
invalidate();
} public void computeScroll() {
if (scroller.computeScrollOffset()) {
// 利用scroller 。设置高度。重复重绘
headerView.setVisitHeight(scroller.getCurrY());
postInvalidate();
}
super.computeScroll();
} public void stopRefresh() {
if (isRefreashing == true) {
isRefreashing = false;
resetHeaderHeight();
}
} public void setIxListener(IXListViewListener listener) {
this.mListViewListener = listener;
} interface IXListViewListener {
public void onRefresh();// 刷新事件的回调函数
}
}
mainactivity
package com.example.test; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ArrayAdapter; import com.example.test.XListView.IXListViewListener; public class MainActivity extends Activity {
private XListView xListView;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
xListView.stopRefresh();
}
}; protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
xListView = new XListView(this);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_expandable_list_item_1);
xListView.setAdapter(adapter);
for (int i = 0; i < 10; i++) {
adapter.add("text" + i);
}
setContentView(xListView);
xListView.setIxListener(new IXListViewListener() {
public void onRefresh() {
new Thread() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}.start();
}
});
}
}
csdn博文编辑不能撤销么。写的东西都没了
下拉刷新XListView的简单分析的更多相关文章
- mui实现分页上拉加载更多 下拉刷新数据的简单实现 移动端下拉上拉
空下来把mui上拉加载更多,下拉刷新数据做了一个简单的实现,希望可以帮助到需要的朋友 demo项目的结构 <!DOCTYPE html> <html> <head> ...
- IOS UIWebView 下拉刷新功能的简单实现
1.运行效果图 2.swift 代码的实现 import UIKit class RefreshWebViewController: UIViewController,UIScrollViewDele ...
- 手把手教你轻松实现listview下拉刷新
很多人觉得自定义一个listview下拉刷新上拉加载更多是一件很牛x的事情,不是大神写不出来,我想大多数童鞋都是做项目用到时就百度,什么pulltorefresh,xlistview...也不看原理, ...
- iOS下拉刷新和上拉刷新
在iOS开发中,我们经常要用到下拉刷新和上拉刷新来加载新的数据,当前这也适合分页.iOS原生就带有该方法,下面就iOS自带的下拉刷新方法来简单操作. 上拉刷新 1.在TableView里,一打开软件, ...
- Android之XListView下拉刷新,更新网络美女图
一.简介: 下拉刷新是一种特定的手动刷新交互,和其他的同类操作不同的地方在于它采用了更加直觉的下拉操作,所以它的交互足够清晰明显. 下拉刷新主要用在类似ListView这样的控件,设计下拉刷新有三 ...
- XListView下拉刷新和上拉加载更多详解
转载本专栏每一篇博客请注明转载出处地址,尊重原创.博客链接地址:小杨的博客 http://blog.csdn.net/qq_32059827/article/details/53167655 市面上有 ...
- Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件
前言: 忙完了结婚乐APP的开发,终于可以花一定的时间放在博客上了.好了,废话不多说,今天我们要带来的效果是苹果版本的QQ下拉刷新.首先看一下目标效果以及demo效果: 因为此效果实现的步骤 ...
- 下拉刷新和UITableView的section headerView冲突的原因分析与解决方案
UITableView:下拉刷新和上拉加载更多 [转载请注明出处] 本文将说明具有多个section的UITableView在使用下拉刷新机制时会遇到的问题及其解决方案. 工程地址在帖子最下方,只需要 ...
- 简单的下拉刷新以及优化--SwipeRefreshLayout
代码工程简要说明:以一个SwipeRefreshLayout包裹ListView,SwipeRefreshLayout接管ListView的下拉事件,若ListView被用户触发下拉动作后,Swipe ...
随机推荐
- 自定义NSOperation下载图片
自定义NSOperation的话,只是需要将要下载图片的操作下载它的main方法里面,考虑到,图片下载完毕,需要回传到控制器里,这里可以采用block,也可以采用代理的方式实现,我采用的是代理的方式实 ...
- TCP、UDP数据包大小的限制
版权声明:本文为灿哥哥http://blog.csdn.net/caoshangpa 原创文章,转载请标明出处. https://blog.csdn.net/caoshangpa/article/de ...
- zmap blacklist
# From IANA IPv4 Special-Purpose Address Registry# http://www.iana.org/assignments/iana-ipv4-special ...
- 【转】Lombok:让JAVA代码更优雅
原文地址:http://blog.didispace.com/java-lombok-1/ 关于Lombok,其实在网上可以找到很多如何使用的文章,但是很少能找到比较齐全的整理.我也一直寻思着想写一篇 ...
- ElasticSearch的安装、使用、踩坑
最近博客写的少了. 本篇介绍在安装ElasticSearch和head插件的过程中遇到的小问题,和一些日常使用的操作(简单搜索语法.分片管理). ElasticSearch 它是一个实时分布式搜索和分 ...
- mongo源码学习(四)invariant
前言 在看MongoDB源码的时候,经常会看到这个玩意儿:invariant. invariant的字面意思是:不变式. 在emacs上跳转到函数定义要安装一个插件,ggtags,费了老大劲儿.这都可 ...
- select 操作选中添加、删除操作Javascript
//添加选中项 function addItem() { var myMember = document.getElementById("myMember"); var other ...
- Python 创建线程的方法
使用的是 threading 模块 代码如下: 1 #!/usr/bin/python3 2 3 import threading 4 import time 5 6 exitFlag = 0 7 / ...
- docker原理
Docker原理11 Linux Namespace 11 AUFS文件系统17 重新理解Docker的各种命令18 Docker原理 Linux Namespace docker是一个容器引擎,容器 ...
- 编译JDK源代码【转】
用Eclipse Debug,当跟踪进jdk api里时(比如javax.swing包里的类),无法查看某些local filed的值.这是因为jdk里的代码在打包时删除了一些用于调试的信息,以减小安 ...