自定義ViewPager切換動畫

實現(xiàn)基礎(chǔ)的ViewPager###

1.主布局文件activity_main.xml,一個用于放ViewPager的RelativeLayout容器,里面就是我們用于展示的ViewPager了。

<?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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg"
    tools:context="com.momo.pagetransformerdemo.activity.MainActivity">

    <RelativeLayout
        android:id="@+id/rl_container"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:clipChildren="false"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="50dp">

        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:clipChildren="false"
            android:layout_centerHorizontal="true">
        </android.support.v4.view.ViewPager>

    </RelativeLayout>
</RelativeLayout>

這里有個需要注意的地方:RelativeLayout和ViewPager的clipChildren屬性都需要設(shè)置為false,目的是讓ViewPager左邊右邊的界面也顯示出來,不然ViewPager只會顯示中間ViewPager在布局上所占的那部分空間。如果不清楚直接看下圖:

clipChildren=“false”
clipChildren=“true”

2.ViewPager的Adapter類,在構(gòu)造函數(shù)中傳入要顯示的數(shù)據(jù)源,這里就是每個page上要顯示的文本。在instantiateItem()方法中加載要顯示的頁面,找到其中的TextView,設(shè)置要顯示的文本。至于這四個函數(shù)的作用可以參考ViewPager 詳解(二)---詳解四大函數(shù)

MyPagerAdapter.java

public class MyPagerAdapter extends PagerAdapter {
    private String[] mDatas;
    private Context mContext;

    public MyPagerAdapter(Context context,String[] mDatas) {
        this.mContext =context;
        this.mDatas = mDatas;
    }

    @Override
    public int getCount() {
        return mDatas.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.view_pager_item,null);
        TextView tv = (TextView) view.findViewById(R.id.tv_text);
        tv.setText(mDatas[position]);
        container.addView(view);
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        View view = (View) object;
        container.removeView(view);
    }

3.ViewPager的每個界面布局view_pager_item.xml,就是一個Textview,沒啥好說的。

<?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"
    android:background="@drawable/item_bg">

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Page 1"
        android:textSize="20dp"
        android:layout_centerInParent="true"
        android:textColor="#ffffff"
        />
</RelativeLayout>

4.MainActivity.java

public class MainActivity extends AppCompatActivity {

    private RelativeLayout rl_container;
    private ViewPager viewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findId();
        initViewPager();
    }

    private void findId() {
        viewPager = (ViewPager) findViewById(R.id.view_pager);
        rl_container = (RelativeLayout) findViewById(R.id.rl_container);
    }

    private void initViewPager() {
        //創(chuàng)建數(shù)據(jù)源
        String[] stringArr = new String[8];
        for(int i=0;i<stringArr.length;i++){
            stringArr[i] = "Page" + String.valueOf(i);
        }
        //設(shè)置Adapter
        viewPager.setAdapter(new MyPagerAdapter(this,stringArr));
        //設(shè)置緩存頁面數(shù)量
        viewPager.setOffscreenPageLimit(stringArr.length/2);
        //設(shè)置頁面間距
        viewPager.setPageMargin(10);
    }
}

寫到這里我們可以來運行一下,結(jié)果發(fā)現(xiàn)只有中間滑動有效果,而如果在兩邊滑動時,ViewPager是不會切換頁面的。這是因為點擊事件被容器RelativeLayout攔截了。加入下面的代碼就能解決問題了。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        initViewPager();
        
        rl_container.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return viewPager.dispatchTouchEvent(event);
            }
        });
    }

給RelativeLayout設(shè)置OnTouchListener使得當(dāng)其接收到觸摸事件時會將該事件轉(zhuǎn)給ViewPager進行分發(fā)。這樣就能讓ViewPager處理左右兩邊的觸摸事件了。

至此,一個簡單的ViewPager就完成了,來看看效果。

PageTransformer###

這個類是我們實現(xiàn)自定義切換動畫的核心。ViewPager有一個setPageTransformer()方法用于設(shè)置切換動畫。goolgle官方提供了兩個動畫效果,DepthPageTransformer和ZoomOutPageTransformer,具體效果可以參考一下鴻洋大神的這篇博客Android 實現(xiàn)個性的ViewPager切換動畫 實戰(zhàn)PageTransformer(兼容Android3.0以下)。關(guān)于下面要講到的position的意義,如果覺得博主講的不清楚也可以參考這篇博客Android 自定義 ViewPager 打造千變?nèi)f化的圖片切換效果。

我們看到PageTransformer中要實現(xiàn)這樣一個函數(shù):

@Override
public void transformPage(View page, float position) {

 }  

解釋一下,這里的page指的就是在滑動的頁面,而position是指該頁面的位置,用下面這張圖解釋:

2017-02-26_195739.png

position取值可以看成該頁面左上角的位置

  • 當(dāng)position<-1的時候,表示該page處在當(dāng)前顯示頁面的左邊一個頁面位置以外。
  • 當(dāng)position=-1的時候,表示該page處在當(dāng)前顯示頁面的左邊一個頁面位置,此時該頁面的右上角就是當(dāng)前顯示頁面的左上角。
  • 當(dāng)position=0的時候,表示該page處在當(dāng)前顯示頁面位置
  • 當(dāng)position=1的時候,表示該page處在當(dāng)前顯示頁面的右邊一個頁面位置,此時該頁面的左上角就是當(dāng)前顯示頁面的右上角。
  • 當(dāng)position>1的時候,表示該page處在當(dāng)前顯示頁面的右邊一個頁面位置以外。

舉個栗子:


上圖中page0的position為-2,page1為-1,page2為當(dāng)前顯示頁面,position為0,page3為1,page4為2。在page2往左邊劃的過程中,page2的position值會從1變到0,同理,page3的position值會從1變到0。

有了這樣的一個position值,便可以開始制作動畫了。
制作動畫前先看一下要用到的幾個函數(shù)(這里可以參考這篇博客【Android開發(fā)】View的平移、縮放、旋轉(zhuǎn)以及位置、坐標(biāo)系)

  • page.setTranslationX(float x)
    page.setTranslationY(float y)

    這兩個函數(shù)用于設(shè)置page在x,y方向的位移
  • page.setScaleX(float scaleX);
    page.setScaleY(float scaleY);

    這兩個函數(shù)用于設(shè)置page在x,y方向的放縮比例
  • page.setAlpha(float alpha)
    這個函數(shù)用于設(shè)置page的透明度
  • page.setRotation(float rotation)
    page.setRotationX(float rotationX)
    page,setRotationY(float rotationY)

    這三個函數(shù)用于設(shè)置page在x,y,z軸方向的旋轉(zhuǎn)角度
  • page.setPivotX(float pivotX)
    page.setPivotY(float pivotY)

    這兩個函數(shù)是最重要的,也相對比較難理解,用于設(shè)置view縮放和旋轉(zhuǎn)的錨點坐標(biāo),正常情況下page的錨點在正中心位置

錨點的位置,將決定page縮放后所在的位置。因為默認是中心坐標(biāo),所以縮放后,其結(jié)果在水平中心位置或垂直中心位置。但是,如果錨點的位置變了,那么View縮放后的位置也將發(fā)生變化。
旋轉(zhuǎn)時,將會以錨點所在的軸線為軸進行旋轉(zhuǎn),比如:

  • 不同錨點setRotation(90)的效果(紅點為錨點位置)
2017-02-26_205747.png
  • 不同錨點setRotationY(45)的效果(紅點為錨點位置)


    setRotationY(45)
注意兩張page0的區(qū)別

了解以上幾個函數(shù)的效果就可以動手寫動畫了,所謂動畫無非就是像總結(jié)一個函數(shù)一樣,對于任意的position能找到一個特定的屬性與之對應(yīng),這個值可以是透明度、旋轉(zhuǎn)角度、位移等等。
舉個栗子,如果我們想實現(xiàn)下面這種動畫,只要找出這樣一個對應(yīng)的函數(shù)就可以了。

** 總結(jié)規(guī)律:**

  • position<=-1時, translationY = 100
  • -1<position<0時, translationY = -position*100(此時position為負)
  • position=0時, translationY = 0
  • 0<position<1時, translationY = position*100
  • position>=1時, translationY =100;

我們還可以根據(jù)上面的規(guī)律畫成圖像:


translationY對應(yīng)的函數(shù)圖像

有了這樣清晰的概念寫出動畫就不是什么難事了。下面看源碼:

@Override
public void transformPage(View page, float position) {
    page.setPivotY(page.getHeight()/2);
    float maxTransform = page.getHeight()/4;
    if (position <= -1)
    { // [-Infinity,-1)
        // This page is way off-screen to the left.
        page.setTranslationY(maxTransform);
    } else if (position < 1)
    { // [-1,1]
        // Modify the default slide transition to shrink the page as well
        if (position < 0)//[0,-1]
        {
            page.setTranslationY(-position * maxTransform);
        } else//[1,0]
        {
            page.setTranslationY(position * maxTransform);
        }
    } else
    { // (1,+Infinity]
        // This page is way off-screen to the right.
        page.setTranslationY(maxTransform);
    }
}

再看個復(fù)雜點的

@Override
public void transformPage(View page, float position) {
    page.setPivotY(page.getHeight()/2);
    float maxRotate = 35f;
    float minScale = 0.8f;
    float maxTranslationX = page.getWidth()/5;
    if (position <= -1)
    { // [-Infinity,-1)
        // This page is way off-screen to the left.
        page.setRotationY(maxRotate);
        page.setPivotX(0);
        page.setScaleX(minScale);
        page.setScaleY(minScale);
        page.setTranslationX(maxTranslationX);
    } else if (position < 1)
    { // [-1,1]
        page.setRotationY(-position * maxRotate);
        if (position < 0)//[0,-1]
        {
            page.setPivotX(0);
            page.setScaleX(1 + position * (1-minScale));
            page.setScaleY(1 + position * (1-minScale));
            page.setTranslationX(-position * maxTranslationX);
        } else//[1,0]
        {
            page.setPivotX(page.getWidth());
            page.setScaleX(1 - position * (1-minScale));
            page.setScaleY(1 - position * (1-minScale));
            page.setTranslationX(-position * maxTranslationX);
        }
    } else
    { // (1,+Infinity]
        // This page is way off-screen to the right.
        page.setRotationY(-1 * maxRotate);
        page.setPivotX(page.getWidth());
        page.setScaleX(minScale);
        page.setScaleY(minScale);
        page.setTranslationX(-maxTranslationX);
    }
}

最后啰嗦兩句###

總算寫完了,第一次寫博客,找各種各樣的工具花了好長時間,錄屏使用的是手機app錄屏專家,再傳到電腦上使用Free Video to GIF Converter轉(zhuǎn)成gif,斷斷續(xù)續(xù)花了一晚上。如果大家有好的錄屏軟件推薦麻煩給博主留個言,謝謝了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 我們都知道項目里的廣告輪播大部分都是viewpager做的,viewpager的使用也是非常簡單的,相信大家...
    vison123閱讀 915評論 0 4
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,326評論 25 708
  • 這篇文章不是我自己寫的, 是 Stew 寫的,我發(fā)在我的簡書,為了公眾賬號可以有一個原文鏈接,因為公眾賬號比較看代...
    張瑞Jerrysher閱讀 6,772評論 4 4
  • 我已經(jīng)有一年半沒有進過簡書。 今日的手機真好,怕是以往,滿肚子的牢騷不知往何處傾倒。秘密說給別人聽就不叫是秘密了,...
    悟空你又搗蛋閱讀 520評論 0 1
  • 給自己一年時間,做開發(fā),明年,一定轉(zhuǎn)產(chǎn)品~
    邁進的香香閱讀 224評論 0 0

友情鏈接更多精彩內(nèi)容