本文原創(chuàng),這篇可不能匿名轉(zhuǎn)載晨另。
背景:我一哥們公司做智能設(shè)備的潭千,該動畫用在手機和家中網(wǎng)絡(luò)連接時用,他讓我看了下需求借尿。剛看到這動畫時感覺產(chǎn)品\UI設(shè)計的不錯刨晴,想著試試。昨天開始做的路翻,本來感覺很簡單狈癞,但做起來貌似沒那么簡單;最后花了近一天時間終于搞定了茂契〉埃看看效果還行!
- 如果有想直接用的同道中人掉冶,看前半部分就行真竖;如果想批評指正我的思考的看看后半部分
1.直接上代碼(NiceLoadingView)
package com.hadisi.niceloading;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* Created by hadisi5216 on 2016/7/12.
*/
public class NiceLoadingView extends View {
private Context mContext;
private Paint mPaint;
private int widthSpecSize;
private int heightSpecSize;
private int radiusSmall = 38;
private int radiusbig = 76;
private int moveX;
private int XPoint;
private int mState = -1;//0失敗,1成功厌小,-1默認
private boolean mflag;
private ValueAnimator animator;
public NiceLoadingView(Context context) {
super(context);
}
public NiceLoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NiceLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
mPaint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(0xFFFFBC53);
mPaint.setAntiAlias(true);
if (Math.abs(moveX) > widthSpecSize * 5 / 4) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 7 / 4 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 7 / 4 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize && Math.abs(moveX) < widthSpecSize * 3 / 2) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 2 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 3 / 2 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize * 3 / 4 && Math.abs(moveX) < widthSpecSize * 5 / 4) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 5 / 4 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 5 / 4 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize / 2 && Math.abs(moveX) < widthSpecSize) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize - Math.abs(moveX) : widthSpecSize - widthSpecSize + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize / 4 && Math.abs(moveX) < widthSpecSize * 3 / 4) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 4 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 3 / 4 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize / 2) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize / 2 - Math.abs(moveX) : widthSpecSize - widthSpecSize / 2 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
//中間大圓
if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize * 5 / 4) {
radiusbig = 2 * radiusSmall - radiusSmall * (Math.abs(moveX)) / (widthSpecSize * 5 / 4);
radiusbig = (radiusbig > radiusSmall) ? radiusbig : radiusSmall;
canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);
}
if (Math.abs(moveX) < 12 && mState >= 0) {
if (mState == 0) {
canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.connect_failed);
canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize / 2 - radiusbig, heightSpecSize / 2 - radiusbig, widthSpecSize / 2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);
}
if (mState == 1) {
canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.connect_success);
canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize / 2 - radiusbig, heightSpecSize / 2 - radiusbig, widthSpecSize / 2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);
}
}
}
public void start() {
if (animator != null)
animator.cancel();
moveX = widthSpecSize * (-9 / 4);
mState = -1;
mflag = true;
post(new Runnable() {
@Override
public void run() {
animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (mState < 0) {
moveX = (moveX > widthSpecSize * 7 / 4) ? widthSpecSize * (-9 / 4) : moveX + 12;
} else {
if (moveX > 0)
moveX = (moveX > widthSpecSize * 7 / 4) ? widthSpecSize * (-9 / 4) : moveX + 12;
else if (moveX < 0 && mflag) {
moveX += 12;
if (Math.abs(moveX) < 12)
mflag = false;
}
}
postInvalidate();
}
});
animator.start();
}
});
}
public void success() {
mState = 1;
}
public void failed() {
mState = 0;
}
}
項目已上傳到github恢共,戳著
2.怎么用?
- 布局文件中
<com.hadisi.niceloading.NiceLoadingView
android:id="@+id/nice_loading"
android:layout_width="match_parent"
android:layout_height="100dp" />
- 你要用的地方
NiceLoadingView niceLoading = (NiceLoadingView) findViewById(R.id.nice_loading);
……
//開始連接時
niceLoading.start();
……
//連接成功時
niceLoading.success();
……
//連接失敗時
niceLoading.failed();
3.我怎么實現(xiàn)的璧亚!
仔細看效果圖可以得出:
1讨韭、有6個小圓依次從屏幕左側(cè)移入屏幕中間,然后又依次從屏幕中間移出屏幕右側(cè)涨岁。
2拐袜、中間有個大圓在隨著小圓的依次靠近慢慢變大,離開慢慢變猩倚健蹬铺;注意在左側(cè)第1個小圓到達中間時才出現(xiàn)大圓,在最后一個小圓準備向右側(cè)移動時消失秉撇;大圓的半徑在小圓半徑和大圓半徑之間甜攀。
3秋泄、不管何時得到成功和失敗的狀態(tài),動畫終止時都是在小圓依次從左邊進入中間后规阀。
4恒序、動畫完成后顯示成功/失敗圖片和大圓。
-
1. 6個小圓的運動
我是這樣想的:當?shù)?個小圓移動到widthSpecSize/4(widthSpecSize 為控件的寬度)時第2個小圓開始移動谁撼、當?shù)?個小圓移動到widthSpecSize/4 時第3個小圓開始移動......當?shù)?個小圓移動到widthSpecSize/4 時第6個小圓開始移動歧胁、第6個小圓移動到widthSpecSize/2 時繼續(xù)移動、當?shù)?個小圓移動到widthSpecSize * 3/4時第5個小圓開始移動......當?shù)?個小圓移動到widthSpecSize * 3/4時第1個小圓開始移動厉碟、最后第1個小球移出屏幕右側(cè)喊巍,到此為一個循環(huán)。
假設(shè)有一個位移變量moveX箍鼓,moveX在不斷增加崭参,其變化范圍為(a,b);可以看出按照我的想法款咖,第1個小圓在范圍的兩邊時開始移動何暮、第6個小圓在變化范圍的中間部分開始移動。
我們可以繼續(xù)假設(shè)變化范圍為(-a,a)铐殃,這樣第1個小圓在范圍的絕對值大時開始移動海洼、第6個小圓在變化范圍的絕對值小時開始移動;其實這種重復(fù)的動作很容易想到絕對值控制
找張紙畫下很容易得到moveX的變化范圍在(-widthSpecSize * 7/4 , widthSpecSize * 7/4)之間富腊。
對照圖很快可以得出下面代碼
if (Math.abs(moveX) > widthSpecSize * 5 / 4) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 7 / 4 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 7 / 4 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize && Math.abs(moveX) < widthSpecSize * 3 / 2) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 2 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 3 / 2 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize * 3 / 4 && Math.abs(moveX) < widthSpecSize * 5 / 4) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 5 / 4 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 5 / 4 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize / 2 && Math.abs(moveX) < widthSpecSize) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize - Math.abs(moveX) : widthSpecSize - widthSpecSize + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > widthSpecSize / 4 && Math.abs(moveX) < widthSpecSize * 3 / 4) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 4 - Math.abs(moveX) : widthSpecSize - widthSpecSize * 3 / 4 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize / 2) {
XPoint = (moveX < 0) ? XPoint = widthSpecSize / 2 - Math.abs(moveX) : widthSpecSize - widthSpecSize / 2 + Math.abs(moveX);
canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);
}
-
2. 中間大圓的運動
中間變化的大圓在左側(cè)第1個小圓到達中間時才出現(xiàn)大圓,在第1個小圓準備向右側(cè)移動時消失蟹肘,變化范圍(-widthSpecSize * 5/4 , widthSpecSize * 5/4)之間。大圓的半徑在小圓半徑和大圓半徑之間俯树,我們用radiusbig = radiusbig - radiusSmall * (Math.abs(moveX)) / (widthSpecSize * 5/4)計算大圓半徑帘腹,可以得到慢慢變大和變小的效果,然后控制在小于radiusSmall時用radiusSmall许饿。
if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize * 5 / 4) {
radiusbig = radiusbig - radiusSmall * (Math.abs(moveX)) / (widthSpecSize * 5 / 4);
radiusbig = (radiusbig > radiusSmall) ? radiusbig : radiusSmall;
canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);
}
-
3. 動畫終止的控制
正常當一個循環(huán)結(jié)束時我們需要重新給moveX賦值為widthSpecSize * (-7/4)阳欲,當收到成功或失敗狀態(tài)時需要判斷當前的狀態(tài),等到動畫進行到結(jié)束狀態(tài)(小圓依次從左邊進入中間后)陋率。見下面代碼球化,mState為當前狀態(tài)(0失敗,1成功瓦糟,-1默認)筒愚。
我重新賦值時將moveX設(shè)為 * widthSpecSize * (-9/4),因為一個循環(huán)結(jié)束后有點停頓會感覺舒服點菩浙,這個無所謂巢掺,自己感覺而已*
if (mState < 0) {
moveX = (moveX > widthSpecSize * 7 / 4) ? widthSpecSize * (-9 / 4) : moveX + 12;
} else {
if (moveX > 0)
moveX = (moveX > widthSpecSize * 7 / 4) ? widthSpecSize * (-9 / 4) : moveX + 12;
else if (moveX < 0 && mflag) {
moveX += 12;
if (Math.abs(moveX) < 12)
mflag = false;
}
}
-
4. 顯示成功/失敗圖片
這個簡單句伶,在收到成功或失敗狀態(tài),待動畫完成時先畫一個大圓陆淀,再畫一個bitmap
if (Math.abs(moveX) < 12 && mState >= 0) {
if (mState == 0) {
canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.connect_failed);
canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize / 2 - radiusbig, heightSpecSize / 2 - radiusbig, widthSpecSize / 2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);
}
if (mState == 1) {
canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.connect_success);
canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize / 2 - radiusbig, heightSpecSize / 2 - radiusbig, widthSpecSize / 2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);
}
}
4.優(yōu)化
- 可以優(yōu)化考余,將paint的顏色等屬性、大小圓的半徑轧苫、優(yōu)化畫小圓的邏輯楚堤,使小圓個數(shù)可變等抽象出來..........
其實核心的就是想法,隨便怎么優(yōu)化含懊。反正我就弄到這了身冬,油而不膩,我覺得挺好绢要,不需要太多優(yōu)化吏恭。吼吼....