Android Material Design風格自定義View1——弧形滾動百分比的圓形進度條

————自定義PercentProgressBar繼承自View套硼,五個自定義屬性progressWidth(進度條寬度)垫毙,progressBackColor(進度條背景色)霹疫,progressFrontColor(進度條前景色),percentTextSize(百分比文字大凶劢妗)丽蝎,percentTextColor(百分比文字顏色),可在布局文件中直接使用毫痕。

如需下載源碼征峦,請訪問
https://github.com/fengchuanfang/PercentProgressBarDemo1

文章原創(chuàng),轉(zhuǎn)載請注明出處:
自定義Material Design風格帶滾動百分比的圓形進度條

運行效果如下:


percent_progress_bar_01.gif
percent_progress_bar_02.gif

引入步驟

訪問git地址下載源碼,拷貝com.feng.edward.percentprogressbardemo1.view包下的PercentProgressBar類和attrs.xml中的PercentProgressBar的五項屬性

或者新建java類PercentProgressBar消请,直接復制以下代碼栏笆,再將attrs.xml中的代碼放入項目對應attrs文件中。便可以在布局文件中直接使用臊泰。

package com.feng.edward.percentprogressbardemo1.view;

import com.feng.edward.percentprogressbardemo1.R;
import com.feng.edward.percentprogressbardemo1.util.DimensionUtils;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

/**
 * 功能描述:帶滾動百分比的圓形進度條
 *
 * @author (作者) edward(馮豐楓)
 * @link http://www.reibang.com/u/f7176d6d53d2
 * 創(chuàng)建時間: 2018/4/16 0016
 */
public class PercentProgressBar extends View {
    private Paint
            progressBackPaint, //進度條背景畫筆
            progressFrontPaint,//進度條前景畫筆
            percentTextPaint;  //百分比文字畫筆

    private RectF
            progressRectF,//進度條圓弧所在的矩形
            percentTextRectF;//百分比文字所在的矩形

    private Path percentTextPath;   //百分比文字所在的路徑
    private float percentTextRadius;  //百分比文字圓弧的半徑
    private float progressPaintWidth; //進度條畫筆的寬度

    private int percentProgress;//百分比進度(0 ~ 100)


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

    public PercentProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.PercentProgressBar, defStyleAttr, 0);

        //初始化進度條畫筆的寬度
        progressPaintWidth = attributes.getDimension(R.styleable.PercentProgressBar_progressWidth, DimensionUtils.dip2px(context, 10));
        //初始化百分比文字的大小
        float percentTextPaintSize = attributes.getDimensionPixelSize(R.styleable.PercentProgressBar_percentTextSize, DimensionUtils.sp2px(context, 10));
        int progressBackColor = attributes.getColor(R.styleable.PercentProgressBar_progressBackColor, 0xffaaaaaa);
        int progressFrontColor = attributes.getColor(R.styleable.PercentProgressBar_progressFrontColor, 0xffFF4081);
        int percentTextColor = attributes.getColor(R.styleable.PercentProgressBar_percentTextColor, 0xffff0077);
        attributes.recycle();
        //初始化進度條背景畫筆
        progressBackPaint = new Paint();
        progressBackPaint.setColor(progressBackColor);
        progressBackPaint.setStrokeWidth(progressPaintWidth);
        progressBackPaint.setAntiAlias(true);
        progressBackPaint.setStyle(Paint.Style.STROKE);

        //初始化進度條前景畫筆
        progressFrontPaint = new Paint();
        progressFrontPaint.setColor(progressFrontColor);
        progressFrontPaint.setStrokeWidth(progressPaintWidth);
        progressFrontPaint.setAntiAlias(true);
        progressFrontPaint.setStyle(Paint.Style.STROKE);

        //初始化百分比文字畫筆
        percentTextPaint = new Paint();
        percentTextPaint.setColor(percentTextColor);
        percentTextPaint.setTextSize(percentTextPaintSize);// 設置文字畫筆的尺寸(px)
        percentTextPaint.setAntiAlias(true);
        percentTextPaint.setStyle(Paint.Style.STROKE);
        percentTextPaint.setTypeface(Typeface.DEFAULT_BOLD);

        //初始化百分比文字路徑
        percentTextPath = new Path();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMeasuredWidth();// 獲取控件的layout_width
        int height = getMeasuredHeight(); // 獲取控件的layout_height
        //獲取內(nèi)切圓圓心坐標
        int centerX = width / 2;
        int centerY = height / 2;
        int radius = Math.min(width, height) / 2;//獲取控件內(nèi)切圓的半徑

        Rect rect = new Rect();
        percentTextPaint.getTextBounds("100%", 0, "100%".length(), rect);//獲取最大百分比文字的高度
        int textHeight = rect.height();
        //比較進度條的寬度和百分比文字的高度蛉加,去兩者中較大者,用以計算進度條的半徑缸逃,保證精度條和百分比文字互為中心
        float radiusArc = radius - (progressPaintWidth > textHeight ? progressPaintWidth / 2 : textHeight / 2);

        //初始化進度條圓弧所在的矩形
        progressRectF = new RectF();
        progressRectF.left = centerX - radiusArc;
        progressRectF.top = centerY - radiusArc;
        progressRectF.right = centerX + radiusArc;
        progressRectF.bottom = centerY + radiusArc;

        percentTextRadius = radiusArc - textHeight / 2;//計算百分比文字圓弧的半徑

        //初始化百分比文字路徑所在的矩形
        percentTextRectF = new RectF();
        percentTextRectF.left = centerX - percentTextRadius;
        percentTextRectF.top = centerY - percentTextRadius;
        percentTextRectF.right = centerX + percentTextRadius;
        percentTextRectF.bottom = centerY + percentTextRadius;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //繪制進度框的背景
        canvas.drawArc(progressRectF, 0, 360, false, progressBackPaint);
        //繪制進度框的前景(從圓形最高點中間针饥,順時針繪制)
        canvas.drawArc(progressRectF, -90, percentProgress / 100.0f * 360, false, progressFrontPaint);
        //百分比文字
        String text = percentProgress + "%";
        //計算百分比文字的弧跨度
        float sweepAngle = (float) (percentTextPaint.measureText(text) * 360 / (2 * Math.PI * percentTextRadius));
        //初始化百分比文字的弧路徑
        percentTextPath.addArc(percentTextRectF, percentProgress * 3.6f - 90.0f - sweepAngle / 2.0f, sweepAngle);
        //繪制弧形百分比文字
        canvas.drawTextOnPath(text, percentTextPath, 0, 0, percentTextPaint);
        //路徑重置
        percentTextPath.reset();
    }

    /**
     * 設置當前百分比進度
     */
    public void setPercentProgress(int percentProgress) {
        this.percentProgress = percentProgress;
        invalidate();
    }
}


attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PercentProgressBar">
        <attr name="progressWidth" format="dimension"/><!--進度條寬度-->
        <attr name="progressBackColor" format="color"/><!--進度條背景色-->
        <attr name="progressFrontColor" format="color"/><!--進度條前景色-->
        <attr name="percentTextSize" format="dimension"/><!--百分比文字的大小-->
        <attr name="percentTextColor" format="color"/><!--百分比文字的顏色-->
    </declare-styleable>
</resources>

在布局文件dialog_layout.xml中使用如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <com.feng.edward.percentprogressbardemo1.view.PercentProgressBar
            android:id="@+id/circle_percent_view"
            android:layout_width="120dp"
            android:layout_height="150dp"
            android:focusable="true"
            android:clickable="true"
            android:layout_gravity="center"
            app:percentTextSize="10sp"
            app:percentTextColor="#ff0000"
            app:progressFrontColor="@color/colorPrimary"
            app:progressWidth="2dp"
    />
    <com.feng.edward.percentprogressbardemo1.view.CircleImageView
            android:id="@+id/circle_image_view"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:alpha="0"
            android:layout_gravity="center"
            android:src="@mipmap/jianshu_logo"/>
</FrameLayout>

如果想了解CircleImageView,請訪問上篇文章史上最簡潔高效的圓形ImageView

自定義進度條彈出框ProgressDialog需频,代碼如下:

package com.feng.edward.percentprogressbardemo1.dialog;

import com.feng.edward.percentprogressbardemo1.R;
import com.feng.edward.percentprogressbardemo1.view.CircleImageView;
import com.feng.edward.percentprogressbardemo1.view.PercentProgressBar;

import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;

/**
 * 功能描述:
 *
 * @author (作者) edward(馮豐楓)
 * @link http://www.reibang.com/u/f7176d6d53d2
 * 創(chuàng)建時間: 2018/4/17 0017
 */
public class ProgressDialog {
    private final Dialog mDialog;
    private final PercentProgressBar mPercentProgress;
    private final CircleImageView mCircleImageView;

    public ProgressDialog(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
        mPercentProgress = view.findViewById(R.id.circle_percent_view);
        mCircleImageView = view.findViewById(R.id.circle_image_view);
        mDialog = new Dialog(context, R.style.ProgressDialogTheme);
        mDialog.setContentView(view);
        mDialog.setCanceledOnTouchOutside(true);
    }

    public void show() {
        mDialog.show();
    }

    public void setPercentProgress(int percentProgress) {
        mPercentProgress.setPercentProgress(percentProgress);
        mCircleImageView.setAlpha((float) percentProgress / 100);
    }

    public void dismiss() {
        mDialog.dismiss();
    }
}

在MainActivity中使用如下:

package com.feng.edward.percentprogressbardemo1;

import com.feng.edward.percentprogressbardemo1.dialog.ProgressDialog;
import com.feng.edward.percentprogressbardemo1.util.IPublishProgress;
import com.feng.edward.percentprogressbardemo1.util.MyAsyncTask;

import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements MyAsyncTask.IIsViewActive {
    private ProgressDialog mProgressDialog;
    private TextView mainText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProgressDialog = new ProgressDialog(this);
        mainText = findViewById(R.id.main_text);
        mainText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mainText.setText(R.string.downloading);
                downLoad();
            }
        });
    }

    private void downLoad() {
        MyAsyncTask.<Void, Integer, Void>newBuilder()
                .setPreExecute(new MyAsyncTask.IPreExecute() {
                    @Override
                    public void onPreExecute() {
                        mProgressDialog.show();
                    }
                })
                .setDoInBackground(new MyAsyncTask.IDoInBackground<Void, Integer, Void>() {
                    @Override
                    public Void doInBackground(IPublishProgress<Integer> publishProgress, Void... voids) {
                        try {
                            for (int i = 0; i <= 100; i++) {
                                Thread.sleep(100);
                                publishProgress.showProgress(i);
                            }
                        } catch (Exception ignore) {
                        }
                        return null;
                    }
                })
                .setProgressUpdate(new MyAsyncTask.IProgressUpdate<Integer>() {
                    @Override
                    public void onProgressUpdate(Integer... values) {
                        mProgressDialog.setPercentProgress(values[0]);
                    }
                })
                .setViewActive(this)
                .setPostExecute(new MyAsyncTask.IPostExecute<Void>() {
                    @Override
                    public void onPostExecute(Void aVoid) {
                        mProgressDialog.dismiss();
                        mainText.setText(R.string.download_finish);
                    }
                })
                .start();
    }

    @Override
    public boolean isViewActive() {
        return !(isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && isDestroyed()));
    }


}

如想了解MyAsyncTask丁眼,請訪問Android AsyncTask 優(yōu)化封裝

activity_mian.xml中的代碼如下:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    <TextView
            android:id="@+id/main_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/main_text"
            android:layout_centerInParent="true"/>
</RelativeLayout>

在布局文件中,設置百分比文字大小percentTextSize和進度條寬度progressWidth昭殉,可呈現(xiàn)以下兩種不同的效果:


percent_progress_bar_text_inside.jpg

對應屬性為:

   app:percentTextSize="10sp"
            app:percentTextColor="#ff0000"
            app:progressWidth="12dp"
            app:progressFrontColor="#3F51B5"
            app:progressBackColor="#ffffff"
percent_progress_bar_text_out.png

對應屬性為:

          app:percentTextSize="10sp"
            app:percentTextColor="#ff0000"
            app:progressWidth="2dp"
            app:progressFrontColor="#3F51B5"
            app:progressBackColor="#ffffff"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苞七,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子挪丢,更是在濱河造成了極大的恐慌蹂风,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乾蓬,死亡現(xiàn)場離奇詭異惠啄,居然都是意外死亡,警方通過查閱死者的電腦和手機任内,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門撵渡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人族奢,你說我怎么就攤上這事姥闭。” “怎么了越走?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵棚品,是天一觀的道長靠欢。 經(jīng)常有香客問我,道長铜跑,這世上最難降的妖魔是什么门怪? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮锅纺,結(jié)果婚禮上掷空,老公的妹妹穿的比我還像新娘。我一直安慰自己囤锉,他們只是感情好坦弟,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著官地,像睡著了一般酿傍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驱入,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天赤炒,我揣著相機與錄音,去河邊找鬼亏较。 笑死莺褒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的雪情。 我是一名探鬼主播遵岩,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼巡通!你這毒婦竟也來了旷余?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤扁达,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蠢熄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跪解,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年签孔,在試婚紗的時候發(fā)現(xiàn)自己被綠了叉讥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡饥追,死狀恐怖图仓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情但绕,我是刑警寧澤救崔,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布惶看,位于F島的核電站,受9級特大地震影響六孵,放射性物質(zhì)發(fā)生泄漏纬黎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一劫窒、第九天 我趴在偏房一處隱蔽的房頂上張望本今。 院中可真熱鬧,春花似錦主巍、人聲如沸冠息。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逛艰。三九已至,卻和暖如春檬果,著一層夾襖步出監(jiān)牢的瞬間瓮孙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工选脊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留杭抠,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓恳啥,卻偏偏與公主長得像偏灿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子钝的,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

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