ViewDragHelper打造简单通用的PullToRefresh

一:概述


世面上下拉刷新简单是数不胜数了,可以说是“百度一下,你就知道”。


下面我将实现使用 ViewDragHelper实现下拉刷新

传统的方式莫过于重写onInterceptTouchEvent

onTouchEvent这两个方法实现事件的分发与重新功能。要写好也是挺费力的。下面我们就来使用ViewDragHelper来实现该功能,省去处理各种事件的繁琐。

1、首先需要自定义View 我们去继承了LinearLayout


public class PullToRefreshLayout extends LinearLayout {

}
2、创建ViewDragHelper 

mDragger=ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
}
3、重写  onInterceptTouchEvent  onTouchEvent


	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		return mDragger.shouldInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		mDragger.processTouchEvent(event);
		return true;
	}

4、处理触摸事件

			/**
	         * 进行捕获拦截,那些View可以进行drag操作
	         * @param <span style="white-space: nowrap; font-family: "Source Code Pro", monospace;">childView</span>

	         * @param <span style="white-space: nowrap; font-family: "Source Code Pro", monospace;">position</span>

	         * @return  直接返回true,拦截所有的VIEW
	         */
			@Override
			public boolean tryCaptureView(View childView, int position) {
				Log.i(TAG, "tryCaptureView");
				isTouch=true;
				return childView==pullLayout && isTop() ;
			}
			
			@Override
			public int clampViewPositionVertical(View child, int top, int dy) {
				Log.i(TAG, "clampViewPositionVertical: top="+top+"dy="+dy+"hideHeaderHeight="+hideHeaderHeight+"maxDragHeight="+maxDragHeight);
				if(top<=hideHeaderHeight){
					return hideHeaderHeight;
				}else if(top>maxDragHeight){
					return maxDragHeight;
				}
				return top;
			}
			 @Override
			public void onViewReleased(View releasedChild, float xvel,	float yvel) {
				 Log.i(TAG, "onViewReleased");
				super.onViewReleased(releasedChild, xvel, yvel);
				if (releasedChild == pullLayout){
					isTouch=false;
					if (currentStatus == STATUS_RELEASE_TO_REFRESH ) {
						// 松手时如果是释放立即刷新状态,就去调用正在刷新的任务
						currentStatus = STATUS_REFRESHING;
						updateHeadView();
	                    mDragger.settleCapturedViewAt(0,0);
	                    invalidate();
	                    if(pullToRefreshListener!=null){
	                    	pullToRefreshListener.onRefresh();
	                    }
					}else if(currentStatus == STATUS_PULL_TO_REFRESH ){
	                    mDragger.settleCapturedViewAt(0,hideHeaderHeight);
	                    invalidate();
					}else if(currentStatus == STATUS_REFRESHING){
						mDragger.settleCapturedViewAt(0,0);
	                    invalidate();
					}

                }
			}
			 
			 @Override
			public void onViewPositionChanged(View changedView, int left,
					int top, int dx, int dy) {
				super.onViewPositionChanged(changedView, left, top, dx, dy);
				Log.i(TAG, "onViewPositionChanged: top="+top);
				currentTop=top;
				if (isTouch && currentStatus != STATUS_REFRESHING){
					if (currentTop <0) {
						currentStatus = STATUS_PULL_TO_REFRESH;
					} else {
						currentStatus = STATUS_RELEASE_TO_REFRESH;
					}
					updateHeadView();
					lastStatus=currentStatus;
				}
			}
			 @Override
			public int getViewVerticalDragRange(View child) {
				 return maxDragHeight;
			}
			
		

5、方法解释

tryCaptureView: tryCaptureView如何返回ture则表示可以捕获该view,你可以根据传入的第一个view参数决定哪些可以捕获

clampViewPositionVertical:  可以在该方法中对child移动的边界进行控制,left , top 分别为即将移动到的位置

onViewReleased:手指释放的时候回调

onViewPositionChanged:当captureview的位置发生改变时回调

getViewVerticalDragRange:如果子view 不消费事件,可以不用重写些方法,如果消费事件,则需要重写该方法,只有这个方法返回大于0的值才能正常的捕获


好了,就是这么简单。下面贴上全部代码,如果有问题,及时指正哦。


package com.example.pulltorefreshlistviewdemo;

import android.content.Context;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

public class PullToRefreshLayout extends LinearLayout {
	private final String TAG="PullToRefreshLayout";
	/**
	 * 下拉状态
	 */
	public static final int STATUS_PULL_TO_REFRESH = 0;

	/**
	 * 释放立即刷新状态
	 */
	public static final int STATUS_RELEASE_TO_REFRESH = 1;

	/**
	 * 正在刷新状态
	 */
	public static final int STATUS_REFRESHING = 2;

	/**
	 * 刷新完成或未刷新状态
	 */
	public static final int STATUS_REFRESH_FINISHED = 3;
	

	/**
	 * 下拉头的View
	 */
	private View header;

	/**
	 * 刷新时显示的进度条
	 */
	private ProgressBar progressBar;

	/**
	 * 指示下拉和释放的文字描述
	 */
	private TextView description;

	/**
	 * 下拉头的布局参数
	 */
	private MarginLayoutParams headerLayoutParams;

	/**
	 * 当前处理什么状态,可选值有STATUS_PULL_TO_REFRESH, STATUS_RELEASE_TO_REFRESH,
	 * STATUS_REFRESHING 和 STATUS_REFRESH_FINISHED
	 */
	private int currentStatus = STATUS_REFRESH_FINISHED;;

	
	/**
	 * 记录上一次的状态是什么,避免进行重复操作
	 */
	private int lastStatus = currentStatus;

	/**
	 * 用于处理事件分发的mDragger 
	 */
	private ViewDragHelper mDragger;

	/**
	 * 需要去下拉刷新的ListView
	 */
	private AbsListView listView;
	
	private LinearLayout pullLayout;
	/**
	 * 下拉头的高度
	 */
	private int hideHeaderHeight;
	
	/**
	 * 当前滑动的距离 
	 */
	private int currentTop;
	
	/**
	 * 允许滑动的最大距
	 */
	private int maxDragHeight;
	/**
	 * 是否加载过
	 */
	private boolean loadOnce=false;
	
	private boolean isTouch=false;
	
	private PullToRefreshListener pullToRefreshListener;
	
	public PullToRefreshLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if(changed && !loadOnce){
			maxDragHeight=getMeasuredHeight()/2;
			hideHeaderHeight = -header.getHeight();
			headerLayoutParams = (MarginLayoutParams) pullLayout.getLayoutParams();
			headerLayoutParams.topMargin = hideHeaderHeight;
			loadOnce = true;
		}
	}
	
	@Override
	public void computeScroll() {
		super.computeScroll();
		if(mDragger.continueSettling(true)){
            invalidate();
        }
	}
	
	/**
	 * 设置刷新回调接口
	 * @param listener
	 */
	public void setPullToRefreshistener(PullToRefreshListener listener){
		this.pullToRefreshListener=listener;
	}
	
	/**
	 * 完成刷新
	 */
	public void finishPullToRefresh(){
		currentStatus=STATUS_REFRESH_FINISHED;
		mDragger.smoothSlideViewTo(pullLayout, 0, hideHeaderHeight);
        invalidate();
	}
	
	/**
	 * 初始化view 
	 */
	private void init(){
		pullLayout=(LinearLayout) getChildAt(0);
		listView = (AbsListView) pullLayout.getChildAt(1);
		header = pullLayout.getChildAt(0);
		progressBar = (ProgressBar) header.findViewById(R.id.progress_bar);
		description = (TextView) header.findViewById(R.id.description);
		
		mDragger=ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
			/**
	         * 进行捕获拦截,那些View可以进行drag操作
	         * @param child
	         * @param pointerId
	         * @return  直接返回true,拦截所有的VIEW
	         */
			@Override
			public boolean tryCaptureView(View childView, int position) {
				Log.i(TAG, "tryCaptureView");
				isTouch=true;
				return childView==pullLayout && isTop() ;
			}
			
			@Override
			public int clampViewPositionVertical(View child, int top, int dy) {
				Log.i(TAG, "clampViewPositionVertical: top="+top+"dy="+dy+"hideHeaderHeight="+hideHeaderHeight+"maxDragHeight="+maxDragHeight);
				if(top<=hideHeaderHeight){
					return hideHeaderHeight;
				}else if(top>maxDragHeight){
					return maxDragHeight;
				}
				return top;
			}
			 @Override
			public void onViewReleased(View releasedChild, float xvel,	float yvel) {
				 Log.i(TAG, "onViewReleased");
				super.onViewReleased(releasedChild, xvel, yvel);
				if (releasedChild == pullLayout){
					isTouch=false;
					if (currentStatus == STATUS_RELEASE_TO_REFRESH ) {
						// 松手时如果是释放立即刷新状态,就去调用正在刷新的任务
						currentStatus = STATUS_REFRESHING;
						updateHeadView();
	                    mDragger.settleCapturedViewAt(0,0);
	                    invalidate();
	                    if(pullToRefreshListener!=null){
	                    	pullToRefreshListener.onRefresh();
	                    }
					}else if(currentStatus == STATUS_PULL_TO_REFRESH ){
	                    mDragger.settleCapturedViewAt(0,hideHeaderHeight);
	                    invalidate();
					}else if(currentStatus == STATUS_REFRESHING){
						mDragger.settleCapturedViewAt(0,0);
	                    invalidate();
					}

                }
			}
			 
			 @Override
			public void onViewPositionChanged(View changedView, int left,
					int top, int dx, int dy) {
				super.onViewPositionChanged(changedView, left, top, dx, dy);
				Log.i(TAG, "onViewPositionChanged: top="+top);
				currentTop=top;
				if (isTouch && currentStatus != STATUS_REFRESHING){
					if (currentTop <0) {
						currentStatus = STATUS_PULL_TO_REFRESH;
					} else {
						currentStatus = STATUS_RELEASE_TO_REFRESH;
					}
					updateHeadView();
					lastStatus=currentStatus;
				}
			}
			 @Override
			public int getViewVerticalDragRange(View child) {
				 return maxDragHeight;
			}
			
		});
		
	}
	
	private void updateHeadView(){
		if(lastStatus!=currentStatus){
			if (currentStatus == STATUS_PULL_TO_REFRESH) {
				description.setText(getResources().getString(R.string.pull_to_refresh));
				progressBar.setVisibility(View.GONE);
			} else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
				description.setText(getResources().getString(R.string.release_to_refresh));
				progressBar.setVisibility(View.GONE);
			} else if (currentStatus == STATUS_REFRESHING) {
				description.setText(getResources().getString(R.string.refreshing));
				progressBar.setVisibility(View.VISIBLE);
			}
		}

	}
	private boolean isTop(){
		return listView.getFirstVisiblePosition() == 0
				&& getScrollY() <= listView.getMeasuredHeight();
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		return mDragger.shouldInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		mDragger.processTouchEvent(event);
		return true;
	}
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		init();
	}
	
	public interface PullToRefreshListener {
		/**
		 * 下拉刷新时调用
		 */
		void onRefresh();
		
		/**
		 * 上拉加载更多的刷新
		 */
//		void onLoadMoreRefresh();

	}

}

布局文件的:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <com.example.pulltorefreshlistviewdemo.PullToRefreshLayout
        android:id="@+id/refreshable_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >

            <include layout="@layout/pull_to_refresh" />

            <ListView
                android:id="@+id/list_view"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:scrollbars="none" >
            </ListView>
        </LinearLayout>
    </com.example.pulltorefreshlistviewdemo.PullToRefreshLayout>

</RelativeLayout>

pull_to_refresh

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/pull_to_refresh_head"
    android:layout_width="fill_parent"
    android:layout_height="45dp "
    android:layout_gravity="center_vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:orientation="horizontal" >

        <ProgressBar
            android:id="@+id/progress_bar"
            android:layout_width="30dip"
            android:layout_height="30dip"
            android:layout_marginRight="5dp"
            android:visibility="visible" />

        <TextView
            android:id="@+id/description"
            android:layout_width="100dp"
            android:layout_height="30dp"
            android:layout_gravity="center"
            android:layout_marginLeft="5dp"
            android:gravity="center"
            android:text="@string/pull_to_refresh" />
    </LinearLayout>

</RelativeLayout>

MainActivity

package com.example.pulltorefreshlistviewdemo;

import com.example.pulltorefreshlistviewdemo.PullToRefreshLayout.PullToRefreshListener;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.ArrayAdapter;
import android.widget.ListView;

/**
 * 
 * @author liuwang
 * @date 20160202
 */
public class MainActivity extends Activity implements PullToRefreshListener{

	private ListView mListView;
	ListView listView;
	ArrayAdapter<String> adapter;
	String[] items = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" ,"M","N","O"};
	
	private Handler handler=new Handler();
	private PullToRefreshLayout refreshLayout;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mListView = (ListView) findViewById(R.id.list_view);
		refreshLayout=(PullToRefreshLayout) findViewById(R.id.refreshable_view);
		adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
		mListView.setAdapter(adapter);
		
		refreshLayout.setPullToRefreshistener(this);
	}
	@Override
	public void onRefresh() {
		
		new Thread(){
			@Override
			public void run() {
				try {
					Thread.sleep(3000);
					handler.post(new Runnable() {
						@Override
						public void run() {
							refreshLayout.finishPullToRefresh();
						}
					});
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}.start();
		
		

	}
	
	
	
	

}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值