日志文章

2020-1-15 aflfte2011

实现ListView的下拉刷新

实现ListView的下拉刷新和上拉加载,需要先添加headerView和footerView,通过在拖动的过程中,控制头尾布局的paddingTop实现。先把paddingTop设为负值,来隐藏header,在下拉的过程中,不断改变headerView的paddingTop,实现下拉过程中headerView慢慢显示的效果。 
下拉刷新有3种状态: 
- 下拉刷新:headerView开始慢慢显示到完全显示出来 
- 释放刷新:headerView完全显示出来,但是你还在下拉,move没有停止 
- 刷新你中:headerView完全显示出来,手指离开屏幕 ACTION_UP

添加headerView
首先我们继承ListView,在构造方法中添加headerView

public class RefreshListView extends ListView implements AbsListView.OnScrollListener {


    public RefreshListView(Context context) {
        this(context, null);
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initHeaderView();
    }
}

添加headerView

private void initHeaderView() {
    headerView = View.inflate(getContext(), R.layout.header_view, null);
    tvStateHeader = headerView.findViewById(R.id.tv_state_header);

    headerView.measure(0, 0);
    headerViewHeight = headerView.getMeasuredHeight();
    headerView.setPadding(0, -headerViewHeight, 0, 0);

    addHeaderView(headerView);
}

move中进行状态判断
我们在move过程中实现下拉

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downY = (int) ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            moveY = (int) ev.getY();
            int instance = moveY - downY;
            if (instance > 0 ) {
                int paddingTop = -headerViewHeight + instance;
                headerView.setPadding(0, paddingTop, 0, 0);
                return true;
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
    return super.onTouchEvent(ev);
}

先要明白一点:在move过程中,根据下拉距离 设置不同的state ,up时再根据state执行相应的操作。

//设置3种下拉刷新状态
public final int DOWN_REFRESH = 1;
public final int RELEASE_REFRESH = 2;
public final int REFRESHING = 3;
private int currentState = DOWN_REFRESH;//当前状态是下拉刷新

我们知道MotionEvent有3种常见的action, ACTION_DOWN:按下;ACTION_MOVE:移动;ACTION_UP:手指抬起。 
在move过程中的对应2个状态:下拉刷新和释放刷新。当下拉距离达到headerView高度前是下拉刷新状态,超过此高度是释放刷新状态,当然条件是我们自己定的,也可以是下拉距离超过1.5倍headerViewHeight才是释放刷新。 
up过程中对应的2个状态:下拉刷新和释放刷新。当时下拉刷新状态时,up后headerView隐藏;当释放刷新状态时,up后进行刷新操作。

move过程中,进行刷新操作需满足3个条件: 
1. 是下拉不是上滑,即 moveY - downY必须大于0 
2. 当前页第一个可见的item的position==0 
3. 当前状态不是刷新中

代码如下:

case MotionEvent.ACTION_MOVE:
    moveY = (int) ev.getY();
    int instance = moveY - downY;
    if (instance > 0 && getFirstVisiblePosition() == 0 && currentStateHeader != REFRESHING) {
        int paddingTop = -headerViewHeight + instance;
        if (paddingTop < 0) {//&& currentStateHeader != RELEASE_REFRESH
            currentStateHeader = DOWN_REFRESH;
            tvStateHeader.setText("下拉刷新");
        } else if (paddingTop > 0) {//&& currentStateHeader == DOWN_REFRESH
            currentStateHeader = RELEASE_REFRESH;
            tvStateHeader.setText("释放刷新");
        }
        //对paddingTop进行限制,paddingTop<=3*headerViewHeight
        if (paddingTop > 3 * headerViewHeight) {
            headerView.setPadding(0, 3 * headerViewHeight, 0, 0);
        } else {
            headerView.setPadding(0, paddingTop, 0, 0);
        }
        return true;
    }
    break;

如果继续下拉的话,会一直滑到底部,所以上面对下滑距离进行了限制,最多为3个headerViewheight。

up中进行刷新操作
当手指移开屏幕后, 
若当前是下拉刷新状态,则隐藏headerView。 
若当前是释放刷新状态,则执行刷新操作。 
若当前是刷新中,则不做任何操作。

case MotionEvent.ACTION_UP:
    if (currentStateHeader == RELEASE_REFRESH) {
        //刷新中
        currentStateHeader = REFRESHING;
        headerView.setPadding(0, 0, 0, 0);
        tvStateHeader.setText("刷新中");
        if (onRefreshListener != null) {
            onRefreshListener.refresh();
        }
    } else if (currentStateHeader == DOWN_REFRESH) {
        headerView.setPadding(0, -headerViewHeight, 0, 0);
    }
    break;

刷新回调:

public interface OnRefreshListener {
    void refresh();
}

private OnRefreshListener onRefreshListener;

public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
    this.onRefreshListener = onRefreshListener;
}

在activity中使用回调

listView.setOnRefreshListener(new ListView2.OnRefreshListener() {
    @Override
    public void refresh() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                dataList.add(0,"header");
                adapter.notifyDataSetChanged();
                listView.finishRefresh();
            }
        },2000);
    }
});

上面的listView.finishRefresh();是结束刷新,需要隐藏headerView,并把当前状态设为下拉刷新。

public void finishRefresh() {
    headerView.setPadding(0, -headerViewHeight, 0, 0);
    currentStateHeader = DOWN_REFRESH;
}

下拉刷新中的动画处理
当结束刷新的时候记得清楚动画

view.clearAnimation()

« textView边框 | 实现ListView上拉加载»