叨叨ViewPager那些事儿(二)
前言
上一篇叨叨ViewPager那些事儿(一)说了一点ViewPager概况,这篇打算说说实际应用。
从动画效果说起
先祭上一份谷歌官方的Transformer示例
效果如下
代码实现是这样
public class DepthPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
乍一看有点懵懂,一步一问 慢慢来
?transformPage(View view, float position)
两个参数是何含义
第一个参数为各页卡对象,第二个是各个页卡对于当前所展示页卡的相对位置,例如,当前页卡的position为0,下一张位置为1,前一张则为-1。而ViewPager滑动中,position的值是处于平滑变化中的,这就为我们处理动画提供了机会。
?为何position < -1
和position > 1
时设置可见度为0
如前问分析,position < -1
和position > 1
指代页卡所处区间为[-Infinity,-1)和(1,+Infinity],在可见范围之外,从节省绘制资源的角度出发,自然可见度为0。
?position (0,1]
区间时为何要设置偏移度为(pageWidth * -position)
官方注释Counteract the default slide transition
,抵消页面滑动时的默认偏移量。如页面处在中间时偏移量为0*width
,移至右边为1*width
,从中间往右移除时position
从0-->1
渐变,默认偏移量为position*width
?滑动动画的缩放效果如何计算
如效果图示,当下一张页卡相对位置从1-->0
变化时,scale的变化方向为MIN_SCALE(m)-->1
,不妨列个公式
(1-position)/(position-0)=(m-scale)/(scale-1)
即s=(1-position)/(1-position*m)
(对。。跟示例代码不一样。。但是效果一样哦。。
也可以理解为1-scale=|(m-1)|/(1-0)*position
,scale从1-->m,位置从0-->1,即每单位position的变化量为|(m-1)|/(1-0),乘上position得出变化量。
理论储备到位之后,自力更生的第一步就是,实践!
下边我们来写一个左右位移,同时展示三张卡片的动画,预想中效果大概是这样
根据设想计算,左右两边的页卡大小应为中间大页卡的0.9倍,左右页卡的偏移量约为±0.2倍页卡宽。
故设定
MIN_Trans_INDEX = 0.2f
,MIN_SCALE_INDEX = 0.9f
。如上分析,我们只关心[-1,1]区间页卡的动画,每单位position的scale变化量绝对值为(1-MIN_SCALE_INDEX)/(1-0),即
1-scale=|position|*(1-MIN_SCALE_INDEX)/(1-0)
即scale=1-|position|*(1-MIN_SCALE_INDEX)
同理,每单位position的translationx变化量为pageWidth*(1-MIN_Trans_INDEX)/(1-0),即
|0-translationx|=|position|*pageWidth*(1-MIN_Trans_INDEX)
即|translationx|=|position|*pageWidth*(1-MIN_Trans_INDEX)
,然而viewpager页卡滑动过程中的默认偏移量绝对值为pageWidth * position,故需在上式基础上减pageWidth * position,|translationx|=|position|*pageWidth*MIN_Trans_INDEX
计算完毕,写入代码后发现,效果是出来了,但是反应好像永远。慢半拍。当前页滑动到位后,下一页才出现,体验不太美妙,如下动图所示
查看代码后思考,应该是[-1,1]位置限定导致,左右两页分别滑动到-1和1时才开始做动画,而此时这两张页卡已经出现在屏幕上,所以看起来就像“慢了半拍”。
尝试增加一点“缓冲量”,改动如下
public class MyTransPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_Trans_INDEX = 0.2f;
private static final float MIN_SCALE_INDEX = 0.9f;
@Override
public void transformPage(@NonNull View page, float position) {
int pageWidth = page.getWidth();
if (position < -1.1) { // [-Infinity,-1.1)
// This page is way off-screen to the left.
page.setAlpha(0);
} else if (position <= 0) { // [-1.1,0]
// Use the default slide transition when moving to the left page
page.setAlpha(1);
page.setTranslationX(-pageWidth * position * MIN_Trans_INDEX);
page.setScaleX((1 - MIN_SCALE_INDEX) * position + 1);
page.setScaleY((1 - MIN_SCALE_INDEX) * position + 1);
Log.i("test", page.getTag()+"--"+((1 - MIN_SCALE_INDEX) * position + 1));
} else if (position <= 1.1) { // (0,1.1]
// Fade the page out.
page.setAlpha(1);
// Counteract the default slide transition
page.setTranslationX(-pageWidth * position * MIN_Trans_INDEX);
page.setScaleX(1 - (1 - MIN_SCALE_INDEX) * position);
page.setScaleY(1 - (1 - MIN_SCALE_INDEX) * position);
} else { // (1.1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
}
再次运行。这才是想要的效果嘛
问题来了
之前遇到过一个现象,刷新ViewPager,调用notifyDataSetChanged()
,如下。
诶,怎么肥事!左右两边的页卡去哪了!冷静一下,刷新时对当前页卡重绘,若不需滚动,则pageoffset始终为0,transformer的动画效果无法显示。看来要想平稳刷新,还需手动更新单条数据。
行动起来,更改如下逻辑
@Override
public void onClick(View v) {
if (v.getId() == R.id.tv_notify) {
if (mCurPageIndex == 3) {//测试,代表逻辑需跳转到的页卡
isNeedNotify = true;
}
//更新当前页卡数据
updateViewPager(mCurPageIndex);
}
}
private void updateViewPager(int position) {
if (isNeedNotify) {//需要滚动时,调用notifyDataSetChanged重走instantiateItem和destroyItem逻辑
mPagerAdapter.notifyDataSetChanged();
mViewPager.setCurrentItem(3);
isNeedNotify = false;
}
//仅需更新当前页时则单独刷新当前页卡view
MyViewPagerItem item = mPageItemList.get(position);
if (item != null) {
PageItemBean bean = new PageItemBean();
bean.num = position;
bean.tip = mContext.getString(R.string.num_tip, position + 1 + "");
item.updateUI(bean);
}
}
重跑程序,问题解决。
为解决问题临时想的方案,是实现更新的一条思路但感觉不够优,欢迎各路大神指点。
最后
本想多写一点,但动画已经占了不少篇幅,为避免超长文先在这结束吧。这个系列会努力补充,欢迎多多指点和关注。