敲酷炫的 ViewPager 切換效果和彈性指示器。

前言

前些天看到這個效果圖
GIF




android

效果然是酷炫極了,感受很biu踢,我們說作就作。

[改裝增強版,改進了圓入框的甩尾效果,最重要的一點是
加強ViewPager切換效果和卡片陰影]

效果圖

集成方式【伸手黨福利】

github地址 : github.com/qdxxxx/Bezi…
多謝老鐵隨手就是一個star,抱拳。
[標題黨通常是: 轉瘋了,項目集成此酷炫動畫只要3步!]git

  • 注入依賴
    Step 1. Add the JitPack repository to your build file
    Step 2. Add the dependency
allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }複製代碼
dependencies {
    compile 'com.github.qdxxxx:BezierViewPager:v1.0.5'
}複製代碼


  • xml佈局代碼
<qdx.bezierviewpager_compile.vPage.BezierViewPager
        android:id="@+id/view_page"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <qdx.bezierviewpager_compile.BezierRoundView
        android:id="@+id/bezRound"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
     />複製代碼


  • Activity裏面集成代碼
CardPagerAdapter cardAdapter = new CardPagerAdapter(getApplicationContext());
 cardAdapter.addImgUrlList(imgList);  //放置圖片url的list

BezierViewPager viewPager = (BezierViewPager) findViewById(R.id.view_page);
viewPager.setAdapter(cardAdapter);

BezierRoundView bezRound = (BezierRoundView) findViewById(R.id.bezRound);
bezRound.attach2ViewPage(viewPager);複製代碼



方法及屬性介紹

  • BezierRoundView
name format 中文解釋
color_bez color 貝塞爾圓球顏色
color_touch color 觸摸反饋
color_stroke color 圓框的顏色
time_animator integer 動畫時間
round_count integer 圓框數量,即Adapter.getCount
radius dimension 貝塞爾圓球半徑,圓框半徑爲(radius-2)
attach2ViewPage BezierViewPager 綁定指定的ViewPager(處理滑動時觸摸事件)
並自動設置round_count


  • BezierViewPager[extends ViewPager]
name format 中文解釋
showTransformer float ViewPager滑動到當前顯示頁的放大比例


  • CardPagerAdapter[extends PagerAdapter]
name format 中文解釋
addImgUrlList List 包含圖片地址的list
setOnCardItemClickListener OnCardItemClickListener 當前ViewPager點擊事件
返回CurPosition
setMaxElevationFactor integer Adapter裏CardView最大的Elevation




實現解剖

1.從實現貝塞爾圓開始

[建議先看這篇文章]
貝塞爾圓github



首先,咱們須要繪製P0,而後 cubicTo p1,p2,p3,再cubicTo p4,p5.p6……
原諒我用這麼簡單粗暴的方式畫圓…canvas

private PointF p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11;
複製代碼
p0 = new PointF(0, -mRadius);//mRadius圓的半徑
        p6 = new PointF(0, mRadius);

        p1 = new PointF(mRadius * bezFactor, -mRadius);//bezFactor即0.5519...
        p5 = new PointF(mRadius * bezFactor, mRadius);

        p2 = new PointF(mRadius, -mRadius * bezFactor);
        p4 = new PointF(mRadius, mRadius * bezFactor);

        p3 = new PointF(mRadius, 0);
        p9 = new PointF(-mRadius, 0);

        p11 = new PointF(-mRadius * bezFactor, -mRadius);
        p7 = new PointF(-mRadius * bezFactor, mRadius);

        p10 = new PointF(-mRadius, -mRadius * bezFactor);
        p8 = new PointF(-mRadius, mRadius * bezFactor);複製代碼

再繪製path數組

mPath.moveTo(p0.x, p0.y);
        mPath.cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
        mPath.cubicTo(p4.x, p4.y, p5.x, p5.y, p6.x, p6.y);
        mPath.cubicTo(p7.x, p7.y, p8.x, p8.y, p9.x, p9.y);
        mPath.cubicTo(p10.x, p10.y, p11.x, p11.y, p0.x, p0.y);
        mPath.close();複製代碼

圓
一個貝(ri)塞(ben)爾(guo)圓(qi)栩栩如生。bash


咱們嘗試經過手指滑動改變,p2,p3,p4的x軸座標來觀察圓的變化app

@Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_DOWN:
                p2 = new PointF(event.getX() - mWidth / 2, -mRadius * bezFactor);
                p3 = new PointF(event.getX() - mWidth / 2, 0);
                p4 = new PointF(event.getX() - mWidth / 2, mRadius * bezFactor);

                invalidate();
                break;
        }

        return true;
    }複製代碼

這裏寫圖片描述這裏寫圖片描述這裏寫圖片描述


2.解剖效果圖

貝塞爾圓

這裏寫圖片描述 這裏寫圖片描述 這裏寫圖片描述


首先咱們不考慮反彈效果,圓的變化有3種狀態maven

  1. bezier圓還沒離開圓框,p2,3,4 x軸座標由 , 變化至 2r
  2. bezier圓離開圓框,至到達中心位置
    [p2,3,4 x軸座標由 2r 變化至 1.5r ],[p8,9,10 x軸座標由 變化至 1.5r ]
  3. bezier圓由中心位置,至到達下一個圓框。
    [p2,3,4, 8,9,10 x軸座標由 1.5r 變化至 ]

老樣子,咱們用ValueAnimator來模擬一下[0,1]變化的值。【由於ViewPager的onPageScrolled監聽中positionOffset是[0,1)變化的,相似。】
ide

驚!下面幾段代碼竟然!男的看了沉默,女的看了流淚。

//展現動畫
    private ValueAnimator animatorStart;
    private TimeInterpolator timeInterpolator = new DecelerateInterpolator();
    private float animatedValue; //[0,1]的值
    public void startAnimator() {
        if (animatorStart != null) {
            if (animatorStart.isRunning()) {
                return;
            }
            animatorStart.start();
        } else {
            animatorStart = ValueAnimator.ofFloat(0, 1f).setDuration(1500);
            animatorStart.setInterpolator(timeInterpolator);
            animatorStart.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    animatedValue = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            animatorStart.start();
        }
    }複製代碼
private float rRadio=1;  //P2,3,4 x軸倍數 
    private float lRadio=1;  //P8,9,10倍數
    private float tbRadio=1;  //y軸縮放倍數

    private float disL = 0.5f;   //離開圓的閾值
    private float disM = 0.8f;  //最大值的閾值
    private float disA = 0.9f;  //到達下個圓框的閾值複製代碼
if (0 < animatedValue && animatedValue <= disL) { //還沒離開圓框的時候
            rRadio = 1f + animatedValue * 2;              //[1,2]
        }
        if (disL < animatedValue && animatedValue <= disM) {//離開圓框,至最大值區域
            rRadio = 2 - range0Until1(disL, disM) * 0.5f;          //  [2,1.5]
            lRadio = 1 + range0Until1(disL, disM) * 0.5f;          // [1,1.5]

        }
        if (disM < animatedValue && animatedValue <= disA) {    //從最大值,至到達下一個圓框
            rRadio = 1.5f - range0Until1(disM, disA) * 0.5f;     //  [1.5,1]
            lRadio = 1.5f - range0Until1(disM, disA) * 0.5f;     //  [1.5,1]

        }複製代碼
/**
     * 將值域轉化爲[0,1]
     *
     * @param minValue 大於等於
     * @param maxValue 小於等於
     * @return 根據當前 animatedValue,返回 [0,1] 對應的數值
     */
    private float range0Until1(float minValue, float maxValue) {
        return (animatedValue - minValue) / (maxValue - minValue);
    }複製代碼

請再次原諒我用這麼簡單粗暴的方式畫圓…佈局

mPath.moveTo(p0.x, p0.y * tbRadio);
        mPath.cubicTo(p1.x, p1.y * tbRadio, p2.x * rRadio, p2.y, p3.x * rRadio, p3.y);
        mPath.cubicTo(p4.x * rRadio, p4.y, p5.x, p5.y * tbRadio, p6.x, p6.y * tbRadio);
        mPath.cubicTo(p7.x, p7.y * tbRadio, p8.x * lRadio, p8.y, p9.x * lRadio, p9.y);
        mPath.cubicTo(p10.x * lRadio, p10.y, p11.x, p11.y * tbRadio, p0.x, p0.y * tbRadio);
        mPath.close();
複製代碼

理清了上面這些代碼,一個有靈性的貝塞爾圓就即將繪製成功。咱們再加上離開圓至到達下一個圓框這個區域y軸變化,[p,5,6,7, 1,0,11],效果就以下所示。
這裏寫圖片描述

3.模擬效果

這時候咱們已經將貝塞爾圓的運動方式給表達出來了,再加上一些效果[位移/反彈/翻轉],咱們就能模擬出貝塞爾圓從一個圓框進入下一個圓框的動畫了。
在上面的基礎上,咱們加上反彈效果

if (0 < animatedValue && animatedValue <= disL) { //還沒離開圓框的時候
            rRadio = 1f + animatedValue * 2;              //[1,2]
        }
        if (disL < animatedValue && animatedValue <= disM) {//離開圓框,至最大值區域
            rRadio = 2 - range0Until1(disL, disM) * 0.5f;          //  [2,1.5]
            lRadio = 1 + range0Until1(disL, disM) * 0.5f;          // [1,1.5]
            tbRadio = 1 - range0Until1(disL, disM) / 3;           // [1 , 2/3]
        }
        if (disM < animatedValue && animatedValue <= disA) {    //從最大值,至到達下一個圓框
            rRadio = 1.5f - range0Until1(disM, disA) * 0.5f;     //  [1.5,1]
            lRadio = 1.5f - range0Until1(disM, disA) * (1.5f - boundRadio);      //反彈效果,進場 內彈boundRadio  lRadio =[1.5,boundRadio]
            tbRadio = (range0Until1(disM, disA) + 2) / 3;        // [ 2/3,1]   
        }
         if (disA < animatedValue && animatedValue <= 1f) {//到達圓框,lRadio=[boundRadio,1]
            rRadio = 1;
            tbRadio = 1;
            lRadio = boundRadio + range0Until1(disA, 1) * (1 - boundRadio);     //反彈效果,飽和
        }複製代碼

再加上位移效果。一開始我在想,貝塞爾圓要不斷的變化形態,還要移動位置。豈不至關的麻煩。後來把它分解成變化狀態+不斷位移效果。

boolean isTrans = false;
        float transX = 1f;
        if (disL <= animatedValue && animatedValue <= disA) { //離開圓框,至到達下一個圓框
            isTrans = true;
            //咱們設置2個圓框距離爲mWidth / 2f
            transX = mWidth / 2f * range0Until1(disL, disA);  //[0,mWidth / 2f]
        }
        if (disA < animatedValue && animatedValue <= 1) {//到達下一個圓
            isTrans = true;
            transX = mWidth / 2;
        }

        if (isTrans) {
            canvas.translate(transX, 0);
        }複製代碼

這裏寫圖片描述


至此貝塞爾圓球進入右側圓框的效果已經實現,那麼若是圓球要從右側圓框進入左側圓框呢?
【題外話:寫完上面這個效果已是月黑風高的時候了,腦神經即將進入假死狀態,我心想,雖然複雜了點,可是應該仍是能夠作的出來的,腦殼運行的速度根本跟不上敲代碼的速度。根據位移方向的判斷從而設定lRadio和rRadio。有點自信回頭的趕腳。。。休息了一覺次日醒來天啊嚕,爲何不用Matrix,只要用path.transform(matrix),就能夠作到鏡像path,因此適當的休息有助於提高效率。】
這裏寫圖片描述

matrix_bounceL = new Matrix();
        matrix_bounceL.preScale(-1, 1);

        mPath.transform(matrix_bounceL);
複製代碼




4.Attach2ViewPager

關聯ViewPager總共有2個要點

  • ViewPager的滑動監聽,onPageScrolled。
    根據positionOffset和position,獲取咱們所要的當前位置/下一個位置/移動方向。
  • 手動選擇ViewPager,即手指點擊非當前圓框。

4.1 onPageScrolled

首先咱們來了解一下onPageScrolled這個方法中2個咱們要用到的參數

  • position : 當前cur位置,若是當前是1,手指按住右滑(vPage向左滑動)那就立馬變爲0。但若是當前是1,手指按住要左滑至下一個位置才爲2
  • positionOffset : [0,1) ,到達下一個pos就置爲0

這裏寫圖片描述這裏寫圖片描述

咱們功能需求分析一下:

  1. 獲取正確的當前位置curPos
  2. 獲取正確的貝塞爾球進入的下一個位置nextPos
  3. 獲取正確的貝塞爾球運動方向
  4. 配置正確的animatedValue

以前咱們用ValueAnimator來模擬運動狀態,如今咱們可使用positionOffset關聯到ViewPager

animatedValue = positionOffset;

        direction = ((position + positionOffset) - curPos > 0);  //運動方向。 true爲右邊(手往左滑動)
        nextPos = direction ? curPos + 1 : curPos - 1;  //右 +1   左 -1

        if (!direction)   //若是是向左
            animatedValue = 1 - animatedValue;  //讓 animatedValue 不論是左滑仍是右滑,都從[0,1)開始計算

        if (positionOffset == 0) { 
            curPos = position;
            nextPos = position;
        }
複製代碼

以上代碼還需動手調試,看看log才能更明白的領悟。

這裏寫圖片描述
從上面的gif能夠發現若是緩慢的滑動,pos的位置正確的,可是若是快速滑動,就會發現問題 : [例如0快速滑動到2,貝塞爾圓球會從0滑動到1,再從0滑動到2],打了Log以後咱們才發現原來快速滑動的時候,positionOffset到達下一個pos不會置爲0!!發現問題後就好解決了。咱們加上這一段代碼就能夠解決該問題。(快速滑動可能存在或多或少的問題,我也是花了些時間去測試的。)
這裏寫圖片描述

//快速滑動的時候,positionOffset有可能不會置於0
        if (direction && position + positionOffset > nextPos) {  //向右,並且
            curPos = position;
            nextPos = position + 1;
        } else if (!direction && position + positionOffset < nextPos) {
            curPos = position;
            nextPos = position - 1;
        }複製代碼



onDraw
咱們先要得到每一個圓框的圓心x軸座標

private float[] bezPos; //記錄每個圓心x軸的位置

    bezPos = new float[default_round_count];  //根據圓框個數
    for (int i = 0; i < default_round_count; i++) {
    bezPos[i] = mWidth / (default_round_count + 1) * (i + 1);
    }
複製代碼

假設咱們的default_round_count 即圓框個數爲4,那麼咱們就要分紅 4+1 份,再綜合上述的求圓心代碼,應該會更清晰一點。
這裏寫圖片描述


根據curPos和nextPos繪製貝塞爾圓球,po出onDraw代碼

canvas.translate(0, mHeight / 2);

        mBezPath.reset();
        for (int i = 0; i < default_round_count; i++) {
            canvas.drawCircle(bezPos[i], 0, mRadius - 2, mRoundStrokePaint);   //繪製圓框
        }
        if (animatedValue == 1) {
            canvas.drawCircle(bezPos[nextPos], 0, mRadius, mBezPaint);
            return;
        }

        canvas.translate(bezPos[curPos], 0); //根據curPos,移動到當前圓框位置

        if (0 < animatedValue && animatedValue <= disL) {
            rRadio = 1f + animatedValue * 2;                         //  [1,2]
            lRadio = 1f;
            tbRadio = 1f;
        }
        if (disL < animatedValue && animatedValue <= disM) {
            rRadio = 2 - range0Until1(disL, disM) * 0.5f;          //  [2,1.5]
            lRadio = 1 + range0Until1(disL, disM) * 0.5f;          // [1,1.5]
            tbRadio = 1 - range0Until1(disL, disM) / 3;           // [1 , 2/3]
        }
        if (disM < animatedValue && animatedValue <= disA) {
            rRadio = 1.5f - range0Until1(disM, disA) * 0.5f;     //  [1.5,1]
            lRadio = 1.5f - range0Until1(disM, disA) * (1.5f - boundRadio);      //反彈效果,進場 內彈boundRadio
            tbRadio = (range0Until1(disM, disA) + 2) / 3;        // [ 2/3,1]
        }
        if (disA < animatedValue && animatedValue <= 1f) {
            rRadio = 1;
            tbRadio = 1;
            lRadio = boundRadio + range0Until1(disA, 1) * (1 - boundRadio);     //反彈效果,飽和
        }
        if (animatedValue == 1 || animatedValue == 0) {  //防止極其粗暴的滑動
            rRadio = 1f;
            lRadio = 1f;
            tbRadio = 1f;
        }

        boolean isTrans = false;  //根據nextPos和curPos求出位移距離
        float transX = (nextPos - curPos) * (mWidth / (default_round_count + 1));
        if (disL <= animatedValue && animatedValue <= disA) {
            isTrans = true;
            transX = transX * (animatedValue - disL) / (disA - disL);
        }
        if (disA < animatedValue && animatedValue <= 1) {
            isTrans = true;
        }
        if (isTrans) {
            canvas.translate(transX, 0);
        }

        mBezPath.moveTo(p0.x, p0.y * tbRadio);
        mBezPath.cubicTo(p1.x, p1.y * tbRadio, p2.x * rRadio, p2.y, p3.x * rRadio, p3.y);
        mBezPath.cubicTo(p4.x * rRadio, p4.y, p5.x, p5.y * tbRadio, p6.x, p6.y * tbRadio);
        mBezPath.cubicTo(p7.x, p7.y * tbRadio, p8.x * lRadio, p8.y, p9.x * lRadio, p9.y);
        mBezPath.cubicTo(p10.x * lRadio, p10.y, p11.x, p11.y * tbRadio, p0.x, p0.y * tbRadio);
        mBezPath.close();

        if (!direction) {
            mBezPath.transform(matrix_bounceL);
        }
        canvas.drawPath(mBezPath, mBezPaint);

        if (isTrans) {
            canvas.save();
        }

複製代碼


4.2 點擊圓框,設置ViewPager的curItem

咱們須要判斷是否點擊到了圓框上,和點擊了具體哪一個圓框。
onPageScrolled方法的時候不進行處理,而是經過ValueAnimator來模擬數值。從而繪製貝塞爾圓球效果。

private float[] xPivotPos;  //根據圓心x軸+mRadius,劃分紅不一樣的區域 ,主要爲了判斷觸摸x軸的位置
        xPivotPos = new float[default_round_count];
        for (int i = 0; i < default_round_count; i++) {
            xPivotPos[i] = mWidth / (default_round_count + 1) * (i + 1) + mRadius;
        }複製代碼

這裏寫圖片描述
針對x軸 : 個人作法是用一個數組xPivotPos 存儲每一個圓框最邊緣的位置,即圓心+mRadius,而後咱們觸摸的時候,就能夠找到當前觸摸touchPos是屬於哪一個(圓框+mRadius)範圍內。只要x >=bezPos[touchPos]-mRadius,就能夠清楚的知道是否觸摸到了該區域的圓框範圍。

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                float x = event.getX();
                float y = event.getY();

                if (y <= mHeight / 2 + mRadius && y >= mHeight / 2 - mRadius && !isAniming) {  //先判斷y,若是y點擊是在圓y軸的範圍
                    int pos = -Arrays.binarySearch(xPivotPos, x) - 1;
                    if (pos >= 0 && pos < default_round_count && x + mRadius >= bezPos[pos]) {
                        nextPos = pos;
                        if (mViewPage != null && curPos != nextPos) {
                            mViewPage.setCurrentItem(pos);
                            isAniming = true;
                            direction = (curPos < pos);
                            startAnimator();  //咱們經過ValueAnimator來模擬具體的值,不使用ViewPager的onPageScrolled方法。
                        }
                    }
                    return true;
                }
                break;
        }
        return super.onTouchEvent(event);
    }複製代碼

至此咱們BezierRoundView的用法和繪製方法已經講解完了,下面來看一下ViewPager是怎麼實現切換效果的。

實現ViewPager切換效果

參考【github.com/rubensousa/…

setClipToPadding

這裏寫圖片描述這裏寫圖片描述
【靈魂畫家】
上圖針對的是ViewPager設置Padding以後,

setClipToPadding 設置true,false不一樣的區別。 
左圖是正常狀況下默認 setClipToPadding(true) 的顯示狀況,設置Padding以後,手機屏幕上只顯示width-PaddingLeft - PaddingRight。 


而若是設置 setClipToPadding(false) 狀況,表示不裁剪Padding,這時候咱們就能夠看到左右的ViewPager,至關於本來兩邊的Padding透明度爲1,而設置false以後透明度爲0。


setMaxCardElevation

CardPagerAdapter是咱們繼承PagerAdapter的類,adapter裏的佈局是cardView

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardView"
    app:cardCornerRadius="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cardPreventCornerOverlap="true"
    app:cardUseCompatPadding="true">
    <!--cardUseCompatPadding 設置陰影以後自動縮小布局大小-->
    <ImageView
        android:id="@+id/item_iv"
        android:scaleType="fitXY"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</android.support.v7.widget.CardView>複製代碼

先來了解一下cardView setCardElevation(float)方法。【針對CardViewApi21】

if (!cardView.getUseCompatPadding()) {
            cardView.setShadowPadding(0, 0, 0, 0);
            return;
        }
        float elevation = getMaxElevation(cardView);
        final float radius = getRadius(cardView);
        int hPadding = (int) Math.ceil(RoundRectDrawableWithShadow
                .calculateHorizontalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
        int vPadding = (int) Math.ceil(RoundRectDrawableWithShadow
                .calculateVerticalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
        cardView.setShadowPadding(hPadding, vPadding, hPadding, vPadding);複製代碼
static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
            boolean addPaddingForCorners) {
        if (addPaddingForCorners) {
            return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
        } else {
            return maxShadowSize * SHADOW_MULTIPLIER;
        }
    }

    static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
            boolean addPaddingForCorners) {
        if (addPaddingForCorners) {
            return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
        } else {
            return maxShadowSize;
        }
    }複製代碼

下面看一下效果測試。

ViewPager效果測試

咱們來看一下ViewPager左右設置Padding爲mWidth / 10的效果

viewPager.setPadding(mWidth / 10, 0, mWidth / 10, 0);
        viewPager.setClipToPadding(false);複製代碼

這裏寫圖片描述

再來看一下CardPagerAdapter設置MaxElevationFactor爲mWidth / 10的效果【adapter.xml的cardCornerRadius不設值,cardUseCompatPadding必定要設置true!!】

int maxFactor = mWidth / 10;
        cardAdapter.setMaxElevationFactor(maxFactor);複製代碼

這裏寫圖片描述

具體我也不贅述了,看圖應該能分析出二者的不一樣。
因此如今綜上所述,制定一個需求

  • 不論是設置padding仍是Elevation都要保持圖片的寬高比例。

也就是說當咱們知道圖片的寬高比例以後,代碼裏面咱們要動態的去調整和設置並保持這個寬高比例。 


【這邊有個坑就是設置setMaxElevation它的寬高比是不可抗的,因此咱們只能在setPadding的時候,去調節這個比例】 


【setMaxElevation
寬的Padding爲maxFactor + 0.3*CornerRadius 【0.3≈≈ (1 - COS_45)】
高的Padding爲maxFactor*1.5f + 0.3*CornerRadius】

這裏寫圖片描述

可是!

(雞生的)
若是咱們在 setMaxElevation的狀況下,在去設置padding,那麼如何保證咱們的寬高比?具體請看以下代碼分析。【能夠經過去掉adapter.xml 裏ImagerView 的android:scaleType=」fitXY」屬性測試一下寬高比例是否調試正確

//已知圖片的寬爲1920,高1080.
        int mWidth = getWindowManager().getDefaultDisplay().getWidth();
        float heightRatio = 0.565f;  //高是寬的 0.565 ,根據圖片比例

        CardPagerAdapter cardAdapter = new CardPagerAdapter(getApplicationContext());
        cardAdapter.addImgUrlList(imgList);//添加加載的圖片集合


        //設置陰影大小,即vPage  左右兩個圖片相距邊框  maxFactor + 0.3*CornerRadius   *2
        //設置陰影大小,即vPage 上下圖片相距邊框  maxFactor*1.5f + 0.3*CornerRadius
        int maxFactor = mWidth / 25;
        cardAdapter.setMaxElevationFactor(maxFactor);

        int mWidthPading = mWidth / 8;
        //由於咱們adapter裏的cardView CornerRadius已經寫死爲10dp,因此0.3*CornerRadius=3
        //設置Elevation以後,控件寬度要減去 (maxFactor + dp2px(3)) * heightRatio
        //heightMore 設置Elevation以後,控件高度 比  控件寬度* heightRatio  多出的部分
        float heightMore = (1.5f * maxFactor + dp2px(3)) - (maxFactor + dp2px(3)) * heightRatio;
        int mHeightPading = (int) (mWidthPading * heightRatio - heightMore);

        BezierViewPager viewPager = (BezierViewPager) findViewById(R.id.view_page);
        viewPager.setLayoutParams(new RelativeLayout.LayoutParams(mWidth, (int) (mWidth * heightRatio)));
        viewPager.setPadding(mWidthPading, mHeightPading, mWidthPading, mHeightPading);
        viewPager.setClipToPadding(false);
        viewPager.setAdapter(cardAdapter);複製代碼


showTransformer

改方法是設置ViewPager移動的時候,cardView放大效果和Elevation陰影效果,具體過程能夠自行在ShadowTransformer查看,實現過程上文基本也有覆蓋。

總結

零零碎碎也搗鼓了一陣子的自定義View,我在想既然邁出這一步了,就得作好它。

人生老是要有信仰,有夢想才能一直前行,哪怕走的再慢,也是在前行。

若是這篇文章寫的還湊合或者勾引發了你的鬥志的話,歡迎點個star
github.com/qdxxxx/Bezi…

相關文章
相關標籤/搜索