Android中自定义可以下拉刷新的ListView(附demo)
2013-08-27 11:23
393 查看
下拉刷新的ListView在很多App中都很常见,以下为我对实现方法的一些分析:
1. ListView需要有一个header,用来显示刷新提示。header中有一个指示箭头,一个使箭头旋转的动画,一个刷新时显示的progressbar,提示“下拉刷新/松开刷新”的TextView,提示上次刷新时间的TextView
2. header有4种状态:
DONE(header隐藏在顶部)
PULL_TO_REFRESH(header正在下拉,箭头向下,progressbar隐藏,提示“下拉刷新”)
RELEASE_TO_REFRESH(header正在下拉,箭头向上,progressbar隐藏,提示“松开刷新”)
REFRESHING(手指已经松开,header位于顶部,箭头隐藏,progressbar旋转,提示“正在刷新”)
下面分析代码:
自定义ListView,实现OnscrollListener接口,目的是实时得到位于ListView顶部的是第几行。
通过init进行初始化,注册OnscrollListener、得到header中所有View、初始化动画、测量header、获得header的高度、将header隐藏、设置ListView的header、设置默认状态,根据状态确定headerView的显示内容。其中非常重要的是measureView和setPadding,前者用来测量header的长宽,后者用来设置header的显示效果(隐藏/部分隐藏)。
对headerView进行测量,此方法与ListView源码中添加每一个Item时候所调用的相同,具体功能就是测量长宽,在调用此方法之前headerView的长宽都是0(其实代码不是很看得懂。。。)。要求headerView必须是LinearLayout,否则会出错(我本来使用的RelativeLayout,出错了,在最外层又加了一个LinearLayout)
两种实现动画的方法,一种是直接通过代码,一种是在res/anim文件夹下创建arrow_rotate文件
根据不同的state,设置header的显示状态。
复写onTouchEvent函数,对触摸事件进行监听,在这个函数中处理下拉刷新的手势。下面是此函数中的内容:
首先说明几个变量:
以下为具体代码:
当设置了OnRefreshListener之后,根据不同的Touch Action进行处理。
// 如果ListView正好在顶部而且y值没有被记录过
startY = tmpY;
isRecored = true;
}
// if has started to drag, and state is release_to_refresh
if (state == RELEASE_TO_REFRESH && (tmpY - startY) / RATIO < headerHeight) {
if ((tmpY - startY) > 0) {
// if state is to change to pull_to_refresh
state = PULL_TO_REFRESH;
changeHeaderViewbyState();
} else if ((tmpY - startY) <= 0) {
// if state is to change to done
state = DONE;
changeHeaderViewbyState();
}
}
// if has started to drag, but state is pull_to_refresh
// and state is to change to release_to_refresh
if (state == PULL_TO_REFRESH && (tmpY - startY) / RATIO > headerHeight) {
state = RELEASE_TO_REFRESH;
changeHeaderViewbyState();
}
// if has not started to drag
if (state == DONE && tmpY - startY > 0) {
state = PULL_TO_REFRESH;
changeHeaderViewbyState();
}
// change the padding
if (state == PULL_TO_REFRESH || state == RELEASE_TO_REFRESH) {
headerView.setPadding(0, -headerHeight + (tmpY - startY) / RATIO, 0, 0);
}
break;case MotionEvent.ACTION_UP:
if (state == PULL_TO_REFRESH) {
state = DONE;
changeHeaderViewbyState();
} else if (state == RELEASE_TO_REFRESH) {
state = REFRESHING;
changeHeaderViewbyState();
new RefreshAsync().execute();
}
isRecored = false;
break;
如果当松开的时候state是RELEASE_TO_REFRESH,则异步进行刷新。
刷新操作是通过调用OnRefreshListener的onRefresh方式实现的。OnRefreshListenerListener是自定义的一个接口:
Activity通过实现这个接口来自定义刷新时进行的具体操作:
Demo下载链接:http://download.csdn.net/detail/u011268102/6018363
1. ListView需要有一个header,用来显示刷新提示。header中有一个指示箭头,一个使箭头旋转的动画,一个刷新时显示的progressbar,提示“下拉刷新/松开刷新”的TextView,提示上次刷新时间的TextView
2. header有4种状态:
DONE(header隐藏在顶部)
PULL_TO_REFRESH(header正在下拉,箭头向下,progressbar隐藏,提示“下拉刷新”)
RELEASE_TO_REFRESH(header正在下拉,箭头向上,progressbar隐藏,提示“松开刷新”)
REFRESHING(手指已经松开,header位于顶部,箭头隐藏,progressbar旋转,提示“正在刷新”)
下面分析代码:
public class MyListView extends ListView implements OnScrollListener {
@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub firstVisibleIndex = firstVisibleItem; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub }
自定义ListView,实现OnscrollListener接口,目的是实时得到位于ListView顶部的是第几行。
// get all the view instances in the header private void init(Context context) { setOnScrollListener(this); LayoutInflater inflater = LayoutInflater.from(context); headerView = inflater.inflate(R.layout.header, null); arrowImageView = (ImageView) headerView.findViewById(R.id.img_arrow); refreshPB = (ProgressBar) headerView.findViewById(R.id.progress_refresh); tipsTextView = (TextView) headerView.findViewById(R.id.text_tips); timeTextView = (TextView) headerView.findViewById(R.id.text_last_refresh_time); timeTextView.setText(getTime()); // arrowRotateAnim = createAnimation(); arrowRotateAnim = AnimationUtils.loadAnimation(context, R.anim.arrow_rotate); // measure the width&height of the headerView, before this the width&height equals 0 measureView(headerView); headerHeight = headerView.getMeasuredHeight(); // hide the headerView on the top headerView.setPadding(0, -headerHeight, 0, 0); // force redraw the view headerView.invalidate(); addHeaderView(headerView); state = DONE; changeHeaderViewbyState(); }
通过init进行初始化,注册OnscrollListener、得到header中所有View、初始化动画、测量header、获得header的高度、将header隐藏、设置ListView的header、设置默认状态,根据状态确定headerView的显示内容。其中非常重要的是measureView和setPadding,前者用来测量header的长宽,后者用来设置header的显示效果(隐藏/部分隐藏)。
private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); }
对headerView进行测量,此方法与ListView源码中添加每一个Item时候所调用的相同,具体功能就是测量长宽,在调用此方法之前headerView的长宽都是0(其实代码不是很看得懂。。。)。要求headerView必须是LinearLayout,否则会出错(我本来使用的RelativeLayout,出错了,在最外层又加了一个LinearLayout)
private Animation createAnimation() { // TODO Auto-generated method stub Animation animation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); animation.setInterpolator(new LinearInterpolator()); animation.setDuration(250); animation.setFillAfter(true); return animation; }
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true" > <rotate android:fromDegrees="0" android:toDegrees="180" android:pivotX="50%" android:pivotY="50%" android:duration="500" /> </set>
两种实现动画的方法,一种是直接通过代码,一种是在res/anim文件夹下创建arrow_rotate文件
// prepare different header according to the state private void changeHeaderViewbyState() { // TODO Auto-generated method stub switch (state) { case DONE: case PULL_TO_REFRESH: arrowImageView.setVisibility(View.VISIBLE); refreshPB.setVisibility(View.GONE); tipsTextView.setText("下拉刷新"); headerView.setPadding(0, -headerHeight, 0, 0); break; case RELEASE_TO_REFRESH: tipsTextView.setText("松开刷新"); // arrowImageView.setAnimation(arrowRotateAnim); arrowImageView.clearAnimation(); arrowImageView.startAnimation(arrowRotateAnim); break; case REFRESHING: arrowImageView.clearAnimation(); arrowImageView.setVisibility(View.GONE); refreshPB.setVisibility(View.VISIBLE); tipsTextView.setText("正在刷新"); timeTextView.setText(getTime()); headerView.setPadding(0, 0, 0, 0); break; default: break; } }
根据不同的state,设置header的显示状态。
@Override public boolean onTouchEvent(MotionEvent ev) {
复写onTouchEvent函数,对触摸事件进行监听,在这个函数中处理下拉刷新的手势。下面是此函数中的内容:
首先说明几个变量:
private boolean refreshable = false; // 用于确定是否对Touch事件进行分析和响应。只有当设置了OnRefreshListener时才会被置为true
private int startY = 0;// 当ListView位于顶部时手指触摸位置的Y值
private boolean isRecored;// 第一次记录下startY后置为true,当手指松开是置为false。用于确保一个拖拽过程中只记录一次startY
以下为具体代码:
if (refreshable) { switch (ev.getAction()) {
当设置了OnRefreshListener之后,根据不同的Touch Action进行处理。
case MotionEvent.ACTION_DOWN: if (firstVisibleIndex == 0 && !isRecored) { // 如果ListView正好在顶部而且y值没有被记录过 startY = (int) ev.getY(); isRecored = true; } break;
case MotionEvent.ACTION_MOVE: int tmpY = (int) ev.getY();if (firstVisibleIndex == 0 && !isRecored) {
// 如果ListView正好在顶部而且y值没有被记录过
startY = tmpY;
isRecored = true;
}
// if has started to drag, and state is release_to_refresh
if (state == RELEASE_TO_REFRESH && (tmpY - startY) / RATIO < headerHeight) {
if ((tmpY - startY) > 0) {
// if state is to change to pull_to_refresh
state = PULL_TO_REFRESH;
changeHeaderViewbyState();
} else if ((tmpY - startY) <= 0) {
// if state is to change to done
state = DONE;
changeHeaderViewbyState();
}
}
// if has started to drag, but state is pull_to_refresh
// and state is to change to release_to_refresh
if (state == PULL_TO_REFRESH && (tmpY - startY) / RATIO > headerHeight) {
state = RELEASE_TO_REFRESH;
changeHeaderViewbyState();
}
// if has not started to drag
if (state == DONE && tmpY - startY > 0) {
state = PULL_TO_REFRESH;
changeHeaderViewbyState();
}
// change the padding
if (state == PULL_TO_REFRESH || state == RELEASE_TO_REFRESH) {
headerView.setPadding(0, -headerHeight + (tmpY - startY) / RATIO, 0, 0);
}
break;case MotionEvent.ACTION_UP:
if (state == PULL_TO_REFRESH) {
state = DONE;
changeHeaderViewbyState();
} else if (state == RELEASE_TO_REFRESH) {
state = REFRESHING;
changeHeaderViewbyState();
new RefreshAsync().execute();
}
isRecored = false;
break;
如果当松开的时候state是RELEASE_TO_REFRESH,则异步进行刷新。
private class RefreshAsync extends AsyncTask<Void, Void, Boolean> { @Override protected Boolean doInBackground(Void... params) { // TODO Auto-generated method stub boolean result = false; if (refreshListener != null) { result = refreshListener.onRefresh(); } return result; } @Override protected void onPostExecute(Boolean result) { // TODO Auto-generated method stub state = DONE; changeHeaderViewbyState(); if (result) { Toast.makeText(getContext(), "refresh success", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getContext(), "refresh fail", Toast.LENGTH_SHORT).show(); } } }
刷新操作是通过调用OnRefreshListener的onRefresh方式实现的。OnRefreshListenerListener是自定义的一个接口:
public interface OnRefreshListener { public boolean onRefresh(); }
Activity通过实现这个接口来自定义刷新时进行的具体操作:
public class MainActivity extends Activity implements OnRefreshListener {
@Override public boolean onRefresh() { // TODO Auto-generated method stub try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return true; }
Demo下载链接:http://download.csdn.net/detail/u011268102/6018363
相关文章推荐
- 自定义可以下拉刷新的listview
- android 自定义listview——实现上拉刷新下拉加载的功能
- Android 自定义ListView实现底部分页刷新与顶部下拉刷新,androidlistview
- Android自定义View之快速实现下拉刷新, 点击加载更多ListView
- Android PullToRrefresh 自定义下拉刷新动画 (listview、scrollview等)
- android 安卓自定义listview实现下拉刷新
- Android 自定义下拉刷新ExpandableListView
- 【Android - 自定义View】之自定义可下拉刷新或上拉加载的ListView
- Android自定义下拉刷新功能的ListView
- Android 自定义ListView实现底部分页刷新与顶部下拉刷新 .
- Android自定义渐变式炫酷ListView下拉刷新动画
- Android 自定义ListView实现底部分页刷新与顶部下拉刷新,androidlistview
- Android PullToRrefresh 自定义下拉刷新动画 (listview、scrollview等)
- Android自定义上拉加载下拉刷新PullToRefreshListView
- Android自定义ListView(一) - 可下拉刷新的ListView
- Android自定义listview布局实现上拉加载下拉刷新功能
- Android——Xlistview上拉刷新下拉加载
- Android 横向带有吸附效果的横向拖动控件(效果同纵向下拉刷新ListView)
- Android listview、ScrollView等布局下拉加载和上拉刷新
- Android中解决ScrollView下拉阴影的问题(ListView也应该可以的,没有亲测)