美文网首页
ScrollView嵌套Recyclerview问题

ScrollView嵌套Recyclerview问题

作者: Evil_c2e1 | 来源:发表于2017-06-01 09:52 被阅读0次

    记录一下ScrollVIew嵌套Recyclerview遇到的问题

    1、先重写LinearLayoutManager

    public classFullyLinearLayoutManagerextendsLinearLayoutManager {

    private static booleancanMakeInsetsDirty=true;

    private staticFieldinsetsDirtyField=null;

    private static final intCHILD_WIDTH=0;

    private static final intCHILD_HEIGHT=1;

    private static final intDEFAULT_CHILD_SIZE=100;

    private final int[]childDimensions=new int[2];

    private finalRecyclerViewview;

    private intchildSize=DEFAULT_CHILD_SIZE;

    private booleanhasChildSize;

    private intoverScrollMode= ViewCompat.OVER_SCROLL_ALWAYS;

    private finalRecttmpRect=newRect();

    publicFullyLinearLayoutManager(Context context) {

    super(context);

    this.view=null;

    }

    publicFullyLinearLayoutManager(Context context, intorientation, booleanreverseLayout) {

    super(context,orientation,reverseLayout);

    this.view=null;

    }

    publicFullyLinearLayoutManager(RecyclerView view) {

    super(view.getContext());

    this.view= view;

    this.overScrollMode= ViewCompat.getOverScrollMode(view);

    }

    publicFullyLinearLayoutManager(RecyclerView view, intorientation, booleanreverseLayout) {

    super(view.getContext(),orientation,reverseLayout);

    this.view= view;

    this.overScrollMode= ViewCompat.getOverScrollMode(view);

    }

    public voidsetOverScrollMode(intoverScrollMode) {

    if(overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS|| overScrollMode > ViewCompat.OVER_SCROLL_NEVER)

    throw newIllegalArgumentException("Unknown overscroll mode: "+ overScrollMode);

    if(this.view==null)throw newIllegalStateException("view == null");

    this.overScrollMode= overScrollMode;

    ViewCompat.setOverScrollMode(view,overScrollMode);

    }

    public static intmakeUnspecifiedSpec() {

    returnView.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);

    }

    @Override

    public voidonMeasure(RecyclerView.Recycler recycler,RecyclerView.State state, intwidthSpec, intheightSpec) {

    final intwidthMode = View.MeasureSpec.getMode(widthSpec);

    final intheightMode = View.MeasureSpec.getMode(heightSpec);

    final intwidthSize = View.MeasureSpec.getSize(widthSpec);

    final intheightSize = View.MeasureSpec.getSize(heightSpec);

    final booleanhasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;

    final booleanhasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;

    final booleanexactWidth = widthMode == View.MeasureSpec.EXACTLY;

    final booleanexactHeight = heightMode == View.MeasureSpec.EXACTLY;

    final intunspecified =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 booleanvertical = getOrientation() ==VERTICAL;

    initChildDimensions(widthSize,heightSize,vertical);

    intwidth =0;

    intheight =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 intstateItemCount = state.getItemCount();

    final intadapterItemCount = 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(inti =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 booleanfit = (vertical && (!hasHeightSize || height < heightSize))

    || (!vertical && (!hasWidthSize || width < widthSize));

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

    }

    }

    private voidlogMeasureWarning(intchild) {

    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 voidinitChildDimensions(intwidth, intheight, booleanvertical) {

    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 voidsetOrientation(intorientation) {

    // 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 voidclearChildSize() {

    hasChildSize=false;

    setChildSize(DEFAULT_CHILD_SIZE);

    }

    public voidsetChildSize(intchildSize) {

    hasChildSize=true;

    if(this.childSize!= childSize) {

    this.childSize= childSize;

    requestLayout();

    }

    }

    private voidmeasureChild(RecyclerView.Recycler recycler, intposition, intwidthSize, intheightSize, int[] dimensions) {

    finalView 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;

    }

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

    final inthPadding = getPaddingLeft() + getPaddingRight();

    final intvPadding = getPaddingTop() + getPaddingBottom();

    final inthMargin = p.leftMargin+ p.rightMargin;

    final intvMargin = 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 inthDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);

    final intvDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

    final intchildWidthSpec =getChildMeasureSpec(widthSize,hPadding + hMargin + hDecoration,p.width,canScrollHorizontally());

    final intchildHeightSpec =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 voidmakeInsetsDirty(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 voidonMakeInsertDirtyFailed() {

    canMakeInsetsDirty=false;

    if(BuildConfig.DEBUG) {

    Log.w("LinearLayoutManager","Can't make LayoutParams insets dirty, decorations measurements might be incorrect");

    }

    }

    }

    2、重写ScrollView解决6.0以上滑动不灵敏问题

    @Override

    public booleanonInterceptTouchEvent(MotionEvent e) {

    intaction = e.getAction();

    switch(action) {

    caseMotionEvent.ACTION_DOWN:

    downX= (int) e.getRawX();

    downY= (int) e.getRawY();

    break;

    caseMotionEvent.ACTION_MOVE:

    intmoveY = (int) e.getRawY();

    if(Math.abs(moveY -downY) >mTouchSlop) {

    return true;

    }

    }

    return super.onInterceptTouchEvent(e);

    }

    3、6.0以上ScrollView和Recyclerview同时能滑动问题,重写Recyclerview

    @Override

    protected voidonMeasure(intwidthSpec, intheightSpec) {

    intexpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,

    MeasureSpec.AT_MOST);

    super.onMeasure(widthSpec,expandSpec);

    }

    4、最重要一点,如果按照以上方法操作6.0机型还会导致Recyclerview显示不全,需要在XML中Recyclerview所在的地方外面套上一层RelativeLayout即可解决。

    相关文章

      网友评论

          本文标题:ScrollView嵌套Recyclerview问题

          本文链接:https://www.haomeiwen.com/subject/kxcenttx.html