ListView下拉刷新
来源:互联网 发布:西安软件开发外包公司 编辑:程序博客网 时间:2024/06/07 14:36
不知为何,一直对ListView的下拉刷新情有独钟。或许是在项目开发过程中,使用的频率太多了吧。ListView下拉刷新,和自动加载更多,对于大多数人来说,是一个头疼的问题。很多时候,我们都选择使用框架去实现下拉刷新,加载更多,无奈,很多框架很难修改成自己所需要的效果。如果这时候,自己有一个自己的下拉刷新框架,该有多好!我也曾使用过很多的下拉刷新框架,经过自己对这些框架的使用经历,我发现该如何做一个下拉刷新框架才是最好的。这里,我将介绍如何去简单的定义出一个流畅的下拉刷新ListView。
先预览demo的效果
在介绍ListView编写之前,有一些知识是需要先了解的。
1.我们将使用LinearLayout里面嵌套ListView组合来实现。因此需要熟悉事件分发机制。这里,我们通过重写LinearLayout的 onInterceptTouchEvent 方法,来进行事件的拦截。如果当前LinearLayout需要事件,来进行下拉刷新,则onInterceptTouchEvent返回true;如果当前不需要下拉刷新,则返回false,事件交给ListView处理,此时ListView就可以滑动了。
2.在LinearLayout中,调用scrollTo()方法,对ListView进行滑动。
3.弹性滑动,ListView下拉刷新后,需要弹性回复,使用Scroller来完成该功能。
4.我将使用mScrollY来进行下拉刷新的判断依据。mScollY初始值为0,随着调用scrollTo()下拉,将会逐渐变小。
所以,要完成ListView的下拉刷新,需要掌握android事件分发机制,View的scrollTo()方法,Scroller弹性滑动。
我将通过代码的方式,使用注释来说明:
package com.mjc.pulltorefreshdemo.refresh;import android.content.Context;import android.graphics.Color;import android.graphics.drawable.ColorDrawable;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.ViewTreeObserver;import android.widget.AbsListView;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.Scroller;import static android.view.GestureDetector.*;/** * Created by mjc on 2015/12/26. */public class PullToRefreshViewListView extends LinearLayout implements AbsListView.OnScrollListener { private static final String TAG = "PullToRefreshViewGroup"; //暂时未用到 private IFooterView mFooterView; //自定义的一个布局,包含了刷新时,下拉时动画等方法。 private IHeaderView mHeaderView; //HeaderView的高度 private int mHeaderViewHeight; private ListView mListView; //当前listview是否允许下拉 private boolean isPullDownEnable = false; //按下时的坐标 private float downY = 0; //阻尼系数 private float rate = 0.4f; //弹性滑动需要使用Scroller private Scroller mScroller; //向下滑动,mScrollY变小;滑动临界值 private int mMaxScrollHeight; //表示是否正在刷新 private boolean isRefreshing = false; //记录当前的一个mScrollY的值,做判断 private int mScrollTop; private int mTouchSlop; public PullToRefreshViewListView(Context context) { super(context); init(context); } public PullToRefreshViewListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { this.setOrientation(VERTICAL); mListView = new ListView(context); LayoutParams listParams = new LayoutParams(-1, -1); mListView.setLayoutParams(listParams); mListView.setOnScrollListener(this); addView(mListView); //添加所有的子View mHeaderView = new CustomHeaderView(context); LayoutParams params = new LayoutParams(-1, -2); mHeaderView.setLayoutParams(params); addView(mHeaderView, 0); //getViewTreeObserver()来获取我们的headerView的高度 getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //获取各个视图的高度 mHeaderViewHeight = mHeaderView.getHeight(); mHeaderView.getViewTreeObserver() .removeGlobalOnLayoutListener(this); //隐藏headerView HideHeaderView(-mHeaderViewHeight); //初始化我们需要的当前滑动距离和滑动临界点 mScrollTop = 0; mMaxScrollHeight = mHeaderViewHeight * 2; } }); mScroller = new Scroller(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } /** * 我们需要隐藏HeaderView * * @param margin */ private void HideHeaderView(int margin) { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); int topMargin = params.topMargin; int newMargin = topMargin + margin; params.topMargin = newMargin; mHeaderView.requestLayout(); } public View getRefreshView() { return mListView; } /** * 用来确定是否允许事件传递给ListView * * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //getScrollY()<0,表示当前正在下拉刷新的过程中,事件需要拦截,由LinearLayout处理 boolean isHandled = false; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //ACTION_DOWN触发时,无法判断是否拦截事件 downY = ev.getY(); isHandled = false; break; case MotionEvent.ACTION_MOVE: float dY = ev.getY() - downY; //isPullDownEnable在ListView的onScrollChange方法中,监听,达到了ListView的最顶端,允许拉下 if (isPullDownEnable && dY >= mTouchSlop) { isHandled = true; } if(dY<mTouchSlop){ isHandled =false; } } return isHandled; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = event.getY(); break; case MotionEvent.ACTION_MOVE: float moveY = event.getY(); float delta = (moveY - downY); //获取需要滑动到的位置,mScrollTop为滑动前的位置,其值等于getScrollY()。 delta = mScrollTop - rate * delta; /** * della为滑动的目标位置。可以取值范围为, -mHeaderViewHeight - mMaxScrollHeight到0 * 这里,0到-mHeaderViewHeight表示下拉刷新状态 * -mHeaderViewHeight到 -mHeaderViewHeight - mMaxScrollHeight,表示松开刷新状态 * 因为mScrollY向正方向滑动时,是负值,逐渐变小的过程 * 表示为:0 ----下拉刷新状态---- (-mHeaderViewHeight) ----松开刷新--- (-mHeaderViewHeight - mMaxScrollHeight) */ //如果目标位置大于0,则让它等于0 if (delta >= 0) { delta = 0; } if (delta <= -mHeaderViewHeight - mMaxScrollHeight) { delta = -mHeaderViewHeight - mMaxScrollHeight; } //滑动到对应位置 scrollTo(0, (int) delta); //根据位置更新状态 resetRefreshState((int) delta); return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: //记录下次要布局的位置 mScrollTop = getScrollY(); //根据位置处理结果 doReslut(mScrollTop); break; } return true; } /** * 处理松开后的结果 * * @param mScrollTop -mHeaderViewHeight - mMaxScrollHeight -松开刷新->-mHeaderViewHeight-下拉刷新->0 */ private void doReslut(int mScrollTop) { if (mScrollTop < 0 && mScrollTop > -mHeaderViewHeight) { //当前为下拉刷新状态,弹性回复到初始状态 //todo 弹性回复到正常状态 if (!isRefreshing) mHeaderView.onNormal(); smoothScrollTo(0); } else if (mScrollTop >= -mHeaderViewHeight - mMaxScrollHeight && mScrollTop <= -mHeaderViewHeight) { //todo 弹性滑动到-mHeaderViewHeight位置,然后开始刷新 smoothScrollTo(-mHeaderViewHeight); //正在刷新 mHeaderView.onRefreshing(); if (isRefreshing) { //如果当前正在刷新,无作为 } else { //如果当前没有刷新,刷新 isRefreshing = true; if (mRefresh != null) mRefresh.onPullDownRefresh(); } } } /** * @param delta 值为 -mHeaderViewHeight - mMaxScrollHeight -松开刷新->-mHeaderViewHeight-下拉刷新->0 */ private void resetRefreshState(float delta) { if (isRefreshing) { return; } if (delta > -mHeaderViewHeight && delta <= 0) { //下拉刷新状态 mHeaderView.onPullToRefresh((int) -delta); Log.v(TAG, "下拉刷新"); } else if (delta >= -mHeaderViewHeight - mMaxScrollHeight && delta <= -mHeaderViewHeight) { //松开刷新 mHeaderView.onReleaseToRefresh((int) -delta); Log.v(TAG, "松开刷新"); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //只有当第一个显示,并且getTop==0,才可以下拉 if (mListView.getChildAt(0) == null) { isPullDownEnable = firstVisibleItem == 0; } else { //如果当前第一个可见的为0并且第一个item的gettop>=0 isPullDownEnable = firstVisibleItem == 0 && mListView.getChildAt(0).getTop() >= 0; } } private IRefresh mRefresh; public void setOnRefreshListener(IRefresh mRefresh) { this.mRefresh = mRefresh; } public void onPullDownRefreshComplete() { isRefreshing = false; //todo 弹性从 -mHeaderViewHeight回复到0 smoothScrollTo(0); } /** * 使用Scroller弹性滑动到指定位置 * * @param scrollValue */ private void smoothScrollTo(int scrollValue) { mScroller.startScroll(0, mScrollTop, 0, scrollValue - mScrollTop, 500); //主动要求重绘视图 invalidate(); } /** * Scroller需要配合重写这个方法才能实现弹性滑动 */ @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { Log.e(TAG, "" + mScroller.getCurrY()); scrollTo(0, mScroller.getCurrY()); mScrollTop = mScroller.getCurrY(); postInvalidate(); } }}
整个下拉刷新的流程,其实很简单。当ListView滑动到最顶端,通过判断是否向下滑动,来判断是否拦截事件,如果拦截事件,当前Linearlayout处理滑动;处理滑动,通过监听手指滑动的距离,使用scrollTo()来滑动Linearlayout的内容;并使用Scroller来进行弹性回复。在headerView中,我们可以自定义各种各样的动画,大家可以进行尝试。
最后,我会将代码打包,,上传到csdn。有什么错误,希望大家指正。
源码下载地址:点击打开链接
- ListView下拉回弹刷新
- ListView下拉刷新
- listView下拉刷新2
- ListView 下拉刷新错误
- Android ListView下拉刷新
- listview下拉刷新
- listview实现下拉刷新
- ListView下拉刷新
- android Listview下拉刷新
- ListView下拉回弹刷新
- Android ListView下拉刷新
- 自定义ListView,下拉刷新
- listview 下拉刷新
- ListView下拉刷新
- ListView下拉刷新
- ListView实现下拉刷新
- ListView下拉回弹刷新
- Listview的下拉刷新
- org.springframework.beans.ConversionNotSupportedException
- java中continue,return,break的区别
- 浅谈android网络编程
- Android APP第一次使用引导界面的制作
- 用JAVA实现大文件上传及显示进度信息
- ListView下拉刷新
- 创建对于用户失败 在当前数据库存已存在
- iOS开发 学习计划图
- C语言中getchar()、gets()和scanf()的特点以及scanf产生的多余回车符问题
- 算法之美_源代码发布(3)
- 求字符串组合
- C语言字符的赋值与输出格式
- Java内存泄露
- Java集合部分