본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] 원형 progress view 그리기

by 돼지왕 왕돼지 2017. 7. 15.
반응형

 [android] 원형 progress view 그리기


http://frogermcs.github.io/InstaMaterial-concept-part-9-photo-publishing/


addlistener, Android, Animator, Canvas, drawarc, drawbitmap, drawCircle, dst_in, Mode, objectanimator, ofFloat, playsequentially, porterduff, porterduffxfermode, postinvalidate, recycle, setantialias, setduration, setinterpolator, setstrokewidth, start, Stroke, [android] 원형 progrss view 그리기, 원형 progress view


Progress view


-

public class SendingProgressView extends View {

    public static final int STATE_NOT_STARTED = 0;

    public static final int STATE_PROGRESS_STARTED = 1;

    public static final int STATE_DONE_STARTED = 2;

    public static final int STATE_FINISHED = 3;


    private static final int PROGRESS_STROKE_SIZE = 10;

    private static final int INNER_CIRCLE_PADDING = 30;

    private static final int MAX_DONE_BG_OFFSET = 800;

    private static final int MAX_DONE_IMG_OFFSET = 400;


    private int state = STATE_NOT_STARTED;

    private float currentProgress = 0;

    private float currentDoneBgOffset = MAX_DONE_BG_OFFSET;

    private float currentCheckmarkOffset = MAX_DONE_IMG_OFFSET;


    private Paint progressPaint;

    private Paint doneBgPaint;

    private Paint maskPaint;


    private RectF progressBounds;


    private Bitmap checkmarkBitmap;

    private Bitmap innerCircleMaskBitmap;


    private int checkmarkXPosition = 0;

    private int checkmarkYPosition = 0;


    private Paint checkmarkPaint;

    private Bitmap tempBitmap;

    private Canvas tempCanvas;


    private ObjectAnimator simulateProgressAnimator;

    private ObjectAnimator doneBgAnimator;

    private ObjectAnimator checkmarkAnimator;


    private OnLoadingFinishedListener onLoadingFinishedListener;


    public SendingProgressView(Context context) {

        super(context);

        init();

    }


    public SendingProgressView(Context context, AttributeSet attrs) {

        super(context, attrs);

        init();

    }


    public SendingProgressView(Context context, AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);

        init();

    }


    @TargetApi(Build.VERSION_CODES.LOLLIPOP)

    public SendingProgressView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

        super(context, attrs, defStyleAttr, defStyleRes);

        init();

    }


    private void init() {

        setupProgressPaint();

        setupDonePaints();

        setupSimulateProgressAnimator();

        setupDoneAnimators();

    }


    private void setupProgressPaint() {

        progressPaint = new Paint();

        progressPaint.setAntiAlias(true);

        progressPaint.setStyle(Paint.Style.STROKE);

        progressPaint.setColor(0xffffffff);

        progressPaint.setStrokeWidth(PROGRESS_STROKE_SIZE);

    }


    private void setupSimulateProgressAnimator() {

        simulateProgressAnimator = ObjectAnimator.ofFloat(this, "currentProgress", 0, 100).setDuration(2000);

        simulateProgressAnimator.setInterpolator(new AccelerateInterpolator());

        simulateProgressAnimator.addListener(new AnimatorListenerAdapter() {

            @Override

            public void onAnimationEnd(Animator animation) {

                changeState(STATE_DONE_STARTED);

            }

        });

    }


    private void setupDonePaints() {

        doneBgPaint = new Paint();

        doneBgPaint.setAntiAlias(true);

        doneBgPaint.setStyle(Paint.Style.FILL);

        doneBgPaint.setColor(0xff39cb72);


        checkmarkPaint = new Paint();


        maskPaint = new Paint();

        maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

    }


    private void setupDoneAnimators() {

        doneBgAnimator = ObjectAnimator.ofFloat(this, "currentDoneBgOffset", MAX_DONE_BG_OFFSET, 0).setDuration(300);

        doneBgAnimator.setInterpolator(new DecelerateInterpolator());


        checkmarkAnimator = ObjectAnimator.ofFloat(this, "currentCheckmarkOffset", MAX_DONE_IMG_OFFSET, 0).setDuration(300);

        checkmarkAnimator.setInterpolator(new OvershootInterpolator());

        checkmarkAnimator.addListener(new AnimatorListenerAdapter() {

            @Override

            public void onAnimationEnd(Animator animation) {

                changeState(STATE_FINISHED);

            }

        });

    }


    @Override

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        super.onSizeChanged(w, h, oldw, oldh);

        updateProgressBounds();

        setupCheckmarkBitmap();

        setupDoneMaskBitmap();

        resetTempCanvas();

    }


    private void updateProgressBounds() {

        progressBounds = new RectF(

                PROGRESS_STROKE_SIZE, PROGRESS_STROKE_SIZE,

                getWidth() - PROGRESS_STROKE_SIZE, getWidth() - PROGRESS_STROKE_SIZE

        );

    }


    private void setupCheckmarkBitmap() {

        checkmarkBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_done_white_48dp);

        checkmarkXPosition = getWidth() / 2 - checkmarkBitmap.getWidth() / 2;

        checkmarkYPosition = getWidth() / 2 - checkmarkBitmap.getHeight() / 2;

    }


    private void setupDoneMaskBitmap() {

        innerCircleMaskBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888);

        Canvas srcCanvas = new Canvas(innerCircleMaskBitmap);

        srcCanvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - INNER_CIRCLE_PADDING, new Paint());

    }


    private void resetTempCanvas() {

        tempBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888);

        tempCanvas = new Canvas(tempBitmap);

    }


    @Override

    protected void onDraw(Canvas canvas) {

        if (state == STATE_PROGRESS_STARTED) {

            drawArcForCurrentProgress();

        } else if (state == STATE_DONE_STARTED) {

            drawFrameForDoneAnimation();

            postInvalidate();

        } else if (state == STATE_FINISHED) {

            drawFinishedState();

        }


        canvas.drawBitmap(tempBitmap, 0, 0, null);

    }


    private void drawArcForCurrentProgress() {

        tempCanvas.drawArc(progressBounds, -90f, 360 * currentProgress / 100, false, progressPaint);

    }


    private void drawFrameForDoneAnimation() {

        tempCanvas.drawCircle(getWidth() / 2, getWidth() / 2 + currentDoneBgOffset, getWidth() / 2 - INNER_CIRCLE_PADDING, doneBgPaint);

        tempCanvas.drawBitmap(checkmarkBitmap, checkmarkXPosition, checkmarkYPosition + currentCheckmarkOffset, checkmarkPaint);

        tempCanvas.drawBitmap(innerCircleMaskBitmap, 0, 0, maskPaint);

        tempCanvas.drawArc(progressBounds, 0, 360f, false, progressPaint);

    }


    private void drawFinishedState() {

        tempCanvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - INNER_CIRCLE_PADDING, doneBgPaint);

        tempCanvas.drawBitmap(checkmarkBitmap, checkmarkXPosition, checkmarkYPosition, checkmarkPaint);

        tempCanvas.drawArc(progressBounds, 0, 360f, false, progressPaint);

    }


    private void changeState(int state) {

        if (this.state == state) {

            return;

        }


        tempBitmap.recycle();

        resetTempCanvas();


        this.state = state;

        if (state == STATE_PROGRESS_STARTED) {

            setCurrentProgress(0);

            simulateProgressAnimator.start();

        } else if (state == STATE_DONE_STARTED) {

            setCurrentDoneBgOffset(MAX_DONE_BG_OFFSET);

            setCurrentCheckmarkOffset(MAX_DONE_IMG_OFFSET);

            AnimatorSet animatorSet = new AnimatorSet();

            animatorSet.playSequentially(doneBgAnimator, checkmarkAnimator);

            animatorSet.start();

        } else if (state == STATE_FINISHED) {

            if (onLoadingFinishedListener != null) {

                onLoadingFinishedListener.onLoadingFinished();

            }

        }

    }


    public void simulateProgress() {

        changeState(STATE_PROGRESS_STARTED);

    }


    public void setCurrentProgress(float currentProgress) {

        this.currentProgress = currentProgress;

        postInvalidate();

    }


    public void setCurrentDoneBgOffset(float currentDoneBgOffset) {

        this.currentDoneBgOffset = currentDoneBgOffset;

        postInvalidate();

    }


    public void setCurrentCheckmarkOffset(float currentCheckmarkOffset) {

        this.currentCheckmarkOffset = currentCheckmarkOffset;

        postInvalidate();

    }


    public void setOnLoadingFinishedListener(OnLoadingFinishedListener onLoadingFinishedListener) {

        this.onLoadingFinishedListener = onLoadingFinishedListener;

    }


    public interface OnLoadingFinishedListener {

        public void onLoadingFinished();

    }

}





반응형

댓글