0
点赞
收藏
分享

微信扫一扫

ScrollView嵌套RecyclerView出现的三个冲突

yellowone 2023-05-15 阅读 44


1.RecyclerView 与 ScrollView 滑动冲突

2.嵌套布局不显示在顶部,直接显示 RecyclerView 第一个item

3.RecyclerView 显示不全

1.解决第一个:

一般出现滑动冲突,按照 ScrollView 滑动即可;去掉 RecyclerView滑动即可;

rcv_rank.setNestedScrollingEnabled(false);

 

注:rcv_rank 为控件 RecyclerView ;

2.解决第二个:

scrollView_rank.smoothScrollTo(0,0);

注:scrollView_rank 为控件 ScrollView ;

3.解决第三个:先去布局文件中为 RecyclerView 嵌套一层 RelativeLayout 

<RelativeLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:descendantFocusability="blocksDescendants">
             <android.support.v7.widget.RecyclerView
                 android:id="@+id/rcv_rank"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:overScrollMode="never"
                 android:scrollbars="vertical" />
            </RelativeLayout>
到此为止,冲突已经解决完毕,如过不行请参考下面方法(一个外国大牛写的),复制下面的类即可:
import android.content.Context;
 import android.graphics.Rect;
 import android.support.v4.BuildConfig;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.View;
 import java.lang.reflect.Field;
 /**
  * Created by qd on 2018/2/23.
  */
 public class AutoLinearLayoutManager extends LinearLayoutManager{
     private static boolean canMakeInsetsDirty = true;
     private static Field insetsDirtyField = null;
     private static final int CHILD_WIDTH = 0;
     private static final int CHILD_HEIGHT = 1;
     private static final int DEFAULT_CHILD_SIZE = 100;
     private final int[] childDimensions = new int[2];
     private final RecyclerView view;
     private int childSize = DEFAULT_CHILD_SIZE;
     private boolean hasChildSize;
     private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
     private final Rect tmpRect = new Rect();

     public AutoLinearLayoutManager(Context context) {
         super(context);
         this.view = null;
     }

     public AutoLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
         super(context, orientation, reverseLayout);
         this.view = null;
     }

     public AutoLinearLayoutManager(RecyclerView view) {
         super(view.getContext());
         this.view = view;
         this.overScrollMode = ViewCompat.getOverScrollMode(view);
     }

     public AutoLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
         super(view.getContext(), orientation, reverseLayout);
         this.view = view;
         this.overScrollMode = ViewCompat.getOverScrollMode(view);
     }

     public void setOverScrollMode(int overScrollMode) {
         if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
             throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
         if (this.view == null) throw new IllegalStateException("view == null");
         this.overScrollMode = overScrollMode;
         ViewCompat.setOverScrollMode(view, overScrollMode);
     }

     public static int makeUnspecifiedSpec() {
         return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
     }

     @Override
     public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
         final int widthMode = View.MeasureSpec.getMode(widthSpec);
         final int heightMode = View.MeasureSpec.getMode(heightSpec);
         final int widthSize = View.MeasureSpec.getSize(widthSpec);
         final int heightSize = View.MeasureSpec.getSize(heightSpec);
         final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
         final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;
         final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
         final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;
         final int unspecified = makeUnspecifiedSpec();

         if (exactWidth && exactHeight) {
             // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
             super.onMeasure(recycler, state, widthSpec, heightSpec);
             return;
         }

         final boolean vertical = getOrientation() == VERTICAL;

         initChildDimensions(widthSize, heightSize, vertical);

         int width = 0;
         int height = 0;

         // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
         // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
         // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
         // called whiles scrolling)
         recycler.clear();

         final int stateItemCount = state.getItemCount();
         final int adapterItemCount = getItemCount();
         // adapter always contains actual data while state might contain old data (f.e. data before the animation is
         // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
         // state
         for (int i = 0; i < adapterItemCount; i++) {
             if (vertical) {
                 if (!hasChildSize) {
                     if (i < stateItemCount) {
                         // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                         // we will use previously calculated dimensions
                         measureChild(recycler, i, widthSize, unspecified, childDimensions);
                     } else {
                         logMeasureWarning(i);
                     }
                 }
                 height += childDimensions[CHILD_HEIGHT];
                 if (i == 0) {
                     width = childDimensions[CHILD_WIDTH];
                 }
                 if (hasHeightSize && height >= heightSize) {
                     break;
                 }
             } else {
                 if (!hasChildSize) {
                     if (i < stateItemCount) {
                         // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                         // we will use previously calculated dimensions
                         measureChild(recycler, i, unspecified, heightSize, childDimensions);
                     } else {
                         logMeasureWarning(i);
                     }
                 }
                 width += childDimensions[CHILD_WIDTH];
                 if (i == 0) {
                     height = childDimensions[CHILD_HEIGHT];
                 }
                 if (hasWidthSize && width >= widthSize) {
                     break;
                 }
             }
         }

         if (exactWidth) {
             width = widthSize;
         } else {
             width += getPaddingLeft() + getPaddingRight();
             if (hasWidthSize) {
                 width = Math.min(width, widthSize);
             }
         }

         if (exactHeight) {
             height = heightSize;
         } else {
             height += getPaddingTop() + getPaddingBottom();
             if (hasHeightSize) {
                 height = Math.min(height, heightSize);
             }
         }

         setMeasuredDimension(width, height);

         if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
             final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
                     || (!vertical && (!hasWidthSize || width < widthSize));


             ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
         }
     }

     private void logMeasureWarning(int child) {
         if (BuildConfig.DEBUG) {
             Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                     "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
         }
     }

     private void initChildDimensions(int width, int height, boolean vertical) {
         if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
             // already initialized, skipping
             return;
         }
         if (vertical) {
             childDimensions[CHILD_WIDTH] = width;
             childDimensions[CHILD_HEIGHT] = childSize;
         } else {
             childDimensions[CHILD_WIDTH] = childSize;
             childDimensions[CHILD_HEIGHT] = height;
         }
     }

     @Override
     public void setOrientation(int orientation) {
         // might be called before the constructor of this class is called
         //noinspection ConstantConditions
         if (childDimensions != null) {
             if (getOrientation() != orientation) {
                 childDimensions[CHILD_WIDTH] = 0;
                 childDimensions[CHILD_HEIGHT] = 0;
             }
         }
         super.setOrientation(orientation);
     }

     public void clearChildSize() {
         hasChildSize = false;
         setChildSize(DEFAULT_CHILD_SIZE);
     }

     public void setChildSize(int childSize) {
         hasChildSize = true;
         if (this.childSize != childSize) {
             this.childSize = childSize;
             requestLayout();
         }
     }

     private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
         final View child;
         try {
             child = recycler.getViewForPosition(position);
         } catch (IndexOutOfBoundsException e) {
             if (BuildConfig.DEBUG) {
                 Log.w("LinearLayoutManager", "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);
             }
             return;
         }

         final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();


         final int hPadding = getPaddingLeft() + getPaddingRight();
         final int vPadding = getPaddingTop() + getPaddingBottom();


         final int hMargin = p.leftMargin + p.rightMargin;
         final int vMargin = p.topMargin + p.bottomMargin;


         // we must make insets dirty in order calculateItemDecorationsForChild to work
         makeInsetsDirty(p);
         // this method should be called before any getXxxDecorationXxx() methods
         calculateItemDecorationsForChild(child, tmpRect);


         final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
         final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);


         final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
         final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());

         child.measure(childWidthSpec, childHeightSpec);

         dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
         dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;

         // as view is recycled let's not keep old measured values
         makeInsetsDirty(p);
         recycler.recycleView(child);
     }

     private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
         if (!canMakeInsetsDirty) {
             return;
         }
         try {
             if (insetsDirtyField == null) {
                 insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
                 insetsDirtyField.setAccessible(true);
             }
             insetsDirtyField.set(p, true);
         } catch (NoSuchFieldException e) {
             onMakeInsertDirtyFailed();
         } catch (IllegalAccessException e) {
             onMakeInsertDirtyFailed();
         }
     }

     private static void onMakeInsertDirtyFailed() {
         canMakeInsetsDirty = false;
         if (BuildConfig.DEBUG) {
             Log.w("LinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
         }
     }}

然后直接设置即可:

AutoLinearLayoutManager manager = new AutoLinearLayoutManager(getActivity());
        rcv_rank.setLayoutManager(manager);

注:rcv_rank 为控件 RecyclerView ;


举报

相关推荐

0 条评论