Android 위 챗 을 모방 하여 메모리 도표 애니메이션 정리(surfaceView 화면 깜빡 임 문제 해결)demo 실례 상세 설명

최근 에 하나의 항목 을 받 았 는데 그 중 에 메모리 청 소 를 실현 하 는 기능 이 있 습 니 다.위 챗 의 효과 와 같 아야 합 니 다.그래서 view 를 계승 하 는 것 이 아니 라 surfaceView 를 사용 하려 고 했 습 니 다.다음은 여러분 에 게 사고의 방향 을 해석 해 드 리 겠 습 니 다.
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)와 블랙 스크린 이 깜빡 입 니 다.
Surface View 대상 마다 두 개의 독립 된 graphic buffer 가 있 는데 공식 SDK 는 이 를'front buffer'와'back buffer'라 고 부른다.
일반적인"double-buffer"는 이렇게 합 니 다.모든 프레임 의 데 이 터 는 back buffer 에 그 려 진 다음 에 back buffer 의 내용 이 front buffer 로 계속 뒤 집 힙 니 다.화면 에 front buffer 가 계속 표 시 됩 니 다.그러나 Android Surface View 의'double-buffer'는 이렇게 합 니 다.buffer A 에 내용 을 그리고 화면 에 buffer A 를 표시 합 니 다.다음 순환 은 buffer B 에 내용 을 그리고 화면 에 buffer B 를 표시 합 니 다.이렇게 왕복 하 다.그래서 화면 에 표 시 된 내용 은 buffer A,B,A,B,.......................................................................
안 드 로 이 드 의'double-buffer'구현 메커니즘 은 플래시 현상 을 잘 설명 할 수 있다.첫 번 째'lockCanvas-draw Canvas-unlockCanvas AndPost'순환 에서 buffer A 의 내용 을 업데이트 합 니 다.다음'lockCanvas-draw Canvas-unlockCanvasAndPost'순환 에서 buffer B 의 내용 을 업데이트 합 니 다.buffer A 와 buffer B 의 한 buffer 내용 이 비어 있 으 면 화면 이 돌아 가면 서 표 시 될 때 화면 블랙 스크린 반 짝 임 현상 이 나타난다.
해결 방법
블랙 스크린 이 나 온 것 은 버퍼 A 와 버퍼 B 중 하나 가 비어 있 고 비어 있 는 쪽 이 화면 에 게시 되 기 때문이다.그래서 두 가지 해결 방향 이 있다.
빈 버퍼 가 나타 나 지 않도록 합 니 다:매번 한 버퍼 에 내용 을 쓰 고 post 를 한 후에 이 버퍼 의 내용 으로 다른 버퍼 를 채 웁 니 다.이렇게 하면 두 버퍼 의 내용 이 동기 화 되 는 것 을 보장 할 수 있 으 며,단점 은 공 부 를 하지 않 고 성능 을 소모 하 는 것 이다.
화면 에 post 빈 buffer 를 사용 하지 않 음:내용 을 업데이트 하려 고 할 때 내용 이 비어 있 는 지 여 부 를 판단 하고 비어 있 지 않 을 때 만'lockCanvas-draw Canvas-unlockCanvas AndPost'프로 세 스 를 시작 합 니 다.
예 를 들 어 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 를 사용 하 는 것 을 권장 합 니 다.다른 설정 을 다시 설정 해서 paint 를 호출 하 는 것 이 아 닙 니 다.사용 할 때 같은 paint 를 사용 하 는 곳 이 많 기 때문에 반 짝 이 고 모든 도형 에 자신의 paint 를 만 들 었 으 면 좋 겠 습 니 다.
위 에서 말 한 것 은 소 편 이 소개 한 안 드 로 이 드 가 위 챗 을 모방 하여 메모리 도표 애니메이션(surfaceView 화면 반 짝 임 문 제 를 해결)demo 인 스 턴 스 를 상세 하 게 설명 하 는 것 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 저 에 게 메 시 지 를 남 겨 주세요.소 편 은 제때에 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기