实现圆形头部渐变进度条的一种方案

实现渐变进度条的一种方案,解决进度小于头部半圆时变形的问题。

image

上方的进度条为系统的ProgressBar,可以看到,第二个进度条progress背景发生了变形(比如progress设置为1)。

custom_progress_primary.xml


<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <corners android:radius="10dp" />

    <solid android:color="@color/colorAccent" />

</shape>

bg_progressbar.xml


<?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 android:shape="rectangle">

            <corners android:radius="15dp" />

            <solid android:color="#eeeeee" />

    <item android:id="@android:id/progress">

            android:drawable="@drawable/custom_progress_primary"

            android:scaleWidth="98%" />

</layer-list>

布局中的代码:


    android:id="@+id/progress_first"

    style="?android:attr/progressBarStyleHorizontal"

    android:layout_width="0dp"

    android:layout_height="15dp"

    android:layout_marginTop="12dp"

    android:layout_marginRight="20dp"

    android:max="100"

    android:progress="50"

    android:progressDrawable="@drawable/bg_progressbar"

    app:layout_constraintLeft_toLeftOf="@id/tv_first"

    app:layout_constraintRight_toRightOf="parent"

    app:layout_constraintTop_toBottomOf="@id/tv_first" />

核心代码即为:paint.setXfermode(new PorterDuffXfermode(mode));在进度小于半圆时,使用混合模式绘制两个圆相交的形状。以下为CornerProgressBar的代码


package com.allan.demo.widget;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.LinearGradient;

import android.graphics.Paint;

import android.graphics.PaintFlagsDrawFilter;

import android.graphics.PorterDuff;

import android.graphics.PorterDuffXfermode;

import android.graphics.Shader;

import android.util.AttributeSet;

import android.view.View;

import com.allan.demo.R;

import java.math.BigDecimal;

public class CornerProgressBarextends View {

private static final int DEFAULT_MAX =100;

    private static final int DEFAULT_PROGRESS =0;

    private static final int DEFAULT_START_COLOR =0xFFFFE60D;

    private static final int DEFAULT_END_COLOR =0xFFFFC629;

    private static final int DEFAULT_BACKGROUD_COLOR =0xFFEFF1F4;

    private static final int DEFAULT_TEXT_COLOR =0xFF333333;

    private static final int DEFAULT_TEXT_SIZE =14;

    private double mMax =DEFAULT_MAX;

    private double mProgress =DEFAULT_PROGRESS;//当前进度

    private int mColor0 =DEFAULT_START_COLOR;//开始渐变色

    private int mColor1 =DEFAULT_END_COLOR;//结束渐变色

    private int mColorBackgroud =DEFAULT_BACKGROUD_COLOR;//背景色

    private int mTextColor =DEFAULT_TEXT_COLOR;

    private float mTextSize;//字体大小

    private Paintpaint;

    private PaintmBackgroudPaint;

    private PorterDuff.Modemode;

    public CornerProgressBar(Context context) {

super(context);

        initStyle(context, null, 0, 0);

        initPaint();

    }

public CornerProgressBar(Context context, AttributeSet attrs) {

super(context, attrs);

        initStyle(context, attrs, 0, 0);

        initPaint();

    }

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

super(context, attrs, defStyleAttr);

        initStyle(context, attrs, defStyleAttr, 0);

        initPaint();

    }

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

super(context, attrs, defStyleAttr, defStyleRes);

        initStyle(context, attrs, defStyleAttr, defStyleRes);

        initPaint();

    }

/**

    * 当progress大于max时,显示格式为150%,200%

*

    * @param max

    */

    public void setMax(double max) {

if (max <0) {

max =0;

        }

this.mMax = max;

//        if (mProgress > max) {

//            mProgress = max;

//        }

        invalidate();

    }

/**

    * 不设定上限 当progress大于max时,显示格式为150%,200%

*

    * @param progress

    */

    public void setProgress(double progress) {

if (progress <0) {

progress =0;

        }

//        if (progress > mMax) {

//            progress = mMax;

//        }

        this.mProgress = progress;

        invalidate();

    }

public double getProgress() {

return mProgress;

    }

public double getMax() {

return mMax;

    }

private void initStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.CornerProgressBar, defStyleAttr, defStyleRes);

        mMax = array.getInt(R.styleable.CornerProgressBar_barMax, DEFAULT_MAX);

        mProgress = array.getInt(R.styleable.CornerProgressBar_barProgress, DEFAULT_PROGRESS);

        mColor0 = array.getColor(R.styleable.CornerProgressBar_barStartColor, DEFAULT_START_COLOR);

        mColor1 = array.getColor(R.styleable.CornerProgressBar_barStartColor, DEFAULT_END_COLOR);

        mColorBackgroud = array.getColor(R.styleable.CornerProgressBar_barBackColor, DEFAULT_BACKGROUD_COLOR);

        mTextColor = array.getColor(R.styleable.CornerProgressBar_barTextColor, DEFAULT_TEXT_COLOR);

        final float scale = getResources().getDisplayMetrics().density;

        float defaultTextSize =DEFAULT_TEXT_SIZE * scale +0.5f;

        mTextSize = array.getDimension(R.styleable.CornerProgressBar_barTextSize, defaultTextSize);

        array.recycle();

    }

private void initPaint() {

paint =new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

        paint.setStrokeWidth(getHeight());

        paint.setStrokeCap(Paint.Cap.ROUND);

        mBackgroudPaint =new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

        mBackgroudPaint.setColor(mColorBackgroud);

        mBackgroudPaint.setStrokeCap(Paint.Cap.ROUND);

        mode = PorterDuff.Mode.SRC_IN;

    }

@Override

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

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

        paint.setStrokeWidth(getHeight());

        mBackgroudPaint.setStrokeWidth(getHeight());

        invalidate();

    }

/**

    * 计算进度宽度

    */

    private int calProgressWidth() {

int width = getWidth();

        int progressWidth = (int) (mProgress /mMax * width);

        return progressWidth <= width ? progressWidth : width;//最大宽度不超过进度条总宽度 但数值可以超过最大数值

    }

public double getProgressPercent() {

return mProgress /mMax;

    }

@Override

    protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

        if (getWidth() ==0) {

return;

        }

float radius = (float) getHeight() /2;

        float offsetBackgroundWidth = getWidth() - radius;

        int progressWidth = calProgressWidth();

        float offsetProgressWidth = progressWidth - radius;//需要绘制的长度是控件长度减去左右半圆的宽,即结束x坐标为宽-半径

        canvas.drawLine(radius, radius, offsetBackgroundWidth, radius, mBackgroudPaint);

        LinearGradient linearGradient =new LinearGradient(0, 0, progressWidth, 0, mColor0, mColor1, Shader.TileMode.CLAMP);

        paint.setShader(linearGradient);

        if (progressWidth > getHeight()) {

canvas.drawLine(radius, radius, offsetProgressWidth, radius, paint);

        }else {

Canvas canvas2 =new Canvas();

            int savedState = canvas.save();

            canvas2.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));//抗锯齿

            Bitmap bt = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

            canvas2.setBitmap(bt);

            canvas2.drawCircle(radius, radius, radius, paint);

            Bitmap bt2 = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

            canvas2.setBitmap(bt2);

            canvas2.drawCircle(progressWidth - radius, radius, radius, paint);

            canvas.saveLayer(0, 0, getWidth(), getHeight(), paint,

                    Canvas.ALL_SAVE_FLAG);

            canvas.drawBitmap(bt, 0, 0, paint);

            paint.setXfermode(new PorterDuffXfermode(mode));

            //注意这里savelayer的paint 是有叠加模式的!!!!!!!!!!!!!!!!!

            canvas.saveLayer(0, 0, getWidth(), getHeight(), paint,

                    Canvas.ALL_SAVE_FLAG);

            paint.setXfermode(null);//注意这里需要吧叠加模式制空 不然会与layer的空位图 进行叠加

            canvas.drawBitmap(bt2, 0, 0, paint);

            canvas.restoreToCount(savedState);

        }

drawProgressText(canvas);

    }

private void drawProgressText(Canvas canvas) {

String result;

        if (mMax !=0) {

BigDecimal progressDecimal = BigDecimal.valueOf(mProgress);

            BigDecimal maxDecimal = BigDecimal.valueOf(mMax);

            BigDecimal decimalHundred = BigDecimal.valueOf(100);

            BigDecimal decimalPercent = progressDecimal.divide(maxDecimal, 3, BigDecimal.ROUND_HALF_UP);

            BigDecimal decimalResult = decimalPercent.multiply(decimalHundred);

            result = decimalResult.setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString() +"%";

        }else {

result ="0.0%";

        }

Paint.FontMetrics fontMetrics =new Paint.FontMetrics();

        paint.setTextSize(mTextSize);

        paint.getFontMetrics(fontMetrics);

        paint.setShader(null);

        paint.setColor(mTextColor);

        float topOffset = (float) getHeight() /2 - (fontMetrics.descent + fontMetrics.ascent) /2;

        float textWidth =paint.measureText(result);

        float leftOffset = (((float) getWidth() - textWidth)) /2;

//        LogUtil.e("drawProgressText", "current=" + text + " width=" + getWidth() + " topOffset=" + topOffset + " textWidth=" + textWidth + " leftOffset=" + leftOffset);

        canvas.drawText(result, leftOffset, topOffset, paint);

    }

}


<declare-styleable name="CornerProgressBar">

    <attr name="barTextSize" format="dimension|reference" />

    <attr name="barTextColor" format="color|reference" />

    <attr name="barStartColor" format="color|reference" />

    <attr name="barEndColor" format="color|reference" />

    <attr name="barBackColor" format="color|reference" />

    <attr name="barMax" format="integer|reference" />

    <attr name="barProgress" format="integer|reference" />

</declare-styleable>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355