自定義圓形彩色進度條 帶陰影效果

2020-02-12
github地址:https://github.com/gggjmhjmh/OldWangDiy

效果:


B.png

C.png

C.png

功能:
1、可設(shè)置進度條底色、進度值的顏色,文字顏色
2谷扣、可動畫設(shè)置進度值,就是設(shè)置進度值時可以有動畫
3捎琐、可定義陰影:陰影的模糊半徑会涎、偏移和顏色
4、設(shè)置彩色進度值
知識點:
1野哭、通過canvas畫文字在塔、畫圓、畫弧形
2拨黔、根據(jù)控件畫圓的大谢桌!(控件多大,圓就畫多大)
3篱蝇、畫筆Paint設(shè)置陰影贺待;陰影所占的寬度對繪制的影響的處理
4、給Paint設(shè)置著色器(實現(xiàn)漸變)零截,以及著色的起始位置的旋轉(zhuǎn)

代碼:

package com.app.yf.myapplication.view.activity.diy;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

public class MyCircleProgessView extends View {
    private static final String TAG = "MyView";
    private Context context;

    //圓的大小 , 控件的寬麸塞、高 , 誰小取誰的值
    private int circleSize;

    //進度值 百分比
    private float progress = 58;

    //線的寬度 px
    private int strokeWidth = 30;

    private int textSize = 42;  //文字大小

    private int textColor = Color.BLUE;  //文字顏色
    private int circleColor = Color.GRAY; //圓的顏色
    private int progessColor = Color.GREEN; //進度顏色
    private int[] progessColors; //進度值的漸變顏色

    private boolean isRound = true; //端點是否為圓頭

    /**
     * 設(shè)置端點是否為圓頭
     *
     * @param isRound
     */
    public void setIsRound(boolean isRound) {
        this.isRound = isRound;
    }

    /**
     * 設(shè)置文字大小 dp
     *
     * @param textSize
     */
    public void setTextSize(int textSize) {
        this.textSize = dip2px(context, textSize);
    }

    public void setTextColor(int textColor) {
        setColor(textColor, circleColor, progessColor);
    }

    public void setCircleColor(int circleColor) {
        setColor(textColor, circleColor, progessColor);
    }

    public void setProgessColor(int progessColor) {
        setColor(textColor, circleColor, progessColor);
    }

    public void setColor(int textColor, int circleColor, int progessColor) {
        this.textColor = textColor;
        this.circleColor = circleColor;
        this.progessColor = progessColor;
//        invalidate();
    }


    /**
     * 設(shè)置進度值漸變顏色
     *
     * @param progessColors 顏色數(shù)組
     * @param isRound       端點是否為圓頭, 注:為圓頭時涧衙,起點的圓頭是結(jié)尾的顏色哪工。開始和結(jié)束的顏色設(shè)為一樣的就沒問題
     */
    public void setProgessColor(int[] progessColors, boolean isRound) {
        this.progessColors = progessColors;
        this.isRound = isRound;
    }

    /**
     * 設(shè)置線的寬度
     *
     * @param strokeWidth dp
     */
    public void setStrokeWidth(int strokeWidth) {
        this.strokeWidth = dip2px(context, strokeWidth);
    }

    private int shadowWidth = 15; //陰影一邊所占的寬度 px

    /**
     * 設(shè)置陰影
     *
     * @param radius      陰影模糊半徑 dp
     * @param dx          偏移x  dp
     * @param dy          偏移y  dp
     * @param shadowColor 陰影顏色
     */
    public void setShadow(float radius, float dx, float dy, int shadowColor) {
        drawCirclePaint.setShadowLayer(dip2px(context, radius), dip2px(context, dx), dip2px(context, dy), shadowColor);
        //陰影所一邊所占的寬度 = 模糊半徑 + 模糊半徑/2 + x與y的偏移最大值
        shadowWidth = dip2px(context, radius + radius / 2 + Math.max(dx, dy));
    }

    public float getProgress() {
        return progress;
    }

    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }

    /**
     * 動畫效果設(shè)置進度值
     *
     * @param progress 進度值
     * @param duration 動畫時間
     */
    public void setProgressAnimation(float progress, long duration) {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "progress", 0, progress);
        objectAnimator.setDuration(duration);
        objectAnimator.start();
    }


    public MyCircleProgessView(Context context) {
        super(context);
        init(context);
    }

    public MyCircleProgessView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        this.context = context;
        setStrokeWidth(10); //設(shè)置默認為10dp
        // 初始化畫筆
        initPaint();
    }

    private Paint mPaint; //畫文字及進度值的畫筆
    private Paint drawCirclePaint; //畫底部圓的畫筆,之所以要再弄個畫筆弧哎,是為了方便設(shè)置陰影

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.CENTER); //對齊方式: 左中右

        drawCirclePaint = new Paint();
        drawCirclePaint.setAntiAlias(true);
        drawCirclePaint.setStyle(Paint.Style.STROKE); //設(shè)置空心
        drawCirclePaint.setStrokeWidth(strokeWidth); //設(shè)置筆畫寬度
        drawCirclePaint.setColor(circleColor); //設(shè)置顏色
        setShadow(2, 1, 2, Color.RED); //設(shè)置陰影
    }


    /**
     * 根據(jù)手機的分辨率從 dp 的單位 轉(zhuǎn)成為 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.i(TAG, "onMeasure: width: " + widthMeasureSpec);
        Log.i(TAG, "onMeasure: height: " + heightMeasureSpec);
        Log.i(TAG, "onMeasure: getWidth: " + getWidth());
        Log.i(TAG, "onMeasure: getheight: " + getHeight());
        Log.i(TAG, "onMeasure: getMeasuredWidth: " + getMeasuredWidth());
        Log.i(TAG, "onMeasure: getMeasuredHeight: " + getMeasuredHeight());

        //圓的大小 , 控件的寬雁比、高 , 誰小取誰的值
        circleSize = Math.min(getMeasuredHeight(), getMeasuredWidth());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();

//        canvas.drawColor(Color.RED);

        //畫文字
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(textColor);
        mPaint.setTextSize(textSize);
        float y = mPaint.getFontSpacing() / 3; //行距:第一行的基線與第二行的基線的距離
        canvas.drawText((int) progress + "%", circleSize / 2, circleSize / 2 + y, mPaint);

        //畫進度條底部圓
        canvas.drawCircle(circleSize / 2, circleSize / 2, circleSize / 2 - strokeWidth / 2 - shadowWidth, drawCirclePaint);
        /* - strokeWidth / 2  畫的線為內(nèi)邊距效果,不然一半的線寬就越出范圍了 */

        mPaint.setStyle(Paint.Style.STROKE); //設(shè)置空心
        mPaint.setStrokeWidth(strokeWidth); //設(shè)置筆畫寬度
        mPaint.setColor(progessColor);
        //如果設(shè)置了漸變
        if (progessColors != null) {
            //設(shè)置著色器撤嫩,畫漸變
            Shader sweepGradient = new SweepGradient(circleSize / 2f, circleSize / 2f, progessColors, null);

            //設(shè)置起始位置旋轉(zhuǎn)90度
            Matrix matrix = new Matrix();
            matrix.setRotate(90, circleSize / 2, circleSize / 2);
            sweepGradient.setLocalMatrix(matrix);

            mPaint.setShader(sweepGradient);
        }

        //設(shè)置端點是否為圓頭
        mPaint.setStrokeCap(isRound ? Paint.Cap.ROUND : Paint.Cap.BUTT);

        //畫進度條的進度值弧線偎捎。  線的寬度/2 為超出本身大小的寬度(因為畫的線不是內(nèi)、外邊距序攘,而是居中) 再加上陰影要占用的寬度
        canvas.drawArc(strokeWidth / 2 + shadowWidth, strokeWidth / 2 + shadowWidth, circleSize - strokeWidth / 2 - shadowWidth, circleSize - strokeWidth / 2 - shadowWidth, 90, progress * 3.6f, false, mPaint);

        mPaint.setShader(null); //清除著色器

        canvas.restore();
    }

}

使用:

  MyCircleProgessView myView = findViewById(R.id.myView);

  myView.setShadow(5, 0, 0, Color.RED); // 設(shè)置陰影
//設(shè)置彩色進度值
  myView.setProgessColor(new int[]{Color.GREEN, Color.BLUE, Color.RED, Color.GREEN}, true);
//動畫效果設(shè)置進度值
  myView .setProgressAnimation(90,800);

github地址:https://github.com/gggjmhjmh/OldWangDiy

完事兒

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茴她,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子程奠,更是在濱河造成了極大的恐慌丈牢,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梦染,死亡現(xiàn)場離奇詭異赡麦,居然都是意外死亡朴皆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門泛粹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遂铡,“玉大人,你說我怎么就攤上這事晶姊“墙樱” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵们衙,是天一觀的道長钾怔。 經(jīng)常有香客問我,道長蒙挑,這世上最難降的妖魔是什么宗侦? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮忆蚀,結(jié)果婚禮上矾利,老公的妹妹穿的比我還像新娘。我一直安慰自己馋袜,他們只是感情好男旗,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欣鳖,像睡著了一般察皇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泽台,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天什荣,我揣著相機與錄音,去河邊找鬼怀酷。 笑死溃睹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的胰坟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼泞辐,長吁一口氣:“原來是場噩夢啊……” “哼笔横!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咐吼,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吹缔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锯茄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厢塘,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡茶没,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了晚碾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抓半。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖格嘁,靈堂內(nèi)的尸體忽然破棺而出笛求,到底是詐尸還是另有隱情,我是刑警寧澤糕簿,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布探入,位于F島的核電站,受9級特大地震影響懂诗,放射性物質(zhì)發(fā)生泄漏蜂嗽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一殃恒、第九天 我趴在偏房一處隱蔽的房頂上張望植旧。 院中可真熱鬧,春花似錦芋类、人聲如沸隆嗅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胖喳。三九已至,卻和暖如春贮竟,著一層夾襖步出監(jiān)牢的瞬間丽焊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工咕别, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留技健,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓惰拱,卻偏偏與公主長得像雌贱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子偿短,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容