com.mianamiana.library.LiquidBallProgressBar.java Source code

Java tutorial

Introduction

Here is the source code for com.mianamiana.library.LiquidBallProgressBar.java

Source

/*
 * Copyright  2015 Mianamiana
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mianamiana.library;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;

/**
 * Created by Mianamiana on 15/9/20.
 */
public class LiquidBallProgressBar extends FrameLayout {

    private Paint mWavePaint;
    private float mWaveTranslationX;
    private float mWaveTranslationY;
    private int mWaveOffsetY;
    private Matrix mWaveShaderMatrix;
    private boolean mIsRunAnimation;
    private LiquidTextView mLiquidTextView;
    private BitmapShader mWaveShader;
    private LiquidAnimationHelper mAnimationHelper;
    private int mWaveH;
    private int mWaveW;
    private Drawable mWave;
    private int mProgress;
    private int mMaxProgress = 100;
    private int mUnfilledWaveColor = 0xfff6f6fc;
    private int mWaveColor = 0xffa2de5a;
    private Paint mBorderPaint;
    private int mBorderWidth = 0;
    private int mBorderColor = 0;
    private int mTextOverlapColor = 0xfff6f6fc;
    private int mTextColor = 0xffa2de5a;
    private OnProgressChangeListener mOnProgressChangeListener;
    private float mTextSize = 20;

    public interface OnProgressChangeListener {
        void onProgressChange(View view, int progress);
    }

    public LiquidBallProgressBar(Context context) {
        this(context, null, 0);
    }

    public LiquidBallProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LiquidBallProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        initAttrs(context, attrs);
        // animation helper
        mAnimationHelper = new LiquidAnimationHelper(this);

        //init  paints
        mWavePaint = new Paint();
        mWavePaint.setAntiAlias(true);
        mWaveShaderMatrix = new Matrix();

        mBorderPaint = new Paint();
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(mBorderColor);

        //add LiquidTextView
        mLiquidTextView = new LiquidTextView(context);
        mLiquidTextView.setText(mProgress + "");
        mLiquidTextView.setTextSize(mTextSize);
        mLiquidTextView.setTextColor(mTextColor);
        mLiquidTextView.setGravity(Gravity.CENTER);
        addView(mLiquidTextView, generateDefaultLayoutParams());

        // wave drawable
        mWave = DrawableCompat.wrap(getResources().getDrawable(R.drawable.wave));
        mWave.mutate();
        mWaveW = mWave.getIntrinsicWidth();
        mWaveH = mWave.getIntrinsicHeight();

        //======
        setMaxProgress(mMaxProgress);
        setProgress(mProgress);

    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LiquidBallProgressBar);
        try {
            mBorderColor = a.getColor(R.styleable.LiquidBallProgressBar_lbpb_borderColor, mBorderColor);
            mTextColor = a.getColor(R.styleable.LiquidBallProgressBar_lbpb_textColor, mTextColor);
            mWaveColor = a.getColor(R.styleable.LiquidBallProgressBar_lbpb_waveColor, mWaveColor);
            mUnfilledWaveColor = a.getColor(R.styleable.LiquidBallProgressBar_lbpb_unfilledColor,
                    mUnfilledWaveColor);
            mTextOverlapColor = a.getColor(R.styleable.LiquidBallProgressBar_lbpb_textOverlapColor,
                    mTextOverlapColor);
            int textsize = a.getDimensionPixelSize(R.styleable.LiquidBallProgressBar_lbpb_textSize, 0);
            if (textsize != 0) {
                mTextSize = textsize / context.getResources().getDisplayMetrics().scaledDensity;
            }
            mBorderWidth = (int) (a.getDimension(R.styleable.LiquidBallProgressBar_lbpb_borderWidth, mBorderWidth)
                    + 0.5f);
            mProgress = a.getInteger(R.styleable.LiquidBallProgressBar_lbpb_progress, mProgress);
            mMaxProgress = a.getInteger(R.styleable.LiquidBallProgressBar_lbpb_maxProgress, mMaxProgress);
        } finally {
            a.recycle();
        }
    }

    public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {
        this.mOnProgressChangeListener = onProgressChangeListener;
    }

    public int getTextOverlapColor() {
        return mTextOverlapColor;
    }

    public void setTextOverlapColor(int textOverlapColor) {
        this.mTextOverlapColor = textOverlapColor;
        mLiquidTextView.resetShader();
    }

    public void setText(String text) {
        mLiquidTextView.setText(text);
    }

    public int getBorderWidth() {
        return mBorderWidth;
    }

    public void setBorderWidth(int borderWidth) {
        this.mBorderWidth = borderWidth;
        resetShader();
        mLiquidTextView.resetShader();
    }

    public int getBorderColor() {
        return mBorderColor;
    }

    public void setBorderColor(int borderColor) {
        this.mBorderColor = borderColor;
        mBorderPaint.setColor(mBorderColor);
    }

    public void setTextSize(float size) {
        mLiquidTextView.setTextSize(size);
    }

    public void setTypeface(Typeface tf) {
        mLiquidTextView.setTypeface(tf);
    }

    public void setTextColor(int color) {
        mLiquidTextView.setTextColor(color);
        mLiquidTextView.resetShader();
    }

    public void setTextColor(ColorStateList colors) {
        mLiquidTextView.setTextColor(colors);
        mLiquidTextView.resetShader();
    }

    public int getUnfilledWaveColor() {
        return mUnfilledWaveColor;
    }

    public void setUnfilledWaveColor(int unfilledWaveColor) {
        mUnfilledWaveColor = unfilledWaveColor;
        resetShader();
    }

    public int getWaveColor() {
        return mWaveColor;
    }

    public void setWaveColor(int waveColor) {
        mWaveColor = waveColor;
        resetShader();
        mLiquidTextView.resetShader();
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        lp.gravity = Gravity.CENTER;
        return lp;
    }

    //Be a quadrate view
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
        int childWithSize = getMeasuredWidth();
        heightMeasureSpec = widthMeasureSpec = MeasureSpec.makeMeasureSpec(childWithSize, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private BitmapShader createShader(int foregroundColor, int backgroundColor) {

        DrawableCompat.setTint(mWave, foregroundColor);

        Bitmap b = Bitmap.createBitmap(mWaveW, mWaveH, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        c.drawColor(backgroundColor);

        mWave.setBounds(0, 0, mWaveW, mWaveH);
        mWave.draw(c);

        BitmapShader shader = new BitmapShader(b, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);

        return shader;
    }

    private BitmapShader getWaveShader() {
        return createShader(mWaveColor, mUnfilledWaveColor);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        resetShader();
    }

    private void resetShader() {

        mWaveOffsetY = getHeight() - mWaveH / 2 - mBorderWidth;
        mWaveShader = getWaveShader();
        mWaveShaderMatrix.setTranslate(mWaveTranslationX, mWaveOffsetY - mWaveTranslationY);
        mWaveShader.setLocalMatrix(mWaveShaderMatrix);
        mWavePaint.setShader(mWaveShader);

    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (getBackground() == null) {
            drawLiqiud(canvas);
        }
        super.dispatchDraw(canvas);
    }

    @Override
    public void draw(Canvas canvas) {
        drawLiqiud(canvas);
        super.draw(canvas);
    }

    private void drawLiqiud(Canvas canvas) {
        float radius = getWidth() / 2.0f;
        float cx = getWidth() / 2.0f;
        float cy = getHeight() / 2.0f;

        if (mIsRunAnimation) {
            mWaveShaderMatrix.setTranslate(mWaveTranslationX, mWaveOffsetY + mWaveTranslationY);
            mWaveShader.setLocalMatrix(mWaveShaderMatrix);
        }
        if (mBorderWidth != 0) {
            canvas.drawCircle(cx, cy, radius, mBorderPaint);
            canvas.drawCircle(cx, cy, radius - mBorderWidth, mWavePaint);
        } else {
            canvas.drawCircle(cx, cy, radius, mWavePaint);
        }

    }

    public void setProgressWithAnimation(int targetProgress) {
        targetProgress = Math.max(Math.min(targetProgress, mMaxProgress), 0);
        mIsRunAnimation = true;
        mAnimationHelper.startAnimation(targetProgress);
    }

    /**
     * start wave animation
     */
    public void startWave() {
        mIsRunAnimation = true;
        mAnimationHelper.startAnimation(getProgress());
    }

    /**
     * cancel wave animation ;
     */
    public void cancelWave() {
        mIsRunAnimation = false;
        mAnimationHelper.cancelAnimation();
    }

    /**
     * Ends the wave. This causes the animation to assign the target progess
     */
    public void endWave() {
        mAnimationHelper.endAnimation();
        mIsRunAnimation = false;
    }

    public float getWaveTranslationX() {
        return mWaveTranslationX;
    }

    public void setWaveTranslationX(float waveTranslationX) {
        this.mWaveTranslationX = waveTranslationX;
        invalidate();
    }

    @Override
    public void invalidate() {
        super.invalidate();
        mLiquidTextView.invalidate();
    }

    public int getMaxProgress() {
        return mMaxProgress;
    }

    public void setMaxProgress(int maxProgress) {
        mMaxProgress = maxProgress;
    }

    public int getProgress() {
        return mProgress;
    }

    public void setProgress(int progress) {
        mProgress = progress;
        //dent of wave.png = 20
        mWaveTranslationY = -(getHeight() + 20 - mBorderWidth * 2) * mProgress / mMaxProgress;
        if (mOnProgressChangeListener != null) {
            mOnProgressChangeListener.onProgressChange(this, progress);
        } else {
            setText(progress + "");
        }
        invalidate();
    }

    ///////////////////////////////////////////////////////////////////////////
    // render text view
    ///////////////////////////////////////////////////////////////////////////
    private class LiquidTextView extends TextView {
        private int offsetY;
        private Matrix textShaderMatrix;
        private BitmapShader textShader;

        public LiquidTextView(Context context) {
            super(context);
            textShaderMatrix = new Matrix();
        }

        private BitmapShader getTextShader() {
            return createShader(mTextOverlapColor, getCurrentTextColor());
        }

        @Override
        protected void onDraw(Canvas canvas) {
            if (mIsRunAnimation) {
                textShaderMatrix.setTranslate(mWaveTranslationX, offsetY + mWaveTranslationY);
                textShader.setLocalMatrix(textShaderMatrix);
            }
            super.onDraw(canvas);
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            resetShader();
        }

        private void resetShader() {

            offsetY = LiquidBallProgressBar.this.getHeight() - LiquidTextView.this.getTop() - mWaveH / 2
                    - mBorderWidth;
            textShader = getTextShader();
            textShaderMatrix.setTranslate(mWaveTranslationX, offsetY + mWaveTranslationY);
            textShader.setLocalMatrix(textShaderMatrix);
            getPaint().setShader(textShader);

        }

    }

}