Android 垂直进度

摘要 : 开启开篇一张图,内容全靠吹的模式(ctrl+c,ctrl+v)。

垂直Progress

ProgressBar 深入分析
ProgressBar 深入分析
通过上面吧垂直progress抽取出来。
修改进度条样式

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip"/>
            <gradient
                android:angle="180"
                android:centerColor="#ff5a5d5a"
                android:centerY="0.75"
                android:endColor="#ff747674"
                android:startColor="#ff9d9e9d"
                />
        </shape>
    </item>
    <item android:id="@android:id/secondaryProgress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="bottom">
            <shape>
                <corners android:radius="5dip"/>
                <gradient
                    android:angle="180"
                    android:centerColor="#80ffb600"
                    android:centerY="0.75"
                    android:endColor="#a0ffcb00"
                    android:startColor="#80ffd300"
                    />
            </shape>
        </clip>
    </item>
    <item android:id="@android:id/progress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="bottom">
            <shape>
                <corners android:radius="5dip"/>
                <gradient
                    android:angle="180"
                    android:centerColor="#ffffb600"
                    android:centerY="0.75"
                    android:endColor="#ffffcb00"
                    android:startColor="#ffffd300"
                    />
            </shape>
        </clip>
    </item>
</layer-list>

android:clipOrientation:截取的方向。可取的值:horizontal和vertical。分别表 示水平和垂直方向截取图像。
android:gravity:表示如何截取图像。例如,left表示从左侧截取图像,right表示从右侧截取图像。

style

    <style name="Progress_Vertical_Custom" parent="@android:style/Widget.ProgressBar.Horizontal">
        <item name="android:progressDrawable">@drawable/progress_vertical</item>
        <item name="android:indeterminateDrawable">
            @android:drawable/progress_indeterminate_horizontal
        </item>
        <item name="android:minWidth">12dip</item>
        <item name="android:maxWidth">12dip</item>
    </style>

借助 NumberProgressBar实现

下载链接: NumberProgressBar
自定义属性

    <declare-styleable name="NumberProgressBar">
        <attr name="progress_current" format="integer"/>
        <attr name="progress_max" format="integer"/>

        <attr name="progress_unreached_color" format="color"/>
        <attr name="progress_reached_color" format="color"/>

        <attr name="progress_reached_bar_height" format="dimension"/>
        <attr name="progress_unreached_bar_height" format="dimension"/>

        <attr name="progress_text_size" format="dimension"/>
        <attr name="progress_text_color" format="color"/>

        <attr name="progress_text_offset" format="dimension"/>

        <attr name="progress_text_visibility" format="enum">
            <enum name="visible" value="0"/>
            <enum name="invisible" value="1"/>
        </attr>
    </declare-styleable>

代码

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.sjjd.hu.office.R;
import com.sjjd.hu.utils.LogUtils;

import static com.sjjd.hu.widget.verticalpb.NumberProgressBar.ProgressTextVisibility.*;

/**
 * =======================================
 * 创建者 : 胡宇林
 * 日 期 : 2018-03-22 - 17:54
 * 描 述 :
 * =======================================
 */


public class NumberProgressBar extends View {
    private final String TAG = "NumberProgressBar->";

    private int mMaxProgress = 100;
    /**
     * 当前进度,不能超过最大进度
     */
    private int mCurrentProgress = 0;
    /**
     * 进度区域颜色
     */
    private int mReachedBarColor;
    /**
     * 非进度区域颜色
     */
    private int mUnreachedBarColor;
    /**
     * 进度文字颜色
     */
    private int mTextColor;
    /**
     * 进度文字大小
     */
    private float mTextSize;
    /**
     * 进度区域高度
     */
    private float mReachedBarHeight;
    /**
     * 非进度区域高度
     */
    private float mUnreachedBarHeight;


    /**
     * 文字显示的样式 perefix(前)、suffix(后)
     */
    private String mPrefix = "";
    private String mSuffix = "%";

    private final int default_text_color = Color.rgb(66, 145, 241);
    private final int default_reached_color = Color.rgb(66, 145, 241);
    private final int default_unreached_color = Color.rgb(204, 204, 204);
    private final float default_progress_text_offset;
    private final float default_text_size;
    private final float default_reached_bar_height;
    private final float default_unreached_bar_height;


    /**
     * 对数据的保存与恢复
     */
    private static final String INSTANCE_STATE = "saved_instance";
    private static final String INSTANCE_TEXT_COLOR = "text_color";
    private static final String INSTANCE_TEXT_SIZE = "text_size";
    private static final String INSTANCE_REACHED_BAR_HEIGHT = "reached_bar_height";
    private static final String INSTANCE_REACHED_BAR_COLOR = "reached_bar_color";
    private static final String INSTANCE_UNREACHED_BAR_HEIGHT = "unreached_bar_height";
    private static final String INSTANCE_UNREACHED_BAR_COLOR = "unreached_bar_color";
    private static final String INSTANCE_MAX = "max";
    private static final String INSTANCE_PROGRESS = "progress";
    private static final String INSTANCE_SUFFIX = "suffix";
    private static final String INSTANCE_PREFIX = "prefix";
    private static final String INSTANCE_TEXT_VISIBILITY = "text_visibility";

    private static final int PROGRESS_TEXT_VISIBLE = 0;

    /**
     * 文字的宽度
     */
    private float mDrawTextWidth;
    /**
     * 文字的开始位置
     */
    private float mDrawTextStart;
    /**
     * 文字的结束位置
     */
    private float mDrawTextEnd;

    /**
     * 需要绘制的文字
     */
    private String mCurrentDrawText;

    /**
     * 进度区域画笔
     */

    private Paint mReachedBarPaint;
    /**
     * 非进度区域画笔
     */
    private Paint mUnreachedBarPaint;

    /**
     * 画进度文字的画笔
     */
    private Paint mTextPaint;

    /**
     * 非进度区域的矩形范围
     */
    private RectF mUnreachedRectF = new RectF(0, 0, 0, 0);

    /**
     * 进度区域矩形
     */
    private RectF mReachedRectF = new RectF(0, 0, 0, 0);

    /**
     * 进度文字偏移量
     */
    private float mOffset;


    /**
     * 是否绘制未到的区域。
     */
    private boolean mDrawUnreachedBar = true;

    private boolean mDrawReachedBar = true;

    private boolean mIfDrawText = true;


    /**
     * listener
     */
    private OnProgressBarListener mListener;

    public enum ProgressTextVisibility {
        Visible, Invisible
    }

    //by add Yulin Hu 2018-03-23
    //在宽比高大时为水平,如果高比宽大时为垂直

    /**
     * w > h = true
     * h >= w = false ;
     */
    private boolean maxWidthHeight = true;
    private int viewWidth = 0;
    private int viewHeight = 0;
    private static final String MAX_WIDTH_HEIGHT = "max_width_height";
    /**
     * 文字的高度
     */
    private float mDrawTextHeight;
    /**
     * 在实际使用中,普片都低于20%。显示效果很差,这里统一增加一个值(这里只针对有文字)
     * 默认为有效高度的1/3
     */
    private float raiseNum;


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

    public NumberProgressBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NumberProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        default_reached_bar_height = dp2px(1.5f);
        default_unreached_bar_height = dp2px(1.0f);
        default_text_size = sp2px(10);
        default_progress_text_offset = dp2px(0.0f);

        //初始化属性
        final TypedArray attributes = context.getTheme().
                obtainStyledAttributes(attrs, R.styleable.NumberProgressBar, defStyleAttr, 0);


        mReachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_reached_color, default_reached_color);
        mUnreachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_unreached_color, default_unreached_color);
        mTextColor = attributes.getColor(R.styleable.NumberProgressBar_progress_text_color, default_text_color);
        mTextSize = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_size, default_text_size);

        mReachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_reached_bar_height, default_reached_bar_height);
        mUnreachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_unreached_bar_height, default_unreached_bar_height);
        mOffset = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_offset, default_progress_text_offset);

        int textVisible = attributes.getInt(R.styleable.NumberProgressBar_progress_text_visibility, PROGRESS_TEXT_VISIBLE);
        if (textVisible != PROGRESS_TEXT_VISIBLE) { //0 = visible
            mIfDrawText = false;
        }

        setProgress(attributes.getInt(R.styleable.NumberProgressBar_progress_current, 0));
        setMax(attributes.getInt(R.styleable.NumberProgressBar_progress_max, 100));

        attributes.recycle();
        initializePainters();
    }

    @Override
    protected int getSuggestedMinimumWidth() {
        return (int) mTextSize;
    }

    @Override
    protected int getSuggestedMinimumHeight() {
        return (int) Math.max(mTextSize, Math.max(mReachedBarHeight, mUnreachedBarHeight));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        viewWidth = measure(widthMeasureSpec, true);
        viewHeight = measure(heightMeasureSpec, false);
        if (viewWidth > viewHeight) {
            maxWidthHeight = true;
        } else {
            maxWidthHeight = false;
            raiseNum = (viewHeight - getPaddingTop() - getPaddingBottom()) / 1.50f;
        }
        LogUtils.i(TAG, "width = " + viewWidth + ",height = " + viewHeight + ",maxWH = " + maxWidthHeight);

        setMeasuredDimension(viewWidth, viewHeight);
    }

    private int measure(int measureSpec, boolean isWidth) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);

        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
            result += padding;

            if (mode == MeasureSpec.AT_MOST) {
                if (isWidth) {
                    result = Math.max(result, size);
                } else { //高是反,余越靠近屏幕顶端越小
                    result = Math.min(result, size);
                }
            }
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (mIfDrawText) {
            if (maxWidthHeight) {
                calculateDrawRectF();
            } else {
                LogUtils.i(TAG, "calculateDrawRectFVertical()");
                calculateDrawRectFVertical();
            }
        } else {
            if (maxWidthHeight) {
                calculateDrawRectFWithoutProgressText();
            } else {
                LogUtils.i(TAG, "calculateDrawRectFWithoutProgressTextVertical()");
                calculateDrawRectFWithoutProgressTextVertical();
            }
        }

        LogUtils.i(TAG, "mReachedRectF = " + mReachedRectF.toString());
        LogUtils.i(TAG, "mUnreachedRectF = " + mUnreachedRectF.toString());


        if (mDrawReachedBar) {
            canvas.drawRect(mReachedRectF, mReachedBarPaint);
        }

        if (mDrawUnreachedBar) {
            canvas.drawRect(mUnreachedRectF, mUnreachedBarPaint);
        }

        if (mIfDrawText) {
            canvas.drawText(mCurrentDrawText, mDrawTextStart, mDrawTextEnd, mTextPaint);
        }
    }

    /**
     * 初始化3支画笔
     */
    private void initializePainters() {
        mReachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mReachedBarPaint.setColor(mReachedBarColor);

        mUnreachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mUnreachedBarPaint.setColor(mUnreachedBarColor);

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
    }

    /**
     * 进度绘制,不带文字
     */
    private void calculateDrawRectFWithoutProgressText() {

        mReachedRectF.left = getPaddingLeft();
        mReachedRectF.top = (getHeight() / 2.0f) - (mReachedBarHeight / 2.0f);
        mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) /
                (getMax() * 1.0f) * getProgress() + getPaddingLeft();
        mReachedRectF.bottom = (getHeight() / 2.0f) + (mReachedBarHeight / 2.0f);

        mUnreachedRectF.left = mReachedRectF.right;
        mUnreachedRectF.right = getWidth() - getPaddingRight();
        mUnreachedRectF.top = getHeight() / 2.0f - mUnreachedBarHeight / 2.0f;
        mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;
    }

    /**
     * 进度绘制,带文字
     */
    private void calculateDrawRectF() {
        mCurrentDrawText = String.format("%d", (getProgress() * 100) / getMax());
        mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;
        mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);

        if (getProgress() == 0) { //暂无具体进度
            mDrawReachedBar = false;
            mDrawTextStart = getPaddingLeft();
        } else {
            mDrawReachedBar = true;
            mReachedRectF.left = getPaddingLeft();
            mReachedRectF.top = (getHeight() / 2.0f) - (mReachedBarHeight / 2.0f);
            mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) /
                    (getMax() * 1.0f) * getProgress() - mOffset + getPaddingLeft();
            mReachedRectF.bottom = (getHeight() / 2.0f) + (mReachedBarHeight / 2.0f);
            mDrawTextStart = (mReachedRectF.right + mOffset);
        }

        mDrawTextEnd = (getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f);
        if (mDrawTextStart + mDrawTextWidth >= getWidth() - getPaddingRight()) {
            mDrawTextStart = getWidth() - getPaddingRight() - mDrawTextWidth;
            mReachedRectF.right = mDrawTextStart - mOffset;
        }

        float unreachedBarStart = mDrawTextStart + mDrawTextWidth + mOffset;
        if (unreachedBarStart > getWidth() - getPaddingRight()) {
            mDrawUnreachedBar = false;
        } else {
            mDrawUnreachedBar = true;
            mUnreachedRectF.left = unreachedBarStart;
            mUnreachedRectF.right = getWidth() - getPaddingRight();
            mUnreachedRectF.top = (getHeight() / 2.0f) - (mUnreachedBarHeight / 2.0f);
            mUnreachedRectF.bottom = (getHeight() / 2.0f) + (mUnreachedBarHeight / 2.0f);
        }
    }

    /**
     * 进度文字颜色
     *
     * @return
     */
    public int getTextColor() {
        return mTextColor;
    }

    /**
     * 进度文字大小
     *
     * @return
     */
    public float getProgressTextSize() {
        return mTextSize;
    }

    /**
     * 进度区域颜色
     *
     * @return
     */
    public int getUnreachedBarColor() {
        return mUnreachedBarColor;
    }

    /**
     * 非进度区域颜色
     *
     * @return
     */
    public int getReachedBarColor() {
        return mReachedBarColor;
    }

    /**
     * 当前进度
     *
     * @return
     */
    public int getProgress() {
        return mCurrentProgress;
    }

    /**
     * 最大进度
     *
     * @return
     */
    public int getMax() {
        return mMaxProgress;
    }

    /**
     * 进度值
     *
     * @return
     */
    public float getReachedBarHeight() {
        return mReachedBarHeight;
    }

    /**
     * 非进度值
     *
     * @return
     */
    public float getUnreachedBarHeight() {
        return mUnreachedBarHeight;
    }

    public void setProgressTextSize(float textSize) {
        this.mTextSize = textSize;
        mTextPaint.setTextSize(mTextSize);
        invalidate();
    }

    public void setProgressTextColor(int textColor) {
        this.mTextColor = textColor;
        mTextPaint.setColor(mTextColor);
        invalidate();
    }

    public void setUnreachedBarColor(int barColor) {
        this.mUnreachedBarColor = barColor;
        mUnreachedBarPaint.setColor(mUnreachedBarColor);
        invalidate();
    }

    public void setReachedBarColor(int progressColor) {
        this.mReachedBarColor = progressColor;
        mReachedBarPaint.setColor(mReachedBarColor);
        invalidate();
    }

    public void setReachedBarHeight(float height) {
        mReachedBarHeight = height;
    }

    public void setUnreachedBarHeight(float height) {
        mUnreachedBarHeight = height;
    }

    /**
     * 设置最大值
     *
     * @param maxProgress
     */
    public void setMax(int maxProgress) {
        if (maxProgress > 0) {
            this.mMaxProgress = maxProgress;
            invalidate();
        }
    }

    public void setSuffix(String suffix) {
        if (suffix == null) {
            mSuffix = "";
        } else {
            mSuffix = suffix;
        }
    }

    public String getSuffix() {
        return mSuffix;
    }

    public void setPrefix(String prefix) {
        if (prefix == null)
            mPrefix = "";
        else {
            mPrefix = prefix;
        }
    }

    public String getPrefix() {
        return mPrefix;
    }

    /**
     * 持续增长
     *
     * @param by
     */
    public void incrementProgressBy(int by) {
        if (by > 0) {
            setProgress(getProgress() + by);
        }

        if (mListener != null) {
            mListener.onProgressChange(getProgress(), getMax());
        }
    }

    /**
     * 设置当前进度
     *
     * @param progress
     */
    public void setProgress(int progress) {
        if (progress <= getMax() && progress >= 0) {
            this.mCurrentProgress = progress;
            invalidate();
        }
    }

    public void setEffectiveDate(int max, int progress, @ColorInt int reachedColor) {
        if (max > 0) {
            this.mMaxProgress = max;
        }

        if (progress <= getMax() && progress >= 0) {
            this.mCurrentProgress = progress;
        }
        this.mReachedBarColor = reachedColor;
        mReachedBarPaint.setColor(mReachedBarColor);

        invalidate();
    }

    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {
        final Bundle bundle = new Bundle();
        bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());
        bundle.putInt(INSTANCE_TEXT_COLOR, getTextColor());
        bundle.putFloat(INSTANCE_TEXT_SIZE, getProgressTextSize());
        bundle.putFloat(INSTANCE_REACHED_BAR_HEIGHT, getReachedBarHeight());
        bundle.putFloat(INSTANCE_UNREACHED_BAR_HEIGHT, getUnreachedBarHeight());
        bundle.putInt(INSTANCE_REACHED_BAR_COLOR, getReachedBarColor());
        bundle.putInt(INSTANCE_UNREACHED_BAR_COLOR, getUnreachedBarColor());
        bundle.putInt(INSTANCE_MAX, getMax());
        bundle.putInt(INSTANCE_PROGRESS, getProgress());
        bundle.putString(INSTANCE_SUFFIX, getSuffix());
        bundle.putString(INSTANCE_PREFIX, getPrefix());
        bundle.putBoolean(INSTANCE_TEXT_VISIBILITY, getProgressTextVisibility());

        bundle.putBoolean(MAX_WIDTH_HEIGHT, maxWidthHeight);

        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            final Bundle bundle = (Bundle) state;
            mTextColor = bundle.getInt(INSTANCE_TEXT_COLOR);
            mTextSize = bundle.getFloat(INSTANCE_TEXT_SIZE);
            mReachedBarHeight = bundle.getFloat(INSTANCE_REACHED_BAR_HEIGHT);
            mUnreachedBarHeight = bundle.getFloat(INSTANCE_UNREACHED_BAR_HEIGHT);
            mReachedBarColor = bundle.getInt(INSTANCE_REACHED_BAR_COLOR);
            mUnreachedBarColor = bundle.getInt(INSTANCE_UNREACHED_BAR_COLOR);

            initializePainters();
            setMax(bundle.getInt(INSTANCE_MAX));
            setProgress(bundle.getInt(INSTANCE_PROGRESS));
            setPrefix(bundle.getString(INSTANCE_PREFIX));
            setSuffix(bundle.getString(INSTANCE_SUFFIX));
            setProgressTextVisibility(bundle.getBoolean(INSTANCE_TEXT_VISIBILITY) ? Visible : Invisible);
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));
            return;
        }
        super.onRestoreInstanceState(state);
    }

    public float dp2px(float dp) {
        final float scale = getResources().getDisplayMetrics().density;
        return dp * scale + 0.5f;
    }

    public float sp2px(float sp) {
        final float scale = getResources().getDisplayMetrics().scaledDensity;
        return sp * scale;
    }

    public void setProgressTextVisibility(ProgressTextVisibility visibility) {
        mIfDrawText = visibility == Visible;
        invalidate();
    }

    public boolean getProgressTextVisibility() {
        return mIfDrawText;
    }

    public void setOnProgressBarListener(OnProgressBarListener listener) {
        mListener = listener;
    }

     by add Yulin Hu

    /**
     * 进度绘制,不带文字
     */
    private void calculateDrawRectFWithoutProgressTextVertical() {

        mReachedRectF.left = getPaddingLeft();
        mReachedRectF.top = getHeight() - ((getHeight() - getPaddingTop() - getPaddingBottom()) / (getMax() * 1.0f) * getProgress()) - getPaddingBottom();
        mReachedRectF.right = getWidth() - getPaddingLeft() - getPaddingRight();
        mReachedRectF.bottom = getHeight() - getPaddingBottom();

        mUnreachedRectF.left = mReachedRectF.left;
        mUnreachedRectF.top = getPaddingTop();
        mUnreachedRectF.right = mReachedRectF.right;
        mUnreachedRectF.bottom = mReachedRectF.top;
    }


    /**
     * 进度绘制,带文字。这里把默认的自增也就算进去
     */
    private void calculateDrawRectFVertical() {
        mCurrentDrawText = String.format("%d", (getProgress() * 100) / getMax());
        mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;
        mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);
        Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
        mDrawTextHeight = metrics.bottom - metrics.top;

        //设置不同默认增长,因为设定一个谷底你给的值后,后导致,没有什么差别了
        float temp = getProgress() * 1.0f / getMax() * 1.0f;
        LogUtils.i(TAG, "temp = " + temp);
        if (temp < 0.05) {
            raiseNum = (getHeight() - getPaddingTop() - getPaddingBottom()) / 8 * 1;
        } else if (temp < 0.1) {
            raiseNum = (getHeight() - getPaddingTop() - getPaddingBottom()) / 8 * 2;
        } else if (temp < 0.15) {
            raiseNum = (getHeight() - getPaddingTop() - getPaddingBottom()) / 8 * 3;
        } else if (temp < 0.2) {
            raiseNum = (getHeight() - getPaddingTop() - getPaddingBottom()) / 8 * 4;
        } else if (temp < 0.25) {
            raiseNum = (getHeight() - getPaddingTop() - getPaddingBottom()) / 8 * 5;
        } else {
            raiseNum = (getHeight() - getPaddingTop() - getPaddingBottom()) / 8 * 6;
        }

        if (getProgress() == 0) { //暂无具体进度
            mDrawReachedBar = false;
            mReachedRectF.top = getHeight() - getPaddingBottom() - raiseNum;
            mDrawTextEnd = getHeight() - getPaddingBottom() - ((Math.abs(mTextPaint.descent() + mTextPaint.ascent())) / 2.0f) - raiseNum;
        } else {
            mDrawReachedBar = true;
            mReachedRectF.left = getPaddingLeft();
            //计算当前值因有的高度,这里需要减去默认raiseNum在计算
            int tempProgressH = (int) ((getHeight() - getPaddingTop() - getPaddingBottom() - raiseNum) / (getMax() * 1.0f) * getProgress());
            mReachedRectF.top = getHeight() - getPaddingBottom() - raiseNum - tempProgressH;//+ mDrawTextHeight + mOffset;
            mReachedRectF.right = getWidth() - getPaddingLeft() - getPaddingRight();
            mReachedRectF.bottom = getHeight() - getPaddingBottom();
            mDrawTextEnd = mReachedRectF.top - mOffset - ((Math.abs(mTextPaint.descent() + mTextPaint.ascent())) / 2.0f);
        }

        mDrawTextStart = ((getWidth() - getPaddingLeft() - getPaddingRight()) / 2.0f) - (mDrawTextWidth / 2.0f);
//        mDrawTextHeight = mTextPaint.descent() - mTextPaint.ascent();

        if (mReachedRectF.top - mOffset - mDrawTextHeight <= getPaddingTop()) {
            mDrawTextEnd = getPaddingTop() + mDrawTextHeight + mOffset - ((Math.abs(mTextPaint.descent() + mTextPaint.ascent())) / 2.0f);
            mReachedRectF.top = mOffset + mDrawTextHeight + getPaddingTop();
        }

        float unreachedBarStart = mReachedRectF.top - mOffset - mDrawTextHeight;
        if (unreachedBarStart <= getPaddingTop()) {
            mDrawUnreachedBar = false;
        } else {
            mDrawUnreachedBar = true;
            mUnreachedRectF.left = getPaddingLeft();
            mUnreachedRectF.right = getWidth() - getPaddingRight();
            mUnreachedRectF.top = getPaddingBottom();
            mUnreachedRectF.bottom = unreachedBarStart;
        }
        LogUtils.i(TAG, "progress = " + getProgress());
        LogUtils.i(TAG, "getHeight() : " + getHeight() + ",baseline : " + Math.abs(mTextPaint.descent() + mTextPaint.ascent()) + ",mDrawTextStart : " + mDrawTextStart + ",mDrawTextHeight : " + mDrawTextHeight);
        LogUtils.i(TAG, ",mDrawTextStart = " + mDrawTextStart + ",mDrawTextEnd = " + mDrawTextEnd + ",mOffset = " + mOffset);
    }

/*
    *//**
     * 进度绘制,带文字
     *//*
    private void calculateDrawRectFVertical() {
        mCurrentDrawText = String.format("%d", (getProgress() * 100) / getMax());
        mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;
        mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);
        Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
        mDrawTextHeight = metrics.bottom - metrics.top;

        if (getProgress() == 0) { //暂无具体进度
            mDrawReachedBar = false;
            mReachedRectF.top = getHeight() - getPaddingBottom();
            mDrawTextEnd = getHeight() - getPaddingBottom() - ((Math.abs(mTextPaint.descent() + mTextPaint.ascent())) / 2.0f);
        } else {
            mDrawReachedBar = true;
            mReachedRectF.left = getPaddingLeft();
            mReachedRectF.top = getHeight() - ((getHeight() - getPaddingTop() - getPaddingBottom()) / (getMax() * 1.0f) * (getProgress())) - getPaddingBottom();//+ mDrawTextHeight + mOffset;
            mReachedRectF.right = getWidth() - getPaddingLeft() - getPaddingRight();
            mReachedRectF.bottom = getHeight() - getPaddingBottom();
            mDrawTextEnd = mReachedRectF.top - mOffset - ((Math.abs(mTextPaint.descent() + mTextPaint.ascent())) / 2.0f);
        }

        mDrawTextStart = ((getWidth() - getPaddingLeft() - getPaddingRight()) / 2.0f) - (mDrawTextWidth / 2.0f);
//        mDrawTextHeight = mTextPaint.descent() - mTextPaint.ascent();

        if (mReachedRectF.top - mOffset - mDrawTextHeight <= getPaddingTop()) {
            mDrawTextEnd = getPaddingTop() + mDrawTextHeight + mOffset - ((Math.abs(mTextPaint.descent() + mTextPaint.ascent())) / 2.0f);
            mReachedRectF.top = mOffset + mDrawTextHeight + getPaddingTop();
        }

        float unreachedBarStart = mReachedRectF.top - mOffset - mDrawTextHeight;
        if (unreachedBarStart <= getPaddingTop()) {
            mDrawUnreachedBar = false;
        } else {
            mDrawUnreachedBar = true;
            mUnreachedRectF.left = getPaddingLeft();
            mUnreachedRectF.right = getWidth() - getPaddingRight();
            mUnreachedRectF.top = getPaddingBottom();
            mUnreachedRectF.bottom = unreachedBarStart;
        }
        LogUtils.i(TAG, "progress = " + getProgress());
        LogUtils.i(TAG, "getHeight() : " + getHeight() + ",baseline : " + Math.abs(mTextPaint.descent() + mTextPaint.ascent()) + ",mDrawTextStart : " + mDrawTextStart + ",mDrawTextHeight : " + mDrawTextHeight);
        LogUtils.i(TAG, ",mDrawTextStart = " + mDrawTextStart + ",mDrawTextEnd = " + mDrawTextEnd + ",mOffset = " + mOffset);
    }*/
}

监听器

public interface OnProgressBarListener {
    void onProgressChange(int current, int max);
}

百分比圆盘

代码

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.sjjd.hu.utils.LogUtils;

/**
 * =======================================
 * 创建者 : 胡宇林
 * 日 期 : 2018-03-26 - 16:36
 * 描 述 :
 * =======================================
 */


public class PieChart extends View {
    private final String TAG = "PieChart->";

    private final int DEFAULT_SIZE = 146;
    private final int DEFAULT_OUTSIDE_CIRCULAR_RADIUS = 50;

    private final int DEFAULT_OUTSIDE_CIRCULAR_COLOR = Color.parseColor("#1C1F21");
    /**
     * 目前只有两块区域,一个为实体值、一个为剩下进度
     */
    private int DEFAULT_VALUE_COLOR = Color.parseColor("#E72C2D");
    private int DEFAULT_UNVALUE_COLOR = Color.parseColor("#FFFFFF");

    private Paint mPiePaint;
    /**
     * 外圈有一个阴影颜色
     */
    private Paint mOutsideCircluarPaint;
    private int mOutsideCircylarRadius; //半径
    private int mOutsideCircylarX; //x
    private int mOutsideCircylarY; //y
    private int mOutsideCircularColor;
    /**
     * 这里为一半的尺寸,圆有左右两个部分
     */
    private int mOutsideCircularSize = 26;

    private int mValueColor;
    private int mUnValueColor;
    private float mValue = 60;
    private float total = 100;
    /**
     * 角度
     */
    private RectF mValueRectF;


    /**
     * 对数据的保存与恢复
     */
    private static final String INSTANCE_STATE = "saved_instance";
    private static final String OUTSIDE_CIRCULAR_SIZE = "outside_circular_size";
    private static final String OUTSIDE_CIRCULAR_COLOR = "outside_circular_color";
    private static final String VALUE_TOTATL = "value_totatl";
    private static final String VALUE_VALUE = "valute_value";
    private static final String VALUE_VALUE_COLOR = "value_value_color";
    private static final String VALUE_UNVALUE_COLOR = "value_unvalue_color";

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

    public PieChart(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PieChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);


        mOutsideCircularColor = DEFAULT_OUTSIDE_CIRCULAR_COLOR;
        mValueColor = DEFAULT_VALUE_COLOR;
        mUnValueColor = DEFAULT_UNVALUE_COLOR;

        mOutsideCircylarRadius = DEFAULT_OUTSIDE_CIRCULAR_RADIUS;

        initializePainters();
    }

    @Override
    protected int getSuggestedMinimumWidth() {
        return DEFAULT_SIZE;
    }

    @Override
    protected int getSuggestedMinimumHeight() {
        return DEFAULT_SIZE;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w = measure(widthMeasureSpec, true);
        int h = measure(heightMeasureSpec, false);

        int size = Math.min(w, h);


        mOutsideCircylarRadius = (int) (size / 2.0f);
        mOutsideCircylarX = (int) ((w - getPaddingLeft() - getPaddingRight()) / 2.0f);
        mOutsideCircylarY = (int) ((h - getPaddingTop() - getPaddingBottom()) / 2.0f);
        mValueRectF = new RectF();
        mValueRectF.left = mOutsideCircylarX - mOutsideCircylarRadius + mOutsideCircularSize;
        mValueRectF.top = mOutsideCircylarY - mOutsideCircylarRadius + mOutsideCircularSize;
        mValueRectF.right = mOutsideCircylarX + mOutsideCircylarX - mOutsideCircularSize;
        mValueRectF.bottom = mOutsideCircylarY + mOutsideCircylarRadius - mOutsideCircularSize;

        LogUtils.i(TAG, "w = " + w + ",h = " + h + ",PaddingLeft() = " + getPaddingLeft() + ",PaddingRight() = " + getPaddingRight());
        LogUtils.i(TAG, "size = " + size + ",x = " + mOutsideCircylarX + ",y = " + mOutsideCircylarY + ",radius = " + mOutsideCircylarRadius);
        setMeasuredDimension(size, size);
    }

    private int measure(int measurespec, boolean isWidth) {
        int result;
        int mode = MeasureSpec.getMode(measurespec);
        int size = MeasureSpec.getSize(measurespec);

        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
            result += padding;

            if (mode == MeasureSpec.AT_MOST) {
                if (isWidth) {
//                    result = Math.max(result, size);
                    //这里也取最小值,因为在wrap情况下宽度为最大值
                    result = Math.min(result, size);
                } else {
                    result = Math.min(result, size);
                }
            }
        }

        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        LogUtils.i(TAG, "paint color = " + Integer.toHexString(mOutsideCircularColor));
        LogUtils.i(TAG, "x = " + mOutsideCircylarX + ",y = " + mOutsideCircylarY + ",radius = " + mOutsideCircylarRadius);
        canvas.drawCircle(mOutsideCircylarX, mOutsideCircylarY, mOutsideCircylarRadius, mOutsideCircluarPaint);

        //默认只有两个区域
        float sweepAngle = (360 / total) * mValue;
        float temp = 360.f - sweepAngle;
        if (temp < 0) {
            temp = 0;
        }
        LogUtils.i(TAG, "sweepAngle = " + sweepAngle + ",temp = " + temp + ",calc = " + (360 / total));

        mPiePaint.setColor(mUnValueColor);
        canvas.drawArc(mValueRectF, -90, temp, true, mPiePaint);

        mPiePaint.setColor(mValueColor);
        canvas.drawArc(mValueRectF, temp - 90, sweepAngle, true, mPiePaint);

    }

    private void initializePainters() {

        mPiePaint = new Paint();
        mPiePaint.setAntiAlias(true);

        mOutsideCircluarPaint = new Paint();
        mOutsideCircluarPaint.setAntiAlias(true);
        mOutsideCircluarPaint.setColor(mOutsideCircularColor);
        mOutsideCircluarPaint.setStrokeWidth(5);
    }

    public int getOutsideCircularColor() {
        return mOutsideCircularColor;
    }

    /**
     * 外框的颜色
     *
     * @param outsideCircularColor
     */
    public void setOutsideCircularColor(@ColorInt int outsideCircularColor) {
        mOutsideCircularColor = outsideCircularColor;
    }

    public int getValueColor() {
        return mValueColor;
    }


    /**
     * 具体值得颜色
     *
     * @param valueColor
     */
    public void setValueColor(int valueColor) {
        mValueColor = valueColor;
    }

    public int getUnValueColor() {
        return mUnValueColor;
    }

    /**
     * 设置具非具体指的圆盘颜色
     *
     * @param unValueColor
     */
    public void setUnValueColor(int unValueColor) {
        mUnValueColor = unValueColor;
    }

    public float getValue() {
        return mValue;
    }

    /**
     * 有效数
     *
     * @param value
     */
    public void setValue(float value) {
        if (value >= 0)
            mValue = value;

        invalidate();
    }

    public float getTotal() {
        return total;
    }

    /**
     * 比例的总数
     *
     * @param total
     */
    public void setTotal(float total) {
        if (total > 0)
            this.total = total;

        invalidate();
    }

    public int getOutsideCircularSize() {
        return mOutsideCircularSize;
    }

    public void setOutsideCircularSize(int outsideCircularSize) {
        if (outsideCircularSize >= 0) {
            mValueRectF.left = mOutsideCircylarX - mOutsideCircylarRadius + mOutsideCircularSize;
            mValueRectF.top = mOutsideCircylarY - mOutsideCircylarRadius + mOutsideCircularSize;
            mValueRectF.right = mOutsideCircylarX + mOutsideCircylarX - mOutsideCircularSize;
            mValueRectF.bottom = mOutsideCircylarY + mOutsideCircylarRadius - mOutsideCircularSize;
            mOutsideCircularSize = outsideCircularSize;
        }
    }

    public void setEffectiveDate(float total, float value, @ColorInt int valueColor) {
        if (total > 0) {
            this.total = total;
        }
        if (value >= 0) {
            this.mValue = value;
        }

        this.mValueColor = valueColor;
        invalidate();
    }

    public void setEffectiveDate(float total, float value, String valueColor) {
        if (total > 0) {
            this.total = total;
        }
        if (value >= 0) {
            this.mValue = value;
        }

        this.mValueColor = Color.parseColor(valueColor);
        invalidate();
    }

    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {

        Bundle bundle = new Bundle();
        bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());
        bundle.putInt(OUTSIDE_CIRCULAR_SIZE, getOutsideCircularSize());
        bundle.putInt(OUTSIDE_CIRCULAR_COLOR, getOutsideCircularColor());
        bundle.putFloat(VALUE_TOTATL, getTotal());
        bundle.putFloat(VALUE_VALUE, getValue());
        bundle.putInt(VALUE_VALUE_COLOR, getValueColor());
        bundle.putInt(VALUE_UNVALUE_COLOR, getUnValueColor());

        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            mOutsideCircularSize = bundle.getInt(OUTSIDE_CIRCULAR_SIZE);
            mOutsideCircularColor = bundle.getInt(OUTSIDE_CIRCULAR_COLOR);
            total = bundle.getFloat(VALUE_TOTATL);
            mValue = bundle.getFloat(VALUE_VALUE);
            mValueColor = bundle.getInt(VALUE_VALUE_COLOR);
            mUnValueColor = bundle.getInt(VALUE_UNVALUE_COLOR);

            setOutsideCircularSize(mOutsideCircularSize);
            setOutsideCircularColor(mOutsideCircularColor);
            setTotal(total);
            setValue(mValue);
            setValueColor(mValueColor);
            setUnValueColor(mUnValueColor);

            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));
        }

        super.onRestoreInstanceState(state);
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值