自定义ProgressBar(包括自定义图片,带进度的圆形进度条、长方形进度条)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/51545889

转载请注明原博客地址:http://blog.csdn.net/gdutxiaoxu/article/details/51545889

参考博客:http://blog.csdn.net/lmj623565791?viewmode=contents

ProgressBar简介

继承于View类,直接子类有AbsSeekBar和ContentLoadingProgressBar,其中AbsSeekBar的子类有SeekBar和RatingBar,可见这二者也是基于ProgressBar实现的。

1、ProgressBar有两个进度,一个是Android:progress,另一个是android:secondaryProgress。后者主要是为缓存需要所涉及的,比如在看网络视频时候都会有一个缓存的进度条以及还要一个播放的进度,在这里缓存的进度就可以是android:secondaryProgress,而播放进度就是android:progress,有了secondProgress,可以很方便定制ProgressBar。

 2、ProgressBar分为确定的和不确定的,确定的是我们能明确看到进度,相反不确定的就是不清楚、不确定一个操作需要多长时间来完成,这个时候就需要用的不确定的ProgressBar了。属性android:indeterminate如果设置为true的话,那么ProgressBar就可能是圆形的滚动条或者水平的滚动条(由样式决定),但是我们一般时候,是直接使用Style类型来区分圆形还是水平ProgressBar的。

3、ProgressBar的样式设定其实有两种方式,在API文档中说明的方式如下:

  • Widget.ProgressBar.Horizontal
  • Widget.ProgressBar.Small
  • Widget.ProgressBar.Large
  • Widget.ProgressBar.Inverse
  • Widget.ProgressBar.Small.Inverse
  • Widget.ProgressBar.Large.Inverse

使用的时候可以这样:style="@android:style/Widget.ProgressBar.Small",另外还有一种方式就是使用系统的attr,下面的方式是系统的style:

  • style="?android:attr/progressBarStyle" 
  • style="?android:attr/progressBarStyleHorizontal" 
  • style="?android:attr/progressBarStyleInverse" 
  • style="?android:attr/progressBarStyleLarge" 
  • style="?android:attr/progressBarStyleLargeInverse" 
  • style="?android:attr/progressBarStyleSmall" 
  • style="?android:attr/progressBarStyleSmallInverse" 
  • style="?android:attr/progressBarStyleSmallTitle"                         

先看一下效果图

源代码下载地址:https://github.com/gdutxiaoxu/CustomProgressBar.git

1 修改ProgressBar的图片

1)第一个效果的实现非常简单

只需要修改ProgressBar的style即可

  <style name="progressBar_custom_drawable" parent="@android:style/Widget.ProgressBar.Small">
        <item name="android:indeterminateDrawable">@drawable/progressbar_circle_1</item>
        <item name="android:minWidth">25dp</item>
        <item name="android:minHeight">25dp</item>
        <item name="android:maxWidth">60dp</item>
        <item name="android:maxHeight">60dp</item>
    </style>

其中修改属性即可更改为我们想要的效果

 <item name="android:indeterminateDrawable">@drawable/progressbar_circle_1</item>

2)这个形状我们可以借助代码实现

在Drawable目录下新建一个XML文件

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0"
        android:pivotX="50%" android:pivotY="50%" android:toDegrees="360">
      
    <shape android:innerRadiusRatio="3" android:shape="ring" android:thicknessRatio="8"
           android:useLevel="false">
            
        <gradient android:centerColor="#FFFFFF" android:centerY="0.50"
                  android:endColor="#1E90FF"
                  android:startColor="#000000" android:type="sweep"
        android:useLevel="false" />
    </shape>
</rotate>

2 自定义长方形进度条

这种效果我们是通过继承ProgressBar实现的

a 先讲一下实现思路

横向那个进度条,主要是通过是通过Canvas类的drawLine()和drawText()方法实现的,

1)要解决的问题,怎样拿到拿到控件的宽度,(不了解的请先自行了解View的绘制原理,本篇博客的重点不在这里,就不详细说了,下面会给出测量的代码

2)拿到宽度以后,我们再通过getProgress()拿到进度,按比例控制绘制线的长短,这样就实现了。

为了控件使用起来方便,我们使用到了自定义属性,如果多自定义属性不熟悉的,建议参考鸿洋的这篇博客:http://blog.csdn.net/lmj623565791/article/details/45022631

下面给出源代码

1)在value目录下新建attr_progress_bar xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="BaseProgressBar">
        <attr name="progress_unreached_color" format="color" />
        <attr name="progress_reached_color" format="color" />
        <attr name="progress_reached_bar_height" format="dimension" />
        <attr name="progress_unreached_bar_height" format="dimension" />
        <attr name="progress_text_size" format="dimension" />
        <attr name="progress_text_color" format="color" />
        <attr name="progress_text_offset" format="dimension" />
        <attr name="progress_text_visibility" format="enum">
            <enum name="visible" value="0" />
            <enum name="invisible" value="1" />
        </attr>
    </declare-styleable>

    <declare-styleable name="RoundProgressBarWidthNumber">
        <attr name="radius" format="dimension" />
    </declare-styleable>
</resources>

2)在构造器里面获取我们的自定义属性,为了代码的复用,我们把它放到基类BaseProgressBar

/**
 * get the styled attributes
 *
 * @param attrs
 */
private void obtainStyledAttributes(AttributeSet attrs) {
    // init values from custom attributes
    final TypedArray attributes = getContext().obtainStyledAttributes(
            attrs, R.styleable.BaseProgressBar);

    mTextColor = attributes
            .getColor(
                    R.styleable.BaseProgressBar_progress_text_color,
                    DEFAULT_TEXT_COLOR);
    mTextSize = (int) attributes.getDimension(
            R.styleable.BaseProgressBar_progress_text_size,
            mTextSize);

    mReachedBarColor = attributes
            .getColor(
                    R.styleable.BaseProgressBar_progress_reached_color,
                    mTextColor);
    mUnReachedBarColor = attributes
            .getColor(
                    R.styleable.BaseProgressBar_progress_unreached_color,
                    DEFAULT_COLOR_UNREACHED_COLOR);
    mReachedProgressBarHeight = (int) attributes
            .getDimension(
                    R.styleable.BaseProgressBar_progress_reached_bar_height,
                    mReachedProgressBarHeight);
    mUnReachedProgressBarHeight = (int) attributes
            .getDimension(
                    R.styleable.BaseProgressBar_progress_unreached_bar_height,
                    mUnReachedProgressBarHeight);
    mTextOffset = (int) attributes
            .getDimension(
                    R.styleable.BaseProgressBar_progress_text_offset,
                    mTextOffset);

    int textVisible = attributes
            .getInt(R.styleable.BaseProgressBar_progress_text_visibility,
                    VISIBLE);
    if (textVisible != VISIBLE) {
        mIfDrawText = false;
    }
    attributes.recycle();
}

public BaseProgressBar(Context context, AttributeSet attrs,
                       int defStyle) {
    super(context, attrs, defStyle);
    obtainStyledAttributes(attrs);
    mPaint.setTextSize(mTextSize);
    mPaint.setColor(mTextColor);
}

3)在onMeasure里面拿到我们空间的高度

<pre name="code" class="java">@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = measureHeight(heightMeasureSpec);
    setMeasuredDimension(width, height);

    mRealWidth = getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
}

   
   private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
//        测量模式等于 MeasureSpec.EXACTLY,交给系统自己处理
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
//             测量模式不等于 MeasureSpec.EXACTLY
        } else {
           
//            测量模式等于MeasureSpec.UNSPECIFIE的时候自己处理
            float textHeight = (mPaint.descent() - mPaint.ascent());
            result = (int) (getPaddingTop() + getPaddingBottom() + Math.max(
                    Math.max(mReachedProgressBarHeight,
                            mUnReachedProgressBarHeight), Math.abs(textHeight)));
//            测量模式等于MeasureSpec.AT_MOST的时候,取较小的一个
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

4)在onDraw里面绘制

 @Override
    protected synchronized void onDraw(Canvas canvas) {

        canvas.save();
        canvas.translate(getPaddingLeft(), getHeight() / 2);

        boolean noNeedBg = false;
        float radio = getProgress() * 1.0f / getMax();
        float progressPosX = (int) (mRealWidth * radio);
        String text = getProgress() + "%";
        // mPaint.getTextBounds(text, 0, text.length(), mTextBound);

        float textWidth = mPaint.measureText(text);
        float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;

        if (progressPosX + textWidth > mRealWidth) {
            progressPosX = mRealWidth - textWidth;
            noNeedBg = true;
        }

        // draw reached bar
        float endX = progressPosX - mTextOffset / 2;
        if (endX > 0) {
            mPaint.setColor(mReachedBarColor);
            mPaint.setStrokeWidth(mReachedProgressBarHeight);
            canvas.drawLine(0, 0, endX, 0, mPaint);
        }
        // draw progress bar
        // measure text bound
        if (mIfDrawText) {
            mPaint.setColor(mTextColor);
            canvas.drawText(text, progressPosX, -textHeight, mPaint);
        }

        // draw unreached bar
        if (!noNeedBg) {
            float start = progressPosX + mTextOffset / 2 + textWidth;
            mPaint.setColor(mUnReachedBarColor);
            mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
            canvas.drawLine(start, 0, mRealWidth, 0, mPaint);
        }

        canvas.restore();

    }

到这里,实现思已经完毕

源代码下载地址:https://github.com/gdutxiaoxu/CustomProgressBar.git

3 自定义圆形进度条

实现思路

1)继承BaseProgressBar,在构造器里面获取我们需要的自定义属性

2)在onMeasure里面拿到我们空间的高度

3)在onDraw里面绘制圆和文本(先绘制一个细一点的圆,然后绘制一个粗一点的弧度,二者叠在一起就行。文本呢,绘制在中间)

代码如下

package com.xujun.administrator.customprogressbar;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.util.AttributeSet;

public class RoundProgressBarWidthNumber extends
        BaseProgressBar {
    /**
     * mRadius of view
     */
    private int mRadius = dp2px(30);
    private int mMaxPaintWidth;

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

    public RoundProgressBarWidthNumber(Context context, AttributeSet attrs) {
        super(context, attrs);

        mReachedProgressBarHeight = (int) (mUnReachedProgressBarHeight * 2.5f);
        TypedArray ta = context.obtainStyledAttributes(attrs,
                R.styleable.RoundProgressBarWidthNumber);
        mRadius = (int) ta.getDimension(
                R.styleable.RoundProgressBarWidthNumber_radius, mRadius);
        ta.recycle();

        mPaint.setStyle(Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStrokeCap(Cap.ROUND);

    }

    /**
     * 这里默认在布局中padding值要么不设置,要么全部设置
     */
    @Override
    protected synchronized void onMeasure(int widthMeasureSpec,
                                          int heightMeasureSpec) {

        mMaxPaintWidth = Math.max(mReachedProgressBarHeight,
                mUnReachedProgressBarHeight);
        int expect = mRadius * 2 + mMaxPaintWidth + getPaddingLeft()
                + getPaddingRight();
        int width = resolveSize(expect, widthMeasureSpec);
        int height = resolveSize(expect, heightMeasureSpec);
        int realWidth = Math.min(width, height);

        mRadius = (realWidth - getPaddingLeft() - getPaddingRight() - mMaxPaintWidth) / 2;

        setMeasuredDimension(realWidth, realWidth);

    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {

        String text = getProgress() + "%";
        float textWidth = mPaint.measureText(text);
        float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;

        canvas.save();
        canvas.translate(getPaddingLeft() + mMaxPaintWidth / 2, getPaddingTop()
                + mMaxPaintWidth / 2);
        mPaint.setStyle(Style.STROKE);
        // draw unreaded bar
        mPaint.setColor(mUnReachedBarColor);
        mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
        // draw reached bar
        mPaint.setColor(mReachedBarColor);
        mPaint.setStrokeWidth(mReachedProgressBarHeight);
        float sweepAngle = getProgress() * 1.0f / getMax() * 360;
        canvas.drawArc(new RectF(0, 0, mRadius * 2, mRadius * 2), 0,
                sweepAngle, false, mPaint);
        // draw text
        mPaint.setStyle(Style.FILL);
        canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight,
                mPaint);

        canvas.restore();

    }

}

源代码下载地址:https://github.com/gdutxiaoxu/CustomProgressBar.git

转载请注明原博客地址:http://blog.csdn.net/gdutxiaoxu/article/details/51545889

参考博客:http://blog.csdn.net/lmj623565791?viewmode=contents

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏wym

见缝插针游戏--实现转圈

14210
来自专栏一“技”之长

Java开发GUI之单复选框 原

    单复选框在处理一些用户选项时十分方便。在Java的GUI体系中,复选框使用Checkbox类来创建,单选框实际上是将多个复选框结合成为组,同一组的复选框...

8520
来自专栏IT 指南者专栏

前端系列之CSS基础知识概述

1、什么是DIV (1)、div就是html一个普通标签,进行区域划分。div特性:独自占一行。独自不能实现复杂效果。必须结合css样式进行渲染。 (2)、d...

392110
来自专栏Keegan小钢

Android样式的开发:View Animation篇

drawable汇总篇讲过两个动画,animation-list定义帧动画,animated-rotate定义旋转动画,这两个属于drawable动画。除了dr...

10320
来自专栏deepcc

jQuery阻止冒泡和HTML默认操作

41550
来自专栏林德熙的博客

win10 uwp 拖动控件 Margin 移动Canvas 拖动控件Manipulation 拖动控件

我们会使用控件拖动,可以让我们做出好看的动画,那么我们如何移动控件,我将会告诉大家多个方法。其中第一个是最差的,最后的才是我希望大神你去用。

16800
来自专栏移动开发的那些事儿

自定义实现垂直滚动的TextView

通过控制y参数可实现文字不同的垂直距离,这里的x,y并不代表默认横向坐标为0,纵向坐标为0的坐标,具体详解我觉得这篇博客解释的比较清楚,我们主要关注的是参数y的...

17020
来自专栏Keegan小钢

Android样式的开发:Property Animation篇

前篇文章说过,Android框架还提供了两种动画体系,前一篇已经总结了视图动画(View Animation)的用法,本篇则接着总结另一种动画体系——属性动画(...

10130
来自专栏HenCoder

HenCoder Android 自定义 View 1-5: 绘制顺序

之前的内容在这里: HenCoder Android 开发进阶 自定义 View 1-1 绘制基础 HenCoder Android 开发进阶 自定义 View...

13430
来自专栏梦魇小栈

JQuery分析及实现part6之动画模块功能及实现

9920

扫码关注云+社区

领取腾讯云代金券