转载自 http://nobugs.sinaapp.com/?p=1286
SwipeRefreshLayout增加上拉加载更多,只需处理3个方法
1. 增加canChildScrollDown(),判断是否可以上拉2. 增加底部上拉view距离
3. 增加onLoadMore接口
什么时候触发SwipeRefreshLayout的上拉
当view可以上拉的时候,不触发SwipeRefreshLayout的上拉;当view不能上拉的时候,触发SwipeRefreshLayout的上拉;
所以增加canChildScrollDown()来判断view是否可以上拉。
/**
* < 14 的没有测试,所以注释掉了,暂时不支持.
* @return Whether it is possible for the child view of this layout to
* scroll down. Override this if the child view is a custom view.
*/
public boolean canChildScrollDown() {
if (android.os.Build.VERSION.SDK_INT < 14) {
return true;//don't support < 14
// if (mTarget instanceof AbsListView) {
// final AbsListView absListView = (AbsListView) mTarget;
// return absListView.getChildCount() > 0
// && (absListView.getLastVisiblePosition() == absListView.getAdapter().getCount() - 1
// || absListView.getChildAt(absListView.getLastVisiblePosition() - 1)
// .getBottom() < absListView.getPaddingBottom());
// } else {
// return mTarget.getScrollY() < 0;
// }
} else {
return ViewCompat.canScrollVertically(mTarget, 1);
}
}
底部上拉view距离
1. onInterceptTouchEvent()处理是否拦截滚动事件(不能上拉赫下拉的时候,拦截)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ensureTarget();
boolean handled = false;
if (mReturningToStart && ev.getAction() == MotionEvent.ACTION_DOWN) {
mReturningToStart = false;
}
if (!mRefreshing && isEnabled() && !mReturningToStart && mMode.permitsPullToRefresh()
&& (!canChildScrollUp() || !canChildScrollDown())) {
handled = onTouchEvent(ev);//增加!canChildScrollDown() 判断
}
return !handled ? super.onInterceptTouchEvent(ev) : handled;
}
2. 计算上拉的距离
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
boolean handled = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
...
case MotionEvent.ACTION_MOVE:
if (mDownEvent != null && !mReturningToStart) {
final float eventY = event.getY();
float yDiff = eventY - mDownEvent.getY();
if(!canChildScrollUp() && yDiff > 0 && mMode.permitsPullFromStart()){
mCurrentMode = Mode.PULL_FROM_START;
} else if (!canChildScrollDown() && yDiff < 0 && mMode.permitsPullFromEnd()) {
mCurrentMode = Mode.PULL_FROM_END;
yDiff = -yDiff;//上拉计算的距离是负数,先设置为正数,后面再取负数.
} else {
yDiff = 0;//对于其他情况,不能上拉和下拉
}
if (yDiff > mTouchSlop) {
// User velocity passed min velocity; trigger a refresh
if (yDiff > mDistanceToTriggerSync) {
// User movement passed distance; trigger a refresh
if(mCurrentMode == Mode.PULL_FROM_START){
startRefresh();//下拉刷新
} else if(mCurrentMode == Mode.PULL_FROM_END){
startLoadMore();//加载更多
}
handled = true;
break;
} else {
// Just track the user's movement
setTriggerPercentage(
mAccelerateInterpolator.getInterpolation(
yDiff / mDistanceToTriggerSync)
);
float offsetTop = yDiff;
if (mPrevY > eventY) {
offsetTop = yDiff - mTouchSlop;
}
updateContentOffsetTop((int) (offsetTop));
if ((mCurrentMode == Mode.PULL_FROM_START && mPrevY > eventY && (mTarget.getTop() < mTouchSlop))
|| (mCurrentMode == Mode.PULL_FROM_END && mPrevY < eventY
&& (mTarget.getBottom() - mHeight < mTouchSlop))) {
// If the user puts the view back at the top, we
// don't need to. This shouldn't be considered
// cancelling the gesture as the user can restart from the top.
removeCallbacks(mCancel);//当用户回退的时候,直接取消动作
} else {
updatePositionTimeout();//当用户300ms没有完成上拉或者下拉,直接复位
}
mPrevY = event.getY();
handled = true;
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
break;
}
return handled;
}
3. updateContentOffsetTop()位置
private void updateContentOffsetTop(int targetTop) {
final int currentTop = mTarget.getTop();
final int currentBottom = mTarget.getBottom() - mHeight;
if(!canChildScrollUp() && mCurrentMode == Mode.PULL_FROM_START){
if (targetTop > mDistanceToTriggerSync) {
targetTop = (int) mDistanceToTriggerSync;
}else if (targetTop < 0){
targetTop = 0;
}
setTargetOffsetTopAndBottom(targetTop - currentTop);
} else {
if (targetTop > mDistanceToTriggerSync) {
targetTop = (int) mDistanceToTriggerSync;
}else if (targetTop < 0){
targetTop = 0;
}
setTargetOffsetTopAndBottom((-targetTop - currentBottom));//-targetTop是个负数,让view向上移动
}
}
onLoadMore接口
/**
* Classes that wish to be notified when the swipe gesture correctly
* triggers a refresh or load more should implement this interface.
*/
public interface OnRefreshListener {
public void onRefresh();
public void onLoadMore();
}
提示:非ListView的使用,需要加个ScrollView
<com.dahuo.learn.swiperefreshandload.view.SwipeRefreshAndLoadLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name" />
</ScrollView>
</com.dahuo.learn.swiperefreshandload.view.SwipeRefreshAndLoadLayout>
提示:默认只打开了下拉刷新,可以设置模式
mSwipeLayout.setmMode(SwipeRefreshAndLoadLayout.Mode.BOTH);