ViewPagerTransforms的深入理解
github地址:
每當viewpager上一個可見或依附的頁面發生了滾動事件就會調用PageTransformer,這讓應用可以使用自定義transformation讓viewpager某一個頁面視圖上實現某些特定的動畫屬性。
但是這樣的屬性動畫只能支持到android3.0版本或以上,在早期的版本上設置viewpager的PageTransformer會被忽略。
公有方法
transformPage 應用屬性動畫到一個指定的頁面
void transformPage (View page, float position)
參數 | 解釋 |
---|---|
pager | View: 應用切換動畫到哪一個頁面上 |
position | float: position表示相對於當前頁正中的位置,0表示在正中的這個頁面,1表示右邊一個完整的頁面,-1表示左邊一個完整的頁面 |
首先是一個很平常的viewpager
package com.example.simple.pagertransformdemo;
public class MainActivity extends AppCompatActivity {
private int imgIds[] = {R.drawable.img_1, R.drawable.img_2, R.drawable.img_3};
private List<View> viewList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < 3; i++) {
View rootView = View.inflate(MainActivity.this, R.layout.item_pager, null);
ImageView imageView = (ImageView) rootView.findViewById(R.id.pager_iv);
imageView.setImageResource(imgIds[i]);
TextView textView = (TextView) rootView.findViewById(R.id.pager_tv);
textView.setText(String.valueOf(i));
viewList.add(i, rootView);
}
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
viewPager.setAdapter(new MyAdapter());
}
private class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return 3;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(viewList.get(position));
return viewList.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(viewList.get(position));
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.simple.pagertransformdemo.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/pager_iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
/>
<TextView
android:id="@+id/pager_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="xxxx"
android:layout_centerInParent="true"
android:textSize="20sp"
android:textColor="@android:color/holo_red_dark"
/>
</RelativeLayout>
接着是一個放大進入的viewpager
我們多加了一行代碼
viewPager.setPageTransformer(true ,new ZoomInTransform());
看看這這句代碼的作用
參數 | 解釋 |
---|---|
reverseDrawingOrder | boolean值,true表示提供的PageTransformer畫view時是倒序,false則是正序 |
transformer | 將修改每一頁動畫屬性的PageTransformer |
看源碼也可以得出
public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
if (Build.VERSION.SDK_INT >= 11) {
final boolean hasTransformer = transformer != null;
final boolean needsPopulate = hasTransformer != (mPageTransformer != null);
mPageTransformer = transformer;
setChildrenDrawingOrderEnabledCompat(hasTransformer);
if (hasTransformer) {
//reverseDrawingOrder等於true時爲倒序,false爲正序
mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;
} else {
mDrawingOrder = DRAW_ORDER_DEFAULT;
}
if (needsPopulate) populate();
}
}
將 reverseDrawingOrder 改爲 false 看看效果
viewPager.setPageTransformer(false ,new ZoomInTransform());
再看看ZoomInTransform的代碼就一目瞭然了
public class ZoomInTransform implements ViewPager.PageTransformer {
public static final String TAG = "simple_PagerTransform";
@Override
public void transformPage(View page, float position) {
int width = page.getWidth();
int height = page.getHeight();
//這裏只對右邊的view做了操作
if (position > 0 && position <= 1) {//right scorlling
//position是1.0->0,但是沒有等於0
Log.e(TAG, "right----position====" + position);
//設置該view的X軸不動
page.setTranslationX(-width * position);
//設置縮放中心點在該view的正中心
page.setPivotX(width / 2);
page.setPivotY(height / 2);
//設置縮放比例(0.0,1.0]
page.setScaleX(1 - position);
page.setScaleY(1 - position);
} else if (position >= -1 && position < 0) {//left scrolling
} else {//center
}
}
}
我們還可以看看transformPage是怎麼來的
//這是viewpager源代碼的一部分,transformPage方法的來源
if (mPageTransformer != null) {
final int scrollX = getScrollX();
final int childCount = getChildCount();
//遍歷viewpager所有的子view
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.isDecor) continue;
//position等於該view的左起點減去在X軸的位移除以viewpager的寬度,也就是算出真正的位移比例
final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
mPageTransformer.transformPage(child, transformPos);
}
}
下面來一個炫酷點的,感覺有點3D效果的樣子
public class SimplePageTransform implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
int width = view.getWidth();
int pivotX = 0;
if (position <= 1 && position > 0) {// right scrolling
pivotX = 0;
} else if (position == 0) {
} else if (position < 0 && position >= -1) {// left scrolling
pivotX = width;
}
//設置x軸的錨點
view.setPivotX(pivotX);
//設置繞Y軸旋轉的角度
view.setRotationY(90f * position);
}
}
我們再將transformPage改造一下
@Override
public void transformPage(View view, float position) {
if (position <= 1 && position > 0) {
int hashCode = view.hashCode();
Log.d(TAG,"right hashCode="+hashCode);
} else if (position == 0) {
int hashCode = view.hashCode();
Log.d(TAG,"center hashCode="+hashCode);
} else if (position < 0 && position >= -1) {
int hashCode = view.hashCode();
Log.w(TAG,"left hashCode="+hashCode);
}
}
看看log信息
下一頁的view hashcode
left hashCode=75472620
right hashCode=55231157
center hashCode=55231157再下一頁的view hashcode
left hashCode=55231157
right hashCode=45932420
center hashCode=45932420上一頁的view hashcode
left hashCode=55231157
right hashCode=45932420
center hashCode=55231157再上一頁的view hashcode
left hashCode=75472620
right hashCode=55231157
center hashCode=75472620
由此以及上面transformPage方法的出處可得出規律,transformPage返回的view並不是一個,而是根據相應的position的返回相應的view。
也由此可以提出viewpager的座標系(ps:不會畫圖請不要打我)
淺藍色爲left view,紅色爲center view,深藍色爲right view,當滑動事件開始,他們的關係也會跟變化,所以可得出在[-1,0)這個區間爲left,(0,1]這個區間爲right,只有當等於0才爲center。如果viewpager裏面view很多肯定就還會有-2,+2等等的其他座標點,但是我們只關注left,center,right這3個view上就可以實現很多炫酷的動畫啦!
總結
- 給viewpager加動畫並不難,只需要實現PageTransformer類transformPage的方法,再利用相應的view和position加上view屬性動畫就好啦!
- 而且還可以設置view切換時的順序,想要實現炫酷的動畫也不難,只要肯動腦。
- transformPage返回的view並不是一個view,而是根據position返回相應的view。
- transformPage返回的view的區間[-1,0)這個區間爲left,(0,1]這個區間爲right,只有當等於0爲center
- viewpager的PageTransformer是在3.0以後加的新特性
- ps:如果想兼容3.0以下也有辦法,要麼用反射,要麼就自己自定義一個view實現viewpager所有功能,其實android很多兼容包都會利用反射技能去兼容低版本無法使用新特性這樣一個缺點