Android 사용자 정의 원형 카운트다운 진행 막대

본 논문 의 사례 는 안 드 로 이 드 카운트다운 진도 표 가 보 여 준 구체 적 인 코드 를 공유 하여 여러분 께 참고 하 시기 바 랍 니 다.구체 적 인 내용 은 다음 과 같 습 니 다.
효과 미리 보기

소스 코드 전송 문:https://github.com/yanzhenjie/CircleTextProgressbar
실현 과 원리
이 문자 의 원형 진도 표 는 우리 가 많은 앱 에서 본 적 이 있다.예 를 들 어 앱 환영 페이지 카운트다운,다운로드 파일 카운트다운 등 이다.
원 리 를 분석 해 보면 어떤 학생 들 은 이 사용자 정의 View 를 보 자마자 당황 할 수도 있 습 니 다.이것 은 View 를 계승 해 야 하 는 것 이 아 닙 니까?그 려 야 하 는 것 이 아 닙 니까?답 은:네.하지만 우리 도 걱정 하지 마 세 요.이 효 과 를 실현 하 는 것 은 정말 so easy 입 니 다.다음은 나 와 함께 핵심 분석 과 코드 를 살 펴 보 자.
원리 분석
먼저 우 리 는 위의 그림 을 관찰 하려 면 몇 가지 부분 으로 구성 해 야 한다.
1.바깥 이 점점 늘 어 나 거나 줄 어드 는 원형 진도 표.
2.원형 진도 바 가운데 에 있 는 전시 문자.
3.원형 진도 바 바깥 에 감 싼 원.
4.원형 진도 바 가운데 채 움 색.
5.글꼴 색상/채 우기 색상 변경 클릭:ColorStateList 클래스.
우 리 는 네 부분 이 필요 하 다 는 것 을 분석 해 냈 다.문자 가 있 는 것 을 보 니 첫 번 째 로 떠 오 르 는 것 은 당연히 TextView 입 니 다.글꼴 색상 의 기록 을 적 게 만 들 수 있 습 니 다.중간 에 채 워 진 색상(원형 은 고려 하지 않 음)을 클릭 할 때 변색 되 므 로 ColorStateList 클래스 가 기록 해 야 합 니 다.남 은 진도 줄,윤곽 원,충전 원 은 우리 가 그 려 야 한다.
내 가 봉 인 된 CircleTextProgressbar 특색
CircleTextProgressbar 는 자동 카운트다운,자동 진도 감소,자동 진도 증가 등 을 지원 합 니 다.
자동 진행 이 필요 하 다 면 사용자 정의 속성 을 설정 한 후 start()방법 을 사용 하면 자동 으로 카운트다운 을 할 수 있 습 니 다.가 고 나 서 다시 한 번 자동 진행 을 하려 면 reStart()를 호출 하면 됩 니 다.
자동 으로 진 도 를 걷 지 않 으 려 면 setProgress()를 통 해 시스템 의 progress 처럼 진 도 를 수정 할 수 있 습 니 다.

//           ,0-100。
progressBar.setProgressType(CircleTextProgressbar.ProgressType.COUNT);
//      。
progressBar.setProgressLineWidth(30);//      。
//          ,  3000  。
progressBar.setTimeMillis(3500);
//        。
progressBar.setProgressColor(Color.RED);
//         。
progressBar.setOutLineColor(Color.RED);
//       。
progressBar.setInCircleColor(Color.RED);
//          ,       。
progressBar.start();
//          ,  100。
progressBar.setProgress(100);

구덩이 밟 는 과정
사실 오 랜 만 에 사용자 정의 View 를 써 봤 는데 잊 어 버 린 것 이 있어 서 이 View 를 쓸 때 예전 의 구 덩이 를 한 번 더 밟 았 습 니 다.다른 친구 들 도 구 덩이 를 당 하지 않도록 제 가 밟 은 구덩이 도 기록 해 두 었 습 니 다.
보기 그리 기 영역
여기 서 문제 가 발생 했 습 니 다.우리 가 계승 한 TextView 문자 가 많 으 면 긴 것 입 니 다.그러면 그 려 진 원 의 길 이 는 똑 같 기 때문에 TextView 에서 그 려 진 원 은 일부분 이나 타원 만 볼 수 있 습 니 다.그래서 우 리 는 View 의 그리 기 영역 을 확대 해 야 한다.그때 제 가 제일 먼저 생각 한 것 은 layot()방법 이 었 습 니 다.View 의 부모 레이아웃 인 onLayout()을 사용 할 때 View 의 layot()를 사용 하여 하위 View 레이아웃 을 만 들 었 기 때 문 입 니 다.저 는 layot 방법 을 다시 썼 습 니 다.

@Override
public void layout(int left, int top, int right, int bottom) {
 int w = right - left;
 int h = bottom - top;
 int size = w > h ? w : h;

 if (w > h) {
 bottom += (size - h);
 } else {
 right += (size - w);
 }
 super.layout(left, top, right, bottom);
}
이 코드 의 원 리 는 바로 너비 와 높이,그것 이 크 면 view 를 이렇게 큰 이 값 으로 확대 하 는 것 이다.
View 를 Layout 에 넣 었 을 때 효과 가 나 오 는 데 문제 가 없 었 지만 여러 개의 View 를 LinearLayout 에 넣 었 을 때 몇 개의 View 가 겹 친 것 을 발 견 했 습 니 다.오 사 트.나 는 문득 크게 깨 달 았 다.이 니 마 의 집 Layout 는 내 가 그린 구역 의 너 비 를 지정 했다.나 는 다른 View 를 강제로 점령 했다.so,나 는 onMeasure()를 다시 써 야 한다.너비 와 높이 를 측정 할 때 아버지 Layout 에 게 내 가 얼마나 큰 땅 을 원 하 는 지 알려 줘 야 한다.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int width = getMeasuredWidth();
 int height = getMeasuredHeight();
 int size = width > height ? width : height;
 setMeasuredDimension(size, size);
}
이 코드 의 뜻 은 쉽게 이해 할 수 있 습 니 다.슈퍼.onMeasure 가 측정 할 때의 너비 와 높이 를 보고 너비 와 높이 를 최대 의 이 값 으로 설정 하 는 것 입 니 다.아버지 Layout 에 게 내 가 얼마나 큰 땅 을 원 하 는 지 알려 주세요.그러면 내 가 그 릴 때 하고 싶 은 대로 놀아 요.
View 구현 그리 기
자,중요 한 곳 에 왔 습 니 다.앞 에 있 는 것 은 모두 해결 되 었 습 니 다.우리 가 우리 의 원 을 어떻게 그 리 는 지 보 겠 습 니 다.원 을 그 리 려 면 onDraw()방법 을 다시 써 야 합 니 다.
우선 붓 이 필요 합 니 다.
Paint mPaint = new Paint();
mPaint.setAntiAlias(true);// 톱날
그리 기 영역 가 져 오기
getDrawingRect(Rect)를 통 해 그리 기 영역 을 얻 을 수 있 습 니 다.그리 기 영역 을 통 해 원 의 반지름 을 계산 할 수 있 습 니 다.

Rect bounds = new Rect();

@Override
protected void onDraw(Canvas canvas) {
 getDrawingRect(bounds);//  view   

 int size = bounds.height() > bounds.width() ? bounds.width() : bounds.height();
 float outerRadius = size / 2; //          
}
채 움 원 그리 기
그러면 방금 클릭 할 때 변색 이 라 고 언급 했 기 때문에 우 리 는 ColorState List 를 사용 해 야 합 니 다.여기 서 초기 화 를 하고 xml 에서 이 속성 을 정의 하 는 것 을 지원 합 니 다.

//       。
ColorStateList inCircleColors = ColorStateList.valueOf(Color.TRANSPARENT);

private void initialize(Context ctx, AttributeSet attributeSet) {
 TypedArray typedArray = ctx.obtainStyledAttributes(attributeSet, R.styleable.Progressbar);
 inCircleColors = typedArray.getColorStateList(R.styleable.Progressbar_circle_color);
 typedArray.recycle();
}
View xml 속성 을 설정 하 는 방법 을 모 르 는 학생 이 Google 에 요청 합 니 다.
클릭,Check,Select 상태 에 따라 원 을 채 우 는 색 을 그립 니 다.채 우 는 색 이기 때문에 여기 Paint Style 은 FILL 입 니 다.

int circleColor = inCircleColors.getColorForState(getDrawableState(), 0);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(circleColor);
canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth, mPaint);
원심 은 구역 을 그 리 는 원심 이 고 반경 은 구역 원 을 그 리 는 반경 에서 외부 윤곽 원 선의 폭 을 뺀 것 이다.이렇게 하면 원 을 채 우 고 외부 윤곽 원 과 겹 치지 않 습 니 다.
외부 테두리 그리 기
이것 은 간단 합 니 다.빈 선 이기 때문에 Style 은 STROKE 입 니 다.그리고 선의 너비,붓 의 색 을 설정 합 니 다.

mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(outLineWidth);
mPaint.setColor(outLineColor);
canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth / 2, mPaint);
원심 은 그리 기 구역 의 원심 이 고 반지름 은 그리 기 구역 원 의 반지름 에서 외부 윤곽 원 선의 너비 의 절반 을 빼 면 외부 윤곽선 과 내부 충전 원 이 바짝 붙 어 있다.
TextView 의 글자 그리 기
우리 의 그림 과 TextView 자체 의 그림 이 겹 치지 않도록 슈퍼.onDraw(canvas)를 제거 합 니 다.그래서 여기 서 우 리 는 TextView 의 글자 도 써 야 한다.
먼저 TextView 의 기본 붓 을 가 져 와 TextView 자체 의 글꼴 색 을 설정 하고 톱니 를 방지 합 니 다.아름 답 기 위해 서 우 리 는 텍스트 를 가운데 로 강제로 합 니 다.

//  
Paint paint = getPaint();
paint.setColor(getCurrentTextColor());
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.CENTER);
float textY = bounds.centerY() - (paint.descent() + paint.ascent()) / 2;
canvas.drawText(getText().toString(), bounds.centerX(), textY, paint);
진행 막대 그리 기
진도 표 는 원 이 아 닙 니 다.정확히 말 하면 원호 입 니 다.
화필 은 기본 화필 을 사용 하여 색상,Style 을 STROKE 로 설정 하고 선의 폭 을 설정 합 니 다.마지막 으로 그리 기 영역 과 원심,각 도 를 지정 합 니 다.

RectF mArcRect = new RectF();
Rect bounds = new Rect();

@Override
protected void onDraw(Canvas canvas) {
 getDrawingRect(bounds);//  view   
 ...

 //        。
 mPaint.setColor(progressLineColor);
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(progressLineWidth);
 mPaint.setStrokeCap(Paint.Cap.ROUND);
 int deleteWidth = progressLineWidth + outLineWidth;
 //       
 mArcRect.set(bounds.left + deleteWidth / 2, bounds.top + deleteWidth / 2,
 bounds.right -deleteWidth / 2, bounds.bottom - deleteWidth / 2);
 canvas.drawArc(mArcRect, 0, 360 * progress / 100, false, mPaint);
}
이 어 려 운 점 은 그림 그리 기 영역 을 지정 하 는 것 입 니 다.외부 윤곽선 을 덮 을 수 없 기 때문에 외부 윤곽선 의 내부 그림 에 가 까 워 야 합 니 다.따라서 가장 바깥쪽 에 원 을 그 려 야 하기 때문에(외부 원 선의 너비+진도 선의 너비)/2 를 빼 야 합 니 다.
전체 코드 그리 기 및 측정
여기까지 핵심 코드 를 다 훑 었 으 니 직접 써 보 세 요.저 는 완전한 onDraw()와 onMeasure()의 소스 코드 를 붙 여 놓 겠 습 니 다.

private int outLineColor = Color.BLACK;
private int outLineWidth = 2;
private ColorStateList inCircleColors = ColorStateList.valueOf(Color.TRANSPARENT);
private int circleColor;
private int progressLineColor = Color.BLUE;
private int progressLineWidth = 8;
private Paint mPaint = new Paint();
private RectF mArcRect = new RectF();
private int progress = 100;
final Rect bounds = new Rect();

@Override
protected void onDraw(Canvas canvas) {
 //  view   
 getDrawingRect(bounds);

 int size = bounds.height() > bounds.width() ? bounds.width() : bounds.height();
 float outerRadius = size / 2;

 //     
 int circleColor = inCircleColors.getColorForState(getDrawableState(), 0);
 mPaint.setStyle(Paint.Style.FILL);
 mPaint.setColor(circleColor);
 canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth, mPaint);

 //    
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(outLineWidth);
 mPaint.setColor(outLineColor);
 canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth / 2, mPaint);

 //  
 Paint paint = getPaint();
 paint.setColor(getCurrentTextColor());
 paint.setAntiAlias(true);
 paint.setTextAlign(Paint.Align.CENTER);
 float textY = bounds.centerY() - (paint.descent() + paint.ascent()) / 2;
 canvas.drawText(getText().toString(), bounds.centerX(), textY, paint);

 //    
 mPaint.setColor(progressLineColor);
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(progressLineWidth);
 mPaint.setStrokeCap(Paint.Cap.ROUND);
 int deleteWidth = progressLineWidth + outLineWidth;
 mArcRect.set(bounds.left + deleteWidth / 2, bounds.top + deleteWidth / 2,
 bounds.right - deleteWidth / 2, bounds.bottom - deleteWidth / 2);

 canvas.drawArc(mArcRect, 0, 360 * progress / 100, false, mPaint);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int lineWidth = 4 * (outLineWidth + progressLineWidth);
 int width = getMeasuredWidth();
 int height = getMeasuredHeight();
 int size = (width > height ? width : height) + lineWidth;
 setMeasuredDimension(size, size);
}

현재 알려 진 호환성 문제 복구
 1.현재 CircleTextProgressbar 는 ReletiveLayot 에서 높이 가 커지 고 진도 가 약간 납작 해 집 니 다.복구 방법 은 다음 과 같다.
ReletiveLayot 에서 CircleTextProgressbar 를 사용 하려 면 onMeasure()방법 을 다시 쓰 지 말고 xml 에서 CircleTextProgressbar 의 너비 와 높이 를 지정 하면 됩 니 다.예 를 들 어 모두 50dp 로 지정 한 다음 에는 문제 가 없습니다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기