準備工作
最近接到需求,要實現一個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