仿微信清理内存图表动画(解决surfaceView闪烁问题)

原创 2016年08月30日 22:41:27

最近一直在做一个清理内存的功能,公司要求和微信的效果一模一样,于是便自己写了一个。

因为要不停的绘制,所以选择了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)与黑屏闪烁

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

  2. 常规的"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"。

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

解决方法

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

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

  2. 不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之后就好了。

还有很多不理解的地方,希望大家指点。

下载地址:

https://github.com/xiaoqiAndroid/ExpandableChartView

http://download.csdn.net/detail/qq_25412055/9617494

顺便给出有关清理缓存方法的链接:

http://blog.csdn.net/qq_25412055/article/details/52371508

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

SurfaceView闪烁问题的根源-双缓存机制

上篇中提到了SurfaceView绘制触摸轨迹时出现的闪烁问题,这里就说明下产生这种困扰的根源——双缓存机制。     Android中的SurfaceView在更新视图时,为了提高更新效率,加强用户...

SurfaceView绘制触摸轨迹闪烁问题的最终解决方案

由于SurfaceView使用双缓存机制,两张画布轮流显示到屏幕上。那么,要存储触摸轨迹并避免两张画布内容不一致造成的闪烁问题,完全可以利用保存绘制过程并不断重新绘制的方法解决闪烁,而且这样还顺带解决...

SurfaceView加载屏幕闪烁、图层不显示的一些解决办法

最近在做一个视频直播的项目,用到surfaceView来播放视频画面,开始做以后才发现这个控件是有多坑爹,下面先说一下我遇到的几个问题,如果大家也遇到了而且有更好地解决方法的话,欢迎交流。 问题一:...

使用SurfaceView实现飘赞动画

最近做直播项目,需要实现点赞动画,一提起动画就想到了使用View的属性动画,后来想了一下,那么多用户点赞,会导致屏幕上出现很多View,开销太大,一定会很卡,所以看主流主播软件用什么方案解决的。 ...

Android-使用SurfaceView多线程绘制动画

使用SurfaceView中的Surface对象进行绘图,其本质就是利用SurfaceHolder的lockCanvas获取到Canvas对象进行绘制的,对于绘制动画来说,必须使用双缓冲,或者采用双线...

Android TV 视频surfaceview 变化导致的闪烁

背景:视频先是旋转90度,把surfaceview左右两边都砍掉了一部分,涂黑处理。 然后旋转180度的时候,重新开始播放视频,需要全屏surfaceview,这个时候会闪一下最后一帧视频,正常是虽...

SurfaceView清空Canvas如何操作

public void ClearDraw(){ Canvas canvas = null; try{ canvas = surfaceHolder...

Android 如何清空 Canvas ?(找了好久的答案)

Android 如何清空 Canvas ? 似乎一直未找到好的解决办法,有的方法限制使用纯色背景,但无论怎么尝试,都未见效果, 终于在 http://stackoverflow.com/quest...

Fragment中包含surfaceView出现闪屏问题解决方法

Fragment中包含有SurfaceView,在第一次初始化的时候屏幕会出现黑屏闪一下,以后再进行Fragment切换的时候却不会再出现这种情况,Activity里面包含SurfaceView却不会...

SurfaceView使用实例(示波器)

SurfaceView是View的子类,但它与View还是有所不同的,他们的区别主要表现在以下几点: 1、View主要适用于主动更新的情况,而SurfaceView适用于被动更新,例如频繁刷新 2、V...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:仿微信清理内存图表动画(解决surfaceView闪烁问题)
举报原因:
原因补充:

(最多只允许输入30个字)