pulltorefresh 刷新与 nestedscroll 冲突的解决办法

 pulltorefresh 用的是这个框架: com.handmark.pulltorefresh.library;

做到一个奇葩功能,就是要使用android material design中的滚动隐藏tab的和搜索框,但是刷新要的是pulltorefresh 刷新 ,列表使用的是recyclerview(recyclerview自带nestedscroll)!

布局大致这样:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    
     <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
         android:orientation="vertical"
        >
     <android.support.v7.widget.Toolbar
        android:layout_width="match_parent"
    	android:layout_height="?attr/actionBarSize"
    	app:layout_scrollFlags="scroll|enterAlways"
    	android:background="@color/head_top_bg"
        android:minHeight="?attr/actionBarSize"/>
        
       <searchview
        	app:layout_scrollFlags="scroll|enterAlways"
        	android:layout_width="match_parent"
        	android:layout_height="wrap_content"/>
        
    
    <android.support.design.widget.TabLayout
            
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/white"
            app:tabTextColor="@android:color/black"
            app:tabIndicatorColor="@color/normalBlue"
            app:tabSelectedTextColor="@color/normalBlue"
             />
    
    </android.support.design.widget.AppBarLayout>
    
    <android.support.v4.view.ViewPager
        
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />  
	</android.support.design.widget.CoordinatorLayout>
要滚动隐藏的就是toobar 和 searchview,viewpager加载的是几个pulltorefresh<RecyclerView>。

然后,让我头疼了整整一下午的事情发生了,下拉刷新和上拉加载的时候,要滚动隐藏的部分一直都会和pulltorefresh拖动刷新冲突,也就是,无法完全滚动隐藏需要隐藏的tab.

试了n次后无果,只能查看通过寻根问底了,查看代码:

pulltorefresh中处理上拉刷新的关键代码如下:

switch (event.getAction()) {
<span style="white-space:pre">			</span>case MotionEvent.ACTION_MOVE: {
<span style="white-space:pre">				</span>final int activePointerIndex = MotionEventCompat.findPointerIndex(event,
                        mActivedPointerId);
<span style="white-space:pre">				</span>
<span style="white-space:pre">				</span> if (activePointerIndex == -1) {
<span style="white-space:pre">					</span> Log.d(LOG_TAG, "Invalid pointerId=" + mActivedPointerId + " in onTouchEvent");
<span style="white-space:pre">	</span>                    break;
<span style="white-space:pre">	</span>                }
<span style="white-space:pre">				</span> 
<span style="white-space:pre">	</span>                if (mIsBeingDragged) {
    <span style="white-space:pre">					</span>mLastMotionY = event.getY();
    <span style="white-space:pre">					</span>mLastMotionX = event.getX();
    <span style="white-space:pre">					</span>pullEvent();
    <span style="white-space:pre">				</span>}
<span style="white-space:pre">				</span> 
<span style="white-space:pre">				</span>
<span style="white-space:pre">				</span>break;
<span style="white-space:pre">			</span>}


<span style="white-space:pre">			</span>case MotionEvent.ACTION_DOWN: {
<span style="white-space:pre">				</span>if (isReadyForPull()) {
<span style="white-space:pre">					</span>mLastMotionY = mInitialMotionY = event.getY();
<span style="white-space:pre">					</span>mLastMotionX = mInitialMotionX = event.getX();
<span style="white-space:pre">					</span>return true;
<span style="white-space:pre">				</span>}
<span style="white-space:pre">				</span>mActivedPointerId = MotionEventCompat.getPointerId(event, 0);
<span style="white-space:pre">				</span>break;
<span style="white-space:pre">			</span>}


<span style="white-space:pre">			</span>case MotionEvent.ACTION_CANCEL:
<span style="white-space:pre">			</span>case MotionEvent.ACTION_UP: {
<span style="white-space:pre">				</span>if (mIsBeingDragged) {
<span style="white-space:pre">					</span>mIsBeingDragged = false;


<span style="white-space:pre">					</span>if (mState == State.RELEASE_TO_REFRESH
<span style="white-space:pre">							</span>&& (null != mOnRefreshListener || null != mOnRefreshListener2)) {
<span style="white-space:pre">							</span>setState(State.REFRESHING, true);
<span style="white-space:pre">						</span>return true;
<span style="white-space:pre">					</span>}


<span style="white-space:pre">					</span>// If we're already refreshing, just scroll back to the top
<span style="white-space:pre">					</span>if (isRefreshing()) {
<span style="white-space:pre">						</span>smoothScrollTo(0);
<span style="white-space:pre">						</span>return true;
<span style="white-space:pre">					</span>}


<span style="white-space:pre">					</span>// If we haven't returned by here, then we're not in a state
<span style="white-space:pre">					</span>// to pull, so just reset
<span style="white-space:pre">					</span>setState(State.RESET);


<span style="white-space:pre">					</span>return true;
<span style="white-space:pre">				</span>}
<span style="white-space:pre">				</span>break;
<span style="white-space:pre">			</span>}
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>return false;
<span style="white-space:pre">	</span>}
就是在ontouchEvent()事件当中进行操作。

下面来看实现recyclerview nestedScroll 的关键代码:

  case MotionEvent.ACTION_MOVE: {
                final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
                if (index < 0) {
                    Log.e(TAG, "Error processing scroll; pointer index for id " +
                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");
                    return false;
                }

                final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
                final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
                int dx = mLastTouchX - x;
                int dy = mLastTouchY - y;

                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
                    dx -= mScrollConsumed[0];
                    dy -= mScrollConsumed[1];
                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
                    // Updated the nested offsets
                    mNestedOffsets[0] += mScrollOffset[0];
                    mNestedOffsets[1] += mScrollOffset[1];
                }

                if (mScrollState != SCROLL_STATE_DRAGGING) {
                    boolean startScroll = false;
                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
                        if (dx > 0) {
                            dx -= mTouchSlop;
                        } else {
                            dx += mTouchSlop;
                        }
                        startScroll = true;
                    }
                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
                        if (dy > 0) {
                            dy -= mTouchSlop;
                        } else {
                            dy += mTouchSlop;
                        }
                        startScroll = true;
                    }
                    if (startScroll) {
                        setScrollState(SCROLL_STATE_DRAGGING);
                    }
                }

                if (mScrollState == SCROLL_STATE_DRAGGING) {
                    mLastTouchX = x - mScrollOffset[0];
                    mLastTouchY = y - mScrollOffset[1];

                    if (scrollByInternal(
                            canScrollHorizontally ? dx : 0,
                            canScrollVertically ? dy : 0,
                            vtev)) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }
            } break;
也是在ontouchEvent()事件当中进行的,但是注意关键方法
<pre name="code" class="java">
 
dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset);
</pre>这个方法就是告诉nestedScrollParent,赶紧滚动你该滚动的东西,滚动了多少在mScrollConsumed里告诉我。我也要滚。然后关键的地方就是,在recyclerview滚完后,事件回传给他的parent 也就是 pulltorefreshBase的ontouchEvent,此时pulltorefreshBase 接收到move后根本就不知道要隐藏的view有没有隐藏了,就开始执行<pre name="code" class="java">pullEvent();
然后,得到的结果就是他开始截断事件传递,不给recyclerview,然后自己去执行拖动刷新。那么,怎么让他知道要滚动的view有没有滚动结束呢?

那么来看pulltoreftreshbase里面的这个方法:

private boolean isReadyForPull() {
		switch (mMode) {
			case PULL_FROM_START:
				return isReadyForPullStart();
			case PULL_FROM_END:
				return isReadyForPullEnd();
			case BOTH:
				return isReadyForPullEnd() || isReadyForPullStart();
			default:
				return false;
		}
	}
这个方法就是询问是否可以开始拖动刷新啦。如果没有准备好,那我就不去实行拖动刷新。那么,要知道滚动刷新何时完成要的是
mScrollConsumed
这个参数,如果这个mScrollConsumed[1] = 0,那么说明已经要滚动的view已经完成滚动了。可以开始拖动刷新,那么在判断
sReadyForPullStart()

isReadyForPullEnd()
的时候加入这个判断就行了,由于这个是个静态方法,那只能去反射判断了,在pulltorefreshbase的继承类里面加上

代码:

@Override
    protected boolean isReadyForPullStart() {
    	int[] scrollconsumed = getNestedScrollConsumed();
    	if(scrollconsumed[1] != 0){
    		return false;
    	}
        if (mRefreshableView.getChildCount() <= 0)
            return true;
		int firstVisiblePosition = mRefreshableView.getChildAdapterPosition(mRefreshableView.getChildAt(0));
        if (firstVisiblePosition == 0)
            return mRefreshableView.getChildAt(0).getTop() == mRefreshableView.getPaddingTop();
        else
            return false;

    }

    @Override
    protected boolean isReadyForPullEnd() {
    	int[] scrollconsumed = getNestedScrollConsumed();
    	if(scrollconsumed[1] != 0){
    		return false;
    	}
    	if(mRefreshableView.getChildCount() <= 0){
    		return true;
    	}
		int lastVisiblePosition = mRefreshableView.getChildAdapterPosition(mRefreshableView.getChildAt(mRefreshableView.getChildCount() -1));
        if (lastVisiblePosition >= mRefreshableView.getAdapter().getItemCount()-1) {
            return mRefreshableView.getChildAt(mRefreshableView.getChildCount() - 1).getBottom() <= mRefreshableView.getBottom();
        }
        return false;
    }
    
    
    private int[] getNestedScrollConsumed(){
    	int[] consumed = new int[2];
    	try {
			Field field = RecyclerView.class.getDeclaredField("mScrollConsumed");
			if(field != null){
				field.setAccessible(true);
				consumed = (int[]) field.get(mRefreshableView);
				return consumed;
			}
			return consumed;
			
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	return consumed;
    	
    }




  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值