ProgressBar实现渐变圆角的效果

national.jpg

一、前言

转眼就到了十一国庆佳节了,看了看上次的动态,已经过去快一个月了。并不是因为自己没有坚持而中断了写文章,真的是因为这一个月公司太忙了。我参与两个项目的编写,一个是之前的直播业务需要做后台的数据打通。一个是新开发的短视频项目。真是忙的不亦乐乎,996的生活就这样持续了一个月,周天真是太累所以选择了休息。现在两个项目都相继发版了,也可以轻松几天了。就在这个时间点上,写一篇关于ProgressBar的文章吧。主要也是因为前段时间开发遇到了关于ProgressBar的一些UI问题,发现有好多细的点之前并没有掌握好,所以仔细浏览了一些文章和官方文档,决定把我掌握的一些细节在这里写出来。

二、ProgressBar的使用场景

在日常开发中,我们经常会遇到有些界面的跳转需要等待访问一个接口,等待接口响应结果回来之后才能跳转,比如说登录界面。我们通常需要在等待接口响应的过程中给用户一个提示之类的东西,这时就用到了ProgressBar。再例如我们下载一个文件,需要显示现在的进度,这个时候也可以用到ProgressBar。ProgressBar的使用场景很多,而且其样式也是五花八门。除了系统提供的几种默认样式外,我们还可以自定义ProgressBar的样式。接下来分别介绍一下系统提供的几种ProgressBar的样式以及一些自定义样式的拓展。

三、ProgressBar的几种系统样式

一、系统样式的种类:

  • 1.Widget.ProgressBar.Horizontal------水平进度条样式(有精确模式和模糊模式两种类型)
  • 2.Widget.ProgressBar------正常的圆形进度条(模糊模式,还有小型的和大型的)
  • 3.Widget.ProgressBar.Inverse------反色的正常圆形进度条(模糊模式, 还有小型的和大型的)
  • 4.Widget.DeviceDefault.ProgressBar------默认的圆形进度条(模糊模式)
  • 5.Widget.Holo.ProgressBar------Holo主题风格
  • 6.Widget.Material.ProgressBar------Material主题风格
    (注意:这里的精确模式和模糊模式的区别在于精确模式能够准确的看到当前的进度,而模糊模式回循环的播放一个动画来标识正在等待中。另外反色模式使用与一些浅色的主题背景)
    在这里,附上一张UI展示图,分别按照上述的样式的顺序来安放的ProgressBar,代码中只是单纯的引用了style的属性,并未进行其他属性的设置,其效果图如下所示:
Progressbar.png

这里需要注意的是,我是运行在Nexus5机型上。在API小于21的机型上,你会发现,Widget.DeviceDefault.ProgressBar风格的ProgressBar的颜色是浅灰色,并且无法修改这个颜色。而Widget.Material.ProgressBar风格的ProgressBar当你在XML布局中引用的时候便会提示你需要在API大于21的机型上才能生效。刚才提到了Holo和Material主题,这里可能很多朋友不太了解Holo主题,因此这里附上一篇文章链接,希望能让你了解这个主题。(Android Design 与 Holo Theme)

二、水平样式的ProgressBar的讲解

上面介绍了几种系统提供的ProgressBar风格,就当是抛砖引玉了,接下来详细讲述本篇文章题目相关的东西。而为什么要着重讲.Widget.ProgressBar.Horizontal样式的呢,因为其他几种系统提供的样式都是模糊模式,即用户只是能看到Loading的状态,但却无法知道任务的进度到底是多少,这一缺点导致现在这样的样式的使用场景越来越少,而现如今更多的产品要么采用水平样式的ProgreessBar,要么选择自定义View来实现类似圆形ProgressBar但是能显示具体进度的效果。我们前段时间的产品在发版的头两天,设计人员提出了一堆的UI上的细节问题,让我们来改。其中有一条就是在我们的等级页面,有一个水平样式的ProgressBar,要修改成颜色渐变,而且两端带有圆角效果的。接下来,我们一步一步分析,这个效果的实现方式!

1.创建一个水平样式的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"   // 进度的最大值,这里我设置成100
        android:progress="30"/>   // 当前的进度

我们先来看一下效果图:

exampleone.png

这就是最简单的一个精确样式的水平进度条样式。可是,这UI未免也太丑了吧,估计没有几个产品会直接采用这样的原生效果,都会在一定程度上修改一下它的UI样式。接下来,我们首先做进度条颜色上的修改。

2.修改进度条的颜色

对于ProgressBar颜色的设置,并不像其他的一些控件,直接设置color属性即可,因为一个精确的水平进度条,是有很多颜色需要设置的。首先,就是进度条的背景色,其次就是进度条的颜色,然后还有可能需要显示缓冲进度条,那么这个时候就还要设置缓冲进度条的颜色。那这么多的颜色该怎么设置呢,这时其实我们应该联想到日常开发我们对一些按钮按压状态以及选择状态时不同颜色的设置方式。没错,就是引用drawable文件进行设置。然后将创建好的drawable引用到progressDrawable属性下。
(1) 背景、进度条都不带圆角的实现方式:


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

    <!--设置ProgressBar背景色-->
    <item android:id="@android:id/background">
        <shape>
            <solid android:color="#f0f0f0"/>
        </shape>
    </item>

    <!--设置ProgressBar进度条颜色-->
    <item android:id="@android:id/progress">
        <clip android:clipOrientation="horizontal">
            <shape>
                <solid android:color="#FF577B"/>
            </shape>
        </clip>
    </item>
</layer-list>

(2)上面是不带圆角的,那我们想一下带圆角的怎么实现啊!估计很多人第一反应就是,直接在两个item的shape里面加上corners-radius就行了啊!我们来试一下:

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

    <!--设置ProgressBar背景色-->
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="8dp"/>   // 圆角
            <solid android:color="#f0f0f0"/>
        </shape>
    </item>

    <!--设置ProgressBar进度条颜色-->
    <item android:id="@android:id/progress">
        <clip android:clipOrientation="horizontal">
            <shape>
                <corners android:radius="8dp"/>   // 圆角
                <solid android:color="#FF577B"/>
            </shape>
        </clip>
    </item>
</layer-list>

运行后,效果图如下:

two.png

奇怪,为什么背景都成了圆角,进度条的左侧也成了圆角,但是进度条右侧为什么不带圆角呢?其实,这就要说道clip这个属性了,它的字面意思就是剪切,我们可以理解它在剪切的过程中是按照水平或者竖直的方向直线剪切的,并不会按照圆角的方向来剪切,因此,在剪切的操作中,就把圆角剪切没了。那么到底如何实现进度条的两端都是圆角呢?

(3)进度条实现两端圆角:
为了实现进度条也是圆角的样式,我们就不能使用clip属性了,因为它只会在某一方向裁剪出直线的边界。这时,我们就需要用到scale这一根属性了。代码如下:

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

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

    <!--设置ProgressBar进度条颜色-->
    <item android:id="@android:id/progress">
        <scale android:scaleWidth="100%"
            android:drawable="@drawable/progress_color"/>
    </item>
</layer-list>

这里我们将clip根属性,换成了scale根属性,并且再引用一个drawable文件,来实现进度条的颜色样式。此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"   // 这里我设置成了渐变色,如果颜色不想渐变就将gradient属性换成solid属性。
        android:startColor="#FF577B"
        android:endColor="#FFC400"/>
</shape>

运行后,样式如下:

three.png

看到了么,是不是实现了进度条和背景都带圆角。哈哈哈!还有的产品要求,某一端带圆角的。我想说,现在两端带圆角和不带圆角的你都会了,还怕设置单一方向圆角的吗???直接选择设置某个角的radius就OK了!
这就是在XML设置水平进度条样式的一些方式,然后可以在代码中动态的控制progress,只需调用setProgress()方法即可。(如果产品需要加上缓冲进度条颜色,那么只需在背景和进度条颜色之间设置一个item,id为secondaryProgress即可)

四、自定义进度条

自定义进度条的方法和样式有太多种,网上的案例更是一搜一大堆。其实这个的实现很简单,当然,具体的样式还是你们的产品设计决定,到最后说白了就是实现一个自定义View。这个View中有一个显示具体进度的文本,一个显示Loading的图标或者动画就OK了。下面,我举出一个我们项目中创建的一个自定义的进度条样式。
在我们的项目中,有一个缓存视频到本地的功能,需要我们先从网络中将视频下载下来,这个过程肯定是需要时间的,因此我们在下载的过程中展示一个类似于ProgressBar的自定义View来显示下载的进度。样式如下:

four.png

有一个显示进度的文本数字,一个转圈的Loading图标,一个显示状态的文本,还有一个黑色透明的圆角背景。下面我直接上代码,看一下这个的实现方式。
(1) 控件采用继承DialogFragment的方式来实现。

/**
 * 使用DialogFragment更方便的控制View的显示与隐藏,以及资源的释放,
 * 使其的使用更加有规律和方便
 */
public class ProgressDialog extends DialogFragment {

    TextView mProgressTv;   // 显示进度的数字
    ImageView mLoadingIv;   // Loading图标

    @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);
        // 设置Loading旋转动画
        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实例
     * @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;
    }

    /**
     * 设置当前的进度
     * @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文件夹下面创建styles_dialog文件, 在里面创建其style。(这个style直接在style文件内创建是无法被DialogFragment引用的)。好人做到底,上图更直接了当,省着有的朋友到时候不知所云。

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>

以上就是一个简单的自定义进度条的实现方式,很简单的吧。其实,这个东西100个产品中可能会出现99个样式,所以,开发时只需要根据自身的产品需求创建相应的样式就行了,实现的方式都是差不多,并不是很难的。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,042评论 25 707
  • 我是小鱼,小麦的好朋友。 今天来说说我的故事。 我和他相识于K房,我还记得一推开门时,看见一个男孩坐在角落里玩手机...
    玛丽莲懵露阅读 212评论 0 0
  • 燕子为什么和人亲近? 传说有一天,圣人正在堂上诵经,忽然从门外窜进来一条五花大蛇,张着血盆大口,对圣人说:“我现在...
    不敢高声语阅读 164评论 0 0
  • 自从听了《时间管理》中关于九宫格日志一讲,并亲自看了《晨间日记的奇迹》,妈妈就将日志格式改成了九宫格,但还是决定每...
    青鸟_01阅读 120评论 0 0