ViewPager實現炫酷的滑動縮放廣告頁
ViewPager是Android開發中一個重要的控制元件,其輕量級的多頁展示功能簡化了開發過程,使我們能夠快速構建一個App的引導頁。但是,如果只是用來做引導頁,那就太浪費了,其實我們可以將它打造的更炫酷一點。
效果圖
(製作的gif圖不知道為什麼不能顯示,所以只能用一張靜態圖片代替了)
需求分析
- 頁面滑動過程中,中間頁向兩邊滑動的過程中進行縮小,兩邊頁面向中間滑動的過程中進行放大,
- 頁面滑動過程中底部顯示選中頁標誌;
- 頁面範圍內都可以滑動;
實現
通過對上面的需求進行分析,我們先確定要實現的位置,需求一要求在縮放過程中進行縮放,那隻能在ViewPager的滑動監聽過程中實現了。
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged (int i) {
}
});
ViewPager的滑動監聽中有三個方法, onPageScrolled在滑動過程中被呼叫,onPageSelected當一個新的頁面被選中時被呼叫(注意:動畫此時不一定結束),onPageScrollStateChanged在滑動狀態改變時被呼叫(i==1表示開始滑動,i==2表示當前頁被選中,i==0表示滑動和動畫都已結束,注意:i==2的狀態後會迅速呼叫i==0的狀態,所以滑動過程中i的變化過程為:i=1 --> i=2 –> i=0)。瞭解了滑動監聽的過程,我們很容易看出,滑動過程的動畫只能在onPageScrolled進行實現了。
onPageScrolled函式原型中的引數說明:
- position:當前選中頁的index, 當posistionOffset為非零時position++;
- positionOffset:當前選中頁的位置偏移量,取值範圍[0, 1)。
- positionOffsetPixels:當前選中頁的偏移距離, 取值範圍[0, viewpager的寬度+ViewPager中頁面的間距)。
滑動過程中引數的變化:
- 左滑過程中positionOffset和positionOffsetPixels逐漸變大,變到 最大後都又變成0,此時position++;
- 右滑過程中positionOffset和positionOffsetPixels從最大值逐漸變為0,此過程中position始終是當前選中頁的前一頁的index;
瞭解了上面函式的引數含義後開始實現我們的需求一:
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// 雖然一屏只顯示三頁,但是當我們滑動的過程中需要設定四頁的縮放效果,因為左邊的頁面被滑出去之後,頁面上將顯示的是後面的三頁
View leftImage = activityMap.get(position - 1);
View currentImage = activityMap.get(position);
View rightImage = activityMap.get(position + 1);
View rightImage2 = activityMap.get(position + 2);
if (positionOffset > 0) {
float scaleAlpha1 = 1 - positionOffset / 4.0f;
float scaleAlpha2 = 0.75f + positionOffset / 4.0f;
// 設定頁面滑動過程中的縮放效果
currentImage.setAlpha(scaleAlpha1);
currentImage.setScaleX(scaleAlpha1);
currentImage.setScaleY(scaleAlpha1);
if (leftImage != null) {
leftImage.setAlpha(scaleAlpha2);
leftImage.setScaleX(scaleAlpha2);
leftImage.setScaleY(scaleAlpha2);
}
if (rightImage != null) {
rightImage.setAlpha(scaleAlpha2);
rightImage.setScaleX(scaleAlpha2);
rightImage.setScaleY(scaleAlpha2);
}
if (rightImage2 != null) {
rightImage2.setAlpha(scaleAlpha1);
rightImage2.setScaleX(scaleAlpha1);
rightImage2.setScaleY(scaleAlpha1);
}
} else {
// 設定頁面初次啟動時的縮放效果
if (leftImage != null && currentImage.getAlpha() == 1) {
leftImage.setAlpha(0.75f);
leftImage.setScaleX(0.75f);
leftImage.setScaleY(0.75f);
}
if (rightImage != null && currentImage.getAlpha() == 1) {
rightImage.setAlpha(0.75f);
rightImage.setScaleX(0.75f);
rightImage.setScaleY(0.75f);
}
}
}
需求二其實簡單,只需要在onPageSelected重新設定選中狀態即可:
@Override
public void onPageSelected(int position) {
selectCurrentDot(position);
}
/**
* 設定當前選中頁的選中狀態(只有一頁時不顯示選中狀態)
* @param position 當前選中頁的index
*/
private void selectCurrentDot(int position) {
if (imageList.size() > 1) {
for (int i = 0; i < indicatorIcon.length; i++) {
if (i == position) {
indicatorIcon[i].setBackgroundResource(R.drawable.indicator_selected);
} else {
indicatorIcon[i].setBackgroundResource(R.drawable.indicatorunselect);
}
}
}
}
到這一步,我們的滑動縮放廣告欄看起來已經完成了,但是滑動的過程中發現只有ViewPager頁面範圍內可以滑動,兩邊的位置都不能滑動,看了下ViewPager的原始碼, 發現它重寫了ViewGroup的dispatchTouchEvent函式,並且也是是公開的,那我們在ViewPager的父View的onTouch事件中返回ViewPager的dispatchTouchEvent應該就可以像ViewPager一樣滑動了。
containLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return viewPager.dispatchTouchEvent(event);
}
});
再次執行發現可以在ViewPager的父View範圍內滑動了。至此,整個滑動過程就完成了。下面貼一下ViewPagerAdapter的程式碼:
private class MyPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return imageList.size();
}
@Override
public boolean isViewFromObject(View view, Object obj) {
return view == obj;
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
RoundImageView imageView = new RoundImageView(context);
ViewGroup.LayoutParams params = container.getLayoutParams();
params.width = getScreenWidth() - dpToPx(140);
params.height = (int)(params.width * H_W);
imageView.setLayoutParams(params);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageDrawable(imageList.get(position));
imageView.setTag(position);
container.addView(imageView);
activityMap.put(position, imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "觸發了點選事件", Toast.LENGTH_SHORT).show();
}
});
return imageView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
}
原始碼
遇到的問題
- ViewPager一屏不能顯示三頁;
- 只有ViewPager的位置可以滑動,兩邊不能滑動;
- ViewPager滑動過程中出現卡頓,多次滑動後App卡死;
解決方案
- 在ViewPager,ViewPager的父佈局和ViewPager的根佈局中新增屬性android:clipChildren=”false”(注意:ViewPager不加此屬性時有些手機會顯示異常);
- 重寫ViewPager父佈局的onTouch事件;
- 先讀取所有的圖片內容,得到drawable或bitmap, 然後設定給ViewPager中的圖片,同時設定ViewPager的預載入時需要預載入所有的Item,只有這樣ViewPager滑動過程中才不會出現卡頓(具體原因有待進一步深究)。通過這種方案,載入了30頁1080*540大小的圖片沒有出現卡頓情況。
如果有什麼錯誤之處,歡迎指正!順便問一下,你們都用什麼軟體來做gif截圖,嘗試了一下感覺很麻煩,而且還不理想。