1. 程式人生 > >Android花樣loading進度條(二)-簡單環形進度條

Android花樣loading進度條(二)-簡單環形進度條

背景

Android花樣loading進度條系列文章主要講解如何自定義所需的進度條,包括水平、圓形、環形、圓弧形、不規則形狀等。
本篇我們從圓形進度條講起,講簡單形式的環形進度條,只有進度色彩,沒有進度文字,主要是使用Canvas繪製圓和圓弧。

效果

先上圖看效果,這裡有6個進度條,樣式上有微妙區別,基本都屬於一個類別的進度條了。
進度條效果
6個進度條基本上分為3類:

  1. 背景條與進度條同寬度;
  2. 背景條與進度條不同寬度的,進度條略小於背景條,呈現出層次效果;
  3. 圓環式或圓餅式的效果區別。

我們以第1種作為基本的形式來講述,需要準備的知識點有:

  • 自定義控制元件的座標軸;
  • Canvas圓環、圓弧繪製方法;
  • 自定義屬性;
  • Handler訊息處理機制。

其中涉及到的每一個知識點都有不少內容,我們只提涉及到本次使用的知識內容。

知識點

1、自定義控制元件的座標軸
Android系統的座標軸示意圖如下,座標軸原點O點,可以理解為螢幕的左上角。(圖片來自網路)
這裡寫圖片描述
在自定義控制元件View的繪製過程中,O點則相當於自定義控制元件View區域的左上角,可以理解為View裡面有一個座標系,座標系的原點O在本View所在位置的左上角。

2、Canvas圓環、圓弧繪製方法

1)Canvas圓環繪製方法

android.graphics.Canvas#drawCircle(float cx, float cy, float radius, Paint paint)

此方法可繪製一個圓形,其中的引數:

  • cx:圓心O的x軸座標;
  • cy:圓心O的y軸座標;
  • radius:圓的半徑;
  • paint:繪製圖形所使用的畫筆。

如果畫筆使用空心模式,則繪製出一個圓環,如效果動畫中的圖1;如果畫筆使用實心模式,則繪製出一個圓餅,如效果動畫中的圖3。

2)Canvas圓弧繪製方法

android.graphics.Canvas#drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

此方法可以繪製一個圓弧,其中:

  • oval:是一個矩形的區域,用以定義圓弧的外邊緣;
  • sweepAngle:是圓弧掃過的角度;
  • userCenter:用以控制圓弧在繪畫的時候,是否經過圓心;
  • paint:繪製圖形所使用的畫筆。

3、自定義屬性
在自定義控制元件中我們免不了要用到各種顏色值、大小等引數。把這些值寫死在程式碼裡使用者使用時就無法靈活控制,我們需要將屬性的值定義放到使用者端,以實現程式碼的解耦。

此時就需要一些自定義屬性來支援我們對控制元件的屬性進行定義和使用。如圓環的寬度,我們可以定義為roundWidth,值使用dimension型別。

自定義屬性需要在專案res-values目錄下新建或修改attrs.xml檔案,具體使用方法可百度查閱,示例在下文給出。
attrs.xml檔案
4、Handler訊息處理機制
Android中不允許子執行緒(我們寫個Thread執行時就運算元執行緒)在主執行緒(可以理解為Activity)中更新UI介面的,比如設定text值、更改大小等,都是不行的。所以我們需要Handler中的訊息機制來實現線上程中更新介面。
因為進度條控制元件在使用的時候都是配合著進度變化不斷變更的,通常進度變化都在子執行緒中來做,當progress值變化時,通過給Handler傳送一個訊息,攜帶progress值,Handler在接收訊息後處理時將progress更新到UI介面上即可。
Handler的技術原理和具體使用要點可以百度查閱,這裡不展開了。下文有使用示例。

自定義屬性

有了上一段的知識點講解後,我們可以著實來繪製自定義控制元件了。繪製之前,我們需要了解下本控制元件有哪些屬性是需要單獨定義下,以供使用者使用時靈活修改的。初步考慮有:

  • 列表內容
  • 圓環的顏色;
  • 圓環的寬度;
  • 圓環上的進度顏色;
  • 圓環上的進度寬度等。

為此我們定義attrs.xml配置檔案的內容為:

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <!--簡單環形進度-->
    <declare-styleable name="SimpleRoundProgress">
        <!--圓環顏色-->
        <attr name="srp_roundColor" format="color" />
        <!--圓環的寬度-->
        <attr name="srp_roundWidth" format="dimension" />
        <!--圓環上的進度顏色-->
        <attr name="srp_progressColor" format="color" />
        <!--圓環上的進度寬度-->
        <attr name="srp_progressWidth" format="dimension" />
        <!--進度值的最大值,一般為100-->
        <attr name="srp_max" format="integer" />
        <!--開始角度,指定進度初始點的繪製位置-->
        <attr name="srp_startAngle" format="integer" />
        <!--樣式,空心還是實心-->
        <attr name="srp_style">
            <enum name="STROKE" value="0" />
            <enum name="FILL" value="1" />
        </attr>
    </declare-styleable>
</resources>

有了屬性定義後,我們在繪製的時候結合這些屬性就可以按設定值繪製了。

進度條圖形繪製

Android自定義圖形都需繼承View類,所以我們定義的進度條SimpleRoundProgress繼承View後,形如:

/**
 * 簡單環形進度條
 */
public class SimpleRoundProgress extends View {
...
}

1、讀取自定義屬性,供後續使用

在SimpleRoundProgress類中定義一些成員變數,用來裝載自定義屬性的值,一會通過屬性載入類來載入屬性值。成員變數的定義有:

public class SimpleRoundProgress extends View {
    private Paint paint; // 畫筆物件的引用
    private int roundColor; // 圓環的顏色
    private float roundWidth; // 圓環的寬度
    private int progressColor; // 圓環進度的顏色
    private float progressWidth; // 圓環進度的寬度
    private int max; // 最大進度
    private int style; // 進度的風格,實心或者空心
    private int startAngle; // 進度條起始角度
    public static final int STROKE = 0; // 樣式:空心
    public static final int FILL = 1; // 樣式:實心
    private int progress; // 當前進度
    ...
}

在建構函式中讀取使用者自定義屬性被賦予的值。

public class SimpleRoundProgress extends View {
    ...
    public SimpleRoundProgress(Context context) {
        this(context, null);
    }

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

    public SimpleRoundProgress(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        paint = new Paint();

        // 讀取自定義屬性的值
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.SimpleRoundProgress);

        // 獲取自定義屬性和預設值
        roundColor = mTypedArray.getColor(R.styleable.SimpleRoundProgress_srp_roundColor, Color.RED);
        roundWidth = mTypedArray.getDimension(R.styleable.SimpleRoundProgress_srp_roundWidth, 5);
        progressColor = mTypedArray.getColor(R.styleable.SimpleRoundProgress_srp_progressColor, Color.GREEN);
        progressWidth = mTypedArray.getDimension(R.styleable.SimpleRoundProgress_srp_progressWidth, roundWidth);
        max = mTypedArray.getInteger(R.styleable.SimpleRoundProgress_srp_max, 100);
        style = mTypedArray.getInt(R.styleable.SimpleRoundProgress_srp_style, 0);
        startAngle = mTypedArray.getInt(R.styleable.SimpleRoundProgress_srp_startAngle, 90);

        mTypedArray.recycle();
    }
    ...
}

2、繪製進度環

繪製進度環主要有兩步:繪製背景環、繪製前景進度環。程式碼均寫在View的onDraw方法體內:

public class SimpleRoundProgress extends View {
    ...
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        ...
    }
    ...
}

1)繪製背景環

因為是圓環型進度條,圓在繪製時圓心位置在整個控制元件的正中心,所以背景環的繪製程式碼:

int centerX = getWidth() / 2; // 獲取圓心的x座標
int radius = (int) (centerX - roundWidth / 2); // 圓環的半徑

// step1 畫最外層的大圓環
paint.setStrokeWidth(roundWidth); // 設定圓環的寬度
paint.setColor(roundColor); // 設定圓環的顏色
paint.setAntiAlias(true); // 消除鋸齒
// 設定畫筆樣式
switch (style) {
    case STROKE:
        paint.setStyle(Paint.Style.STROKE);
        break;
    case FILL:
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        break;
}
canvas.drawCircle(centerX, centerX, radius, paint); // 畫出圓環

作兩點說明:

  • 圓心的x/y軸座標值都是getWidth() / 2(一般寬高一樣);
  • paint.setStyle可以設定畫筆是空心型還是實心型,對應的效果就是圓環或圓餅。

2)繪製前景進度環

前景進度環中有涉及到進度的計算,以總進度值為100為例,當進度在50時,如果是橫向進度條應該繪製整個控制元件一半的進度長度,如果是圓環型進度條,應該繪製180度的圓環。依次類推,進度環的角度計算為:

int sweepAngle = 360 * progress / max; // 計算進度值在圓環所佔的角度

前景進度環的繪製程式碼為:

// step2 畫圓弧-畫圓環的進度
paint.setStrokeWidth(progressWidth); // 設定畫筆的寬度使用進度條的寬度
paint.setColor(progressColor); // 設定進度的顏色
RectF oval = new RectF(centerX - radius , centerX - radius , centerX + radius , centerX + radius ); // 用於定義的圓弧的形狀和大小的界限

int sweepAngle = 360 * progress / max; // 計算進度值在圓環所佔的角度
// 根據進度畫圓弧
switch (style) {
    case STROKE:
        // 空心
        canvas.drawArc(oval, startAngle, sweepAngle, false, paint);
        break;
    case FILL:
        // 實心
        canvas.drawArc(oval, startAngle, sweepAngle, true, paint);
        break;
}

至此,進度條介面繪製就完成了,整個SimpleRoundProgress的程式碼為:

package com.dommy.loading.widget;

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.util.AttributeSet;
import android.view.View;

import com.dommy.loading.R;

/**
 * 簡單環形進度條
 */
public class SimpleRoundProgress extends View {
    private Paint paint; // 畫筆物件的引用
    private int roundColor; // 圓環的顏色
    private float roundWidth; // 圓環的寬度
    private int progressColor; // 圓環進度的顏色
    private float progressWidth; // 圓環進度的寬度
    private int max; // 最大進度
    private int style; // 進度的風格,實心或者空心
    private int startAngle; // 進度條起始角度
    public static final int STROKE = 0; // 樣式:空心
    public static final int FILL = 1; // 樣式:實心
    private int progress; // 當前進度

    public SimpleRoundProgress(Context context) {
        this(context, null);
    }

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

    public SimpleRoundProgress(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        paint = new Paint();

        // 讀取自定義屬性的值
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.SimpleRoundProgress);

        // 獲取自定義屬性和預設值
        roundColor = mTypedArray.getColor(R.styleable.SimpleRoundProgress_srp_roundColor, Color.RED);
        roundWidth = mTypedArray.getDimension(R.styleable.SimpleRoundProgress_srp_roundWidth, 5);
        progressColor = mTypedArray.getColor(R.styleable.SimpleRoundProgress_srp_progressColor, Color.GREEN);
        progressWidth = mTypedArray.getDimension(R.styleable.SimpleRoundProgress_srp_progressWidth, roundWidth);
        max = mTypedArray.getInteger(R.styleable.SimpleRoundProgress_srp_max, 100);
        style = mTypedArray.getInt(R.styleable.SimpleRoundProgress_srp_style, 0);
        startAngle = mTypedArray.getInt(R.styleable.SimpleRoundProgress_srp_startAngle, 90);

        mTypedArray.recycle();
    }

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

        int centerX = getWidth() / 2; // 獲取圓心的x座標
        int radius = (int) (centerX - roundWidth / 2); // 圓環的半徑

        // step1 畫最外層的大圓環
        paint.setStrokeWidth(roundWidth); // 設定圓環的寬度
        paint.setColor(roundColor); // 設定圓環的顏色
        paint.setAntiAlias(true); // 消除鋸齒
        // 設定畫筆樣式
        switch (style) {
            case STROKE:
                paint.setStyle(Paint.Style.STROKE);
                break;
            case FILL:
                paint.setStyle(Paint.Style.FILL_AND_STROKE);
                break;
        }
        canvas.drawCircle(centerX, centerX, radius, paint); // 畫出圓環

        // step2 畫圓弧-畫圓環的進度
        paint.setStrokeWidth(progressWidth); // 設定畫筆的寬度使用進度條的寬度
        paint.setColor(progressColor); // 設定進度的顏色
        RectF oval = new RectF(centerX - radius , centerX - radius , centerX + radius , centerX + radius ); // 用於定義的圓弧的形狀和大小的界限

        int sweepAngle = 360 * progress / max; // 計算進度值在圓環所佔的角度
        // 根據進度畫圓弧
        switch (style) {
            case STROKE:
                // 空心
                canvas.drawArc(oval, startAngle, sweepAngle, false, paint);
                break;
            case FILL:
                // 實心
                canvas.drawArc(oval, startAngle, sweepAngle, true, paint);
                break;
        }
    }

    /**
     * 設定進度的最大值
     * <p>根據需要,最大值一般設定為100,也可以設定為1000、10000等</p>
     *
     * @param max int最大值
     */
    public synchronized void setMax(int max) {
        if (max < 0) {
            throw new IllegalArgumentException("max not less than 0");
        }
        this.max = max;
    }

    /**
     * 獲取進度
     *
     * @return int 當前進度值
     */
    public synchronized int getProgress() {
        return progress;
    }

    /**
     * 設定進度,此為執行緒安全控制元件
     *
     * @param progress 進度值
     */
    public synchronized void setProgress(int progress) {
        if (progress < 0) {
            throw new IllegalArgumentException("progress not less than 0");
        }
        if (progress > max) {
            progress = max;
        }
        this.progress = progress;
        // 重新整理介面呼叫postInvalidate()能在非UI執行緒重新整理
        postInvalidate();
    }
}

環形進度條的使用

1、頁面控制元件配置

自定義控制元件在頁面中使用時,要使用類名來作為標籤,自定義屬性要通過名稱空間引入,整體如:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    ...
    tools:context="com.dommy.loading.SimpleRoundActivity">

    ...

    <com.dommy.loading.widget.SimpleRoundProgress
        android:id="@+id/srp_stroke_0"
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:layout_gravity="center"
        app:srp_max="100"
        app:srp_progressColor="@color/red_web"
        app:srp_roundColor="@color/pro_bg"
        app:srp_roundWidth="6dip"
        app:srp_startAngle="0"
        app:srp_style="STROKE" />

     ...
</LinearLayout>

介面編寫時的preview效果:
預覽效果
因為預設的progress為0,所以沒有前景進度顏色。

2、Activity程式碼控制

通過方法com.dommy.loading.widget.SimpleRoundProgress#setProgress就可以設定控制元件顯示指定的進度,如

srpStroke0.setProgress(39);

的效果為:
進度值39的效果
3、讓進度條動起來
當進度值為39時,如果要讓進度條動起來,可以讓進度值由0-39逐漸遞增顯示,通過一個執行緒控制進度值由0-39逐漸遞增的程式碼:

private void refresh() {
    final int percent = RandomUtil.getRandomPercent();
    new Thread(new Runnable() {
        Message msg = null;

        @Override
        public void run() {
            int start = 0;
            while (start <= percent) {
                msg = new Message();
                msg.what = MSG_REFRESH_PROGRESS;
                msg.arg1 = start;
                handler.sendMessage(msg);
                start++;
                try {
                    Thread.sleep(25);
                } catch (InterruptedException e) {
                }
            }
        }
    }).start();
}

這裡的percent是一個0-100的隨機數,當percent為39時就是我們上述的顯示效果。
當執行緒更新start變數值後,通知handler更新介面狀態,handler的定義為:

private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case MSG_REFRESH_PROGRESS:
                srpStroke0.setProgress(msg.arg1);
                break;
        }
    }

};

到這裡就實現了這樣的效果:
動態變動效果

改變效果

由於在圖形繪製時讀取了自定義屬性的值,如果需要更改環形進度的樣式,直接通過屬性值就可以修改了,比較方便。文章最初貼出來的效果動畫中的6個環形進度,就是通過樣式更改的出來的,其實質上都是SimpleRoundProgress控制元件。

如果覺得控制元件的自定義屬性不夠用,可以自行新增和使用,在此基礎上進行擴充套件,做出你想要的進度效果即可。

原始碼下載

相關推薦

Android花樣loading進度-簡單環形進度

背景 Android花樣loading進度條系列文章主要講解如何自定義所需的進度條,包括水平、圓形、環形、圓弧形、不規則形狀等。 本篇我們從圓形進度條講起,講簡單形式的環形進度條,只有進度色彩,沒有進度文字,主要是使用Canvas繪製圓和圓弧。 效果

html js 定製進度

 版本一遺留的問題: 1 滑鼠訊息捕獲問題(圖層覆蓋),解決方法設定圖層,影象層置於底層,滑鼠訊息層置於上層且設定為透明,完美解決。 function createMouseEventDiv() { do{ var parentCanvas

利用css3的animation實現點點點loading動畫效果

設置 str ack rdp 提交 ssi frame spin color box-shadow實現的打點效果 簡介 box-shadow理論上可以生成任意的圖形效果,當然也就可以實現點點點的loading效果了。 實現原理 html代碼,首先需要寫如下html代

aNDROID的語言設置

aid android andro list oid hao123 androi syn ron aNDROIDsYNCHRONIZED%E6%AD%BB%E9%94%81 http://music.baidu.com/songlist/495775942 http:/

個人開發—進度記錄

完成 設計 網頁 class 缺少 自己 整體 log 頁面 時間:2018/2/7 計劃:首頁的頂部標題部分,登錄,註冊,訂單,客服,定位,頁面跳轉 進行:手繪網頁整體布局與概括設計要點 完成:搜索素材 問題:概括設計要點時沒有清晰的思路,主要原因是具體的問題處理經驗不足

Android框架原始碼解析之OKhttp

原始碼在:https://github.com/square/okhttp 包實在是太多了,OKhttp核心在這塊https://github.com/square/okhttp/tree/master/okhttp 直接匯入Android Studio中即可。 基本使用:

Android IPC程序間通訊Messenger

Messenger實現程序間低併發即時通訊 Messenger是一種輕量級的IPC,底層實現是AIDL,即可認為Binder。通過在Message中攜帶Bundle進而實現程序之間傳遞資料。由於Messenger一次只能處理一個請求,因此服務端們不用考慮執行緒同步問題。 一,我們在服務端

Android Paging library詳解

重要API及原始碼分析 文章目錄 1.重要API介紹 1.1 DataSource 1.2 PageList 1.3 PagedListAdapter 2.原始碼解析 1.重要API介紹 Pagin

【安卓本卓】Android系統原始碼篇之Source Insight

Source Insight(以下簡稱SI) 一、SI簡介        SI是一款面向專案開發的程式編輯器和程式碼瀏覽器,它提供了一個檢視將分散在各個地方的程式碼匯合在一起形成一個虛擬的整體,供開發者方便地閱讀和編輯,如下圖所示。它已經持續維護了10多年了,旨在提

Android四大元件之ContentProvider

上節提到的四大元件之ContentProvider的簡單使用,在這篇文章中詳細的介紹其中的一些方法。 1.String getType(Uri uri)方法 首先看看官方對它的解釋: /** * Implement this to handle requests

Android-螢幕左右側滑

第二種方式我們介紹的是使用Android源生控制元件android.support.v4.widget.DrawerLayout來實現螢幕的左側滑和右側滑(其中包括點選側滑和手動滑動側滑),還可以用程式碼來控制開啟和關閉手動側滑: 先付上兩張效果圖供參考,如下: 首頁 左側滑

Android 有效地展示圖片Processing Bitmaps Off the UI Thread 在ui執行緒外處理bitmap

原文連結http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html 我們在上節課討論了BitmapFactory.decode系列的方法,但是如果原圖的資料需要從硬碟或者網路或者別的途徑而非記憶

Android學習筆記——View入門

1:什麼是View? (1)螢幕方塊內所顯示的內容,都可以稱為View或View的子類。(例如:TextView,Button等) 2:Activity怎麼獲取View的物件 (1)通過findById()方法獲取。(例如:TextView textView =  (Te

Android之訊息處理機制Handler的本質-Message和Looper到底是什麼?

目錄 Android之訊息處理機制(二) 以下皆為乾貨,比較幹,需要讀者細細理解。  前面(一)已經解釋了Handler的基本機制了,下面來概括一下本質。 一、MessageQueue        MessageQueue其實就

Android底部導航欄實現之RadioGroup

這裡簡單記錄一下Android底部導航欄通過RadioGroup+Fragment的實現。 這裡寫圖片描述 佈局: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:and

Android 9 功能和 API-----------顯示屏缺口支援劉海屏或凹凸屏

詳細的適配方案,大家可以看我之前的文章: https://blog.csdn.net/wypeng2010/article/details/81019361     Android 9 支援最新的全面屏,其中包含為攝像頭和揚聲器預留空間的螢幕缺口。 通過 

Android學習之動畫總結

寫在前面:本文是根據hencoder提供的教程寫的總結。HenCoder https://hencoder.com。      關於ObjectAnimator可以用ofInt()來做整數的屬性動畫和ofFloat()來做小數的屬性動畫。當需要對其他型別的屬性來做動畫就需要

Android開發————簡易APP設計

實驗內容 使用sqlite 給備忘錄app增加資料儲存功能 實驗步驟 活動1: Xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://sche

ArcGIS for Android 的學習與應用 如何移除指定的點和線?

在地圖上新增點和線的時候,我們有時候會遇到要移除或者切換指定的點和線的操作。那麼如何移除指定的點和線呢? ArcGIS的api裡點和線都是由GraphicsOverlay類來進行建立新增的。通過Graphic物件將點或者線的圖形物件(SimpleMarkerSy

android M Launcher之LauncherModel

上一篇我們通過LauncherModel的建立 ,例項化,以及與LauncherModel之間的溝通方式。初步瞭解了LauncherModel一些功能及用法,如果對LauncherModel一系列初始化動作還不瞭解的可以看 android M Launcher之LauncherMod