最近做項目要求做一個類似騰訊WiFi網絡檢測的動畫,最近正好項目開發完成,手頭也沒什么事,就給大家分享一下。當時項目也非常緊為了趕周期就沒考慮代碼優化的問題,希望大拿們不喜勿噴。
話不多說先來看看效果(轉成GIF后比較卡頓)
第一步:在沒有旋轉動畫之前呢是一個圖片,把圖片和自定義的旋轉動畫放在一個Layout當中,當開始檢測的時候就打開自定義試圖的動畫。
布局文件
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<com.zhizun.zhizunwifi.widget.CircleViewPhy
android:id="@+id/circle_safe"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
app:circleWidth="2dp"
app:firstColor="@color/white"
app:speed="2"
android:visibility="gone"
/>
<ImageView
android:id="@+id/speed_rotating"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:src="@drawable/img_scanning"
android:layout_gravity="center_vertical"
android:visibility="gone"
/>
<ImageView
android:id="@+id/speed_icon_safe"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_centerInParent="true"
android:src="@drawable/icon_safe"
android:layout_gravity="center_vertical"
/>
</RelativeLayout>
speed_icon_safe這個就是一張圖片,speed_rotating中間旋轉漸變的扇形圖片,circle_safe從開始畫圓點然后圓弧旋轉一直到檢測完畢畫實心圓弧都有這個自定義試圖完成。
第二步:關鍵的自定義試圖代碼
public class CircleViewPhy extends View {
/**
* 第一種顏色
*/
private int mFirstColor;
/**
* 第二種顏色
*/
private int mSecondColor;
/**
* 圓弧的寬度
*/
private int mCircleWidth;
/**
* 畫筆
*/
private Paint mPaint;
private Paint wPaint;
private Paint TPaint;
/**
* 圓弧的度數
*/
private int mProgress;
/**
* 圓弧的度數
*/
private int TProgress;
/**
* 圓弧繪制的速度
*/
private int mSpeed;
/**
* 是不是開始繪制下一個圓弧
*/
private boolean isNext = false;
private int phase;
private Rect mSrcRect, mDestRect;
private float mStartAngle;
private float mSweepAngle = 0;
private static final int MIN_ANGLE_SWEEP = 3;
private static final int MAX_ANGLE_SWEEP = 155;
private int mAngleIncrement = 3;
Bitmap bmp;
private ImageView imageCircle;
private Context mContext;
private boolean isFinsh = false;
private boolean autoRun = false;
public CircleViewPhy(Context context) {
this(context, null);
}
public CircleViewPhy(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public void setImageCircle(ImageView imageCircle) {
this.imageCircle = imageCircle;
}
public void setfinsh(boolean finsh){
this.isFinsh = finsh;
}
public void setautoRun(boolean run){
this.autoRun = run;
}
/**
* 獲取自定義控件的一些值
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public CircleViewPhy(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleViewPhy, defStyleAttr, 0);
mContext = context;
for (int i = 0; i < a.getIndexCount(); i++) {
switch (a.getIndex(i)) {
case R.styleable.CircleViewPhy_firstColor:
mFirstColor = a.getColor(a.getIndex(i), Color.WHITE);
break;
case R.styleable.CircleViewPhy_secondColor:
mSecondColor = a.getColor(a.getIndex(i), Color.RED);
break;
case R.styleable.CircleViewPhy_speed:
mSpeed = a.getInt(a.getIndex(i), 20);
break;
case R.styleable.CircleViewPhy_circleWidth:
mCircleWidth = a.getDimensionPixelOffset(a.getIndex(i), (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
mPaint = new Paint();
wPaint = new Paint();
TPaint = new Paint();
//繪圖線程
new Thread() {
@Override
public void run() {
while (true) {
if (autoRun){
if (isFinsh){
TProgress ++;
if (TProgress == 360){
TProgress = 360;
}
}
if (mProgress != 360) {
mProgress++;
}else {
final float angle = 5;
mStartAngle += angle;
if (mStartAngle > 360) {
mStartAngle -= 360;
}
if (mSweepAngle > MAX_ANGLE_SWEEP) {
mAngleIncrement = -mAngleIncrement;
} else if (mSweepAngle < MIN_ANGLE_SWEEP) {
mSweepAngle = MIN_ANGLE_SWEEP;
} else if (mSweepAngle == MIN_ANGLE_SWEEP) {
mAngleIncrement = -mAngleIncrement;
}
mSweepAngle += mAngleIncrement;
}
}
postInvalidate();
try {
if (mProgress == 360 && !isFinsh){
Thread.sleep(mSpeed + 5); //通過傳遞過來的速度參數來決定線程休眠的時間從而達到繪制速度的快慢
}else {
Thread.sleep(mSpeed); //通過傳遞過來的速度參數來決定線程休眠的時間從而達到繪制速度的快慢
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@Override
protected void onDraw(Canvas canvas) {
int center = getWidth() / 2;
int radius = center - mCircleWidth / 2;
mPaint.setStrokeWidth(mCircleWidth); // 設置圓環的寬度
wPaint.setStrokeWidth(mCircleWidth);
TPaint.setStrokeWidth(mCircleWidth + 8);
mPaint.setAntiAlias(true); // 消除鋸齒
wPaint.setAntiAlias(true); // 消除鋸齒
TPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE); // 設置空心
wPaint.setStyle(Paint.Style.STROKE);
TPaint.setStyle(Paint.Style.STROKE);
RectF oval = new RectF(center - radius + 5, center - radius + 5, center + radius - 5, center + radius - 5); // 用于定義的圓弧的形狀和大小的界限
RectF oval0 = new RectF(center - radius + 50, center - radius + 50, center + radius - 50, center + radius - 50); // 用于定義的圓弧的形狀和大小的界限
if (!isFinsh){
Path path = new Path();
path.addCircle(0, 0, 5, Path.Direction.CCW);
PathEffect pathEffect = new PathDashPathEffect(path,30, phase, PathDashPathEffect.Style.ROTATE);
mPaint.setPathEffect(pathEffect);
mPaint.setColor(mFirstColor); // 設置圓環的顏色
canvas.drawArc(oval, -90, mProgress, false, mPaint); // 根據進度畫圓弧
if (mProgress == 360){
wPaint.setColor(getResources().getColor(R.color.white_alpha_144)); // 設置圓環的顏色
canvas.drawArc(oval0, -90, 360, false, wPaint);
TPaint.setColor(Color.WHITE);
canvas.drawArc(oval, mStartAngle, mSweepAngle, false, TPaint);
}
}else {
wPaint.setColor(getResources().getColor(R.color.white)); // 設置圓環的顏色
canvas.drawArc(oval0, -90, TProgress, false, wPaint);
}
}
}
當時本來說是圓環要顏色變化的,但是后來統一改成白色了,所以代碼中顏色的片段就不用管了。我先來說說思路,首先動畫開始是一個圓點的畫圓弧動畫。
把筆觸設置為圓點
Path path = new Path();
path.addCircle(0, 0, 5, Path.Direction.CCW);
PathEffect pathEffect = new PathDashPathEffect(path,30, phase, PathDashPathEffect.Style.ROTATE);
mPaint.setPathEffect(pathEffect);
所有的動畫效果都是通過畫筆在畫圖的過程中設置一個線程休眠來體現的。
postInvalidate();
try {
if (mProgress == 360 && !isFinsh){
Thread.sleep(mSpeed + 5); //通過傳遞過來的速度參數來決定線程休眠的時間從而達到繪制速度的快慢
}else {
Thread.sleep(mSpeed); //通過傳遞過來的速度參數來決定線程休眠的時間從而達到繪制速度的快慢
}
} catch (InterruptedException e) {
e.printStackTrace();
}
當圓點的圓弧畫到360度之后就開始畫第二層的實心圓
if (mProgress == 360){
wPaint.setColor(getResources().getColor(R.color.white_alpha_144)); // 設置圓環的顏色
canvas.drawArc(oval0, -90, 360, false, wPaint);
TPaint.setColor(Color.WHITE);
canvas.drawArc(oval, mStartAngle, mSweepAngle, false, TPaint);
}
<declare-styleable name="CircleViewPhy">
<attr name="firstColor" format="color"/>
<attr name="secondColor" format="color"/>
<attr name="circleWidth" format="dimension"/>
<attr name="speed" format="integer"/>
</declare-styleable>
當檢測過程完畢之后又Activity傳入isFinsh為True標識完畢,停止繪制動畫,畫最后一層高亮的圓弧。