自定义不占满全屏可左右滑动的卡片

感情迁移 提交于 2019-12-01 16:04:22

效果图

方案一 修改ViewPager

实践中发现许多较为关键的类、变量、方法都是私有的,无法进行操作,只好定义SuperViewPager继承自ViewGroup,复制ViewPager里面所有内容并进行修改

  • 1.定义两个自定义属性,pagerSpace代表两个卡片之间的距离,pageOerlayWidth代表卡片越界布局的宽度

    12345678910
    public (Context context, AttributeSet attrs) {       super(context, attrs);       TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuperPager);       pagerSpace = a.getDimensionPixelSize(R.styleable.SuperPager_sp_pagerSpace, (int) dp2px(20));       pageOerlayWidth = a.getDimensionPixelSize(R.styleable.SuperPager_sp_pageOerlayWidth, (int) dp2px(12));       a.recycle();       initViewPager();   }
  • 2.修改onLayout()方法

    1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
       protected void onLayout(boolean changed, int l, int t, int r, int b) {       final int count = getChildCount();       int width = r - l;       int height = b - t;       int paddingLeft = getPaddingLeft();       int paddingTop = getPaddingTop();       int paddingRight = getPaddingRight();       int paddingBottom = getPaddingBottom();       final int scrollX = getScrollX();       int decorCount = 0;              // we have the proper offsets for non-decor views later.       for (int i = 0; i < count; i++) {           final View child = getChildAt(i);           if (child.getVisibility() != GONE) {               final LayoutParams lp = (LayoutParams) child.getLayoutParams();               int childLeft = 0;               int childTop = 0;               if (lp.isDecor) {                   final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;                   final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;                   switch (hgrav) {                       default:                           childLeft = paddingLeft;                           break;                       case Gravity.LEFT:                           childLeft = paddingLeft;                           paddingLeft += child.getMeasuredWidth();                           break;                       case Gravity.CENTER_HORIZONTAL:                           childLeft = Math.max((width - child.getMeasuredWidth()) / 2,                                   paddingLeft);                           break;                       case Gravity.RIGHT:                           childLeft = width - paddingRight - child.getMeasuredWidth();                           paddingRight += child.getMeasuredWidth();                           break;                   }                   switch (vgrav) {                       default:                           childTop = paddingTop;                           break;                       case Gravity.TOP:                           childTop = paddingTop;                           paddingTop += child.getMeasuredHeight();                           break;                       case Gravity.CENTER_VERTICAL:                           childTop = Math.max((height - child.getMeasuredHeight()) / 2,                                   paddingTop);                           break;                       case Gravity.BOTTOM:                           childTop = height - paddingBottom - child.getMeasuredHeight();                           paddingBottom += child.getMeasuredHeight();                           break;                   }                   childLeft += scrollX;                   child.layout(childLeft, childTop,                           childLeft + child.getMeasuredWidth(),                           childTop + child.getMeasuredHeight());                   decorCount++;               }           }       }       //以下为关键代码       int pageWidth = width - paddingLeft - paddingRight - 2 * pagerSpace - 2 * pageOerlayWidth;       // Page views. Do this once we have the right padding offsets from above.       for (int i = 0; i < count; i++) {           final View child = getChildAt(i);           if (child.getVisibility() != GONE) {               final LayoutParams lp = (LayoutParams) child.getLayoutParams();               ItemInfo ii;               if (!lp.isDecor && (ii = infoForChild(child)) != null) {                   int loff = (int) ((pageWidth+pagerSpace) * ii.offset);                   int childLeft = paddingLeft + pageOerlayWidth + pagerSpace + loff;                   int childTop = paddingTop;                   child.layout(childLeft, childTop,                           childLeft + pageWidth,                           childTop + child.getMeasuredHeight());               }           }       }       mTopPageBounds = paddingTop;       mBottomPageBounds = height - paddingBottom;       mDecorChildCount = decorCount;       if (mFirstLayout) {           scrollToItem(mCurItem, false, 0, false);大专栏  自定义不占满全屏可左右滑动的卡片v>       }       mFirstLayout = false;   }

    先计算卡片宽度为:viewpager宽度-padding值-两边的pagerSpace-两边的pageOverlayWidth

    然后根据child计算左边坐标的偏移量

  • 3.修改scrollToItem(int item, boolean smoothScroll, int velocity,

    boolean dispatchSelected)方法 
    123456789101112131415161718192021222324252627282930313233
     private void scrollToItem(int item, boolean smoothScroll, int velocity,                             boolean dispatchSelected) {       final ItemInfo curInfo = infoForPosition(item);       int destX = 0;       if (curInfo != null) {           final int width = getClientWidth();           destX = (int) ((width - 2 * pageOerlayWidth - pagerSpace) * Math.max(mFirstOffset, Math.min(curInfo.offset, mLastOffset)));       }       if (smoothScroll) {           smoothScrollTo(destX, 0, velocity);           if (dispatchSelected) {               dispatchOnPageSelected(item);           }       } else {           if (dispatchSelected) {               dispatchOnPageSelected(item);           }           completeScroll(false);           scrollTo(destX, 0);           pageScrolled(destX);       }   } ```    修正滚动x坐标的计算,确保滚动后卡片能位于中间- 4.修改滚动动效为回弹效果  在initViewPager()方法中修改  ```java  mScroller = new Scroller(context, new OvershootInterpolator(1.5F));

    注,ViewPager外部可通过映射的方法访问私有成员mScroller

    123456789101112
    private void setViewPagerScrollSpeed(SuperViewPager viewPager) {      try {          Field field = ViewPager.class.getDeclaredField("mScroller");          field.setAccessible(true);          Scroller viewPagerScroller = new Scroller(viewPager.getContext(), new OvershootInterpolator(1.5F));          field.set(viewPager, viewPagerScroller);      } catch (NoSuchFieldException e) {          e.printStackTrace();      } catch (IllegalAccessException e) {          e.printStackTrace();      }  }
  • 5.现存bug

    页面滚动较困难,需修改touch事件

方案二 自定义ViewPager

上一个方案主要修改onLayout()方法,该方法主要修改onMeasure()方法,为了及时测量Page宽度,要求必须设置matchChildWidth属性(传入需测量的布局id)

  • 使用方法
1234567891011121314
viewpager布局<com.lcodecore.openlib.MultiViewPager        android:id="@+id/card_pager"        android:layout_width="match_parent"        android:layout_height="match_parent"        app:matchChildWidth="@+id/vg_cover"/>item布局<RelativeLayout            android:id="@+id/vg_cover"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_marginLeft="40dp"            android:layout_marginRight="40dp">
1234
java代码需要调用setOffscreenPageLimit()方法CardPagerAdapter pagerAdapter = new CardPagerAdapter(getFragmentManager(),cards);card_pager.setAdapter(pagerAdapter);card_pager.setOffscreenPageLimit(8);
  • 代码实现

自定义MultiViewPager继承自ViewPager,重写onMeasure()方法

     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         //记录测量的宽高         size.set(MeasureSpec.getSize(widthMeasureSpec),MeasureSpec.getSize(heightMeasureSpec));         if (mMaxWidth >= 0 || mMaxHeight >= 0) {             //自定义属性最大宽高             maxSize.set(mMaxWidth, mMaxHeight);             //比较             constrainTo(size, maxSize);             widthMeasureSpec = MeasureSpec.makeMeasureSpec(                     size.x,                     MeasureSpec.EXACTLY);             heightMeasureSpec = MeasureSpec.makeMeasureSpec(                     size.y,                     MeasureSpec.EXACTLY);         }         super.onMeasure(widthMeasureSpec, heightMeasureSpec);         onMeasurePage(widthMeasureSpec, heightMeasureSpec);     }      protected void onMeasurePage(int widthMeasureSpec, int heightMeasureSpec) {         // Only measure if a measurement pass was scheduled         if (!mNeedsMeasurePage) {             return;         }         if (mMatchWidthChildResId == 0) {             mNeedsMeasurePage = false;         } else if (getChildCount() > 0) {             View child = getChildAt(0);             child.measure(widthMeasureSpec, heightMeasureSpec);             int pageWidth = child.getMeasuredWidth();             View match = child.findViewById(mMatchWidthChildResId);             if (match == null) {                 throw new NullPointerException(                         "MatchWithChildResId did not find that ID in the first fragment of the ViewPager; "                                 + "is that view defined in the child view's layout? Note that MultiViewPager "                                 + "only measures the child for index 0.");             }             int childWidth = match.getMeasuredWidth();             // Check that the measurement was successful             if (childWidth > 0) {                 mNeedsMeasurePage = false;                 int difference = pageWidth - childWidth;                 setPageMargin(-difference);                 int offscreen = (int) Math.ceil((float) pageWidth / (float) childWidth) + 1;                 setOffscreenPageLimit(offscreen);                 requestLayout();             }         }     } 

方案三 修改RecyclerView

TODO

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!