前言
上一篇叨叨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);
}
}
重跑程序,問題解決。
爲解決問題臨時想的方案,是實現更新的一條思路但感覺不夠優,歡迎各路大神指點。
最後
本想多寫一點,但動畫已經佔了不少篇幅,爲避免超長文先在這結束吧。這個系列會努力補充,歡迎多多指點和關注。