自定义控件:ListView下拉刷新和上拉加载

在介绍之前,先要了解paddingTop的作用,我们知道paddingTop>0,那么内容和View的上边是有一段内边距的;那么如果paddingTop<0,会出现什么情况呢?就是内容超出了上边框。

TextView  tv = findViewById(R.id.tv);
tv.setPadding(0, 0, 0, 0);

的情况是:

tv.setPadding(0, -50, 0, 0);

设为负值:

先实现下拉刷新

这里写图片描述
实现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()

上拉加载更多

这里写图片描述

添加footerView

initFooterView();
private void initFooterView() {
    footerView = View.inflate(getContext(), R.layout.footer_view, null);
    tvFooter = footerView.findViewById(R.id.tv_footer);

    footerView.measure(0, 0);
    footerViewHeight = footerView.getMeasuredHeight();
    footerView.setPadding(0, -footerViewHeight, 0, 0);

    addFooterView(footerView);
}

上拉加载更多就不在onTouchEvent()中逻辑处理了,当滑动状态改变时,处理上拉加载。
上拉加载有2个状态:上拉加载和加载中。
执行加载更多需要满足3个条件

  • 当scrollState是SCROLL_STATE_FLING || SCROLL_STATE_IDLE时
  • 最后一个可见的item的position为getCount() - 1,
  • 当前的状态时上拉刷新

实现 setOnScrollListener(this);

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (scrollState == SCROLL_STATE_FLING || scrollState == SCROLL_STATE_IDLE) {
        if (getLastVisiblePosition() == getCount() - 1 && currentStateFooter == LOAD_MORE) {
            currentStateFooter = LOAD_MORE_ING;
            footerView.setPadding(0, 0, 0, 0);
            setSelection(getCount());
            if (onLoadMoreListener != null) {
                onLoadMoreListener.loadMore();
            }
        }
    }
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

}

加载更多回调:

public interface OnLoadMoreListener {
    void loadMore();
}

private OnLoadMoreListener onLoadMoreListener;

public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
    this.onLoadMoreListener = onLoadMoreListener;
}

在activity中调用:

listView.setOnLoadMoreListener(new ListView2.OnLoadMoreListener() {
    @Override
    public void loadMore() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                dataList.add("footer");
                adapter.notifyDataSetChanged();
                listView.finishLoadMore();
            }
        },2000);
    }
});

结束加载更多:隐藏footerView+修改状态为上拉加载

public void finishLoadMore() {
    currentStateFooter = LOAD_MORE;
    footerView.setPadding(0, -footerViewHeight, 0, 0);
}

Demo

ListView2.java:下拉刷新+上拉加载
ListView3.java:下拉刷新+上拉加载+下拉刷新headerView中的动画
demo地址: https://gitee.com/customView/RefreshListView.git

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值