本站客服在线 微信/QQ:643878698
  • 本站每日更新最新课程!客服在线 微信:643878698
JieGuoJieGuo  2021-06-05 20:29 秀源码 隐藏边栏 |   5 条评论  59 
文章评分 0 次,平均分 0.0

(1)使用NestedScrollView嵌套RecyclerView时,滑动lRecyclerView列表会出现强烈的卡顿感
解决方式:setNestedScrollingEnabled()方法

<code class="language-javascript">
//启用嵌套滚动
mRecyclerView.setNestedScrollingEnabled(false);
</code

源码解析

<code class="language-javascript">
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
...
}
</code

接口NestedScrollingChild 的解析

<code class="language-javascript">
public interface NestedScrollingChild {  
    /** 
     * 设置嵌套滑动是否能用
     * 
     *  @param enabled true to enable nested scrolling, false to disable
     */  
    public void setNestedScrollingEnabled(boolean enabled);  
  
    /** 
     * 判断嵌套滑动是否可用 
     * 
     * @return true if nested scrolling is enabled
     */  
    public boolean isNestedScrollingEnabled();  
  
    /** 
     * 开始嵌套滑动
     * 
     * @param axes 表示方向轴,有横向和竖向
     */  
    public boolean startNestedScroll(int axes);  
  
    /** 
     * 停止嵌套滑动 
     */  
    public void stopNestedScroll();  
  
    /** 
     * 判断是否有父View 支持嵌套滑动 
     * @return whether this view has a nested scrolling parent
     */  
    public boolean hasNestedScrollingParent();  
  
    /** 
     * 在子View的onInterceptTouchEvent或者onTouch中,调用该方法通知父View滑动的距离
     *
     * @param dx  x轴上滑动的距离
     * @param dy  y轴上滑动的距离
     * @param consumed 父view消费掉的scroll长度
     * @param offsetInWindow   子View的窗体偏移量
     * @return 支持的嵌套的父View 是否处理了 滑动事件 
     */  
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);  

    /** 
     * 子view处理scroll后调用
     *
     * @param dxConsumed x轴上被消费的距离(横向) 
     * @param dyConsumed y轴上被消费的距离(竖向)
     * @param dxUnconsumed x轴上未被消费的距离 
     * @param dyUnconsumed y轴上未被消费的距离 
     * @param offsetInWindow 子View的窗体偏移量
     * @return  true if the event was dispatched, false if it could not be dispatched.
     */  
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,  
          int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);  
  

  
    /** 
     * 滑行时调用 
     *
     * @param velocityX x 轴上的滑动速率
     * @param velocityY y 轴上的滑动速率
     * @param consumed 是否被消费 
     * @return  true if the nested scrolling parent consumed or otherwise reacted to the fling
     */  
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);  
  
    /** 
     * 进行滑行前调用
     *
     * @param velocityX x 轴上的滑动速率
     * @param velocityY y 轴上的滑动速率 
     * @return true if a nested scrolling parent consumed the fling
     */  
    public boolean dispatchNestedPreFling(float velocityX, float velocityY);  
}


//RecyclerView解析 
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {

public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    if (attrs != null) {
        TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
        mClipToPadding = a.getBoolean(0, true);
        a.recycle();
    } else {
        mClipToPadding = true;
    }
    setScrollContainer(true);
  //默认获取焦点
    setFocusableInTouchMode(true);

    final ViewConfiguration vc = ViewConfiguration.get(context);
    mTouchSlop = vc.getScaledTouchSlop();
    mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
    mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
    setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);

    mItemAnimator.setListener(mItemAnimatorListener);
    initAdapterManager();
    initChildrenHelper();
    // If not explicitly specified this view is important for accessibility.
    if (ViewCompat.getImportantForAccessibility(this)
            == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
        ViewCompat.setImportantForAccessibility(this,
                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
    }
    mAccessibilityManager = (AccessibilityManager) getContext()
            .getSystemService(Context.ACCESSIBILITY_SERVICE);
    setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
    // Create the layoutManager if specified.

    boolean nestedScrollingEnabled = true;

    if (attrs != null) {
        int defStyleRes = 0;
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
                defStyle, defStyleRes);
        String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
        int descendantFocusability = a.getInt(
                R.styleable.RecyclerView_android_descendantFocusability, -1);
        if (descendantFocusability == -1) {
            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        }
        a.recycle();
        createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);

        if (Build.VERSION.SDK_INT >= 21) {
            a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
                    defStyle, defStyleRes);
            nestedScrollingEnabled = a.getBoolean(0, true);
            a.recycle();
        }
    } else {
        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    }
  //默认支持嵌套滚动
    // Re-set whether nested scrolling is enabled so that it is set on all API levels
    setNestedScrollingEnabled(nestedScrollingEnabled);
}


  @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        getScrollingChildHelper().setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return getScrollingChildHelper().isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return getScrollingChildHelper().startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        getScrollingChildHelper().stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return getScrollingChildHelper().hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
            int dyUnconsumed, int[] offsetInWindow) {
        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
                dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
    }
这是全部都交给getScrollingChildHelper()这个方法的返回对象处理了啊,看看这个方法是怎么实现的。

private NestedScrollingChildHelper getScrollingChildHelper() {
        if (mScrollingChildHelper == null) {
            mScrollingChildHelper = new NestedScrollingChildHelper(this);
        }
        return mScrollingChildHelper;
    }
</code

NestedScrollingChild 接口的方法都交给NestedScrollingChildHelper这个代理对象处理

<code class="language-javascript">
public class NestedScrollingChildHelper {
   private final View mView;
    private ViewParent mNestedScrollingParent;
    private boolean mIsNestedScrollingEnabled;
    private int[] mTempNestedScrollConsumed;

    /**
     * Construct a new helper for a given view.
     */
    public NestedScrollingChildHelper(View view) {
        mView = view;
    }

    /**
     * Enable nested scrolling.
     *
     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
     * method/{@link android.support.v4.view.NestedScrollingChild} interface method with the same
     * signature to implement the standard policy.</p>
     *
     * @param enabled true to enable nested scrolling dispatch from this view, false otherwise
     */
    public void setNestedScrollingEnabled(boolean enabled) {
        if (mIsNestedScrollingEnabled) {
            ViewCompat.stopNestedScroll(mView);
        }
        mIsNestedScrollingEnabled = enabled;
    }
  /**
   * Check if nested scrolling is enabled for this view.
   *
   * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
   * method/{@link android.support.v4.view.NestedScrollingChild} interface method with the same
   * signature to implement the standard policy.</p>
   *
   * @return true if nested scrolling is enabled for this view
   */
  public boolean isNestedScrollingEnabled() {
    return mIsNestedScrollingEnabled;
  }
.
.
.
}
</code

RecyclerView默认是setNestedScrollingEnabled(true),是支持嵌套滚动的,也就是说当它嵌套在NestedScrollView中时,默认会随着NestedScrollView滚动而滚动,放弃了自己的滚动

(2)使用NestedScrollView嵌套RecyclerView时,每次打开界面都是定位在RecyclerView在屏幕顶端,列表上面的布局都被顶上去
查看RecyclerView的源码发现,它会在构造方法中调用setFocusableInTouchMode(true),所以抢到焦点后一定会定位到第一行的位置突出RecyclerView的显示

解决方法就是NestScrollView节点添加

<code class="language-javascript">
android:focusableInTouchMode="true"
</code

在NestScrollView的子节点view添加:

<code class="language-javascript">
android:descendantFocusability="blocksDescendants"
</code

或者 直接mRecyclerVIew.setFocusableInTouchMode(false)

总结:

源码 :有些问题只能从源码里找出究竟 , 一点一点剖析才能看出问题的根本, 弄透一个接口的作用 , 那么有类似需求的时候,可以自定义控件实现该接口;再一个,就是在看的过程中, 我们会熟悉源码的风格,命令的方式和逻辑


————————————————
版权声明:本文为CSDN博主「彭老希」的原创文章
原文链接:https://blog.csdn.net/weixin_44720673/article/details/117463538

「点点赞赏,手留余香」

还没有人赞赏,快来当第一个赞赏的人吧!

JieGuo给JieGuo打赏
×
予人玫瑰,手有余香
  • 2
  • 5
  • 10
  • 20
  • 50
2
支付

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

JieGuo
JieGuo 关注:1    粉丝:12
改变世界的我们!

发表评论

评论
正在努力加载中...

本周热门

扫一扫二维码分享
×

开通会员可免费观看全站资源! ×

11.11大优惠:会员降至168 ,会员可观看/下载站内全部资源 ,本站新增至尊会员!可一键获取全站资源【让资源掌握在自己手中】!活动时间:11月1号-11月30号
Tips:提示: 本站每日更新最新资源,所有资源链接自动更新,如遇问题可咨询客服:643878698,第一时间为您解决(网站顶部公告-客服7*24小时在线)!