效果圖如下:
因爲GIF上傳有限制而且有點小卡,錄製的效果不是特別的好,但是也可以瞭解要顯示的效果是怎樣的。
實現思路
這個 view 大致分爲 4 個過程,分段來,後面會給出完整代碼
- 1、點擊之前顯示爲:
想要實現這個效果非常簡單,只需要調用 canvas 的 drawAcdrawArc 方法即可實現,難點在於 對勾 的繪製,我這邊是這樣計算的:
//初始化鉤子三個點的座標
index = new float[6];
index[0] = mCircleRadiu * 2 / 3; //第一個點 x 大約在 1/3 圓直徑的位置
index[1] = mCircleRadiu; //第一個點 y 大約在 1/2 圓直徑的位置
index[2] = mCircleRadiu; //第二個點 x 大約在 1/2 圓直徑的位置
index[3] = (float) (1.3 * mCircleRadiu); //第二個點 y 大約在圓直徑的 1/2 偏下一點位置
index[4] = mCircleRadiu * 2 / 3 * 2; //第三個點 x 大約在 2/3 圓直徑的位置
index[5] = (float) (0.7 * mCircleRadiu); //第三個點 y 大約在圓直徑 1/2 偏上一點的位置
- 2、點擊 View 之後,是這樣顯示的
這個繪製的過程也非常簡單,只需要使用動畫控制一下就可以
ValueAnimator animator = ValueAnimator.ofInt(0, 360);
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
sweepAngle = (int) animation.getAnimatedValue();
invalidate();
}
});
OnDraw()方法代碼
//動態繪製選中狀態 時圓環
canvas.drawArc(new RectF(mStrokeWidth, mStrokeWidth, mCircleRadiu * 2, mCircleRadiu * 2), 0, sweepAngle, false, mCirclePaint);
- 3、這個效果是在 上面 那個效果完成之後開始繪製的
代碼也是非常簡單,貼一下吧,
ValueAnimator animator = ValueAnimator.ofInt(0, 360);
animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
sweepAngle = (int) animation.getAnimatedValue();
invalidate();
}
});
final ValueAnimator animator1 = ValueAnimator.ofFloat(mCircleRadiu, 0);
animator1.setDuration(1000);
animator1.setInterpolator(new LinearInterpolator());
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
radiu = (float) animation.getAnimatedValue();
invalidate();
}
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
drawArcEnd = true;
resetPaint();
invalidate();
animator1.start();
}
});
改繪製是在上一個動畫結束之後繪製的,所以需要監聽上一個動畫的完成狀態
- 4、最後一個狀態,是一個放大縮小的過程
代碼也是非常的簡單,也是通過動畫進行控制的,來看下代碼,這個繪製也是在上一個繪製完成之後纔開始的
final ValueAnimator animator1 = ValueAnimator.ofFloat(mCircleRadiu, 0);
animator1.setDuration(1000);
animator1.setInterpolator(new LinearInterpolator());
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
radiu = (float) animation.getAnimatedValue();
invalidate();
}
});
final ValueAnimator animator2 = ValueAnimator.ofFloat(mCircleRadiu, (float) (mCircleRadiu * 1.5), mCircleRadiu);
animator2.setDuration(500);
animator2.setInterpolator(new LinearInterpolator());
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
radiu1 = (float) animation.getAnimatedValue();
invalidate();
}
});
animator1.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
drawCircleEnd = true;
animator2.start();
}
//繪製底部圓形黃色背景
canvas.drawCircle(mCircleRadiu, mCircleRadiu, radiu1, mCirclePaint);
canvas.drawPath(path, mHookPaint);
這就是這個效果的分解動畫,這個效果非常簡單,這裏就不在進行詳細介紹了,有了思路分分鐘搞定,下面給出完整代碼,當然這個代碼並沒有一些小細節並沒有處理的特別的好,裏面槽點還是挺多的,有興趣的可以自己完善下
package com.summary.hecom.custom.view;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import com.summary.hecom.R;
/**
* Created by hecom on 2018/5/1.
*/
public class TickView extends View implements View.OnClickListener {
private final Context mContext;
private Paint paint;
private Path path;
private float mCircleRadiu;
private float mStrokeWidth;
private TypedArray typedArray;
private int mSelectCircleColor;
private int mSelectedCircleColor;
private int mSelectHookColor;
private int mSelectedHookColor;
private Paint mCirclePaint;
private Paint mHookPaint;
private boolean isCheck;
private float mCircleStartX;
private float mCircleStartY;
private float[] index;
private int sweepAngle;
private boolean isDrawArc;
private float radiu;
private boolean drawArcEnd;
private boolean drawCircleEnd;
private float radiu1;
public TickView(Context context) {
this(context, null);
}
public TickView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public TickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initAttr(attrs);
init();
resetPaint();
}
private void initAttr(AttributeSet attrs) {
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.TickView);
//圓半徑
mCircleRadiu = typedArray.getDimension(R.styleable.TickView_circleRadiu, 150);
//畫筆寬度(弧寬度)
mStrokeWidth = typedArray.getDimension(R.styleable.TickView_circle_width, 0);
if (mStrokeWidth == 0) {
mStrokeWidth = 10;
}
//未點擊時,圓弧顏色
mSelectCircleColor = typedArray.getColor(R.styleable.TickView_nocheck_circle_color, getResources().getColor(R.color.lightgray));
//點擊 View 後 顏色
mSelectedCircleColor = typedArray.getColor(R.styleable.TickView_check_circle_color, getResources().getColor(R.color.yellow));
//未點擊 View 時,鉤子的顏色
mSelectHookColor = typedArray.getColor(R.styleable.TickView_nochecck_hook_color, getResources().getColor(R.color.lightgray));
//點擊後鉤子的顏色
mSelectedHookColor = typedArray.getColor(R.styleable.TickView_check_hook_color, getResources().getColor(R.color.white));
}
private void init() {
//圓弧(圓)畫筆
mCirclePaint = new Paint();
//鉤子畫筆
mHookPaint = new Paint();
//一個逐漸縮小的圓
paint = new Paint();
//鉤子路徑
path = new Path();
//初始化鉤子三個點的座標
index = new float[6];
index[0] = mCircleRadiu * 2 / 3; //第一個點 x 大約在 1/3 圓直徑的位置
index[1] = mCircleRadiu; //第一個點 y 大約在 1/2 圓直徑的位置
index[2] = mCircleRadiu; //第二個點 x 大約在 1/2 圓直徑的位置
index[3] = (float) (1.3 * mCircleRadiu); //第二個點 y 大約在圓直徑的 1/2 偏下一點位置
index[4] = mCircleRadiu * 2 / 3 * 2; //第三個點 x 大約在 2/3 圓直徑的位置
index[5] = (float) (0.7 * mCircleRadiu); //第三個點 y 大約在圓直徑 1/2 偏上一點的位置
setOnClickListener(this);
}
//每次狀態改變後重置畫筆
public void resetPaint() {
mCirclePaint.reset();
mHookPaint.reset();
path.reset();
if (isCheck) {
if (drawArcEnd) {
mCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
} else {
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mStrokeWidth);
}
mCirclePaint.setColor(mSelectedCircleColor);
mHookPaint.setColor(mSelectedHookColor);
} else {
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mStrokeWidth);
mCirclePaint.setColor(mSelectCircleColor);
mHookPaint.setColor(mSelectHookColor);
}
mHookPaint.setStyle(Paint.Style.STROKE);
mHookPaint.setStrokeWidth(mStrokeWidth);
mCirclePaint.setAntiAlias(true);
mHookPaint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setColor(getResources().getColor(R.color.white));
paint.setAntiAlias(true);
path.moveTo(index[0], index[1]);
path.lineTo(index[2], index[3]);
path.lineTo(index[4], index[5]);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//View 被點擊
if (isCheck) {
//圓弧動態繪製結束(在這裏是黃色的圓環)
if (drawArcEnd) {
//繪製底部圓形黃色背景
canvas.drawCircle(mCircleRadiu, mCircleRadiu, mCircleRadiu, mCirclePaint);
//繪製上面白色的圓(是一個越來越小,最終會消失的一個白色的圓)
canvas.drawCircle(mCircleRadiu, mCircleRadiu, radiu, paint);
if (drawCircleEnd) {
//繪製底部圓形黃色背景(一個抖動效果,先變大,在變回原來大小)
canvas.drawCircle(mCircleRadiu, mCircleRadiu, radiu1, mCirclePaint);
//繪製鉤子
canvas.drawPath(path, mHookPaint);
}
} else {
//動態繪製選中狀態 時圓環
canvas.drawArc(new RectF(mStrokeWidth, mStrokeWidth, mCircleRadiu * 2, mCircleRadiu * 2), 0, sweepAngle, false, mCirclePaint);
}
} else { //默認狀態(未被點擊時狀態)
//繪製未選中狀態
canvas.drawArc(new RectF(mStrokeWidth, mStrokeWidth, mCircleRadiu * 2, mCircleRadiu * 2), 0, 360, false, mCirclePaint);
//繪製鉤子
canvas.drawPath(path, mHookPaint);
}
}
public void animation() {
//圓弧動畫(黃色圓環)
ValueAnimator animator = ValueAnimator.ofInt(0, 360);
animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
sweepAngle = (int) animation.getAnimatedValue();
invalidate();
}
});
//緩慢顯示黃色圓背景的動畫(也是是文章中提到的第三步)
final ValueAnimator animator1 = ValueAnimator.ofFloat(mCircleRadiu, 0);
animator1.setDuration(1000);
animator1.setInterpolator(new LinearInterpolator());
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
radiu = (float) animation.getAnimatedValue();
invalidate();
}
});
//一個抖動效果(讓圓先變大,然後在變爲原來的大小)
final ValueAnimator animator2 = ValueAnimator.ofFloat(mCircleRadiu, (float) (mCircleRadiu * 1.5), mCircleRadiu);
animator2.setDuration(500);
animator2.setInterpolator(new LinearInterpolator());
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
radiu1 = (float) animation.getAnimatedValue();
invalidate();
}
});
//圓弧(圓環)繪製結束後,開始後面的繪製
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
drawArcEnd = true;
resetPaint();
invalidate();
animator1.start();
}
});
//繪製最後一步
animator1.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
drawCircleEnd = true;
animator2.start();
}
});
animator.start();
}
@Override
public void onClick(View v) {
isCheck = true;
resetPaint();
animation();
}
}
自定義屬性:
<declare-styleable name="TickView">
<attr name="circleRadiu" format="dimension"/>
<attr name="nocheck_circle_color" format="color" />
<attr name="check_circle_color" format="color" />
<attr name="nochecck_hook_color" format="color" />
<attr name="check_hook_color" format="color" />
<attr name="circle_width" format="dimension" />
<attr name="circle_startx" format="dimension" />
<attr name="circle_starty" format="dimension" />
</declare-styleable>
完事………