背景
长江后浪推前浪,无聊的需求一浪接一浪。
最近做到一个关于卡片堆叠的需求,觉得挺有意思,所以特此记录一下。
文末将附上源码链接
首先看设计图:
可以看到,是一个卡片堆叠的效果,关于这种UI的实现,方法有很多,例如用recyclerview,viewpager,甚至说自定义view都可以实现,本文将讲述如何使用viewpager实现这种效果。
开发环境
win10
jdk 8
as 4+
实现效果
由于是demo的演示,所以就不用过多在意颜色,基础功能实现即可。
问题
1、如何修改viewpager的一个卡片堆叠位置
2、如何在滑动的过程中,动态去修改卡片的宽高
思路
对于viewpager中,有个ViewPager.PageTransformer的属性,这个属性是用来干嘛的?
看一下源码的描述:
/**
* A PageTransformer is invoked whenever a visible/attached page is scrolled.
* This offers an opportunity for the application to apply a custom transformation
* to the page views using animation properties.
*
* <p>As property animation is only supported as of Android 3.0 and forward,
* setting a PageTransformer on a ViewPager on earlier platform versions will
* be ignored.</p>
*/
public interface PageTransformer {
/**
* Apply a property transformation to the given page.
*
* @param page Apply the transformation to this page
* @param position Position of page relative to the current front-and-center
* position of the pager. 0 is front and center. 1 is one full
* page position to the right, and -1 is one page position to the left.
*/
void transformPage(@NonNull View page, float position);
}
简单来讲,就是控制页面的进出场的,简单的你可以用该类实现一些viewpager页面切换进出场的效果,复杂一点的,可以动态计算viewpager滑动过程中,相关view的一个控件属性,等等。
通过继承方式,继承PageTransformer实现一个子类,重写其中的
void transformPage(@NonNull View page, float position);方法。
该方法中,第一个参数就是滑动中的view对象。第二参数,就是view的相对滑动位置。这里要注意,不是view在viewpager中的位置,仅仅是一个滑动位置,简单粗暴地可以理解为是
“负右正左零中间”。当position为0的时候,就是当前显示view,同样地-1,就是0的左边,1就是0的右边。如下图
这里的0,就是相对于position等于0的情况。
以上就是关于transformPage方法中,参数回调情况的描述。
---------------------------------分割线-------------------------------------
既然有了view对象,有了position左右滑动的回调,那么,就可以做很多事情了。首先position的数值,是受viewpager左右滑动影响,右滑为负数,左滑为正数。这个时候,可以根据左右滑动,做一些控件大小的转换!
注意,在数据处理的过程中,position的正负值影响。
实现
1、实现viewpager的卡片堆叠
通过setTranslationX,可以设置view的x位置!
通过LayoutParams,可以设置view的右侧边距!
设想一下,通过setTranslationX设置一个负值,是不是可以吧viewpager第二个位置的view直接放到第一个view的位置?只不过说,放完以后还是会被第一个view遮挡,导致看不到而已如此类推,就完成了一个viewpager中,view的位置摆放了。
再到LayoutParams,那不是送分题?根据position直接设置右边距,那不就得了吗?
说到这里,相信大部分人都已经懂了。
不懂的也没有关系,可以看下下面的代码:
@Override
public void transformPage(@NonNull View view, float position) {
int pagerWidth = boundViewPager.getWidth();
LogUtil.d("transformPage tag: " + view.hashCode() + " pos: " + position + " pagerWidth: " + pagerWidth);
float scaleWidth = pagerWidth * CENTER_PAGE_SCALE;
float widthInterval = (pagerWidth - scaleWidth) / 2;
view.setScaleX(CENTER_PAGE_SCALE);
view.setScaleY(CENTER_PAGE_SCALE);
//设置间距----------------------------------------------------------------------
ViewGroup llRoot = view.findViewById(R.id.llRoot);
if (llRoot != null) {
ViewGroup.LayoutParams layoutParams = llRoot.getLayoutParams();
if (layoutParams instanceof RelativeLayout.LayoutParams) {
((RelativeLayout.LayoutParams) layoutParams).setMarginEnd((int) ((2 - Math.abs(position)) * endInterval));
((RelativeLayout.LayoutParams) layoutParams).topMargin = (int) (Math.abs(position) * verticalInterval);
((RelativeLayout.LayoutParams) layoutParams).bottomMargin = (int) (Math.abs(position) * verticalInterval);
llRoot.setLayoutParams(layoutParams);
} else if (layoutParams instanceof CardView.LayoutParams) {
((FrameLayout.LayoutParams) layoutParams).setMarginEnd((int) ((2 - Math.abs(position)) * endInterval));
((FrameLayout.LayoutParams) layoutParams).topMargin = (int) (Math.abs(position) * verticalInterval);
((FrameLayout.LayoutParams) layoutParams).bottomMargin = (int) (Math.abs(position) * verticalInterval);
llRoot.setLayoutParams(layoutParams);
}
}
//设置偏移量----------------------------------------------------------------------
if (position >= 0) {
view.setTranslationX(-pagerWidth * position);
}
if (position > -1 && position < 0) {
view.setAlpha((position * position * position + 1));
} else if (position > offscreenPageLimit - 1) {
view.setAlpha((float) (1 - position + Math.floor(position)));
} else {
view.setAlpha(1);
}
}
}
简洁明了,两个方法,即可实现。
好了,实现了交互,那么,还有一个“无限循环”的问题!
方案无非是两种,一种是adapter getCount设置为无限大,一种是通过前后补数据给用户做一个视觉差。但是具体到我们这里的实现,明显是第一种方法比较合适。这里也不过多叙述了。
最后,源码地址:代码地址
搜索VPTestActivity类
that's all--------------------------------------------------------------------------------------------------