文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android仿微信清理内存图表动画(解决surfaceView屏幕闪烁问题)demo实例详解

2022-06-06 07:40

关注

最近接了一个项目其中有功能要实现一个清理内存,要求和微信的效果一样。于是想到用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使用SurfaceView实现飘赞动画利用SurfaceView实现下雨与下雪动画效果详解(Kotlin语法)Android利用SurfaceView实现下雨的天气动画效果Android App中使用SurfaceView制作多线程动画的实例讲解如何使用SurfaceView实现鱼儿游动动画


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯