Android自定義控制元件——點贊效果(仿Twitter)
阿新 • • 發佈:2019-01-10
前言
通過自定義控制元件,意欲模仿Twitter的點贊效果。
主要涉及:
1.三次貝塞爾曲線應用;
2.屬性動畫的綜合應用;
3.自定義View流程.
拆解原效果
我們先看一下Twitter上的原版效果是怎樣的.
放大後:
好吧!原速的看不太清楚,逐幀延遲後:
因為這個效果有需要使用多個動畫雜糅而成,為了更確切得出每個子動畫階段所佔比例還是用PS大法把它開啟,根據該階段的幀數以及總幀數來確定動畫時長如何分配。
實現
1.動畫控制
這裡使用ValueAnimator並設定插值器為LinearInterpolator來獲得隨時間正比例變化的逐漸增大的整數值。這個整數值在這裡有三個作用。
- 每監聽到一個整數值變化重繪一次View.
- 根據整數值的大小範圍來劃分所處的不同階段,這裡共劃分為五個狀態.
- 繪製心形並伴隨縮小和顏色漸變.
- 繪製圓並伴隨放大和顏色漸變.
- 繪製圓環並伴隨放大和顏色漸變.
- 圓環減消失、心形放大、周圍環繞十四圓點.
- 環繞的十四圓點向外移動並縮小、透明度漸變、漸隱.
- 以整數值為基礎來實現其他動畫效果避免出現大量的ObjectAnimator.
/**
* 展現View點選後的變化效果
*/
private void startViewMotion() {
if (animatorTime != null && animatorTime.isRunning())
return;
resetState();
animatorTime = ValueAnimator.ofInt(0, 1200);
animatorTime.setDuration(mCycleTime);
animatorTime.setInterpolator(new LinearInterpolator());//需要隨時間勻速變化
animatorTime.start();
animatorTime.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
if (animatedValue == 0) {
if (animatorArgb == null || !animatorArgb.isRunning()) {
animatorArgb = ofArgb(mDefaultColor, 0Xfff74769, 0Xffde7bcc);
animatorArgb.setDuration(mCycleTime * 28 / 120);
animatorArgb.setInterpolator(new LinearInterpolator());
animatorArgb.start();
}
} else if (animatedValue <= 100) {
float percent = calcPercent(0f, 100f, animatedValue);
mCurrentRadius = (int) (mRadius - mRadius * percent);
if (animatorArgb != null && animatorArgb.isRunning())
mCurrentColor = (int) animatorArgb.getAnimatedValue();
mCurrentState = HEART_VIEW;
invalidate();
} else if (animatedValue <= 280) {
float percent = calcPercent(100f, 340f, animatedValue);//此階段未達到最大半徑
mCurrentRadius = (int) (2 * mRadius * percent);
if (animatorArgb != null && animatorArgb.isRunning())
mCurrentColor = (int) animatorArgb.getAnimatedValue();
mCurrentState = CIRCLE_VIEW;
invalidate();
} else if (animatedValue <= 340) {
float percent = calcPercent(100f, 340f, animatedValue);//半徑接上一階段增加,此階段外環半徑已經最大值
mCurrentPercent = 1f - percent + 0.2f > 1f ? 1f : 1f - percent + 0.2f;//用於計算圓環寬度,最小0.2,與動畫進度負相關
mCurrentRadius = (int) (2 * mRadius * percent);
if (animatorArgb != null && animatorArgb.isRunning())
mCurrentColor = (int) animatorArgb.getAnimatedValue();
mCurrentState = RING_VIEW;
invalidate();
} else if (animatedValue <= 480) {
float percent = calcPercent(340f, 480f, animatedValue);//內環半徑增大直至消亡
mCurrentPercent = percent;
mCurrentRadius = (int) (2 * mRadius);//外環半徑不再改變
mCurrentState = RING_DOT__HEART_VIEW;
invalidate();
} else if (animatedValue <= 1200) {
float percent = calcPercent(480f, 1200f, animatedValue);
mCurrentPercent = percent;
mCurrentState = DOT__HEART_VIEW;
if (animatedValue == 1200) {
animatorTime.cancel();
animatorTime.removeAllListeners();
state = true;
}
invalidate();
}
}
});
}
2.圖形繪製
心形
這裡使用貝塞爾曲線來繪製心形,通過四組控制點的改變來擬合心形。當然專案中為了方便此處的繪製可以用圖片代替。
//繪製心形
private void drawHeart(Canvas canvas, int radius, int color) {
initControlPoints(radius);
mPaint.setColor(color);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
Path path = new Path();
path.moveTo(tPointB.x, tPointB.y);
path.cubicTo(tPointC.x, tPointC.y, rPointA.x, rPointA.y, rPointB.x, rPointB.y);
path.cubicTo(rPointC.x, rPointC.y, bPointC.x, bPointC.y, bPointB.x, bPointB.y);
path.cubicTo(bPointA.x, bPointA.y, lPointC.x, lPointC.y, lPointB.x, lPointB.y);
path.cubicTo(lPointA.x, lPointA.y, tPointA.x, tPointA.y, tPointB.x, tPointB.y);
canvas.drawPath(path, mPaint);
}
其他
還有一些 圓、圓點、圓環的繪製比較簡單這裡不再列出,重點是這些圖形疊加交錯的動畫變化。
3.點選事件
對外提供點選事件監聽,以便處理點贊與取消點讚的邏輯。
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (x + getLeft() < getRight() && y + getTop() < getBottom()) {//點選在View區域內
if (state) {
deselectLike();
} else {
startViewMotion();
}
if (mListener != null)
mListener.onClick(this);
}
break;
}
return true;
}
對外提供設定監聽的方法
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
mListener = l;
}
獲取是否已點讚的狀態
/**
* Indicates whether this LikeView is selected or not.
*
* @return true if the LikeView is selected now, false is deselected
*/
public boolean getState() {
return this.state;
}
4.最終效果
總結
這裡大致實現了Twitter的點贊效果。雖然是根據原效果影象幀比例來確定動畫應分配時間的,放慢觀察似乎還是不太理想。另有些狀態確定不了是顏色漸變還是透明度變化,臨界消失時縮放有沒有伴隨移動,這些都從簡處理了。
需要強調一下的是這裡用到了顏色漸變動畫,而這個方法系統是API21才提供的,
這裡直接拷貝系統原始碼的ArgbEvaluator到專案裡了,其實就相當於屬性動畫自定義TypeEvaluator,既然原始碼裡有,就不客氣了。
歡迎指正