前幾天在網頁上看到一個不錯的加載動畫View後,想将它在安卓上實作一遍。效果如下,很簡單。
因為控件實作動畫時,更新視圖的頻率會很高。為了減少記憶體的占用,決定使用SurfaceView來實作。
一、對控件的測量
使用的規則是:當控件的寬/高不是固定時,寬/高的大小為預設的寬高+padding值。反之,使用傳遞過來的寬高。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
if(modeWidth == MeasureSpec.AT_MOST || modeWidth == MeasureSpec.UNSPECIFIED ){
sizeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bwidth,getContext().getResources().getDisplayMetrics());
sizeWidth += getPaddingLeft()+getPaddingRight();
}
if(modeHeight == MeasureSpec.AT_MOST || modeHeight == MeasureSpec.UNSPECIFIED ){
sizeHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bheight,getContext().getResources().getDisplayMetrics());
sizeHeight += getPaddingBottom()+getPaddingTop();
}
setMeasuredDimension(sizeWidth,sizeHeight);
}
二、對控件的繪制
private void drawView(Canvas canvas){
// 繪制背景圖
canvas.drawColor(backgroundColor);
// 繪制電池框
canvas.drawRoundRect(SrectF,bRadius,bRadius,paintS);
// 繪制電池的小點
canvas.drawRect(centerX+bwidth/2-distance,centerY-bheight/4,centerX+bwidth/2,centerY+bheight/4,paintF);
// 更新電池填充的右邊大小
FrectF.right = FrectF.left+varyInnerWidth;
// 繪制電池的填充
canvas.drawRoundRect(FrectF,bRadius,bRadius,paintF);
// 根據不同情況,修改電池填充的右邊大小
if(SrectF.left+varyInnerWidth>centerX+bwidth/2-distance*2){
// 超過範圍時
varyInnerWidth = centerX+bwidth/2-distance*2;
}else if(SrectF.left+varyInnerWidth==centerX+bwidth/2-distance*2){
// 等于範圍時:
varyInnerWidth = 0;
}else {
// 增長時:
varyInnerWidth +=2;
}
}
三、異步更新控件視圖
@Override
public void run() {
while (isThreadRunning) {
// 擷取視圖,鎖定視圖
canvas = surfaceHolder.lockCanvas();
if(canvas!=null){
drawView(canvas);
// 更新視圖
surfaceHolder.unlockCanvasAndPost(canvas);
}
try {
Thread.sleep(40); // 相當于幀頻,數值越小畫面就越流暢
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、自定義控件屬性
隻提取了一些簡單的屬性。
<declare-styleable name="BatteryLoadingView">
<attr name="outBWidth" format="dimension"/>
<attr name="outBHeight" format="dimension"/>
<attr name="bRadius" format="float"/>
<attr name="backgroundColor" format="color"/>
<attr name="bColor" format="color"/>
</declare-styleable>
五、完整BatteryView代碼
package com.cxmscb.cxm.videosplash;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* Created by cxm on 2016/9/22.
*/
public class BatteryView extends SurfaceView implements SurfaceHolder.Callback,Runnable{
private Paint paintS,paintF;
private int bwidth;
private int bheight;
int varyInnerWidth;
private int bColor,backgroundColor;
private float bRadius;
private RectF SrectF,FrectF;
private Thread thread;
private SurfaceHolder surfaceHolder;
private Canvas canvas;
private boolean isThreadRunning = true;
private int distance;
private int centerX,centerY;
public BatteryView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public BatteryView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public BatteryView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(AttributeSet attrs) {
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
SrectF = new RectF();
FrectF = new RectF();
distance = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,4,getContext().getResources().getDisplayMetrics());
TypedArray attrArray = getContext().obtainStyledAttributes(attrs, R.styleable.BatteryLoadingView);
if (attrArray != null) {
bwidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,80,getContext().getResources().getDisplayMetrics());
bheight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,30,getContext().getResources().getDisplayMetrics());
bwidth = attrArray.getDimensionPixelSize(R.styleable.BatteryLoadingView_outBWidth, bwidth);
bheight = attrArray.getDimensionPixelSize(R.styleable.BatteryLoadingView_outBHeight, bheight);
bColor = attrArray.getColor(R.styleable.BatteryLoadingView_bColor,Color.GREEN);
backgroundColor = attrArray.getColor(R.styleable.BatteryLoadingView_backgroundColor,Color.WHITE);
bRadius = attrArray.getFloat(R.styleable.BatteryLoadingView_bRadius,9f);
attrArray.recycle();
}
initPaint();
}
private void initPaint() {
paintS = new Paint();
paintF = new Paint();
paintS.setColor(bColor);
paintF.setColor(bColor);
paintS.setStyle(Paint.Style.STROKE);
paintS.setStrokeWidth(6);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new Thread(this);
thread.start();
isThreadRunning = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
varyInnerWidth = 0;
centerX = getMeasuredWidth()/2;
centerY = getMeasuredHeight()/2 ;
SrectF.left = centerX-bwidth/2 ;
SrectF.top = centerY-bheight/2;
SrectF.right = centerX+bwidth/2-distance*2;
SrectF.bottom = centerY+bheight/2;
FrectF.left = SrectF.left ;
FrectF.top = SrectF.top;
FrectF.right = SrectF.left;
FrectF.bottom = SrectF.bottom;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isThreadRunning = false;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
if(modeWidth == MeasureSpec.AT_MOST || modeWidth == MeasureSpec.UNSPECIFIED ){
sizeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bwidth,getContext().getResources().getDisplayMetrics());
sizeWidth += getPaddingLeft()+getPaddingRight();
}
if(modeHeight == MeasureSpec.AT_MOST || modeHeight == MeasureSpec.UNSPECIFIED ){
sizeHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bheight,getContext().getResources().getDisplayMetrics());
sizeHeight += getPaddingBottom()+getPaddingTop();
}
setMeasuredDimension(sizeWidth,sizeHeight);
}
private void drawView(Canvas canvas){
canvas.drawColor(backgroundColor);
canvas.drawRoundRect(SrectF,bRadius,bRadius,paintS);
canvas.drawRect(centerX+bwidth/2-distance,centerY-bheight/4,centerX+bwidth/2,centerY+bheight/4,paintF);
FrectF.right = FrectF.left+varyInnerWidth;
canvas.drawRoundRect(FrectF,bRadius,bRadius,paintF);
if(SrectF.left+varyInnerWidth>centerX+bwidth/2-distance*2){
varyInnerWidth = centerX+bwidth/2-distance*2;
}else if(SrectF.left+varyInnerWidth==centerX+bwidth/2-distance*2){
varyInnerWidth = 0;
}else {
varyInnerWidth +=2;
}
}
@Override
public void run() {
while (isThreadRunning) {
canvas = surfaceHolder.lockCanvas();
if(canvas!=null){
drawView(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
try {
Thread.sleep(40); // 相當于幀頻了,數值越小畫面就越流暢
} catch (Exception e) {
e.printStackTrace();
}
}
}
}