Android 사용자 정의 컨트롤: 진도 바 의 네 가지 실현 방식
https://github.com/Todd-Davies/ProgressWheel
앞의 세 가지 실현 방식 코드 는 다음 과 같다.
http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/
(원본 코드 는 마지막)
최근 에는 사용자 정의 컨트롤 을 배 워 서 큰 소 들 블 로그 에서 공유 하 는 작은 튜 토리 얼 을 많이 찾 았 고 GitHub 에서 비슷 한 컨트롤 을 찾 아 배 웠 습 니 다.읽 기 가 쉽 지 않 은 것 을 발견 하고 이런 글 을 공부 노트 로 쓰 고 싶 었 다.
1. 컨트롤 소개:
진도 바 는 App 에서 흔히 볼 수 있 습 니 다. 예 를 들 어 다운로드 진도, 그림 불 러 오기, 글 열기, 웹 페이지 열기 등...............................................................만약 이러한 효과 가 없다 면 사용 자 는 물건 이 다운로드 되 었 는 지, 그림 이 불 러 왔 는 지, 글 이 열 렸 는 지 알 수 없 을 것 이다. 사용 자 를 매우 불쾌 하 게 할 것 이다.이러한 상황 을 바탕 으로 우리 UI 디자이너 들 은 이러한 컨트롤 을 만 들 었 다.
2. 이 글 은 관련 된 지식 점:
나 처럼 막 입문 한 안 드 로 이 드 초보 들 은 이 지식 들 을 먼저 알 고 내 려 다 보 는 것 을 추천 합 니 다.이런 지식 을 저도 블 로 그 를 추천 합 니 다. 문서 의 설명 을 보 여 드 리 는 것 도 추천 합 니 다. 물론 큰 소 들 은 무시 할 수 있 습 니 다.
1. ClipDrawable 클래스: 하나의 drawable 클래스 를 자 를 수 있 습 니 다. (즉, 특정한 영역 만 표시 하고 다른 부분 은 숨 깁 니 다) 얼마나 큰 영역 이 level 에 의 해 제어 되 는 지 표시 합 니 다. (level 추출 값 은 0 ~ 10000)
[블 로그:http://blog.csdn.net/lonelyroamer/article/details/8244777] 、 문서 가 없 는 것 은 여기 서 볼 수 있 습 니 다 [http://www.apihome.cn/api/android/ClipDrawable.html】
2. 사용자 정의 뷰: guolin 대신 의 깊 은 학습 View 4 부작
[Android Layout Inflater 원리 분석, 당신 을 데 리 고 한 걸음 한 걸음 더 깊이 있 게 View -http://blog.csdn.net/guolin_blog/article/details/12921889】
[안 드 로 이 드 보기 그리 기 프로 세 스 가 완전히 해석 되 었 습 니 다. 한 걸음 한 걸음 더 View 에 대해 알 아 보 겠 습 니 다.http://blog.csdn.net/guolin_blog/article/details/16330267】
[안 드 로 이 드 보기 상태 및 다시 그리 기 프로 세 스 분석, 당신 을 데 리 고 한 걸음 더 깊이 있 게 View -http://blog.csdn.net/guolin_blog/article/details/17045157】
[Android 사용자 정의 View 의 실현 방법, 한 걸음 한 걸음 더 View 에 대해 알 아 보 세 요 -http://blog.csdn.net/guolin_blog/article/details/17357967】
3. 제 가 쓴 것 을 보지 못 했 습 니 다. Android 사용자 정의 컨트롤 인 오래된 유쿠 3 급 메뉴 를 보 려 면 이것 을 봐 야 할 지도 모 릅 니 다.
[RotateAnimation 상세 설명 -http://blog.csdn.net/u012403246/article/details/41415799】
3. Android 의 실현 방식:
(앞의 세 가지 방법 은 비교적 간단 하 다. 네 번 째 방법 은 GitHub 프로젝트 의 해석 이다. 앞의 세 가지 에 관심 이 없 으 면 바로 뒤로 넘 어 갈 수 있다.)
1. 효과 도:
진도 바 의 변환 과정 을 한 프레임 한 프레임 의 그림 으로 분해 하여 이 한 프레임 한 프레임 의 그림 을 연결 하여 애니메이션 을 구성한다.자주 사용 되 는 것 은 핸드폰 으로 웹 페이지 를 읽 고 지역 사 회 를 돌아 다 닐 때 사진, 글 등 을 불 러 올 때 불 러 오 는 진 도 를 알 필요 가 없 지만 불 러 오 는 상황 을 알 아야 한다.
이 방법 은 animation - list 의 XML 파일 을 만 들 고 시스템 API 에 제공 하 는 ProgressBar 의 indetermate Drawable 속성 을 실현 하면 됩 니 다.(이 속성 은 애니메이션 설정 과 유사 할 것 같 습 니 다...)
XML:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_01"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_02"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_03"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_04"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_05"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_06"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_07"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_08"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_09"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_10"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_11"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_12"
android:gravity="left"/>
</item>
</animation-list>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/progressbar1"
/>
2
, 효과 도:
이전 사용자 정의 컨트롤 에 관 한 블 로그 에서 회전 효 과 를 실현 하기 위해 Rotate Animation 클래스 를 사 용 했 습 니 다 (http://blog.csdn.net/u012403246/article/details/41309161) 사실 우 리 는 여기 서도 그림 한 장 을 회전 을 통 해 우리 가 원 하 는 효 과 를 얻 을 수 있다.본질 적 으로 이전 방법 과 크게 다 르 지 않다.
우 리 는 rotate XML 을 만 들 고 그 속성 에 대해 간단 한 설정 을 한 다음 에 우리 가 사용 할 그림 을 추가 하면 됩 니 다.
XML:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="360"
android:interpolator="@android:anim/accelerate_decelerate_interpolator" >
<bitmap
android:antialias="true"
android:filter="true"
android:src="@drawable/loading_360"/>
</rotate>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/progressbar2"/>
3. 효과 도:
우 리 는 두 장의 사진 을 만 들 수 있다. 첫 번 째 사진 은 검은색 이 고, 그 다음 에 이 사진 센터 를 원 으로 파 내 고, 원 구역 을 흰색 으로 만 들 고, 파 낸 원 을 두 번 째 사진 으로 만 들 수 있다.두 장의 사진 을 겹 쳐 서 보 여 주 는 것 도 좋 습 니 다. 처음에 두 번 째 사진 을 완전히 가 렸 습 니 다. 로드 진도 가 증가 함 에 따라 가 려 진 구역 을 줄 이 고 두 번 째 사진 을 천천히 보 여 주 었 습 니 다.
안 드 로 이 드 에는 마침 이런 ClipDrawable 클래스 가 있어 서 커팅 과정 을 실현 할 수 있 습 니 다.이런 방식 으로 진도 바 컨트롤 을 어떻게 정의 하 는 지 살 펴 보 자.
코드:
public class MyProgressBar extends FrameLayout{
private boolean running;
private int progress = 0;
private static final int MAX_PROGRESS = 10000;
private ClipDrawable clip;
private Handler handler = new Handler(){
@Override
public void handleMessage(android.os.Message msg) {
if(msg.what == 0x123)
clip.setLevel(progress);
}
};
public MyProgressBar(Context context){
this(context,null,0);
}
public MyProgressBar(Context context,AttributeSet attrs){
this(context,null,0);
}
public MyProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Init(context);
}
public void Init(Context context){
View view = LayoutInflater.from(context).inflate(R.layout.view, null);
ImageView iv = (ImageView)view.findViewById(R.id.progress_img);
addView(view);
clip = (ClipDrawable)iv.getDrawable();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
running = true;
while(running){
handler.sendEmptyMessage(0x123);
if(progress == MAX_PROGRESS)
progress = 0;
progress += 100;
try {
Thread.sleep(18);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
public void stop(){
progress = 0;
running = false;
}
}
코드 를 통 해 알 수 있 듯 이 논 리 는 매우 간단 하 다. 관건 은 ClipDrawable 의 setLevel () 방법 이다. 이것 은 커팅 효 과 를 설정 하 는 것 이다.
4. 효과 도:
View 의 하위 클래스 인 Progress Wheel 류 를 실현 하여 진도 항목 효 과 를 실현 합 니 다.구체 적 인 내용 은 주석 에 적 혀 있 습 니 다. 사용자 정의 컨트롤 지식 을 모 르 면 guolin 블 로그 에서 사용자 정의 View 4 부작 의 설명 을 읽 고 잘 할 수 있 습 니 다.
코드:
public class ProgressWheel extends View {
// View 、
private int layout_height = 0;
private int layout_width = 0;
private int fullRadius = 100;
private int circleRadius = 80;
private int barLength = 60;
private int barWidth = 20;
private int rimWidth = 20;
private int textSize = 20;
private float contourSize = 0;
//
private int paddingTop = 5;
private int paddingBottom = 5;
private int paddingLeft = 5;
private int paddingRight = 5;
//View
private int barColor = 0xAA000000;
private int contourColor = 0xAA000000;
private int circleColor = 0x00000000;
private int rimColor = 0xAADDDDDD;
private int textColor = 0xFF000000;
//
private Paint barPaint = new Paint();
private Paint circlePaint = new Paint();
private Paint rimPaint = new Paint();
private Paint textPaint = new Paint();
private Paint contourPaint = new Paint();
//
@SuppressWarnings("unused")
private RectF rectBounds = new RectF();
private RectF circleBounds = new RectF();
private RectF circleOuterContour = new RectF();
private RectF circleInnerContour = new RectF();
//
//
private int spinSpeed = 2;
//
private int delayMillis = 0;
int progress = 0;
boolean isSpinning = false;
//
private String text = "";
private String[] splitText = {};
/**
* ProgressWheel
*
* @param context
* @param attrs
*/
public ProgressWheel(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes(context.obtainStyledAttributes(attrs,
R.styleable.ProgressWheel));
}
//----------------------------------
//
//----------------------------------
/*
* , View
* From: http://www.jayway.com/2012/12/12/creating-custom-android-views-part-4-measuring-and-how-to-force-a-view-to-be-square/
*
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// onMeasure
// 、
//
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// getWidth() getHeight()。
// View , View , Layout
// getMeasuredWidth() getMeasuredHeight()
int size = 0;
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom();
// View setMeasuredDimension() View
// View , View ,
// View(View ) , , View
if (widthWithoutPadding > heigthWithoutPadding) {
size = heigthWithoutPadding;
} else {
size = widthWithoutPadding;
}
// onMeasure() , setMeasuredDimension()
// View
// setMeasuredDimension() , ,
// onMeasure() , setMeasuredDimension()
// , onMeasure() , setMeasuredDimension()
setMeasuredDimension(size + getPaddingLeft() + getPaddingRight(), size + getPaddingTop() + getPaddingBottom());
}
/**
* onSizeChanged onAttachedToWindow View
* MATCH_PARENT WRAP_CONTENT
* View
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Share the dimensions
layout_width = w;
layout_height = h;
setupBounds();
setupPaints();
invalidate();
}
/**
* progress wheel
*/
private void setupPaints() {
barPaint.setColor(barColor);
barPaint.setAntiAlias(true);
barPaint.setStyle(Style.STROKE);
barPaint.setStrokeWidth(barWidth);
rimPaint.setColor(rimColor);
rimPaint.setAntiAlias(true);
rimPaint.setStyle(Style.STROKE);
rimPaint.setStrokeWidth(rimWidth);
circlePaint.setColor(circleColor);
circlePaint.setAntiAlias(true);
circlePaint.setStyle(Style.FILL);
textPaint.setColor(textColor);
textPaint.setStyle(Style.FILL);
textPaint.setAntiAlias(true);
textPaint.setTextSize(textSize);
contourPaint.setColor(contourColor);
contourPaint.setAntiAlias(true);
contourPaint.setStyle(Style.STROKE);
contourPaint.setStrokeWidth(contourSize);
}
/**
*
*/
private void setupBounds() {
// , layout_width layout_height ,
int minValue = Math.min(layout_width, layout_height);
// x,y
int xOffset = layout_width - minValue;
int yOffset = layout_height - minValue;
//
paddingTop = this.getPaddingTop() + (yOffset / 2);
paddingBottom = this.getPaddingBottom() + (yOffset / 2);
paddingLeft = this.getPaddingLeft() + (xOffset / 2);
paddingRight = this.getPaddingRight() + (xOffset / 2);
int width = getWidth(); //this.getLayoutParams().width;
int height = getHeight(); //this.getLayoutParams().height;
rectBounds = new RectF(paddingLeft,
paddingTop,
width - paddingRight,
height - paddingBottom);
circleBounds = new RectF(paddingLeft + barWidth,
paddingTop + barWidth,
width - paddingRight - barWidth,
height - paddingBottom - barWidth);
circleInnerContour = new RectF(circleBounds.left + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.top + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.right - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.bottom - (rimWidth / 2.0f) - (contourSize / 2.0f));
circleOuterContour = new RectF(circleBounds.left - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.top - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.right + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.bottom + (rimWidth / 2.0f) + (contourSize / 2.0f));
fullRadius = (width - paddingRight - barWidth) / 2;
circleRadius = (fullRadius - barWidth) + 1;
}
/**
* XML
*
* @param a the attributes to parse
*/
private void parseAttributes(TypedArray a) {
barWidth = (int) a.getDimension(R.styleable.ProgressWheel_barWidth,
barWidth);
rimWidth = (int) a.getDimension(R.styleable.ProgressWheel_rimWidth,
rimWidth);
spinSpeed = (int) a.getDimension(R.styleable.ProgressWheel_spinSpeed,
spinSpeed);
delayMillis = a.getInteger(R.styleable.ProgressWheel_delayMillis,
delayMillis);
if (delayMillis < 0) {
delayMillis = 0;
}
barColor = a.getColor(R.styleable.ProgressWheel_barColor, barColor);
barLength = (int) a.getDimension(R.styleable.ProgressWheel_barLength,
barLength);
textSize = (int) a.getDimension(R.styleable.ProgressWheel_textSize,
textSize);
textColor = (int) a.getColor(R.styleable.ProgressWheel_textColor,
textColor);
// text ,
if (a.hasValue(R.styleable.ProgressWheel_text)) {
setText(a.getString(R.styleable.ProgressWheel_text));
}
rimColor = (int) a.getColor(R.styleable.ProgressWheel_rimColor,
rimColor);
circleColor = (int) a.getColor(R.styleable.ProgressWheel_circleColor,
circleColor);
contourColor = a.getColor(R.styleable.ProgressWheel_contourColor, contourColor);
contourSize = a.getDimension(R.styleable.ProgressWheel_contourSize, contourSize);
// TypedArray : TypedArray
a.recycle();
}
//----------------------------------
//
//----------------------------------
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//
canvas.drawArc(circleBounds, 360, 360, false, circlePaint);
//
canvas.drawArc(circleBounds, 360, 360, false, rimPaint);
canvas.drawArc(circleOuterContour, 360, 360, false, contourPaint);
canvas.drawArc(circleInnerContour, 360, 360, false, contourPaint);
//
if (isSpinning) {
canvas.drawArc(circleBounds, progress - 90, barLength, false,
barPaint);
} else {
canvas.drawArc(circleBounds, -90, progress, false, barPaint);
}
// ( )
float textHeight = textPaint.descent() - textPaint.ascent();
float verticalTextOffset = (textHeight / 2) - textPaint.descent();
for (String s : splitText) {
float horizontalTextOffset = textPaint.measureText(s) / 2;
canvas.drawText(s, this.getWidth() / 2 - horizontalTextOffset,
this.getHeight() / 2 + verticalTextOffset, textPaint);
}
if (isSpinning) {
scheduleRedraw();
}
}
private void scheduleRedraw() {
progress += spinSpeed;
if (progress > 360) {
progress = 0;
}
postInvalidateDelayed(delayMillis);
}
/**
* wheel
*/
public boolean isSpinning() {
if(isSpinning){
return true;
} else {
return false;
}
}
/**
*
*/
public void resetCount() {
progress = 0;
setText("0%");
invalidate();
}
/**
*
*/
public void stopSpinning() {
isSpinning = false;
progress = 0;
postInvalidate();
}
/**
*
*/
public void spin() {
isSpinning = true;
postInvalidate();
}
/**
* 1( 360)
*/
public void incrementProgress() {
isSpinning = false;
progress++;
if (progress > 360)
progress = 0;
setText(Math.round(((float) progress / 360) * 100) + "%");
postInvalidate();
}
/**
*
*/
public void setProgress(int i) {
isSpinning = false;
progress = i;
postInvalidate();
}
//----------------------------------
//get set
//----------------------------------
/**
* progress bar View
*
* @param text the text to show ('
' constitutes a new line)
*/
public void setText(String text) {
this.text = text;
splitText = this.text.split("
");
}
public int getCircleRadius() {
return circleRadius;
}
public void setCircleRadius(int circleRadius) {
this.circleRadius = circleRadius;
}
public int getBarLength() {
return barLength;
}
public void setBarLength(int barLength) {
this.barLength = barLength;
}
public int getBarWidth() {
return barWidth;
}
public void setBarWidth(int barWidth) {
this.barWidth = barWidth;
if ( this.barPaint != null ) {
this.barPaint.setStrokeWidth( this.barWidth );
}
}
public int getTextSize() {
return textSize;
}
public void setTextSize(int textSize) {
this.textSize = textSize;
if ( this.textPaint != null ) {
this.textPaint.setTextSize( this.textSize );
}
}
public int getPaddingTop() {
return paddingTop;
}
public void setPaddingTop(int paddingTop) {
this.paddingTop = paddingTop;
}
public int getPaddingBottom() {
return paddingBottom;
}
public void setPaddingBottom(int paddingBottom) {
this.paddingBottom = paddingBottom;
}
public int getPaddingLeft() {
return paddingLeft;
}
public void setPaddingLeft(int paddingLeft) {
this.paddingLeft = paddingLeft;
}
public int getPaddingRight() {
return paddingRight;
}
public void setPaddingRight(int paddingRight) {
this.paddingRight = paddingRight;
}
public int getBarColor() {
return barColor;
}
public void setBarColor(int barColor) {
this.barColor = barColor;
if ( this.barPaint != null ) {
this.barPaint.setColor( this.barColor );
}
}
public int getCircleColor() {
return circleColor;
}
public void setCircleColor(int circleColor) {
this.circleColor = circleColor;
if ( this.circlePaint != null ) {
this.circlePaint.setColor( this.circleColor);
}
}
public int getRimColor() {
return rimColor;
}
public void setRimColor(int rimColor) {
this.rimColor = rimColor;
if ( this.rimPaint != null ) {
this.rimPaint.setColor( this.rimColor );
}
}
public Shader getRimShader() {
return rimPaint.getShader();
}
public void setRimShader(Shader shader) {
this.rimPaint.setShader(shader);
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
if ( this.textPaint != null ) {
this.textPaint.setColor( this.textColor );
}
}
public int getSpinSpeed() {
return spinSpeed;
}
public void setSpinSpeed(int spinSpeed) {
this.spinSpeed = spinSpeed;
}
public int getRimWidth() {
return rimWidth;
}
public void setRimWidth(int rimWidth) {
this.rimWidth = rimWidth;
if ( this.rimPaint != null ) {
this.rimPaint.setStrokeWidth( this.rimWidth );
}
}
public int getDelayMillis() {
return delayMillis;
}
public void setDelayMillis(int delayMillis) {
this.delayMillis = delayMillis;
}
public int getContourColor() {
return contourColor;
}
public void setContourColor(int contourColor) {
this.contourColor = contourColor;
if ( contourPaint != null ) {
this.contourPaint.setColor( this.contourColor );
}
}
public float getContourSize() {
return this.contourSize;
}
public void setContourSize(float contourSize) {
this.contourSize = contourSize;
if ( contourPaint != null ) {
this.contourPaint.setStrokeWidth( this.contourSize );
}
}
}
원본 코드 다운로드
다음으로 이동:http://blog.csdn.net/u012403246/article/details/41477427?utm_source=tuicool
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.