우선 개발을 하다가 기존 안드로이드에서 제공하는 ProgressBar 클래스가 있는데 이거에 크나큰 문제점을 발견 하였다. 그 문제점인게 무엇이냐면 ProgressBar 가 문제 인게 아니라 Android View 자체의 문제점이라고 있고, Developer 사이트에 들어가면 ProgressBar 는 결국 View 로 상속되어 있는 녀석입니다.
그 말인 즉슨 해당 뷰가 빠르게 갱신해야 하는 요건이 들어오면 사용자가 원하는 만큼의 퍼포먼스를 못본다는 문제입니다.
그래서 Android 니까 이 문제는 해결을 못한다? 기본으로 제공하는 클래스의 한계점이 있어서 이 문제를 해결못한다는거는 말이 안되는 것이라 판단하여 SurfaceView 기반으로 ProgressBar를 만들어 보기로 했습니다.
우선 소스부터 공유 해드리겠습니다.
attrs.xml
<!-- CustomProgressView Attr [Start] --> <declare-styleable name="CustomProgressView"> <attr name="type" format="enum"> <!-- ProgressView type horizontal or vertical --> <enum name="horizontal" value="0" /> <enum name="vertical" value="1" /> </attr> <attr name="gradientRadius" format="dimension" /> <!-- defines gradient radius --> <attr name="gradientStartColor" format="color" /> <!-- defines gradient start color --> <attr name="gradientCenterColor" format="color" /> <!-- defines gradient center color --> <attr name="gradientEndColor" format="color" /> <!-- defines gradient end color --> <attr name="gradientLocation" format="float" /> <!-- defines gradient color location --> <attr name="bgColor" format="color" /> <!-- Background Color --> <attr name="max" format="integer" /> <!-- defines the maximum value --> <attr name="min" format="integer" /> <!-- defines the minimum value --> </declare-styleable> <!-- CustomProgressView Attr [End] -->
import android.content.Context;
import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import jsieun.com.androidwork.R; import jsieun.com.androidwork.utils.Logger; /** * SurfaceView 기반의 Progress View Class * Created by hmju on 2019-01-27. */ public class CustomProgressView extends SurfaceView implements Callback {
private final Context mContext; private final SurfaceHolder mHolder; private final int DEFAULT_MIN = 0; private final int DEFAULT_MAX = 100; final int DEFAULT_COLOR = Color.BLACK; final int ID_TYPE = R.styleable.CustomProgressView_type; final int ID_MAX = R.styleable.CustomProgressView_max; final int ID_MIN = R.styleable.CustomProgressView_min; final int ID_BG_COLOR = R.styleable.CustomProgressView_bgColor; final int ID_GRADIENT_RADIUS = R.styleable.CustomProgressView_gradientRadius; final int ID_GRADIENT_START_COLOR = R.styleable.CustomProgressView_gradientStartColor; final int ID_GRADIENT_CENTER_COLOR = R.styleable.CustomProgressView_gradientCenterColor; final int ID_GRADIENT_END_COLOR = R.styleable.CustomProgressView_gradientEndColor; final int ID_GRADIENT_LOCATION = R.styleable.CustomProgressView_gradientLocation; // attribute value define [START] private enum TYPE { HORIZONTAL, VERTICAL }
// 프로그래스 색상 및 라운딩 처리에 대한 정보 구조체. protected class GradientInfo { int radius; int startColor; int centerColor; int endColor; float location; }
private GradientInfo mGradientInfo = new GradientInfo(); private TYPE mType = TYPE.HORIZONTAL; private int mMin = DEFAULT_MIN; private int mMax = DEFAULT_MAX; private int mBgColor; // attribute value define [END] private ProgressThread mThread; private int mCurrentProgress = 0; // surface life cycle // Type 은 2가지로 표현 한다. 더 필요한 경우 생명주기 타입 추가. private enum LIFE { CAN_DRAW, NOT_DRAW }
private LIFE mCurrentLife = LIFE.NOT_DRAW; public CustomProgressView(Context context) { this(context, null); }
/** * 현재 프로그래스 진행 률 표시 하는 함수. * * @param progress */ public void setProgress(int progress) { mCurrentProgress = progress; if (mCurrentLife == LIFE.CAN_DRAW) { mThread.draw(); } }
/** * 현재 프로그래스 진행률 get * * @return mCurrentProgress * @author hmju */ public int getProgress() { return mCurrentProgress; }
/** * 현재 프로그래스가 증가율을 표시하는 함수. * * @param diff */ public void incrementProgressBy(int diff) { mCurrentProgress += diff; if (mCurrentLife == LIFE.CAN_DRAW) { mThread.draw(); } }
@Override public void surfaceCreated(SurfaceHolder surfaceHolder) { }
@Override public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
if (mThread != null) { mThread.closeThread(); mThread = null; }
mCurrentLife = LIFE.CAN_DRAW; mThread = new ProgressThread(width, height); }
중간 중간 이해하기 쉽도록 주석 처리를 하여 더이상 설명은 안하도록...(귀찮기도 하고..흠..) 만약에 궁금한게 있으시면 댓글 남겨주시길 바랍니다 :D
아무튼 최대한 사용하기 쉽게 코드를 짜보았습니다. 그리고 기존 ProgressBar 에서 사용하는 함수 이름이나, 속성 이름들을 같은 거로 셋팅할려고 했습니다.
만약 여기서 현재 몇 퍼센트까지 왔는지에 대해서 노출 하고 싶다면, runDraw 라는 Runnable 클래스안에
canvas.drawText 라고 있다. 이부분이 주석 처리 되어있는데 풀고, 이와 관련된 것에 주석을 풀면 됩니다.
한가지 더 설명하자면. 기본으로 제공하는 ProgressBar 에서 setProgress 라는 함수를 사용하여 현재 진행 상태를 표시하는데, 이 함수는 되도록 사용하지 않는 것을 추천 합니다.
incrementProgressBy 라는 함수를 사용하는 것을 적극 추천합니다. 이유는 setProgress 라는 함수는 말그대록 현재 진행률을 다시 셋팅해서 View 단에서 그리는 것이고, incrementProgressBy 는 현재 진행 상태에서 증가율을 누적해서 더한후 그리는 것입니다.
아무튼 제가 만든 CustomProgressView 에서도 incrementProgressBy 이 함수를 사용하는 것을 추천 드립니다.
그리고, 쓰레드나 핸들러 이런거 사용안하고 그냥 반복문에 'incrementProgressBy(int 증가율)' 이 함수를 때려 박으시면 됩니다. 내부적으로 안에서 다 처리 하기 때문에 불 필요한 핸들러 사용은 추천 드리지 않습니다. (오히려 더 느리게 작동될수 있습니다.)