自定义ProgressBar打造酷炫进度条

Android系统默认的ProgressBar往往都不能满足实际开发需要,一般都会开发者自定义ProgressBar。

在Android开发中,自定义ProgressBar一般有三种思路来完成。

一、在系统进度条基础上优化

首先来看一下style="@android:style/Widget.ProgressBar.Horizontal"的源码。鼠标移动到style属性值上,按住Ctrl键,鼠标左键点击即可打开对应资源文件。

当然也可以直接找到源码文件直接打开进行查看,本地相对路径为sdk\platforms\android-25\data\res\values\styles.xml。打开后可以看到源码如下:

<style name="Widget.ProgressBar.Horizontal">
        <item name="indeterminateOnly">false</item>
        <item name="progressDrawable">@drawable/progress_horizontal</item>
        <item name="indeterminateDrawable">@drawable/progress_indeterminate_horizontal</item>
        <item name="minHeight">20dip</item>
        <item name="maxHeight">20dip</item>
        <item name="mirrorForRtl">true</item>
    </style>

从上述代码可以看到,水平进度条的核心就是android:progressDrawable,接着继续去寻找progress_horizontal的源码(本地相对路径为sdk\platforms\android-25\data\res\drawable\progress_horizontal.xml),如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->


<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip" />
            <gradient
                    android:startColor="#ff9d9e9d"
                    android:centerColor="#ff5a5d5a"
                    android:centerY="0.75"
                    android:endColor="#ff747674"
                    android:angle="270"
            />
        </shape>
    </item>


    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape>
                <corners android:radius="5dip" />
                <gradient
                        android:startColor="#80ffd300"
                        android:centerColor="#80ffb600"
                        android:centerY="0.75"
                        android:endColor="#a0ffcb00"
                        android:angle="270"
                />
            </shape>
        </clip>
    </item>


    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <corners android:radius="5dip" />
                <gradient
                        android:startColor="#ffffd300"
                        android:centerColor="#ffffb600"
                        android:centerY="0.75"
                        android:endColor="#ffffcb00"
                        android:angle="270"
                />
            </shape>
        </clip>
    </item>

</layer-list>

从上述代码可以发现,progress_horizontal一共包括3个item,分别为background、secondProgress、progress,看名字就能知道其大概作用,其中我们比较关心的应该是后两个。

其实把这个文件copy一份到自己的项目下,就可以随心所欲的修改shape属性的圆角、渐变等,实现我们想要的效果了。

由于此处我们还没有学习Drawable资源,所以这里先给大家介绍一下概念,关于具体如何操作可以等学了Drawable资源后,再回过头来自定义ProgressBar。欢迎关注微信公众分享达人秀ShareExpert获取第一手教程,也可添加小编微信jinwenyu2010,然后拽入Android零基础入门技术讨论微信群一同学习。

二、使用动画来代替进度条

使用动画来替代进度条,其实就是使用一套连续图片,形成一个帧动画,当需要进度图的时候,让动画可见,不需要的时候让动画不可见即可。

继续使用WidgetSample工程的advancedviewsample模块,首先在drawable目录下准备一组连续的图片,然后在res/drawable/目录下定义一个myprogressbar.xml的资源文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/loading_01" android:duration="200" />
    <item android:drawable="@drawable/loading_02" android:duration="200" />
    <item android:drawable="@drawable/loading_03" android:duration="200" />
    <item android:drawable="@drawable/loading_04" android:duration="200" />
    <item android:drawable="@drawable/loading_05" android:duration="200" />
    <item android:drawable="@drawable/loading_06" android:duration="200" />
    <item android:drawable="@drawable/loading_07" android:duration="200" />
    <item android:drawable="@drawable/loading_08" android:duration="200" />
    <item android:drawable="@drawable/loading_09" android:duration="200" />
    <item android:drawable="@drawable/loading_10" android:duration="200" />
    <item android:drawable="@drawable/loading_11" android:duration="200" />
    <item android:drawable="@drawable/loading_12" android:duration="200" />
</animation-list>

关于上述代码的具体意义会在Android动画进行讲解,此处大家知道如何操作即可。

接着新建一个布局文件myprogressbar_layout.xml,里面仅仅有一个ImageView即可,用于显示进度条,把src设置为上述drawable资源即可,代码如下:

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

    <ImageView
        android:id="@+id/mypg_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:src="@drawable/myprogressbar"/>

</RelativeLayout>

新建MyProgressbarActivity.java文件,加载上面新建的布局文件,具体代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample;


import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;


public class MyProgressbarActivity extends AppCompatActivity {
    private ImageView mProgressbarImg = null;
    private AnimationDrawable mProgressAnimation = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myprogressbar_layout);


        mProgressbarImg = (ImageView) findViewById(R.id.mypg_img);
        mProgressAnimation = (AnimationDrawable) mProgressbarImg.getDrawable();


        // 启动动画
        mProgressbarImg.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 启动动画
                mProgressAnimation.start();
            }
        }, 100);
    }
}

这里只是简单启动上述定义的动画,在开发中你可以根据需要显示和隐藏即可。

修改启动的Activity,运行可以看到如下图所示效果。

三、通过自定义View来实现进度条

使用动画来完成进度条实际上比较巧妙,但还是不能满足实际开发需要,那么最强大的自定义ProgressBar就是重写View来实现了,可以定义出任何需要的进度条。

如定义一个CircleProgressBar类,继承View,并重写其方法,示例代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample.view;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;


/**
 * @创建者 鑫鱻
 * @描述 Android零基础入门到精通系列教程,欢迎关注微信公众号ShareExpert
 */
public class CircleProgressBar extends View {
    private Paint mBackPaint;
    private Paint mFrontPaint;
    private Paint mTextPaint;
    private float mStrokeWidth = 50;
    private float mHalfStrokeWidth = mStrokeWidth / 2;
    private float mRadius = 200;
    private RectF mRect;
    private int mProgress = 0;
    private int mTargetProgress = 90;
    private int mMax = 100;
    private int mWidth;
    private int mHeight;


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

    public CircleProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    // 完成相关参数初始化
    private void init() {
        mBackPaint = new Paint();
        mBackPaint.setColor(Color.WHITE);
        mBackPaint.setAntiAlias(true);
        mBackPaint.setStyle(Paint.Style.STROKE);
        mBackPaint.setStrokeWidth(mStrokeWidth);
        mFrontPaint = new Paint();
        mFrontPaint.setColor(Color.GREEN);
        mFrontPaint.setAntiAlias(true);
        mFrontPaint.setStyle(Paint.Style.STROKE);
        mFrontPaint.setStrokeWidth(mStrokeWidth);
        mTextPaint = new Paint();
        mTextPaint.setColor(Color.GREEN);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(80);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }


    // 重写测量大小的onMeasure方法和绘制View的核心方法onDraw()
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getRealSize(widthMeasureSpec);
        mHeight = getRealSize(heightMeasureSpec);
        setMeasuredDimension(mWidth, mHeight);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        initRect();
        float angle = mProgress / (float) mMax * 360;
        canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mBackPaint);
        canvas.drawArc(mRect, -90, angle, false, mFrontPaint);
        canvas.drawText(mProgress + "%", mWidth / 2 + mHalfStrokeWidth,
                mHeight / 2 + mHalfStrokeWidth, mTextPaint);
        if (mProgress < mTargetProgress) {
            mProgress += 1;
            invalidate();
        }
    }


    public int getRealSize(int measureSpec) {
        int result = 1;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED) {
            //自己计算
            result = (int) (mRadius * 2 + mStrokeWidth);
        } else {
            result = size;
        }
        return result;
    }


    private void initRect() {
        if (mRect == null) {
            mRect = new RectF();
            int viewSize = (int) (mRadius * 2);
            int left = (mWidth - viewSize) / 2;
            int top = (mHeight - viewSize) / 2;
            int right = left + viewSize;
            int bottom = top + viewSize;
            mRect.set(left, top, right, bottom);
        }
    }
}

关于上述代码的具体含义,此处不理解没关系,可以等学了Android绘图后再回过头来进行学习。

然后新建一个布局文件circleprogressbar_layout.xml,使用上述的自定义进度条类,代码如下:

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

    <com.jinyu.cqkxzsxy.android.advancedviewsample.view.CircleProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

修改MainActivity.java里面加载的布局文件,运行后其效果如下图所示。

如果已经学会本期里面提及的内容,那就可以放手去打造属于你自己的酷炫进度条了。如果不会也没关系,这里只做了解,等后期学完后再来回顾即可。

原文发布于微信公众号 - 分享达人秀(ShareExpert)

原文发表时间:2017-09-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

Selector使用

Selector使用 Selector使其能够在不同的状态下更换某个View的背景图片。 <?xml version="1.0" encoding="utf-8...

23080
来自专栏分享达人秀

常见Button使用详解

Button(按钮)是Android开发中使用非常频繁的组件,主要是在UI界面上生成一个按钮,该按钮可以供用户单击,当用户单击按钮时,按钮会触发一个onC...

248100
来自专栏跟着阿笨一起玩NET

c# 模拟window 操作鼠标|winapi

适合场景:LinkButton如果想要弹出右键菜单的时候,可以在Click事件中通过API模拟鼠标右击事件。

23610
来自专栏项勇

笔记82 | 在ScrollView中加载 需要全部展开ListView

wifi列表需要动态加载更新 所有的wifi列表需要全部展开显示 直接把listView放到一个scrollview中放的话,listView只会显示一个ite...

7910
来自专栏用户3030674的专栏

Android 性能优化——之控件的优化

  前面讲了图像的优化,接下来分享一下控件的性能优化,这里主要是面向自定义View的优化。

13830
来自专栏分享达人秀

CheckBox和RadioButton使用大全

本期先来学习Button的两个子控件,无论是单选还是复选,在实际开发中都是使用的较多的控件,相信通过本期的学习即可轻松掌握。 一、CheckBox ...

463100
来自专栏Hongten

android开发-TextView控件学习

public class MainActivity extends Activity{

15120
来自专栏向治洪

关于Android PullTorefreshScrollview回到顶部实例

列表滑动下面显示按钮,点击按钮回到顶部的功能,一般scrollview会有滑动监听的事件,通过setOnScrollChangeListener()滑动监听滑动...

22690
来自专栏向治洪

Android优化之Hardware Layer

项目中越来越多的动画,越来越多的效果导致了应用性能越来越低。该如何提升。 简介 在View播放动画的过程中每一帧都需要被重绘。如果使用view layer...

31290
来自专栏Sorrower的专栏

界面无小事(六):来做个好看得侧拉菜单!

13620

扫码关注云+社区

领取腾讯云代金券