自定义不占满全屏可左右滑动的卡片
效果图
方案一 修改ViewPager
实践中发现许多较为关键的类、变量、方法都是私有的,无法进行操作,只好定义SuperViewPager继承自ViewGroup,复制ViewPager里面所有内容并进行修改
1.定义两个自定义属性,pagerSpace代表两个卡片之间的距离,pageOerlayWidth代表卡片越界布局的宽度
12345678910public SuperViewPager(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()方法
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495protected 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;// First pass - decor views. We need to do this in two passes so that// 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);}mFirstLayout = false;}先计算卡片宽度为:viewpager宽度-padding值-两边的pagerSpace-两边的pageOverlayWidth
然后根据child计算左边坐标的偏移量
3.修改scrollToItem(int item, boolean smoothScroll, int velocity,
boolean dispatchSelected)方法
123456789101112131415161718192021222324252627282930313233private 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()方法中修改```javamScroller = new Scroller(context, new OvershootInterpolator(1.5F));注,ViewPager外部可通过映射的方法访问私有成员mScroller
123456789101112private 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)
- 使用方法
|
|
|
|
- 代码实现
自定义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