在介绍之前,先要了解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