Android控件PullRefreshViewGroup实现下拉刷新和上拉加载
本文实例为大家分享了Android实现下拉刷新和上拉加载更多的具体代码,供大家参考,具体内容如下
通渭ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为成都创新互联的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:18982081108(备注:SSL证书合作)期待与您的合作!
先分享下源码:Android实现下拉刷新和上拉加载更多
实现思路:由PullRefreshViewGroup控件来接管标准控件(比如RecyclerView、ListView等)的滑动,调用标准控件的内部方法进行短距离滑动,不再由标准控件自己来处理事件,而完全由PullRefreshViewGroup控件来处理触摸事件。标准控件内部的滑动距离等属性,通过反射获得computeVerticalScrollExtent、computeVerticalScrollRange、computeVerticalScrollOffset这三个方法来获得。
PullRefreshViewGroup控件的布局如下
部分代码实现
触摸滑动事件处理
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean bret = false; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { mPreY = ev.getY(); Log.d(TAG, "mPreY:" + String.valueOf(mPreY)); } break; case MotionEvent.ACTION_MOVE: { float curY = ev.getY(); float distance = curY - mPreY; if (Math.abs(distance) >= mTouchSlop) { mSliding = bret = true; //修正第一次滑动的卡顿 if (distance > 0) { mPreY += mTouchSlop; } else { mPreY -= mTouchSlop; } if (!mScroller.isFinished()) { mScroller.abortAnimation(); } } else { mSliding = bret = false; } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { bret = mSliding; } break; } return bret ? true : super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { boolean bret = false; vTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { mPreY = event.getY(); bret = true; } break; case MotionEvent.ACTION_MOVE: { float curY = event.getY(); float distance = curY - mPreY; Log.d(TAG, "mPreY:" + String.valueOf(mPreY) + " distance:" + String.valueOf(distance)); if (distance != 0) { bret = true; if (mSrcHeightHead == -1 && mHasPullRefresh) { View child0View = mHeadView; mSrcHeightHead = child0View.getHeight(); } scrollBy(distance); } mPreY = curY; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { mPreCurY = 0; View child0View = mHeadView; int child0Height = null != child0View ? child0View.getHeight() : 0; int child0Height2 = null != child0View ? child0View.getLayoutParams().height : 0; //视图的最终高度是有这个来决定的,请看onMeasure 函数的实现 // int child0Height3 = child0View.getMeasuredHeight(); if (child0Height2 != child0Height) { child0Height = child0Height2; } int child0Top = null != child0View ? child0View.getTop() : 0; int dy = child0Height - mSrcHeightHead + (mSrcHeightHead - Math.abs(child0Top)); Log.d(TAG, "onTouchEvent()ACTION_UP child0Height:" + String.valueOf(child0Height) + " mSrcHeightHead:" + String.valueOf(mSrcHeightHead) + " child0Top:" + String.valueOf(child0Top)); if (dy > 0) {//恢复拉伸视图的位置 if (!mLoadingMore && dy > mCanRefreshHeight && child0Top + child0Height2 > mCanRefreshHeight && mRefreshLoad != null) { dy -= mCanRefreshHeight; if (!mPullRefreshLoading) { mPullRefreshLoading = true; mTvRefreshInfo.setText("正在加载..."); mRefreshLoad.pullRefreshStartLoad(); } } mScroller.startScroll(0, 0, 0, -dy); invalidate(); } else { vTracker.computeCurrentVelocity(1000); float yvel = vTracker.getYVelocity(); if (yvel != 0) {//为了满足内部视图的快速滚动( 中间内容视图 ) mScroller.fling(0, 0, 0, (int) yvel, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE); invalidate(); } } vTracker.clear(); bret = true; } break; } return bret ? true : super.onTouchEvent(event); }
小距离滑动代码
private void scrollBy(float distance) { View child0View = mHeadView; View child1View = getChildAt(null == mHeadView ? 0 : 1); float distanceRemain = 0; int child0Top = null != child0View ? child0View.getTop() : 0; // int child0Height = child0View.getHeight(); if (distance < 0) {//向上 int child1Top = child1View.getTop(); // int child1Height = child1View.getHeight(); //child0View 缩小 if (-1 != mSrcHeightHead && null != child0View && child0View.getHeight() > mSrcHeightHead) { float off = distance; if (child0View.getHeight() + distance < mSrcHeightHead) { off = -(child0View.getHeight() - mSrcHeightHead); distance -= off; } else { distance = 0; } child0View.getLayoutParams().height += (int) off; child1Top += (int) off; //child0view 缩小的同时, child1view 的高度也会随之上升 这里很重要 requestLayout(); child1View.offsetTopAndBottom((int) off); if (null != mTailView) { mTailView.offsetTopAndBottom((int) off); } } if (distance != 0) { if (child0Top + mSrcHeightHead + distance <= 0) { distanceRemain = -(distance + (child0Top + mSrcHeightHead));//正数 distance = -(child0Top + mSrcHeightHead);//负数 } //可以显示加载更多吗? boolean bDirectDown = false; boolean bCanScroll = child1View.canScrollVertically(1) || child1View.canScrollVertically(-1); if (!bCanScroll) { int child1ChildCount = 0; if (child1View instanceof ViewGroup) { child1ChildCount = ((ViewGroup) child1View).getChildCount(); } if (child1ChildCount > 0) { ViewGroup viewGroupChild1 = (ViewGroup) child1View; View child1LastItem = viewGroupChild1.getChildAt(child1ChildCount - 1); int child1ViewBottom = viewGroupChild1.getBottom(); int child1LastItemBottom = child1LastItem.getBottom() + child1Top; //相对于 ImageScaleViewGroup 的位置 //增加 child1ViewBottom > getHeight() 来控制 ScrollView if (child1LastItemBottom == getHeight()) { bDirectDown = true; } } } //正在下拉刷新的时候,不能显示加载更多 if ((bCanScroll || bDirectDown) && null != mTailView && !mPullRefreshLoading) { int nVerticalScrollExtent = 0, nVerticalScrollRange = 0, nVerticalScrollOffset = 0; Class c = null; try { c = Class.forName(child1View.getClass().getName()); } catch (Exception ex) { } try { if (null == mComputeVerticalScrollExtent) { Method computeVerticalScrollExtent = findcomputeVerticalMethod(c, "computeVerticalScrollExtent"); computeVerticalScrollExtent.setAccessible(true); mComputeVerticalScrollExtent = computeVerticalScrollExtent; } nVerticalScrollExtent = (int) mComputeVerticalScrollExtent.invoke(child1View); } catch (Exception ex) { } try { if (null == mComputeVerticalScrollRange) { Method computeVerticalScrollRange = findcomputeVerticalMethod(c, "computeVerticalScrollRange"); computeVerticalScrollRange.setAccessible(true); mComputeVerticalScrollRange = computeVerticalScrollRange; } nVerticalScrollRange = (int) mComputeVerticalScrollRange.invoke(child1View); } catch (Exception ex) { } try { if (null == mComputeVerticalScrollOffset) { Method computeVerticalScrollOffset = findcomputeVerticalMethod(c, "computeVerticalScrollOffset"); computeVerticalScrollOffset.setAccessible(true); mComputeVerticalScrollOffset = computeVerticalScrollOffset; } nVerticalScrollOffset = (int) mComputeVerticalScrollOffset.invoke(child1View); } catch (Exception ex) { } int range = nVerticalScrollRange - nVerticalScrollExtent; if (nVerticalScrollOffset + distanceRemain > range) { float nOff = distanceRemain - (range - nVerticalScrollOffset); distanceRemain = range - nVerticalScrollOffset; distance -= nOff; } int child3Bottom = mTailView.getBottom(); if (child3Bottom + distance < getHeight()) { distance = getHeight() - child3Bottom; } } if (!bCanScroll) { distanceRemain = 0; } } } else {//向下 int nScrollOffset = 0; try { Class c = Class.forName(child1View.getClass().getName()); Method computeVerticalScrollOffset = findcomputeVerticalMethod(c, "computeVerticalScrollOffset");//c.getDeclaredMethod("computeVerticalScrollOffset"); computeVerticalScrollOffset.setAccessible(true); nScrollOffset = (int) computeVerticalScrollOffset.invoke(child1View); } catch (Exception ex) { } int child2Top = null != mTailView ? mTailView.getTop() : getHeight();//注意默认值 if (child2Top < getHeight()) { if (child2Top + distance > getHeight()) { distanceRemain = distance - (getHeight() - child2Top); distance = getHeight() - child2Top; } } else if (nScrollOffset > 0) {//内部有滚动,那么就要计算内部滚动距离,其他分配给整体滚动 if (nScrollOffset - distance <= 0) { distanceRemain = -nScrollOffset; // distance = distance - nScrollOffset; distance = 0; //内部能滚动,不让外部去滚动 if (!mScroller.isFinished()) { mScroller.abortAnimation(); //内部滚动完后,立即停止 } } else { distanceRemain = -distance;//负数 distance = 0; } } else { if (child0Top + distance > 0) {//child0放大,child1移动 int off = (int) (child0Top + distance); distance = -child0Top; if (null != child0View) { child0View.getLayoutParams().height += off; requestLayout(); } else { off = 0; } child1View.offsetTopAndBottom(off); if (null != mTailView) { mTailView.offsetTopAndBottom(off); } } } } if (0 != (int) distance) { if (null != child0View) { child0View.offsetTopAndBottom((int) distance); } child1View.offsetTopAndBottom((int) distance); if (null != mTailView) { mTailView.offsetTopAndBottom((int) distance); } requestLayout();//奇酷360这里必须调用, 否则显示有点问题 } scrollByForMidView(distanceRemain);//外部无法滚动的时候,内部滚动 if (!mPullRefreshLoading && !mLoadingMore) { int tailviewTop = null != mTailView ? mTailView.getTop() : getHeight();//注意默认值 if (tailviewTop < getHeight() && mHasLoadMore) {//加载更多 mLoadingMore = true; if (mRefreshLoad != null) { mRefreshLoad.pullUpStartLoadMore(); } } else { if (mHasPullRefresh) { if (distance < 0) { int child0Bottom = child0View.getBottom(); if (child0Bottom < mCanRefreshHeight) { mTvRefreshInfo.setText("下拉刷新"); } } else { int child0Bottom = child0View.getBottom(); if (child0Bottom > mCanRefreshHeight) { mTvRefreshInfo.setText("松开刷新"); } } } } } }
处理标准控件小距离滚动代码,这里ListView有点特殊。
private void scrollByForMidView(float distanceRemain) { if (0 != (int) distanceRemain) { View child1View = getChildAt(null == mHeadView ? 0 : 1); if (child1View instanceof ListView) { ((ListView) child1View).smoothScrollBy((int) distanceRemain, 0); } /*else if (child1View instanceof ScrollView){ ((ScrollView) child1View).smoothScrollBy(0,(int) distanceRemain); }*/ else { child1View.scrollBy(0, (int) distanceRemain); } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。
新闻标题:Android控件PullRefreshViewGroup实现下拉刷新和上拉加载
分享地址:http://azwzsj.com/article/gepdgg.html