一:概述
世面上下拉刷新简单是数不胜数了,可以说是“百度一下,你就知道”。
下面我将实现使用 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();
}
}