ViewPager個性化切換

一、概述

如果大家關注了我的微信公衆號的話,一定知道我在5月6號的時候推送了一篇文章,文章名爲Android超高仿QQ附近的人搜索展示(一),通過該文可以利用ViewPager實現單頁顯示多個Item且能夠添加一些炫酷的動畫效果。我當時閱讀這篇文章的時候,簡單做了下記錄,然後想了想,可以按照該思路做一個比較特殊輪播效果,如圖:

其實看到這個大家肯定不陌生,對於ViewPager切換有個很出名的庫叫JazzViewPager,沒錯,我又跑了下JazzyViewPager的例子,看看有什麼動畫效果可以借鑑的,ok,最終呢,產生以下幾個效果圖。

貼效果圖前,簡單說下我的公衆號,恩,我是在上週決定正式開始好好打理的,目前很多東西都在嘗試階段,當然支持大家的投稿,目前存在一些文章過長,或者代碼過長的排版問題,不過都在嘗試改善與解決,以及對推送文章的選材都在考慮,所以多謝大家的支持,也歡迎大家的關注(二維碼在側欄),相信我一定會做的更好。

此外,針對不好閱讀的問題,大家可以通過該倉庫,看到所有推送文章的一個列表,https://github.com/hongyangAndroid/hongyangWeixinArticles該倉庫會和公衆號推送的文章同步更新。

下面進入正題,本文主要是利用ViewPager做類似上圖風格的Banner,這種Banner在app上不是很常見,不過在web端還有tv的app上還是很常見的。

不過原理很簡單,說到核心,就兩個地方:

  • android:clipChildren="false"
  • viewPager.setPageTransformer

很久之前也寫過類似的文章,可以參考

二、效果圖

  • Rotate Down

  • Rotate Up

  • ScaleIn

貼三個意思下,恩,更多效果見https://github.com/hongyangAndroid/MagicViewPager.

三、ViewPager一屏顯示多個頁面

ok,首先說明下控件,上述效果採用的控件是ViewPager,大家都清楚哇,使用ViewPager一般我們都是一屏幕顯示一個頁面,那麼如何做到一屏顯示多個頁面呢?

ViewPager如何做到一屏顯示多個頁面呢?

原理就一個屬性Android:clipChildren="false",該屬性的意思就是在子View進行繪製時不要去裁切它們的顯示範圍。ok,知道要使用這個屬性之後,剩下的事情就不麻煩了:

我們的佈局文件這麼寫:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="160dp"
    android:clipChildren="false"
    android:layout_centerInParent="true"
    android:background="#aadc71ff"
    >
    <android.support.v4.view.ViewPager
        android:id="@+id/id_viewpager"
        android:layout_width="match_parent"
        android:layout_marginLeft="60dp"
        android:layout_marginRight="60dp"
        android:clipChildren="false"
        android:layout_height="120dp"
        android:layout_gravity="center"
        >
    </android.support.v4.view.ViewPager>

</FrameLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

我們設置了ViewPager外層控件以及ViewPager都設置了android:clipChildren="false"

我們的ViewPager的寬度是match_parent,左後個設置了60dp的邊距,就是爲了顯示出左右部分的Page.

接下來可以對ViewPager設置Adapter等相關屬性。

public class MainActivity extends AppCompatActivity
{

    private ViewPager mViewPager;
    private PagerAdapter mAdapter;

    int[] imgRes = {R.drawable.a, R.drawable.b, R.drawable.c...};

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

        mViewPager = (ViewPager) findViewById(R.id.id_viewpager);
         //設置Page間間距
        mViewPager.setPageMargin(20);
        //設置緩存的頁面數量
        mViewPager.setOffscreenPageLimit(3);
        mViewPager.setAdapter(mAdapter = new PagerAdapter()
        {
            @Override
            public Object instantiateItem(ViewGroup container, int position)
            {
                ImageView view = new ImageView(MainActivity.this);
                view.setImageResource(imgRes[position]);
                container.addView(view);
                return view;
            }

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

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

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

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

ok,沒有任何複雜的地方,注意

 //設置Page間間距
mViewPager.setPageMargin(20);
  • 1
  • 2
  • 1
  • 2

以及

//設置緩存的頁面數量
 mViewPager.setOffscreenPageLimit(3);
  • 1
  • 2
  • 1
  • 2

我們這裏最多可見就是3頁。

此時運行:

可以看到,我們已經實現了單屏幕顯示出多個page,而且是ViewPager所以肯定可以左右滑動。

這麼看,是不是非常簡單,接下來就是加特效了,大家都清楚對於ViewPager可以通過設置PageTransformer來利用屬性動畫來設置特效,注意目前該方法添加的動畫在3.0即以上的手機中有效,因爲3.0以下並不存在屬性動畫,所以setPageTransformer內部加了個判斷,不過現在已經幾乎沒有3.0以下的手機了,但是如果你非要較真,參考文章開始時給出的兩篇文章,裏面有解決方案。

四、給ViewPager加特效

這裏我們簡單抽取兩個動畫效果來講,其實以前的文章裏面也有詳細的描述,所以不準備花費太多的時間描述。

(1) AlphaPageTransformer

首先講個最簡單的動畫,叫AlphaPageTransformer,顧名思義就是一個漸變的變化,那麼我們的步驟是這樣的:

  • 實現AlphaPageTransformer implements ViewPager.PageTransformer
  • 調用viewPager.setPageTransformer(new AlphaPageTransformer())

對於ViewPager.PageTransformer就一個方法需要實現

#AlphaPageTransformer

private static final float DEFAULT_MIN_ALPHA = 0.5f;
private float mMinAlpha = DEFAULT_MIN_ALPHA;

public void pageTransform(View view, float position)
{
    if (position < -1)
    { 
        view.setAlpha(mMinAlpha);
    } else if (position <= 1)
    { // [-1,1]

        if (position < 0) //[0,-1]
        { 
            float factor = mMinAlpha + (1 - mMinAlpha) * (1 + position);
            view.setAlpha(factor);
        } else//[1,0]
        {
            float factor = mMinAlpha + (1 - mMinAlpha) * (1 - position);
            view.setAlpha(factor);
        }
    } else
    { // (1,+Infinity]
        view.setAlpha(mMinAlpha);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

代碼非常簡短,簡單的介紹下,可以看到postion主要分爲

  • [-Infinity,-1)
  • (1,+Infinity]
  • [-1,1]

這三個區間,對於前兩個,拿我們的頁面上目前顯示的3個Page來說,前兩個分別對應左右兩個露出一點的Page,那麼對於alpha值,只需要設置爲最小值即可。

對於[-1,1],這個就需要詳細分析了,我們這裏拿:第一頁->第二頁這個過程來說,主要看position的變化

第1頁->第2頁

  • 頁1的postion變化爲:從0到-1
  • 頁2的postion變化爲:從1到0

第一頁到第二頁,實際上就是左滑,第一頁到左邊,第二頁成爲currentItem到達中間,那麼對應alpha的變化應該是:

  • 頁1到左邊,對應alpha應該是:1到minAlpha
  • 頁2到中間,成爲currentItem,對應alpha應該是:minAlpha到1

分析到這就是寫代碼了:

對於頁1

//注意該代碼判斷在(position <= 1)的條件內
if (position < 0) //[0,-1]
{ 
    float factor = mMinAlpha + (1 - mMinAlpha) * (1 + position);
    view.setAlpha(factor);
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

position是0到-1的變化

那麼1+position就是從1到0的變化

(1 - mMinAlpha) * (1 + position)就是1 - mMinAlpha到0的變化

再加上一個mMinAlpha,就變爲1到mMinAlpha的變化。

其實繞來繞去就是爲了實現factor是1到minAlpha的變化,具體這樣的算式,每個人的思路可能不同,但是達到相同的效果即可。

同理,頁2是minAlpha到1的變化。

對應算式(postion爲1到0變化)

float factor = mMinAlpha + (1 - mMinAlpha) * (1 - position);
  • 1
  • 1

這個留給大家自己算,或者自己去總結出一個相同結果的算式。

ok,當我們完成AlphaPageTransformer的編碼,然後ViewPager設置後,效果就是這樣的:

(2) RotateDownPageTransformer

再介紹個RotateDownPageTransformer,因爲這個涉及到旋轉中心的變化,即:

view.setPivotX();
view.setPivotY();
  • 1
  • 2
  • 1
  • 2

直接看代碼:

private static final float DEFAULT_MAX_ROTATE = 15.0f;
private float mMaxRotate = DEFAULT_MAX_ROTATE;

public void pageTransform(View view, float position)
{
    if (position < -1)
    { // [-Infinity,-1)
        // This page is way off-screen to the left.  
        view.setRotation(mMaxRotate * -1);
        view.setPivotX(view.getWidth());
        view.setPivotY(view.getHeight());

    } else if (position <= 1)
    { // [-1,1]  

        if (position < 0)//[0,-1]
        {
            view.setPivotX(view.getWidth() * (0.5f + 0.5f * (-position)));
            view.setPivotY(view.getHeight());
            view.setRotation(mMaxRotate * position);
        } else//[1,0]
        {
            view.setPivotX(view.getWidth() * 0.5f * (1 - position));
            view.setPivotY(view.getHeight());
            view.setRotation(mMaxRotate * position);
        }
    } else
    { // (1,+Infinity]  
        // This page is way off-screen to the right.  
        view.setRotation(mMaxRotate);
        view.setPivotX(view.getWidth() * 0);
        view.setPivotY(view.getHeight());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

經過上面的分析,我們直接鎖定到第一頁到第二頁時,第一頁的相關變化的代碼:

if (position < 0)//[0,-1]
{
    float factor = view.getWidth() * (0.5f + 0.5f * (-position));
    view.setPivotX(factor);
    view.setPivotY(view.getHeight());
    view.setRotation(mMaxRotate * position);
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第一頁開始時滑動時,旋轉中心上圖原點,即(width/2 , height).

第一頁滑動結束時,旋轉中心在左邊頁面的右下角,即(width,height).

恩,這個旋轉中心的位置是我自己定義的,不一定是最好的效果,如果有必要大家可以自己選擇,保證良好的顯示效果。

可以看到旋轉中心的縱座標沒有發生變化,主要看橫座標

 float factor = view.getWidth() * (0.5f + 0.5f * (-position));
  • 1
  • 1

position爲0到-1,乘以-0.5之後,變爲0到0.5

在加上0.5,變爲0.5到1的變化

再乘以width,即變爲width/2到width的變化。

對應我們的旋轉中心x是需要從width/2到width,是不是剛好匹配。

旋轉中心的變化說明白了;再簡單說下,角度的變化,第一頁到達左邊頁面的狀態,角度是-15度,開始狀態是0度,那麼變化就是0到-15度之間,因爲position是0到-1之間變化,所以直接乘以15即可

float rotation = position * 15f 
  • 1
  • 1

好了,經過上面的分析,本文就基本結束了,有興趣可以下載源碼多分析幾個,或者創造幾個動畫效果,千萬不要忘了告訴我,我可以加入到這個動畫庫中。

五、總結

本文的內容其實涉及到的API實際上比較多,再多的動畫其實質性的原理都是一樣的,關鍵在於找規律,所以帶大家梳理一下步驟:

  1. 確定View需要變化的屬性
  2. 確定該屬性的初始值,終值
  3. 確定該View對應的position變化的梯度
  4. 根據position的變化梯度,計算出需要變化的屬性的變化梯度
  5. 剩下的就是調用屬性動畫的API了

ok,相信通過該步驟大家一定能夠自己去定義出形態各異的動畫,此外,切記如果學習,一定要嘗試編寫,看一看就認爲了解的,可能有些坑是發現不了的。

轉載自:http://blog.csdn.net/lmj623565791/article/details/51339751; 鴻洋的博客你值得擁有!!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章