自定義ViewPager指示器動效從分析到實現

準備工作

  最近接到需求,要實現一個ViewPager的指示器。拿到視覺稿,就三個小點感覺分分鐘的事。然而,仔細一看發現不簡單啊,要實現順滑切換,兩種顏色自然過渡還需要體力。
這裏寫圖片描述
* 對動效進行拆解分析

  第一個點切換到第二個點時,第一個點往右縮短,第二個點往右增長;同時第一個點由紅色平滑過渡到藍色,第二個點反之。注:點的消長隨手勢方向,但消長的邊有所不同

  • 大體思路

  實現原理很簡單,就是畫圖、動效細節需要花時間調。三個點用RoundRect畫,監聽ViewPager的滑動;傳入滑動距離,根據滑動距離計算RoundRect的left/right值實現消長;同時根據滑動距離比例計算過渡顏色值,給畫筆賦值過渡顏色。

動手開擼

  • 監聽ViewPager

  這些動效的動作來源當然是ViewPager了。我們需要監聽到ViewPager的滑動比例positionOffset及切換position,position決定圓點的切換方向,positionOffset*圓點間距mDistance就是圓點的消長距離了;然後重新繪圖,形成動效。

viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    //記錄上一次滑動的positionOffsetPixels值
    private int lastValue = -1;

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        boolean isLeft = mIsLeft;
        if (lastValue / 10 > positionOffsetPixels / 10) {
            //右滑
            isLeft = false;
        } else if (lastValue / 10 < positionOffsetPixels / 10) {
            //左滑
            isLeft = true;
        }
        if (mNum > 0) {
            move(positionOffset, position % mNum, isLeft);
        }
        lastValue = positionOffsetPixels;       
    }
});
  • 繪圖及動效實現

  點分爲普通點和長方形點,假設有mNum個點,可以看做mNum+1個普通點,長點佔兩個坑位,如圖。
這裏寫圖片描述
position爲當前所在點即長點的位置。左滑時,position位置的點與右邊的點聯動;右滑時與左邊的點聯動。

先畫普通的點。mIsLeft-是否左滑,mRadius*2-矩形邊長

RectF tip = new RectF(0, -mRadius, 0, mRadius);
int offset = mIsLeft ? 1 : 0;
//右邊的點
for (int i = mPosition + 2 + offset; i <= mNum; i++) {
    tip.left = -(mNum) * 0.5f * mDistance + i * mDistance - mRadius;
    tip.right = -(mNum) * 0.5f * mDistance + i * mDistance + mRadius;
    canvas.drawRoundRect(tip, mRadius / 2, mRadius / 2, paintDefault);
}
//左邊的點
for (int i = mPosition - 1 + offset; i >= 0; i--) {
    tip.left = -(mNum) * 0.5f * mDistance + i * mDistance - mRadius;
    tip.right = -(mNum) * 0.5f * mDistance + i * mDistance + mRadius;
    canvas.drawRoundRect(tip, mRadius / 2, mRadius / 2, paintDefault);
}

再畫動效聯動的兩點

//第一個點消失
float leftClose = -(mNum) * 0.5f * mDistance + mPosition * mDistance - mRadius;
float rightClose = leftClose + 2 * mRadius + mDistance - mOffset;
RectF rectClose = new RectF(leftClose, -mRadius, rightClose, mRadius);// 設置個新的長方形
canvas.drawRoundRect(rectClose, mRadius / 2, mRadius / 2, paintSelect);
//第二個點增長
float rightOpen = -(mNum) * 0.5f * mDistance + (mPosition + 2) * mDistance + mRadius;
float leftOpen = rightOpen - 2 * mRadius - mOffset;
RectF rectOpen = new RectF(leftOpen, -mRadius, rightOpen, mRadius);// 設置個新的長方形
canvas.drawRoundRect(rectOpen, mRadius / 2, mRadius / 2, paintSelect);
  • 顏色的平滑過渡

  此消彼長的動效實現了,顏色的平滑過渡就簡單了,原理類似。將選中色與未選中色變爲數值,根據移動比例percent來從兩個顏色值中取過渡色。

  假設選中色1的值爲A1,R1,G1,B1。未選中色2的值爲A2,R2,G2,B2。求A1過渡到A2百分比爲P的顏色3。

A3 = (A2 - A1)*P + A1;    G3 = (G2 - G1)*P + G1

R3 = (R2 - R1)*P + R1;    B3 = (B2 - B1)*P + B1

  得到所需顏色ARGB值後,還不能直接使用。ARGB是四個十進制的值,而xml中可用顏色爲一個十六進制值。對應關係如下
這裏寫圖片描述
將ARGB四個值分別轉十六進制然後按順序拼接,就得到可用的顏色值

public static String caculateColor(String startColor, String endColor, float franch){

        int startAlpha = Integer.parseInt(startColor.substring(1, 3), 16);
        int startRed = Integer.parseInt(startColor.substring(3, 5), 16);
        int startGreen = Integer.parseInt(startColor.substring(5, 7), 16);
        int startBlue = Integer.parseInt(startColor.substring(7), 16);

        int endAlpha = Integer.parseInt(endColor.substring(1, 3), 16);
        int endRed = Integer.parseInt(endColor.substring(3, 5), 16);
        int endGreen = Integer.parseInt(endColor.substring(5, 7), 16);
        int endBlue = Integer.parseInt(endColor.substring(7), 16);

        int currentAlpha = (int) ((endAlpha - startAlpha) * franch + startAlpha);
        int currentRed = (int) ((endRed - startRed) * franch + startRed);
        int currentGreen = (int) ((endGreen - startGreen) * franch + startGreen);
        int currentBlue = (int) ((endBlue - startBlue) * franch + startBlue);

        return "#" + getHexString(currentAlpha) + getHexString(currentRed)
                + getHexString(currentGreen) + getHexString(currentBlue);

    }
    /**
     * 將10進制顏色值轉換成16進制。
     */
    public static String getHexString(int value) {
        String hexString = Integer.toHexString(value);
        if (hexString.length() == 1) {
            hexString = "0" + hexString;
        }
        return hexString;
    }

  最後將得到的顏色值賦給對應畫筆,appearColor賦給第一個點增長,dismissColor賦給第二個消失的點。大功告成!

int appearColor = GradientColorUtil.caculateColor(mDefault_color, mSelected_color, mPercent);
int dismissColor = GradientColorUtil.caculateColor(mSelected_color, mDefault_color, mPercent);

GitHub地址:https://github.com/VDshixiaoming/SmoothTransIndicator

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