ProgressBar實(shí)現(xiàn)漸變圓角的效果

national.jpg

一、前言

轉(zhuǎn)眼就到了十一國(guó)慶佳節(jié)了,看了看上次的動(dòng)態(tài),已經(jīng)過(guò)去快一個(gè)月了。并不是因?yàn)樽约簺](méi)有堅(jiān)持而中斷了寫文章,真的是因?yàn)檫@一個(gè)月公司太忙了。我參與兩個(gè)項(xiàng)目的編寫,一個(gè)是之前的直播業(yè)務(wù)需要做后臺(tái)的數(shù)據(jù)打通。一個(gè)是新開發(fā)的短視頻項(xiàng)目。真是忙的不亦樂(lè)乎,996的生活就這樣持續(xù)了一個(gè)月,周天真是太累所以選擇了休息?,F(xiàn)在兩個(gè)項(xiàng)目都相繼發(fā)版了,也可以輕松幾天了。就在這個(gè)時(shí)間點(diǎn)上,寫一篇關(guān)于ProgressBar的文章吧。主要也是因?yàn)榍岸螘r(shí)間開發(fā)遇到了關(guān)于ProgressBar的一些UI問(wèn)題,發(fā)現(xiàn)有好多細(xì)的點(diǎn)之前并沒(méi)有掌握好,所以仔細(xì)瀏覽了一些文章和官方文檔,決定把我掌握的一些細(xì)節(jié)在這里寫出來(lái)。

二、ProgressBar的使用場(chǎng)景

在日常開發(fā)中,我們經(jīng)常會(huì)遇到有些界面的跳轉(zhuǎn)需要等待訪問(wèn)一個(gè)接口,等待接口響應(yīng)結(jié)果回來(lái)之后才能跳轉(zhuǎn),比如說(shuō)登錄界面。我們通常需要在等待接口響應(yīng)的過(guò)程中給用戶一個(gè)提示之類的東西,這時(shí)就用到了ProgressBar。再例如我們下載一個(gè)文件,需要顯示現(xiàn)在的進(jìn)度,這個(gè)時(shí)候也可以用到ProgressBar。ProgressBar的使用場(chǎng)景很多,而且其樣式也是五花八門。除了系統(tǒng)提供的幾種默認(rèn)樣式外,我們還可以自定義ProgressBar的樣式。接下來(lái)分別介紹一下系統(tǒng)提供的幾種ProgressBar的樣式以及一些自定義樣式的拓展。

三、ProgressBar的幾種系統(tǒng)樣式

一、系統(tǒng)樣式的種類:

  • 1.Widget.ProgressBar.Horizontal------水平進(jìn)度條樣式(有精確模式和模糊模式兩種類型)
  • 2.Widget.ProgressBar------正常的圓形進(jìn)度條(模糊模式,還有小型的和大型的)
  • 3.Widget.ProgressBar.Inverse------反色的正常圓形進(jìn)度條(模糊模式, 還有小型的和大型的)
  • 4.Widget.DeviceDefault.ProgressBar------默認(rèn)的圓形進(jìn)度條(模糊模式)
  • 5.Widget.Holo.ProgressBar------Holo主題風(fēng)格
  • 6.Widget.Material.ProgressBar------Material主題風(fēng)格
    (注意:這里的精確模式和模糊模式的區(qū)別在于精確模式能夠準(zhǔn)確的看到當(dāng)前的進(jìn)度,而模糊模式回循環(huán)的播放一個(gè)動(dòng)畫來(lái)標(biāo)識(shí)正在等待中。另外反色模式使用與一些淺色的主題背景)
    在這里,附上一張UI展示圖,分別按照上述的樣式的順序來(lái)安放的ProgressBar,代碼中只是單純的引用了style的屬性,并未進(jìn)行其他屬性的設(shè)置,其效果圖如下所示:
Progressbar.png

這里需要注意的是,我是運(yùn)行在Nexus5機(jī)型上。在API小于21的機(jī)型上,你會(huì)發(fā)現(xiàn),Widget.DeviceDefault.ProgressBar風(fēng)格的ProgressBar的顏色是淺灰色,并且無(wú)法修改這個(gè)顏色。而Widget.Material.ProgressBar風(fēng)格的ProgressBar當(dāng)你在XML布局中引用的時(shí)候便會(huì)提示你需要在API大于21的機(jī)型上才能生效。剛才提到了Holo和Material主題,這里可能很多朋友不太了解Holo主題,因此這里附上一篇文章鏈接,希望能讓你了解這個(gè)主題。(Android Design 與 Holo Theme)

二、水平樣式的ProgressBar的講解

上面介紹了幾種系統(tǒng)提供的ProgressBar風(fēng)格,就當(dāng)是拋磚引玉了,接下來(lái)詳細(xì)講述本篇文章題目相關(guān)的東西。而為什么要著重講.Widget.ProgressBar.Horizontal樣式的呢,因?yàn)槠渌麕追N系統(tǒng)提供的樣式都是模糊模式,即用戶只是能看到Loading的狀態(tài),但卻無(wú)法知道任務(wù)的進(jìn)度到底是多少,這一缺點(diǎn)導(dǎo)致現(xiàn)在這樣的樣式的使用場(chǎng)景越來(lái)越少,而現(xiàn)如今更多的產(chǎn)品要么采用水平樣式的ProgreessBar,要么選擇自定義View來(lái)實(shí)現(xiàn)類似圓形ProgressBar但是能顯示具體進(jìn)度的效果。我們前段時(shí)間的產(chǎn)品在發(fā)版的頭兩天,設(shè)計(jì)人員提出了一堆的UI上的細(xì)節(jié)問(wèn)題,讓我們來(lái)改。其中有一條就是在我們的等級(jí)頁(yè)面,有一個(gè)水平樣式的ProgressBar,要修改成顏色漸變,而且兩端帶有圓角效果的。接下來(lái),我們一步一步分析,這個(gè)效果的實(shí)現(xiàn)方式!

1.創(chuàng)建一個(gè)水平樣式的ProgressBar
<ProgressBar
        android:id="@+id/pb_horizontal"
        android:layout_width="240dp"
        android:layout_height="10dp"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:indeterminate="false"   // 為true代表為模糊模式,false代表精確模式
        android:max="100"   // 進(jìn)度的最大值,這里我設(shè)置成100
        android:progress="30"/>   // 當(dāng)前的進(jìn)度

我們先來(lái)看一下效果圖:

exampleone.png

這就是最簡(jiǎn)單的一個(gè)精確樣式的水平進(jìn)度條樣式。可是,這UI未免也太丑了吧,估計(jì)沒(méi)有幾個(gè)產(chǎn)品會(huì)直接采用這樣的原生效果,都會(huì)在一定程度上修改一下它的UI樣式。接下來(lái),我們首先做進(jìn)度條顏色上的修改。

2.修改進(jìn)度條的顏色

對(duì)于ProgressBar顏色的設(shè)置,并不像其他的一些控件,直接設(shè)置color屬性即可,因?yàn)橐粋€(gè)精確的水平進(jìn)度條,是有很多顏色需要設(shè)置的。首先,就是進(jìn)度條的背景色,其次就是進(jìn)度條的顏色,然后還有可能需要顯示緩沖進(jìn)度條,那么這個(gè)時(shí)候就還要設(shè)置緩沖進(jìn)度條的顏色。那這么多的顏色該怎么設(shè)置呢,這時(shí)其實(shí)我們應(yīng)該聯(lián)想到日常開發(fā)我們對(duì)一些按鈕按壓狀態(tài)以及選擇狀態(tài)時(shí)不同顏色的設(shè)置方式。沒(méi)錯(cuò),就是引用drawable文件進(jìn)行設(shè)置。然后將創(chuàng)建好的drawable引用到progressDrawable屬性下。
(1) 背景、進(jìn)度條都不帶圓角的實(shí)現(xiàn)方式:


one.png
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!--設(shè)置ProgressBar背景色-->
    <item android:id="@android:id/background">
        <shape>
            <solid android:color="#f0f0f0"/>
        </shape>
    </item>

    <!--設(shè)置ProgressBar進(jìn)度條顏色-->
    <item android:id="@android:id/progress">
        <clip android:clipOrientation="horizontal">
            <shape>
                <solid android:color="#FF577B"/>
            </shape>
        </clip>
    </item>
</layer-list>

(2)上面是不帶圓角的,那我們想一下帶圓角的怎么實(shí)現(xiàn)??!估計(jì)很多人第一反應(yīng)就是,直接在兩個(gè)item的shape里面加上corners-radius就行了??!我們來(lái)試一下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!--設(shè)置ProgressBar背景色-->
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="8dp"/>   // 圓角
            <solid android:color="#f0f0f0"/>
        </shape>
    </item>

    <!--設(shè)置ProgressBar進(jìn)度條顏色-->
    <item android:id="@android:id/progress">
        <clip android:clipOrientation="horizontal">
            <shape>
                <corners android:radius="8dp"/>   // 圓角
                <solid android:color="#FF577B"/>
            </shape>
        </clip>
    </item>
</layer-list>

運(yùn)行后,效果圖如下:

two.png

奇怪,為什么背景都成了圓角,進(jìn)度條的左側(cè)也成了圓角,但是進(jìn)度條右側(cè)為什么不帶圓角呢?其實(shí),這就要說(shuō)道clip這個(gè)屬性了,它的字面意思就是剪切,我們可以理解它在剪切的過(guò)程中是按照水平或者豎直的方向直線剪切的,并不會(huì)按照?qǐng)A角的方向來(lái)剪切,因此,在剪切的操作中,就把圓角剪切沒(méi)了。那么到底如何實(shí)現(xiàn)進(jìn)度條的兩端都是圓角呢?

(3)進(jìn)度條實(shí)現(xiàn)兩端圓角:
為了實(shí)現(xiàn)進(jìn)度條也是圓角的樣式,我們就不能使用clip屬性了,因?yàn)樗粫?huì)在某一方向裁剪出直線的邊界。這時(shí),我們就需要用到scale這一根屬性了。代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!--設(shè)置ProgressBar背景色-->
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="8dp"/>
            <solid android:color="#f0f0f0"/>
        </shape>
    </item>

    <!--設(shè)置ProgressBar進(jìn)度條顏色-->
    <item android:id="@android:id/progress">
        <scale android:scaleWidth="100%"
            android:drawable="@drawable/progress_color"/>
    </item>
</layer-list>

這里我們將clip根屬性,換成了scale根屬性,并且再引用一個(gè)drawable文件,來(lái)實(shí)現(xiàn)進(jìn)度條的顏色樣式。此drawable文件的代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

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

    <gradient android:angle="180"   // 這里我設(shè)置成了漸變色,如果顏色不想漸變就將gradient屬性換成solid屬性。
        android:startColor="#FF577B"
        android:endColor="#FFC400"/>
</shape>

運(yùn)行后,樣式如下:

three.png

看到了么,是不是實(shí)現(xiàn)了進(jìn)度條和背景都帶圓角。哈哈哈!還有的產(chǎn)品要求,某一端帶圓角的。我想說(shuō),現(xiàn)在兩端帶圓角和不帶圓角的你都會(huì)了,還怕設(shè)置單一方向圓角的嗎???直接選擇設(shè)置某個(gè)角的radius就OK了!
這就是在XML設(shè)置水平進(jìn)度條樣式的一些方式,然后可以在代碼中動(dòng)態(tài)的控制progress,只需調(diào)用setProgress()方法即可。(如果產(chǎn)品需要加上緩沖進(jìn)度條顏色,那么只需在背景和進(jìn)度條顏色之間設(shè)置一個(gè)item,id為secondaryProgress即可)

四、自定義進(jìn)度條

自定義進(jìn)度條的方法和樣式有太多種,網(wǎng)上的案例更是一搜一大堆。其實(shí)這個(gè)的實(shí)現(xiàn)很簡(jiǎn)單,當(dāng)然,具體的樣式還是你們的產(chǎn)品設(shè)計(jì)決定,到最后說(shuō)白了就是實(shí)現(xiàn)一個(gè)自定義View。這個(gè)View中有一個(gè)顯示具體進(jìn)度的文本,一個(gè)顯示Loading的圖標(biāo)或者動(dòng)畫就OK了。下面,我舉出一個(gè)我們項(xiàng)目中創(chuàng)建的一個(gè)自定義的進(jìn)度條樣式。
在我們的項(xiàng)目中,有一個(gè)緩存視頻到本地的功能,需要我們先從網(wǎng)絡(luò)中將視頻下載下來(lái),這個(gè)過(guò)程肯定是需要時(shí)間的,因此我們?cè)谙螺d的過(guò)程中展示一個(gè)類似于ProgressBar的自定義View來(lái)顯示下載的進(jìn)度。樣式如下:

four.png

有一個(gè)顯示進(jìn)度的文本數(shù)字,一個(gè)轉(zhuǎn)圈的Loading圖標(biāo),一個(gè)顯示狀態(tài)的文本,還有一個(gè)黑色透明的圓角背景。下面我直接上代碼,看一下這個(gè)的實(shí)現(xiàn)方式。
(1) 控件采用繼承DialogFragment的方式來(lái)實(shí)現(xiàn)。

/**
 * 使用DialogFragment更方便的控制View的顯示與隱藏,以及資源的釋放,
 * 使其的使用更加有規(guī)律和方便
 */
public class ProgressDialog extends DialogFragment {

    TextView mProgressTv;   // 顯示進(jìn)度的數(shù)字
    ImageView mLoadingIv;   // Loading圖標(biāo)

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NO_TITLE, R.style.DialogStyle);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        getDialog().setCanceledOnTouchOutside(true);
        View mRootView = inflater.inflate(R.layout.layout_progress_dialog, container, false);
        mProgressTv = mRootView.findViewById(R.id.tv_progress_num);
        mLoadingIv = mRootView.findViewById(R.id.iv_progress);
        return mRootView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 設(shè)置Loading旋轉(zhuǎn)動(dòng)畫
        RotateAnimation rotateAnimation = new RotateAnimation(0f, 359f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setInterpolator(new LinearInterpolator());
        rotateAnimation.setDuration(400L);
        rotateAnimation.setRepeatMode(Animation.RESTART);
        rotateAnimation.setRepeatCount(Animation.INFINITE);
        mLoadingIv.startAnimation(rotateAnimation);
    }

    @Override
    public void onStart() {
        super.onStart();
        Window window = getDialog().getWindow();
        WindowManager.LayoutParams params = window.getAttributes();

        params.dimAmount = 0.5f;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.gravity = Gravity.CENTER;

        window.setAttributes(params);
    }

    //IllegalStateException : Can not perform this action after onSaveInstanceSate
    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            super.show(manager, tag);
        } catch (IllegalStateException ignore) {
        }
    }

    /**
     * 初始化ProgressDialog實(shí)例
     * @return
     */
    public static ProgressDialog create() {
        ProgressDialog dialog = new ProgressDialog();
        return dialog;
    }

    /**
     * 顯示
     * @param manger
     * @return
     */
    public ProgressDialog show(FragmentManager manger) {
        show(manger, this.getTag());
        return this;
    }

    /**
     * 設(shè)置當(dāng)前的進(jìn)度
     * @param progress
     */
    public void setProgress(int progress) {
        mProgressTv.setText(String.valueOf(progress));
    }
}

(2) XML的布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="120dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:background="@drawable/bg_progress_dialog"   // 在drawable文件夾中定義
        android:gravity="center"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center">

            <ImageView
                android:id="@+id/iv_progress"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:src="@drawable/ic_loading" />

            <TextView
                android:id="@+id/tv_progress_num"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:textColor="#FFFFFF"
                android:textSize="8sp"
                android:text="66" />
        </FrameLayout>

        <TextView
            android:id="@+id/tv_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_marginTop="8dp"
            android:textColor="#FFFFFF"
            android:textSize="12sp"
            android:text="Loading"/>
    </LinearLayout>
</FrameLayout>

(3) DialogFragment的Style樣式在res下的value文件夾下面創(chuàng)建styles_dialog文件, 在里面創(chuàng)建其style。(這個(gè)style直接在style文件內(nèi)創(chuàng)建是無(wú)法被DialogFragment引用的)。好人做到底,上圖更直接了當(dāng),省著有的朋友到時(shí)候不知所云。

five.png

代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--android:windowBackground 后面的背景。background前置的背景-->
    <style name="DialogStyle" parent="@android:style/Theme.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:backgroundDimEnabled">true</item>
    </style>
</resources>

以上就是一個(gè)簡(jiǎn)單的自定義進(jìn)度條的實(shí)現(xiàn)方式,很簡(jiǎn)單的吧。其實(shí),這個(gè)東西100個(gè)產(chǎn)品中可能會(huì)出現(xiàn)99個(gè)樣式,所以,開發(fā)時(shí)只需要根據(jù)自身的產(chǎn)品需求創(chuàng)建相應(yīng)的樣式就行了,實(shí)現(xiàn)的方式都是差不多,并不是很難的。

一篇很基礎(chǔ)的文章,就寫到這里了,希望能幫助一些朋友。最后,提前祝大家十一快樂(lè)?。?/h5>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評(píng)論 25 709
  • 我是小魚,小麥的好朋友。 今天來(lái)說(shuō)說(shuō)我的故事。 我和他相識(shí)于K房,我還記得一推開門時(shí),看見一個(gè)男孩坐在角落里玩手機(jī)...
    瑪麗蓮懵露閱讀 314評(píng)論 0 0
  • 燕子為什么和人親近? 傳說(shuō)有一天,圣人正在堂上誦經(jīng),忽然從門外竄進(jìn)來(lái)一條五花大蛇,張著血盆大口,對(duì)圣人說(shuō):“我現(xiàn)在...
    不敢高聲語(yǔ)閱讀 192評(píng)論 0 0
  • 自從聽了《時(shí)間管理》中關(guān)于九宮格日志一講,并親自看了《晨間日記的奇跡》,媽媽就將日志格式改成了九宮格,但還是決定每...
    青鳥_01閱讀 158評(píng)論 0 0

友情鏈接更多精彩內(nèi)容