最近接了一个项目其中有功能要实现一个清理内存,要求和微信的效果一样。于是想到用surfaceView而不是继承view。下面小编给大家解析下实现思路。

surfaceView是为了解决频繁绘制动画产生了闪烁,而采用了双缓冲机制,即A、B两个缓冲轮流显示在画布上,同时,使用不当,同样容易产生闪烁,这是由于A、B中有一个缓冲没有改变。

在我写这个view的时候就遇到了这个问题,研究了好久终于解决。

首先说一下思路:

微信清理缓存的动画是:

一个圆环不停的转动,同时中间有文字显示-->加载完成后,出现一个慢慢展开的图标,同时第一块区域要突出一点。

这就是微信的动画效果。但是具体实现是怎么样的呢?

下面说一下我实现的方法:

1、旋转圆环:

这个圆环由两部分组成,一个圆和一个深灰的弧线,而弧线一直在转动,产生了圆环在旋转的效果。

因此,这个就很好解决了。我们画两个图形,一个圆形,一个弧线,而弧线的角度不停的变化就产了旋转的效果。为了让它不断变化,就要用到一个动画类ValueAnimator,通过这个类不停的给出一个角度,然后我们不停的绘制,就可以完成这个效果。

2、文字:

文字是和圆环是一部分的,当然他们其实应该同时绘制。但是每次绘制,为了避免文字的重叠,我们需要将canvas清除。

我们通过计算出总的旋转动画时间和一个由绘制动画开始,到具体当前绘制时的时间差来模拟出百分比。

3、会展开的图表:

这个是这个效果的难点部分,这里遇到的问题也比较多。

这是一个慢慢展开的动画,看似是一个圆在慢慢显现,其实不是。只不过是一个扇形再不停的旋转,但是没有清除之前的画布,这样产生了平铺效果。而第一块区域的扇形较大只不过是半径大一点而已。因此这部分我们同样利用ValueAnimator,也可以画出。

通过对第一个旋转动画进行监听,当第一个效果结束的时候,第二个图表的动画开始进行。

4、具体的内存大小信息:

这个比较简单,只需要确定坐标即可,但是在写的时候也遇到了闪烁情况。

下面是具体实现

最初版本:

package xiaoqi.expandablechartview;

import android.animation.Animator;

import android.animation.PropertyValuesHolder;

import android.animation.ValueAnimator;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.PixelFormat;

import android.graphics.PorterDuff;

import android.graphics.RectF;

import android.util.AttributeSet;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.animation.LinearInterpolator;

public class ChartView extends SurfaceView implements SurfaceHolder.Callback {

private Context context;

private SurfaceHolder holder;

private ValueAnimator chartAnimator;

private ValueAnimator circleAnimator;

//中间内存信息方块的坐标

private float centerDetailLeft;

private float centerDetailTop;

private float centerDetailRight;

private float centerDetailBottom;

//chart外接正方形坐标

private float chartLeft;

private float chartTop;

private float chartRight;

private float chartBottom;

//起始角度

private float startAngle = 270;

//半径

private float radius;

//各区域角度

private float area1Angle;

private float area2Angle;

//区域的量

private float total;

private float area1;

private float area2;

private long time;

private int repeatCount = 2;

//是否为第一次显示,用于防止surface闪烁

private boolean area1IsFirstShow = true;

private boolean area2IsFirstShow = true;

//大扇形外接正方形

private RectF rectF;

//小扇形外接正方形

private RectF rectF2;

private Paint area1Paint;

private Paint area2Paint;

private Paint area3Paint;

private Paint circlePaint;

private Paint arcPaint;

private Paint loadingPaint;

private Paint textPaint;

private static final int CIRCLE_DURATION = 1000;

public ChartView(Context context) {

super(context);

this.context = context;

init();

}

public ChartView(Context context, AttributeSet attrs) {

super(context, attrs);

this.context = context;

init();

}

public ChartView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

this.context = context;

init();

}

private void init() {

radius = Utility.dip2px(context, 100);

holder = getHolder();

holder.addCallback(this);

setZOrderOnTop(true);

holder.setFormat(PixelFormat.TRANSLUCENT);

initPaint();

initAnimator();

}

private void initAnimator() {

PropertyValuesHolder angleValues = PropertyValuesHolder.ofFloat("angle", 0f, 360f);

chartAnimator = ValueAnimator.ofPropertyValuesHolder(angleValues);

chartAnimator.setDuration(2000);

chartAnimator.setInterpolator(new LinearInterpolator());

chartAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

float angle = obj2Float(animation.getAnimatedValue("angle"));

Canvas canvas = holder.lockCanvas(null);

if(canvas != null){

// canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

drawDetail(canvas);

// canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));

// if (!area1IsFirstShow) {

// canvas.drawArc(rectF, startAngle, area1Angle, true, area1Paint);

// }

// if (!area2IsFirstShow) {

// canvas.drawArc(rectF2, area1Angle + startAngle, area2Angle, true, area2Paint);

// }

if (angle < area1Angle) {

canvas.drawArc(rectF, startAngle, angle, true, area1Paint);

} else if (angle <= area2Angle + area1Angle) {

// if (area1IsFirstShow) {

// area1IsFirstShow = false;

// canvas.drawArc(rectF, startAngle, area1Angle, true, area1Paint);

// } else {

canvas.drawArc(rectF2, startAngle+area1Angle, angle - area1Angle, true, area2Paint);

// }

} else {

// if (area2IsFirstShow) {

// area2IsFirstShow = false;

// canvas.drawArc(rectF2, area1Angle + startAngle, area2Angle, true, area2Paint);

// } else {

canvas.drawArc(rectF2, startAngle + area1Angle + area2Angle, angle - area2Angle - area1Angle,

true, area3Paint);

// }

}

holder.unlockCanvasAndPost(canvas);

}

}

});

circleAnimator = ValueAnimator.ofPropertyValuesHolder(angleValues);

circleAnimator.setInterpolator(new LinearInterpolator());

circleAnimator.setDuration(CIRCLE_DURATION);

circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

float angle = obj2Float(animation.getAnimatedValue("angle"));

Canvas canvas = holder.lockCanvas(null);

if(canvas != null){

long nowTime = System.currentTimeMillis();

int rate = (int) (nowTime - time) / (CIRCLE_DURATION * (repeatCount + 1) / 100);

if (rate <= 100) {

canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

canvas.drawText("正在加载" + rate + "%", getMeasuredWidth() / 2 - radius / 2,

getMeasuredHeight() / 2, loadingPaint);

}

canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2 - Utility.dip2px(context, 10),

radius, circlePaint);

canvas.drawArc(rectF2, 180 + angle, 30, false, arcPaint);

holder.unlockCanvasAndPost(canvas);

}

}

});

circleAnimator.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

time = System.currentTimeMillis();

}

@Override

public void onAnimationEnd(Animator animation) {

chartAnimator.start();

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

}

private void initPaint() {

area1Paint = new Paint();

area1Paint.setAntiAlias(true);

area1Paint.setStyle(Paint.Style.FILL);

area1Paint.setTextSize((Utility.dip2px(context, 15)));

area1Paint.setColor(context.getResources().getColor(R.color.background_blue));

area2Paint = new Paint();

area2Paint.setAntiAlias(true);

area2Paint.setStyle(Paint.Style.FILL);

area2Paint.setTextSize((Utility.dip2px(context, 15)));

area2Paint.setColor(context.getResources().getColor(R.color.chart_blue));

area3Paint = new Paint();

area3Paint.setAntiAlias(true);

area3Paint.setStyle(Paint.Style.FILL);

area3Paint.setTextSize((Utility.dip2px(context, 15)));

area3Paint.setColor(context.getResources().getColor(R.color.light_gary));

circlePaint = new Paint();

circlePaint.setAntiAlias(true);

circlePaint.setStrokeWidth(Utility.dip2px(context, 5));

circlePaint.setStyle(Paint.Style.STROKE);

circlePaint.setColor(context.getResources().getColor(R.color.background_gray));

arcPaint = new Paint();

arcPaint.setAntiAlias(true);

arcPaint.setStrokeWidth(Utility.dip2px(context, 5));

arcPaint.setStyle(Paint.Style.STROKE);

arcPaint.setColor(context.getResources().getColor(R.color.textcolor_gray));

loadingPaint = new Paint();

loadingPaint.setTextSize((Utility.dip2px(context, 15)));

loadingPaint.setColor(context.getResources().getColor(R.color.textcolor_gray));

textPaint = new Paint();

textPaint.setTextSize((Utility.dip2px(context, 15)));

textPaint.setColor(context.getResources().getColor(R.color.black));

}

private float obj2Float(Object o) {

return ((Number) o).floatValue();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

chartLeft = getMeasuredWidth() / 2 - radius;

chartTop = getMeasuredHeight() / 2 - radius - Utility.dip2px(context, 10);

chartRight = getMeasuredWidth() / 2 + radius;

chartBottom = getMeasuredHeight() / 2 + radius - Utility.dip2px(context, 10);

centerDetailLeft = getMeasuredWidth() / 2 - Utility.dip2px(context, 20);

centerDetailTop = getMeasuredHeight() / 2 + radius + Utility.dip2px(context, 15);

centerDetailRight = getMeasuredWidth() / 2;

centerDetailBottom = getMeasuredHeight() / 2 + radius + Utility.dip2px(context, 35);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

}

@Override

public void surfaceCreated(SurfaceHolder holder) {

rectF = new RectF(chartLeft - Utility.dip2px(context, 5), chartTop - Utility.dip2px(context, 5), chartRight +

Utility.dip2px(context, 5), chartBottom + Utility.dip2px(context, 5));

rectF2 = new RectF(chartLeft, chartTop, chartRight, chartBottom);

// valueAnimator.start();

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

circleAnimator.cancel();

chartAnimator.cancel();

}

private void drawDetail(Canvas canvas) {

canvas.drawRect(centerDetailLeft - Utility.dip2px(context, 150), centerDetailTop,

centerDetailRight - Utility.dip2px(context, 150), centerDetailBottom, area1Paint);

canvas.drawRect(centerDetailLeft, centerDetailTop, centerDetailRight, centerDetailBottom, area2Paint);

canvas.drawRect(centerDetailLeft + Utility.dip2px(context, 150), centerDetailTop,

centerDetailRight + Utility.dip2px(context, 150), centerDetailBottom, area3Paint);

drawText(canvas);

}

private void drawText(Canvas canvas) {

canvas.drawText("本软件", centerDetailRight - Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 10), area1Paint);

canvas.drawText("200MB", centerDetailRight - Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 25), textPaint);

canvas.drawText("其他", centerDetailRight + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 10), area2Paint);

canvas.drawText("24.1GB", centerDetailRight + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 25), textPaint);

canvas.drawText("可用", centerDetailRight + Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 10), area3Paint);

canvas.drawText("30GB", centerDetailRight + Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 25), textPaint);

}

public void show() {

circleAnimator.setRepeatCount(repeatCount);

circleAnimator.start();

}

public void setArea1Color(int color) {

area1Paint.setColor(color);

}

public void setArea2Color(int color) {

area2Paint.setColor(color);

}

public void setArea3Color(int color) {

area3Paint.setColor(color);

}

public void setRadius(float radius) {

this.radius = radius;

}

public void setScale(float total, float area1, float area2){

area1Angle = area1/total * 360;

area2Angle = area2/total * 360;

}

public void setRepeatCount(int repeatCount){

this.repeatCount = repeatCount;

}

}

效果:

模仿微信的效果基本显示出来了,但是当区域改变的时候,会不停闪烁,其实下面标注信息的小正方形也在闪烁,只不过我已经修改好了。

查了网上许多方法都没有给出一个很直接的答案,大部分都是说要对surfaceView前后缓存都进行绘制,这样就不产生闪烁问题。还有一种方法就是通过背景覆盖,让A缓冲在该区域的背景与B缓冲相同,这样自然而然切换的时候,就不会看到缓存交替而产生的闪烁问题了。

关于第一种,我并不是很理解,说是每次要改变前后两个缓冲,不能只变一个。。。。。。(网上都是这么说,但是我到底怎么改才算改!!?)

第二种方法,我经过了多次尝试实现了,通过切换画笔之后,每次画图都覆盖上一层,这样保持了之前闪烁部分的缓存一致。

该部分为找到的一些资料:

双缓存(Double-buffer)与黑屏闪烁

每个SurfaceView 对象有两个独立的graphic buffer,官方SDK将它们称作"front buffer"和"back buffer"。

常规的"double-buffer"会这么做:每一帧的数据都被绘制到back buffer,然后back buffer的内容被持续翻转(flip)到front buffer;屏幕一直显示front buffer。但Android SurfaceView的"double-buffer"却是这么做的:在buffer A里绘制内容,然后让屏幕显示buffer A; 下一个循环,在buffer B里绘制内容,然后让屏幕显示buffer B; 如此往复。于是,屏幕上显示的内容依次来自buffer A, B, A, B,....这样看来,两个buffer其实没有主从的分别,与其称之为"front buffer""back buffer",毋宁称之为"buffer A""buffer B"。

Android中"double-buffer"的实现机制,可以很好地解释闪屏现象。在第一个"lockCanvas-drawCanvas-unlockCanvasAndPost"循环中,更新的是buffer A的内容;到下一个"lockCanvas-drawCanvas-unlockCanvasAndPost"循环中,更新的是buffer B的内容。如果buffer A与buffer B中某个buffer内容为空,当屏幕轮流显示它们时,就会出现画面黑屏闪烁现象。

解决方法

出现黑屏是因为buffer A与buffer B中一者内容为空,而且为空的一方还被post到了屏幕。于是有两种解决思路:

不让空buffer出现:每次向一个buffer写完内容并post之后,顺便用这个buffer的内容填充另一个buffer。这样能保证两个buffer的内容是同步的,缺点是做了无用功,耗费性能。

不post空buffer到屏幕:当准备更新内容时,先判断内容是否为空,只有非空时才启动"lockCanvas-drawCanvas-unlockCanvasAndPost"这个流程。

就好比,A缓存是白色,B缓冲是黑色(也就是前后surfaceView的缓存)。而黑色是surfaceView的默认色。比如下面的伪代码,通过线程不停的绘制:

canvas = holder.lockCanvas();

if(flag) {

canvas.drawColor(Color.WHITE);

}

holder.unlockCanvasAndPost(canvas);

flag = false;

看似没有什么问题,但是在实际过程却一直在黑白闪烁,这就是因为,虽然A我们每次都绘制了,但是B一直没变还是黑色。这时,我们通过覆盖,讲背景变为白色,就解决了这个问题,而我的解决方法也类似于这种。

下面贴出代码:

package xiaoqi.expandablechartview;

import android.animation.Animator;

import android.animation.PropertyValuesHolder;

import android.animation.ValueAnimator;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.PixelFormat;

import android.graphics.PorterDuff;

import android.graphics.RectF;

import android.util.AttributeSet;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.animation.LinearInterpolator;

public class ChartView extends SurfaceView implements SurfaceHolder.Callback {

private Context context;

private SurfaceHolder holder;

private ValueAnimator chartAnimator;

private ValueAnimator circleAnimator;

//中间内存信息方块的坐标

private float centerDetailLeft;

private float centerDetailTop;

private float centerDetailRight;

private float centerDetailBottom;

//chart外接正方形坐标

private float chartLeft;

private float chartTop;

private float chartRight;

private float chartBottom;

//起始角度

private float startAngle = 270;

//半径

private float radius;

//各区域角度

private float area1Angle;

private float area2Angle;

//区域的量

private float total;

private float area1;

private float area2;

private long time;

private int repeatCount = 2;

//是否为第一次显示,用于防止surface闪烁

private boolean area1IsFirstShow = true;

private boolean area2IsFirstShow = true;

//大扇形外接正方形

private RectF rectF;

//小扇形外接正方形

private RectF rectF2;

private Paint area1Paint;

private Paint area2Paint;

private Paint area3Paint;

private Paint circlePaint;

private Paint arcPaint;

private Paint loadingPaint;

private Paint textPaint;

private static final int CIRCLE_DURATION = 1000;

public ChartView(Context context) {

super(context);

this.context = context;

init();

}

public ChartView(Context context, AttributeSet attrs) {

super(context, attrs);

this.context = context;

init();

}

public ChartView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

this.context = context;

init();

}

private void init() {

radius = Utility.dip2px(context, 100);

holder = getHolder();

holder.addCallback(this);

setZOrderOnTop(true);

holder.setFormat(PixelFormat.TRANSLUCENT);

initPaint();

initAnimator();

}

private void initAnimator() {

PropertyValuesHolder angleValues = PropertyValuesHolder.ofFloat("angle", 0f, 360f);

chartAnimator = ValueAnimator.ofPropertyValuesHolder(angleValues);

chartAnimator.setDuration(2000);

chartAnimator.setInterpolator(new LinearInterpolator());

chartAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

float angle = obj2Float(animation.getAnimatedValue("angle"));

Canvas canvas = holder.lockCanvas(null);

if(canvas != null){

canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

drawDetail(canvas);

// canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));

if (!area1IsFirstShow) {

canvas.drawArc(rectF, startAngle, area1Angle, true, area1Paint);

}

if (!area2IsFirstShow) {

canvas.drawArc(rectF2, area1Angle + startAngle, area2Angle, true, area2Paint);

}

if (angle < area1Angle) {

canvas.drawArc(rectF, startAngle, angle, true, area1Paint);

} else if (angle <= area2Angle + area1Angle) {

if (area1IsFirstShow) {

area1IsFirstShow = false;

canvas.drawArc(rectF, startAngle, area1Angle, true, area1Paint);

} else {

canvas.drawArc(rectF2, startAngle+area1Angle, angle - area1Angle, true, area2Paint);

}

} else {

if (area2IsFirstShow) {

area2IsFirstShow = false;

canvas.drawArc(rectF2, area1Angle + startAngle, area2Angle, true, area2Paint);

} else {

canvas.drawArc(rectF2, startAngle + area1Angle + area2Angle, angle - area2Angle - area1Angle,

true, area3Paint);

}

}

holder.unlockCanvasAndPost(canvas);

}

}

});

circleAnimator = ValueAnimator.ofPropertyValuesHolder(angleValues);

circleAnimator.setInterpolator(new LinearInterpolator());

circleAnimator.setDuration(CIRCLE_DURATION);

circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

float angle = obj2Float(animation.getAnimatedValue("angle"));

Canvas canvas = holder.lockCanvas(null);

if(canvas != null){

long nowTime = System.currentTimeMillis();

int rate = (int) (nowTime - time) / (CIRCLE_DURATION * (repeatCount + 1) / 100);

if (rate <= 100) {

canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

canvas.drawText("正在加载" + rate + "%", getMeasuredWidth() / 2 - radius / 2,

getMeasuredHeight() / 2, loadingPaint);

}

canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2 - Utility.dip2px(context, 10),

radius, circlePaint);

canvas.drawArc(rectF2, 180 + angle, 30, false, arcPaint);

holder.unlockCanvasAndPost(canvas);

}

}

});

circleAnimator.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

time = System.currentTimeMillis();

}

@Override

public void onAnimationEnd(Animator animation) {

chartAnimator.start();

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

}

private void initPaint() {

area1Paint = new Paint();

area1Paint.setAntiAlias(true);

area1Paint.setStyle(Paint.Style.FILL);

area1Paint.setTextSize((Utility.dip2px(context, 15)));

area1Paint.setColor(context.getResources().getColor(R.color.background_blue));

area2Paint = new Paint();

area2Paint.setAntiAlias(true);

area2Paint.setStyle(Paint.Style.FILL);

area2Paint.setTextSize((Utility.dip2px(context, 15)));

area2Paint.setColor(context.getResources().getColor(R.color.chart_blue));

area3Paint = new Paint();

area3Paint.setAntiAlias(true);

area3Paint.setStyle(Paint.Style.FILL);

area3Paint.setTextSize((Utility.dip2px(context, 15)));

area3Paint.setColor(context.getResources().getColor(R.color.light_gary));

circlePaint = new Paint();

circlePaint.setAntiAlias(true);

circlePaint.setStrokeWidth(Utility.dip2px(context, 5));

circlePaint.setStyle(Paint.Style.STROKE);

circlePaint.setColor(context.getResources().getColor(R.color.background_gray));

arcPaint = new Paint();

arcPaint.setAntiAlias(true);

arcPaint.setStrokeWidth(Utility.dip2px(context, 5));

arcPaint.setStyle(Paint.Style.STROKE);

arcPaint.setColor(context.getResources().getColor(R.color.textcolor_gray));

loadingPaint = new Paint();

loadingPaint.setTextSize((Utility.dip2px(context, 15)));

loadingPaint.setColor(context.getResources().getColor(R.color.textcolor_gray));

textPaint = new Paint();

textPaint.setTextSize((Utility.dip2px(context, 15)));

textPaint.setColor(context.getResources().getColor(R.color.black));

}

private float obj2Float(Object o) {

return ((Number) o).floatValue();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

chartLeft = getMeasuredWidth() / 2 - radius;

chartTop = getMeasuredHeight() / 2 - radius - Utility.dip2px(context, 10);

chartRight = getMeasuredWidth() / 2 + radius;

chartBottom = getMeasuredHeight() / 2 + radius - Utility.dip2px(context, 10);

centerDetailLeft = getMeasuredWidth() / 2 - Utility.dip2px(context, 20);

centerDetailTop = getMeasuredHeight() / 2 + radius + Utility.dip2px(context, 15);

centerDetailRight = getMeasuredWidth() / 2;

centerDetailBottom = getMeasuredHeight() / 2 + radius + Utility.dip2px(context, 35);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

}

@Override

public void surfaceCreated(SurfaceHolder holder) {

rectF = new RectF(chartLeft - Utility.dip2px(context, 5), chartTop - Utility.dip2px(context, 5), chartRight +

Utility.dip2px(context, 5), chartBottom + Utility.dip2px(context, 5));

rectF2 = new RectF(chartLeft, chartTop, chartRight, chartBottom);

// valueAnimator.start();

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

circleAnimator.cancel();

chartAnimator.cancel();

}

private void drawDetail(Canvas canvas) {

canvas.drawRect(centerDetailLeft - Utility.dip2px(context, 150), centerDetailTop,

centerDetailRight - Utility.dip2px(context, 150), centerDetailBottom, area1Paint);

canvas.drawRect(centerDetailLeft, centerDetailTop, centerDetailRight, centerDetailBottom, area2Paint);

canvas.drawRect(centerDetailLeft + Utility.dip2px(context, 150), centerDetailTop,

centerDetailRight + Utility.dip2px(context, 150), centerDetailBottom, area3Paint);

drawText(canvas);

}

private void drawText(Canvas canvas) {

canvas.drawText("本软件", centerDetailRight - Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 10), area1Paint);

canvas.drawText("200MB", centerDetailRight - Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 25), textPaint);

canvas.drawText("其他", centerDetailRight + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 10), area2Paint);

canvas.drawText("24.1GB", centerDetailRight + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 25), textPaint);

canvas.drawText("可用", centerDetailRight + Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 10), area3Paint);

canvas.drawText("30GB", centerDetailRight + Utility.dip2px(context, 150) + Utility.dip2px(context, 5),

centerDetailTop + Utility.dip2px(context, 25), textPaint);

}

public void show() {

circleAnimator.setRepeatCount(repeatCount);

circleAnimator.start();

}

public void setArea1Color(int color) {

area1Paint.setColor(color);

}

public void setArea2Color(int color) {

area2Paint.setColor(color);

}

public void setArea3Color(int color) {

area3Paint.setColor(color);

}

public void setRadius(float radius) {

this.radius = radius;

}

public void setScale(float total, float area1, float area2){

area1Angle = area1/total * 360;

area2Angle = area2/total * 360;

}

public void setRepeatCount(int repeatCount){

this.repeatCount = repeatCount;

}

}

效果:

同时建议每个图形都用自己的paint,而不是通过重新set不同设置来调用paint,因为在使用时,我发现,因为很多地方用的是同一个paint也导致了闪烁,而为每个图形都创建了自己的paint之后就好了。

以上所述是小编给大家介绍的Android仿微信清理内存图表动画(解决surfaceView屏幕闪烁问题)demo实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

android 清理缓存动画,Android仿微信清理内存图表动画(解决surfaceView屏幕闪烁问题)demo实例详解...相关推荐

  1. 小程序怎么处理轮训ajax,微信小程序中使用Promise进行异步流程处理的实例详解...

    微信小程序中使用Promise进行异步流程处理的实例详解 我们知道,JavaScript是单进程执行的,同步操作会对程序的执行进行阻塞处理.比如在浏览器页面程序中,如果一段同步的代码需要执行很长时间( ...

  2. 微信小程序中form 表单提交和取值实例详解

    2019独角兽企业重金招聘Python工程师标准>>> 我们知道,如果我们直接给 input 添加 bindinput,比如:<input bindinput="on ...

  3. php做一个详情页面,微信小程序实现头条新闻详情页图文显示功能的实例详解

    众所周知,微信小程序编译好后是不能解析HTML标签的,那么问题来了:后台中通过所见即所得编辑器编辑的图文并茂的文章如何在微信小程序端也能显示呢?这就是本文要讨论的主题. 本文内容如下 一.如何解析HT ...

  4. Android 二维码扫描(仿微信界面),根据Google zxing

    Android 二维码扫描(仿微信界面),根据Google zxing Android项目开发中经常会用到二维码扫描,例如登陆.支付等谷歌方面已经有了一个开源库(地址: https://github. ...

  5. Android 使用 CameraX 快速实现仿微信短视频录制

    Android 使用 CameraX 快速实现仿微信短视频录制(轻触拍照.长按录像) https://github.com/ldlywt/MyCameraX 微信短视频android端 https:/ ...

  6. 一键清空朋友圈软件_微信清理朋友圈app下载-微信清理朋友圈下载v1.8.0 安卓版-西西软件下载...

    微信清理朋友圈app一款强大的微信清理工具,当你想要重置自己的微信号并删除所有的内容之时,又觉得一条条删太麻烦了,就可以用到这款神器工具,可以帮助你一键删除掉朋友圈内的所有内容,帮助你轻松的重置微信号 ...

  7. Android百度地图实例详解之仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,导航等)

    Android百度地图实例详解之仿摩拜单车APP(包括附近车辆.规划路径.行驶距离.行驶轨迹记录,导航等) 标签: android百度地图行驶轨迹记录共享单车行驶距离和时间 2017-03-08 20 ...

  8. 仿微信打开红包旋转动画

    介绍   公司的项目中包含IM模块,其中包含红包功能,在做打开红包的时候,觉得打开的按钮若不旋转感觉太过单调,没有乐趣,所以参考微信的红包,打开的时候,"开"字旋转起来.这里主要用 ...

  9. android 创建帧动画,Android 逐帧动画创建实例详解

    Android 逐帧动画创建实例详解 前言: 我们看早期电影的时候,电影通常是一张一张播放,用我们现在专有名词来说,就是一帧帧来,安卓同样有这样动画效果的编排形式. 那么我们先定义逐帧动画xml文件 ...

最新文章

  1. All About Angular 2.0
  2. 强化学习note2——value iteration和policy iteration的区别,MC和TD的区别
  3. IT运维服务管理问题总结 #F#
  4. 不可变的基础架构,热部署和JVM
  5. C#通过VS连接MySQL数据库实现增删改查基本操作
  6. vue3 创建路由「vue-router 4.x 的使用」
  7. ORACLE数据库表空间查询
  8. 跨浏览器的元素的竖直排列
  9. AndroidTV开发7实现仿小米电视和各种盒子TV焦点放大缩小效果
  10. vue 一周日历展示,上一周下一周展示。
  11. 使用搜狗接口爬取微信公众号
  12. 2022苹果ios个人开发中注册遇到的问题
  13. .NETReflector的破解
  14. 免费比对工具DiffMerge
  15. 石油工程课程设计c语言,东北石油大学-石油工程抽油设计C语言编程.doc
  16. 微信小程序点击事件失效
  17. 高端运动耳机哪个品牌最好、最好的运动耳机品牌排行
  18. Hazel引擎学习(一)
  19. 嵌入式 - 瑞萨宣讲
  20. java定义三角类_Java先创建一个point类然后定义trianglete类,在trianglete类中定义三个point的实体表示三角形的三个点。...

热门文章

  1. vs2008简体中文正式版下载
  2. 使用人人开源项目遇到的一些坑
  3. MathType中文版公式编辑器操作激活教程
  4. Python写一个自动化交易程序
  5. AWTRIX像素屏时钟搭建
  6. Vue、Nuxt服务端渲染,NodeJS全栈项目,面试小白的博客系统~~
  7. 区块链、NFT 与元宇宙中的稀缺性技术
  8. 14. 手机蓝牙遥控机器人制作
  9. 网页服务器磁盘满了怎么办,远程服务器磁盘满了怎么办
  10. 超级玛丽游戏(C语言)