通过继承系统提供的SwipeRefreshLayout ,实现下拉刷新和上拉加载:
- 允许设置加载时的最小子条目数
- 重置状态时的两种效果
- 允许ListView子条目左右滑动
使用时:
- 在xml文件中,使用SwipeRefreshLayout 包裹listView;
- 在activity或者fragment中,获取SwipeRefreshLayout 对象,设置刷新和加载更多监听,实现刷新和加载方法即可
- 设置允许上拉加载的最小子条目数,大于最小条目数则运行加载,否则不允许加载更多(默认10条)
- 设置是否允许子条目左右滑动 (默认不可以)
- 在重置状态时,通过控制添加的脚布局的setPadding(left,top,right,bottom);值来控制显示效果(默认改变top值)
改变top值,则会逐渐覆盖脚布局
改变bottom值,则会整体下移消失
注意:
如果在viewPager中嵌套MyRefreshLayout,MyRefreshLayout中嵌套ListView,ListView中的子条目需要左右滑动时, 没有在自定义的子View中实现从viewPager中获取左右滑动时的事件,暂时通过计算listView中子条目的高度方法来解决,找到解决方案再来替代 TODO
所以在内部对子条目的行为进行了判断,如果是可以左右滑动,则计算子条目的总高度,在listView范围内自己处理事件,否则交给系统处理
public class MyRefreshLayout extends SwipeRefreshLayout {
/**
* listview实例
*/
private ListView mListView;
/**
* 上拉监听器, 到了最底部的上拉加载操作
*/
private OnLoadListener mOnLoadListener;
/**
* ListView的加载中footer
*/
private View mListViewFooter;
/**
* 按下时的y坐标
*/
private int mYDown;
/**
* 抬起时的y坐标, 与mYDown一起用于滑动到底部时判断是上拉还是下拉
*/
private int mMoveY;
private ProgressBar loading_pb;
private TextView loading_txt;
// 能够上拉加载时,当前页最小条目数
private int loadCount = 10;
// 没有加载
private static final int NOLOAD = 2;
// 准备加载
private static final int ISPRELOAD = 3;
// 正在加载中
private static final int ISLOADING = 3;
// 当前加载状态
private int currentState = 2;
private int padding;
private int measuredHeight;
private int lastVisiblePosition;
private int i;
private float downX;
private float downY;
private int totalHeight;
private ListView listView;
private int headHeight;
private boolean isDrag;
/**
* @param context
*/
public MyRefreshLayout (Context context) {
this(context, null);
}
@SuppressLint("InflateParams")
public MyRefreshLayout (Context context, AttributeSet attrs) {
super(context, attrs);
addFootView(context);
}
private void addFootView(Context context) {
mListViewFooter = LayoutInflater.from(context).inflate(
R.layout.onloading, null, false);
mListViewFooter.measure(0, 0);
measuredHeight = mListViewFooter.getMeasuredHeight();
loading_pb = (ProgressBar) mListViewFooter.findViewById(R.id.loading_pb);
loading_txt = (TextView) mListViewFooter.findViewById(R.id.loading_txt);
resetState();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 初始化ListView对象
if (mListView == null) {
getListView();
mListView.addFooterView(mListViewFooter);
}
}
/**
* 获取ListView对象
*/
private void getListView() {
int childs = getChildCount();
if (childs > 1) {
View childView = getChildAt(1);
if (childView instanceof ListView) {
mListView = (ListView) childView;
}
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
lastVisiblePosition = mListView.getLastVisiblePosition();
i = mListView.getAdapter().getCount() - mListView.getFooterViewsCount() - 1;
// 如果添加的脚布局可见,则通过改变脚布局的padding值,实现上拉效果
if (lastVisiblePosition >= i) {
// 获取滑动到最底部时的按下坐标
if (mYDown == 0)
mYDown = (int) event.getY();
// 移动时坐标
mMoveY = (int) event.getY();
int disY = mMoveY - mYDown;
if (disY < 0) {
padding = padding - disY;
mListViewFooter.setPadding(0, 0, 0, padding);
if (!canLoad()) { //判断是否满足需要加载条件
mListViewFooter.setVisibility(GONE); //不满足加载更多的条件,则隐藏脚布局,只进行上拉操作
} else if (currentState == NOLOAD) // 能加载则进行状态切换
currentState = ISPRELOAD; //改变加载状态为准备加载
else if (currentState == ISLOADING)
isMove = true;// 设置正在加载时,向上拖动的标记
}
}
break;
case MotionEvent.ACTION_UP:
// 松手时,恢复状态
isMove = false;
mYDown = 0;
// 抬起
if (lastVisiblePosition > i) {
//当上拉时脚布局完全显示,则松手时开始执行加载操作
releaseAndLoad();
} else {
//重置状态
resetState();
}
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 在viewPager中嵌套MyRefreshLayout,RefreshLayout中嵌套ListView,ListView中的子条目需要左右滑动时, 子View无法从viewPager抢夺焦点,暂时通过计算listView中子条目的高度方法打一补丁,找到解决方案再来替代 TODO
if (isDrag) { //如果子条目可以拖动,则计算listView中子条目的总高度
if (totalHeight == 0) {
for (int i = 0; i < getChildCount(); i++) {
View childAt = getChildAt(i);
if (childAt instanceof ListView)
listView = (ListView) childAt;
}
if (listView.getAdapter() != null && listView.getChildAt(0) != null)
for (int i = 0; i < listView.getChildCount(); i++) {
View listItem = listView.getChildAt(i);
if (listItem != null) {
listItem.measure(0, 0);
// 统计所有子项的总高度
totalHeight += listItem.getMeasuredHeight();
if (i < listView.getHeaderViewsCount())
headHeight += totalHeight;
}
}
}
float y = ev.getY();
if (y > totalHeight || y < headHeight) { //如果触摸范围不在listView的子条目范围内则允许父控件打断事件
getParent().requestDisallowInterceptTouchEvent(false);
} else //如果触摸范围在listView的子条目范围内则将事件交给子条目处理
getParent().requestDisallowInterceptTouchEvent(true);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = ev.getX();
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float moveX = ev.getX();
float moveY = ev.getY();
float disX = moveX - downX;
float dixY = moveY - downY;
if (Math.abs(disX) > Math.abs(dixY)) { //如果是左右滑动,则将事件向下传递
return false;
}
downX = moveX;
downY = moveY;
break;
}
return super.onInterceptTouchEvent(ev);
}
/**
* 释放并加载
*/
private void releaseAndLoad() {
mListViewFooter.setPadding(0, 0, 0, 0); // 将脚布局位置置于底部
if (currentState == ISPRELOAD) { // 如果状态为准备加载,则开始加载
// 是否需要上拉加载
mOnLoadListener.onLoad();
currentState = ISLOADING; //改变加载状态
} else
resetState();
}
/**
* 加载完成
*
* @param isHasData 是否加载到数据
*/
public void setLoading(boolean isHasData) {
if (isHasData) { //如果加载到数据,则重置状态
resetState();
} else { //没有加载到数据,则显示没有更多数据,并做动画隐藏脚布局
loading_txt.setText("没有更多数据");
loading_pb.setVisibility(GONE);
doAnimator();
}
}
/**
* 是否正在滑动
*/
boolean isMove;
/**
* 缓慢隐藏脚布局
*/
private void doAnimator() {
if (isMove)
return;
ValueAnimator animator = ValueAnimator.ofInt(0, -measuredHeight);
animator.setDuration(500);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
//可以通过改变top或者bottom的值,来实现隐藏脚布局时的动画效果:覆盖隐藏获取下移消失
mListViewFooter.setPadding(0, value, 0, 0);
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
resetState();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
/**
* 重置显示状态
*/
private void resetState() {
mListViewFooter.setVisibility(VISIBLE);
loading_pb.setVisibility(VISIBLE);
loading_txt.setText("努力加载中..");
currentState = NOLOAD;
padding = -measuredHeight;
mListViewFooter.setPadding(0, 0, 0, padding);
}
/**
* 根据现有条目数决定是否需要加载更多
*
* @return
*/
private boolean canLoad() {
if (mListView != null && mOnLoadListener != null) {
int count = mListView.getAdapter().getCount();
if (count - mListView.getHeaderViewsCount() - mListView.getFooterViewsCount() >= loadCount) {
return true;
}
}
return false;
}
/**
* 子条目是否可以左右拖动
*
* @param isDrag
*/
public void setIsDrag(boolean isDrag) {
this.isDrag = isDrag;
}
/**
* 加载更多接口
*/
public interface OnLoadListener {
void onLoad();
}
/**
* 加载更多回调
*
* @param onLoadListener
*/
public void setOnLoadListener(OnLoadListener onLoadListener) {
mOnLoadListener = onLoadListener;
}
}