Android ProgressBar详解以及自定义

来源:互联网 发布:网络捕鱼游戏大厅 编辑:程序博客网 时间:2024/06/11 02:43

from http://blog.csdn.net/wangjinyu501/article/details/25963993

  这一次主要说一下Android下的进度条,为什么是它呢,因为近期被其各种美轮美奂的设计所倾倒,计划逐渐去实现。另外一个因素也是它也是为数不多的直接继承于View类的控件,从中可以学习到一些自定义控件的知识。下面列举了一些个人觉得还算漂亮的进度条,仅供参考。




  是不是很漂亮,其实就像上面图形展示的那样,进度条大体上无非就是这几种形式。这样一来肯定是需要自定义了,所以方向有两个:要么继承于系统的ProgressBar;要么继承于View类(前者就是如此实现)。那就先看一下系统的进度条吧。

   继承于View类,直接子类有AbsSeekBar和ContentLoadingProgressBar,其中AbsSeekBar的子类有SeekBar和RatingBar,可见这二者也是基于ProgressBar实现的。对于ProgressBar的使用,有三个地方需要注意一下:
  1、ProgressBar有两个进度,一个是android:progress,另一个是android:secondaryProgress。后者主要是为缓存需要所涉及的,比如在看网络视频时候都会有一个缓存的进度条以及还要一个播放的进度,在这里缓存的进度就可以是android:secondaryProgress,而播放进度就是android:progress。
  2、ProgressBar分为确定的和不确定的,上面说的播放进度、缓存等就是确定的。相反地,不确定的就是不清楚、不确定一个操作需要多长时间来完成,这个时候就需要用的不确定的ProgressBar了。这个是由属性android:indeterminate来控制的,如果设置为true的话,那么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" 
  然后再看一下ProgressBar的其他常用属性,

  关于这些属性的使用还是比较简单,不多做介绍。其中第一个android:animationResolution已经呗舍弃了,所以不要去研究它了。重点说一下android:progressDrawable以及android:indeterminateDrawable。那这个Drawable在ProgressBar中是如何使用的呢,如果我们是这样在xml中设置ProgressBar的话,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <ProgressBar  
  2.      android:id="@+id/progressbar"  
  3.      style="@android:style/Widget.ProgressBar.Horizontal"  
  4.      android:layout_width="match_parent"  
  5.      android:layout_height="wrap_content"  
  6.      android:secondaryProgress="50" />  
  虽然没有设置android:indeterminateDrawable,但是样式Widget.ProgressBar.Horizontal已经帮我们设置好了。查看源码如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <style name="Widget.ProgressBar.Horizontal">  
  2.     <item name="android:indeterminateOnly">false</item>  
  3.     <item name="android:progressDrawable">@android:drawable/progress_horizontal</item>  
  4.     <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>  
  5.     <item name="android:minHeight">20dip</item>  
  6.     <item name="android:maxHeight">20dip</item>  
  7.     <item name="android:mirrorForRtl">true</item>  
  8. </style>  
  先看一下progress_horizontal,源码如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!-- Copyright (C) 2008 The Android Open Source Project  
  3.   
  4.      Licensed under the Apache License, Version 2.0 (the "License");  
  5.      you may not use this file except in compliance with the License.  
  6.      You may obtain a copy of the License at  
  7.   
  8.           http://www.apache.org/licenses/LICENSE-2.0  
  9.   
  10.      Unless required by applicable law or agreed to in writing, software  
  11.      distributed under the License is distributed on an "AS IS" BASIS,  
  12.      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13.      See the License for the specific language governing permissions and  
  14.      limitations under the License.  
  15. -->  
  16.   
  17. <layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
  18.      
  19.     <item android:id="@android:id/background">  
  20.         <shape>  
  21.             <corners android:radius="5dip" />  
  22.             <gradient  
  23.                     android:startColor="#ff9d9e9d"  
  24.                     android:centerColor="#ff5a5d5a"  
  25.                     android:centerY="0.75"  
  26.                     android:endColor="#ff747674"  
  27.                     android:angle="270"  
  28.             />  
  29.         </shape>  
  30.     </item>  
  31.      
  32.     <item android:id="@android:id/secondaryProgress">  
  33.         <clip>  
  34.             <shape>  
  35.                 <corners android:radius="5dip" />  
  36.                 <gradient  
  37.                         android:startColor="#80ffd300"  
  38.                         android:centerColor="#80ffb600"  
  39.                         android:centerY="0.75"  
  40.                         android:endColor="#a0ffcb00"  
  41.                         android:angle="270"  
  42.                 />  
  43.             </shape>  
  44.         </clip>  
  45.     </item>  
  46.      
  47.     <item android:id="@android:id/progress">  
  48.         <clip>  
  49.             <shape>  
  50.                 <corners android:radius="5dip" />  
  51.                 <gradient  
  52.                         android:startColor="#ffffd300"  
  53.                         android:centerColor="#ffffb600"  
  54.                         android:centerY="0.75"  
  55.                         android:endColor="#ffffcb00"  
  56.                         android:angle="270"  
  57.                 />  
  58.             </shape>  
  59.         </clip>  
  60.     </item>  
  61.      
  62. </layer-list>  
  可以看到,系统使用的是图层方式,以覆盖的方式进行的。所以如果需要其他的样式的话,改变系统默认的值即可,或者参考一下系统自带的样式设置就行了。
  紧接着,说一下ProgressBar的方法,总体来说,可以分为两个部分。一是和自身属性相关的,比如获取进度、设置进度的最大值、设置插入器等等。二是和绘制相关的部分,如图所示:



  所以、所以我们本次最重要的部分来了,那就是如何自定义一个漂亮ProgressBar。在自定义之前,先看一下系统是如何实现的。Android下ProgressBar的代码量不算多,除去注释估计也就是几百行左右。首先从构造方法看是看,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /**  
  2.    * Create a new progress bar with range 0...100 and initial progress of 0.  
  3.    * @param context the application environment  
  4.    */  
  5.   public ProgressBar(Context context) {  
  6.       this(context, null);  
  7.   }  
  8.    
  9.   public ProgressBar(Context context, AttributeSet attrs) {  
  10.       this(context, attrs, com.android.internal.R.attr.progressBarStyle);  
  11.   }  
  12.   
  13.   public ProgressBar(Context context, AttributeSet attrs, int defStyle) {  
  14.       this(context, attrs, defStyle, 0);  
  15.   }  
  16.   
  17.   /**  
  18.    * @hide  
  19.    */  
  20.   public ProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) {  
  21.       super(context, attrs, defStyle);  
  22.       mUiThreadId = Thread.currentThread().getId();  
  23.       initProgressBar();  
  24.   
  25.       TypedArray a =  
  26.           context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, styleRes);  
  27.        
  28.       mNoInvalidate = true;  
  29.        
  30.       Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);  
  31.       if (drawable != null) {  
  32.           drawable = tileify(drawable, false);  
  33.           // Calling this method can set mMaxHeight, make sure the corresponding  
  34.           // XML attribute for mMaxHeight is read after calling this method  
  35.           setProgressDrawable(drawable);  
  36.       }  
  37.   
  38.   
  39.       mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);  
  40.   
  41.       mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);  
  42.       mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth);  
  43.       mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight);  
  44.       mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight);  
  45.   
  46.       mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);  
  47.   
  48.       final int resID = a.getResourceId(  
  49.               com.android.internal.R.styleable.ProgressBar_interpolator,  
  50.               android.R.anim. linear_interpolator); // default to linear interpolator  
  51.       if (resID > 0) {  
  52.           setInterpolator(context, resID);  
  53.       }  
  54.   
  55.       setMax(a.getInt(R.styleable.ProgressBar_max, mMax));  
  56.   
  57.       setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));  
  58.   
  59.       setSecondaryProgress(  
  60.               a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));  
  61.   
  62.       drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);  
  63.       if (drawable != null) {  
  64.           drawable = tileifyIndeterminate(drawable);  
  65.           setIndeterminateDrawable(drawable);  
  66.       }  
  67.   
  68.       mOnlyIndeterminate = a.getBoolean(  
  69.               R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate);  
  70.   
  71.       mNoInvalidate = false;  
  72.   
  73.       setIndeterminate( mOnlyIndeterminate || a.getBoolean(  
  74.               R.styleable.ProgressBar_indeterminate, mIndeterminate));  
  75.   
  76.       mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);  
  77.   
  78.       a.recycle();  
  79.   }  
  样式文件如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. R.styleable.Progre:  
  2.  <declare-styleable name="ProgressBar">  
  3.         <!-- Defines the maximum value the progress can take. -->  
  4.         <attr name="max" format="integer" />  
  5.         <!-- Defines the default progress value, between 0 and max. -->  
  6.         <attr name="progress" format="integer" />  
  7.         <!-- Defines the secondary progress value, between 0 and max. This progress is drawn between  
  8.              the primary progress and the background.  It can be ideal for media scenarios such as  
  9.              showing the buffering progress while the default progress shows the play progress. -->  
  10.         <attr name="secondaryProgress" format="integer" />  
  11.         <!-- Allows to enable the indeterminate mode. In this mode the progress  
  12.          bar plays an infinite looping animation. -->  
  13.         <attr name="indeterminate" format="boolean" />  
  14.         <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->  
  15.         <attr name="indeterminateOnly" format="boolean" />  
  16.         <!-- Drawable used for the indeterminate mode. -->  
  17.         <attr name="indeterminateDrawable" format="reference" />  
  18.         <!-- Drawable used for the progress mode. -->  
  19.         <attr name="progressDrawable" format="reference" />  
  20.         <!-- Duration of the indeterminate animation. -->  
  21.         <attr name="indeterminateDuration" format="integer" min="1" />  
  22.         <!-- Defines how the indeterminate mode should behave when the progress  
  23.         reaches max. -->  
  24.         <attr name="indeterminateBehavior">  
  25.             <!-- Progress starts over from 0. -->  
  26.             <enum name="repeat" value="1" />  
  27.             <!-- Progress keeps the current value and goes back to 0. -->  
  28.             <enum name="cycle" value="2" />  
  29.         </attr>  
  30.         <attr name="minWidth" format="dimension" />  
  31.         <attr name="maxWidth" />  
  32.         <attr name="minHeight" format="dimension" />  
  33.         <attr name="maxHeight" />  
  34.         <attr name="interpolator" format="reference" />  
  35.         <!-- Timeout between frames of animation in milliseconds  
  36.              {@deprecated Not used by the framework.} -->  
  37.         <attr name="animationResolution" format="integer" />  
  38.     </declare-styleable>  
  ProgressBar把三个构造方法都列出来了,并使用了递归调用的方式,还有一个方式就是分别在每一个构造方法中都调用初始化的代码,个人觉得还是此处比较正规。然后看一下第三个构造方法,在这里主要做了两件事情,一个是从attrs文件中读取设置的属性;一个是initProgressBar()方法,为ProgressBar设置一些默认的属性值。
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void initProgressBar() {  
  2.       mMax = 100;  
  3.       mProgress = 0;  
  4.       mSecondaryProgress = 0;  
  5.       mIndeterminate = false;  
  6.       mOnlyIndeterminate = false;  
  7.       mDuration = 4000;  
  8.       mBehavior = AlphaAnimation.RESTART;  
  9.       mMinWidth = 24;  
  10.       mMaxWidth = 48;  
  11.       mMinHeight = 24;  
  12.       mMaxHeight = 48;  
  13.   }  
  这就是默认的属性值。这在自定义View中算是最基础的了,不多说,不过在这里需要注意两个地方。一是mUiThreadId,他是干嘛的呢,它获取的是当前UI线程的id,然后在更新ProgressBar进度的时候进行一个判断,如果是UI线程,那么直接进行更新,如果不是就post出去,使用Handler等进行更新。二是tileify(drawable, false)方法和tileifyIndeterminate(drawable)方法。这两个方法主要是对Drawable进行一个解析、转换的过程。在这里需要重点强调一下,在ProgressBar中,最重要的部分就是Drawable的使用了,因为不仅是它的背景包括进度等都是使用Drawable来完成的,所以在源码中也可以看到基本上百分之七八十的代码都是和Drawable有关的。因为这一部分篇幅较多,所以就不详细介绍了,下面重点说一下如何绘制ProgressBar,首先看onMeasure()方法,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.    protected synchronized void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {  
  3.        Drawable d = mCurrentDrawable;  
  4.   
  5.        int dw = 0;  
  6.        int dh = 0;  
  7.        if (d != null) {  
  8.            dw = Math. max(mMinWidth , Math.min( mMaxWidth, d.getIntrinsicWidth()));  
  9.            dh = Math. max(mMinHeight , Math.min( mMaxHeight, d.getIntrinsicHeight()));  
  10.        }  
  11.        updateDrawableState();  
  12.        dw += mPaddingLeft + mPaddingRight;  
  13.        dh += mPaddingTop + mPaddingBottom;  
  14.   
  15.        setMeasuredDimension( resolveSizeAndState(dw, widthMeasureSpec, 0),  
  16.                resolveSizeAndState(dh, heightMeasureSpec, 0));  
  17.    }  
  这是测量View大小的方法,也就是ProgressBar的大小,因为每一个ProgressBar默认都会使用Drawable。所以ProgressBar的大小即是Drawable的大小加上Padding的大小,如果没有Padding,那很显然就是Drawable的大小。最后使用setMeasuredDimension()方法设置ProgressBar的大小。
  按照正常的流程,有些朋友可能会想到重写onLayout()方法了,但是这里ProgressBar只是一个View,不需要进行位置的处理。所以直接进入onDraw()方法,在
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.    protected synchronized void onDraw(Canvas canvas) {  
  3.        super.onDraw(canvas);  
  4.   
  5.        Drawable d = mCurrentDrawable;  
  6.        if (d != null) {  
  7.            // Translate canvas so a indeterminate circular progress bar with padding  
  8.            // rotates properly in its animation  
  9.            canvas.save();  
  10.            if(isLayoutRtl() && mMirrorForRtl) {  
  11.                canvas.translate(getWidth() - mPaddingRight, mPaddingTop);  
  12.                canvas.scale(-1.0f, 1.0f);  
  13.            } else {  
  14.                canvas.translate(mPaddingLeft, mPaddingTop);  
  15.            }  
  16.            long time = getDrawingTime();  
  17.            if ( mHasAnimation) {  
  18.                mAnimation.getTransformation(time, mTransformation);  
  19.                float scale = mTransformation.getAlpha();  
  20.                try {  
  21.                    mInDrawing = true;  
  22.                    d.setLevel(( int) (scale * MAX_LEVEL));  
  23.                } finally {  
  24.                    mInDrawing = false;  
  25.                }  
  26.                postInvalidateOnAnimation();  
  27.            }  
  28.            d.draw(canvas);  
  29.            canvas.restore();  
  30.            if ( mShouldStartAnimationDrawable && d instanceof Animatable) {  
  31.                ((Animatable) d).start();  
  32.                mShouldStartAnimationDrawable = false ;  
  33.            }  
  34.        }  
  首先也是先获取当前的Drawable对象,如果不为空就开始绘图,先是一个判断,根据布局的方向来转移画布,isLayoutRtl()是View类的方法,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public boolean isLayoutRtl() {  
  2.       return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);  
  3.   }  
  这个LAYOUT_DIRECTION_RTL是LayoutDirection的一个常量,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package android.util;  
  2.   
  3. /**  
  4. * A class for defining layout directions. A layout direction can be left-to-right (LTR)  
  5. * or right-to-left (RTL). It can also be inherited (from a parent) or deduced from the default  
  6. * language script of a locale.  
  7. */  
  8. public final class LayoutDirection {  
  9.   
  10.     // No instantiation  
  11.     private LayoutDirection() {}  
  12.   
  13.     /**  
  14.      * Horizontal layout direction is from Left to Right.  
  15.      */  
  16.     public static final int LTR = 0;  
  17.   
  18.     /**  
  19.      * Horizontal layout direction is from Right to Left.  
  20.      */  
  21.     public static final int RTL = 1;  
  22.   
  23.     /**  
  24.      * Horizontal layout direction is inherited.  
  25.      */  
  26.     public static final int INHERIT = 2;  
  27.   
  28.     /**  
  29.      * Horizontal layout direction is deduced from the default language script for the locale.  
  30.      */  
  31.     public static final int LOCALE = 3;  
  32. }  
  然后再判断有没有动画,如果有的话,就调用View类的postInvalidateOnAnimation()方法去执行一个动画。最后调用Drawable对象去画出来d.draw(canvas)。
  总的来说,系统的ProgressBar是和Drawable紧密相关的,所以说,如果我们自定义的ProgressBar和Drawable有关,那么完全可以继承于系统的ProgressBar来开发即可。如果你的自定义ProgressBar和Drawable关系不大,比如是这样的,
  其实,就不需要Drawable了,完全可以直接继承于View类开发。
  那下面就从两个方面来自定义ProgressBar,
一、继承于系统ProgressBar
  首先看一下上面给出的进度条其中的一个,
  思路:
  Mini ProgressBar在原生ProgressBar的基础上加入了一个指示器,并且有文字显示。实现的时候可以这样,
  
  也就是说,自定义的ProgressBar包含了两个部分,一部分是默认的;另一部分是新添加的指示器。其实指示器就是一个Drawable和文本的组合,而且直接画在系统ProgressBar的上面即可。接着,关于自定义的ProgressBar的属性也要定义一下,比如Drawable、比如文本、比如间隔等。所以attrs文件可以这样来写了:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <?xml version"1.0" encoding ="utf-8"?>  
  2. <resources>  
  3.   
  4.     <declare-styleable >  
  5.         <attr name"progressIndicator" format="reference" ></attr>  
  6.         <attr name"offset" format ="dimension"></ attr>  
  7.         <attr name"textSize" format ="dimension"></ attr>  
  8.         <attr name"textColor" format="reference|color" ></attr>  
  9.         <attr name"textStyle">  
  10.             <flag name"normal" value ="0" />  
  11.             <flag name"bold" value ="1" />  
  12.             <flag name"italic" value ="2" />  
  13.         </attr>  
  14.         <attr name"textAlign">  
  15.             <flag name"left" value ="0" />  
  16.             <flag name"center" value ="1" />  
  17.             <flag name"right" value ="2" />  
  18.         </attr>  
  19.     </declare-styleable >  
  20.   
  21. </resources>  
  ps:我发现eclipse在写declare-styleable不会自动提示,不清楚什么原因,知道的朋友望告知。
  
  之后我们新建一个类继承于ProgressBar,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /**  
  2.  * @author kince  
  3.  *  
  4.  */  
  5. public class IndicatorProgressBar extends ProgressBar {  
  6.   
  7.      public IndicatorProgressBar(Context context) {  
  8.            this(context, null);  
  9.   
  10.      }  
  11.   
  12.      public IndicatorProgressBar(Context context, AttributeSet attrs) {  
  13.            this(context, attrs, 0);  
  14.   
  15.      }  
  16.   
  17.      public IndicatorProgressBar(Context context, AttributeSet attrs,  
  18.                int defStyle) {  
  19.            super(context, attrs, defStyle);  
  20.   
  21.      }  
  22.   
  23.        
  24. }  
  然后在第三个构造方法中初始化数据,因为用到了文本以及Drawable,所以还需要声明全局变量,初始化完毕后代码如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1.  /**  
  2. *  
  3. */  
  4. package com.example.indicatorprogressbar.widget;  
  5.   
  6. import com.example.indicatorprogressbar.R;  
  7.   
  8. import android.content.Context;  
  9. import android.content.res.TypedArray;  
  10. import android.graphics.Color;  
  11. import android.graphics.Paint;  
  12. import android.graphics.Paint.Align;  
  13. import android.graphics.drawable.Drawable;  
  14. import android.text.TextPaint;  
  15. import android.util.AttributeSet;  
  16. import android.widget.ProgressBar;  
  17.   
  18. /**  
  19. * @author kince  
  20. *  
  21. */  
  22. public class IndicatorProgressBar extends ProgressBar {  
  23.   
  24.       
  25.      private TextPaint mTextPaint;  
  26.      private Drawable mDrawableIndicator;  
  27.      private int offset=5;  
  28.       
  29.       
  30.      public IndicatorProgressBar(Context context) {  
  31.           this(context, null);  
  32.   
  33.      }  
  34.   
  35.      public IndicatorProgressBar(Context context, AttributeSet attrs) {  
  36.           this(context, attrs, 0);  
  37.           mTextPaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);  
  38.           mTextPaint.density=getResources().getDisplayMetrics().density;  
  39.            
  40.           mTextPaint.setColor(Color.WHITE);  
  41.           mTextPaint.setTextSize(10);  
  42.           mTextPaint.setTextAlign(Align.CENTER);  
  43.           mTextPaint.setFakeBoldText(true);  
  44.      }  
  45.   
  46.      public IndicatorProgressBar(Context context, AttributeSet attrs,  
  47.                int defStyle) {  
  48.           super(context, attrs, defStyle);  
  49.   
  50.           TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.IndicatorProgressBar, defStyle, 0);  
  51.           if(array!=null){  
  52.                mDrawableIndicator=array.getDrawable(R.styleable.IndicatorProgressBar_progressIndicator);  
  53.                offset=array.getInt(R.styleable.IndicatorProgressBar_offset, 0);  
  54.                array.recycle();  
  55.           }  
  56.            
  57.      }  
  58.   
  59. }  
  然后,为全局变量设置set、get方法,方便在程序中调用。
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public Drawable getmDrawableIndicator() {  
  2.           return mDrawableIndicator ;  
  3.     }  
  4.   
  5.     public void setmDrawableIndicator(Drawable mDrawableIndicator) {  
  6.           this.mDrawableIndicator = mDrawableIndicator;  
  7.     }  
  8.   
  9.     public int getOffset() {  
  10.           return offset ;  
  11.     }  
  12.   
  13.     public void setOffset(int offset) {  
  14.           this.offset = offset;  
  15.     }  
  接下来,就是重写onMeasure()、onDraw()方法了。在onMeasure()中,需要对进度条计算好具体大小,那根据上面的图示,这个进度条的宽度和系统进度条的宽度是一样的,也就是getMeasuredWidth();高度的话,因为加了一个指示器,所以高度是指示器的高度加上系统进度条的高度。因此在onMeasure()方法中就可以这样来写:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected synchronized void onMeasure(int widthMeasureSpec,  
  3.         int heightMeasureSpec) {  
  4.     // TODO Auto-generated method stub  
  5.     super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  6.     if(mDrawableIndicator!=null){  
  7.         //获取系统进度条的宽度 这个宽度也是自定义进度条的宽度 所以在这里直接赋值  
  8.         final int width=getMeasuredWidth();  
  9.         final int height=getMeasuredHeight()+getIndicatorHeight();  
  10.         setMeasuredDimension(width, height);  
  11.     }  
  12.       
  13. }  
  14.   
  15. /**  
  16.  * @category 获取指示器的高度  
  17.  * @return  
  18.  */  
  19. private int getIndicatorHeight(){  
  20.     if(mDrawableIndicator==null){  
  21.         return 0;  
  22.     }  
  23.     Rect r=mDrawableIndicator.copyBounds();  
  24.     int height=r.height();  
  25.     return height;  
  26. }  
  然后是onDraw()方法,因为在onMeasure()方法中增加了进度条的高度,所以在画的时候需要将系统进度条与指示器分隔开来。在进度条的样式文件中,我们是这样配置的:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <style name="Widget.ProgressBar.RegularProgressBar">  
  2.         <item name="android:indeterminateOnly" >false </item>  
  3.         <item name="android:progressDrawable" >@drawable/progressbar </item>  
  4.         <item name="android:indeterminateDrawable" >@android:drawable/progress_indeterminate_horizontal </item>  
  5.         <item name"android:minHeight">1dip</item >  
  6.         <item name"android:maxHeight">10dip</item >  
  7.     </style >  
  在android:progressDrawable的属性中,使用的drawable是这样的:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >  
  2.   
  3.     <item  
  4.         android:id="@android:id/background"  
  5.         android:drawable="@drawable/progressbar_bg" />  
  6.     <item  
  7.         android:id="@+id/progress"  
  8.         android:drawable="@drawable/progressbar_bar" >  
  9.     </item >  
  10.     <item android:id="@+id/pattern">  
  11.         <bitmap  
  12.             android:src="@drawable/progressbar_pattern"  
  13.             android:tileMode="repeat" />  
  14.     </item >  
  15.   
  16. </layer-list>  
  可以发现,是一个layer类型的drawable,所以在计算大小的时候,需要特别考虑这个情况。代码如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. if (m_indicator != null) {  
  2.               if (progressDrawable != null  
  3.                        && progressDrawable instanceof LayerDrawable) {  
  4.                   LayerDrawable d = (LayerDrawable) progressDrawable;  
  5.   
  6.                    for (int i = 0; i < d.getNumberOfLayers(); i++) {  
  7.                        d.getDrawable(i).getBounds(). top = getIndicatorHeight();  
  8.                        d.getDrawable(i).getBounds(). bottom = d.getDrawable(i)  
  9.                                  .getBounds().height()  
  10.                                  + getIndicatorHeight();  
  11.                   }  
  12.              } else if (progressDrawable != null) {  
  13.                   progressDrawable.getBounds(). top = m_indicator  
  14.                             .getIntrinsicHeight();  
  15.                   progressDrawable.getBounds(). bottom = progressDrawable  
  16.                             .getBounds().height() + getIndicatorHeight();  
  17.              }  
  18.          }  
  然后需要更新进度条的位置,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void updateProgressBar () {  
  2.           Drawable progressDrawable = getProgressDrawable();  
  3.   
  4.            if (progressDrawable != null  
  5.                    && progressDrawable instanceof LayerDrawable) {  
  6.               LayerDrawable d = (LayerDrawable) progressDrawable;  
  7.   
  8.                final float scale = getScale(getProgress());  
  9.   
  10.                // 获取进度条 更新它的大小  
  11.               Drawable progressBar = d.findDrawableByLayerId(R.id.progress );  
  12.   
  13.                final int width = d.getBounds(). right - d.getBounds().left ;  
  14.   
  15.                if (progressBar != null) {  
  16.                    Rect progressBarBounds = progressBar.getBounds();  
  17.                    progressBarBounds. right = progressBarBounds.left  
  18.                              + ( int ) (width * scale + 0.5f);  
  19.                    progressBar.setBounds(progressBarBounds);  
  20.               }  
  21.   
  22.                // 获取叠加的图层  
  23.               Drawable patternOverlay = d.findDrawableByLayerId(R.id.pattern );  
  24.   
  25.                if (patternOverlay != null) {  
  26.                     if (progressBar != null) {  
  27.                          // 使叠加图层适应进度条大小  
  28.                         Rect patternOverlayBounds = progressBar.copyBounds();  
  29.                          final int left = patternOverlayBounds.left ;  
  30.                          final int right = patternOverlayBounds.right ;  
  31.   
  32.                         patternOverlayBounds. left = (left + 1 > right) ? left  
  33.                                   : left + 1;  
  34.                         patternOverlayBounds. right = (right > 0) ? right - 1  
  35.                                   : right;  
  36.                         patternOverlay.setBounds(patternOverlayBounds);  
  37.                    } else {  
  38.                          // 没有叠加图层  
  39.                         Rect patternOverlayBounds = patternOverlay.getBounds();  
  40.                         patternOverlayBounds. right = patternOverlayBounds.left  
  41.                                   + ( int ) (width * scale + 0.5f);  
  42.                         patternOverlay.setBounds(patternOverlayBounds);  
  43.                    }  
  44.               }  
  45.           }  
  46.      }  
  最后,需要把指示器画出来,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. if (m_indicator != null) {  
  2.               canvas.save();  
  3.                int dx = 0;  
  4.   
  5.                // 获取系统进度条最右边的位置 也就是头部的位置  
  6.                if (progressDrawable != null  
  7.                         && progressDrawable instanceof LayerDrawable) {  
  8.                    LayerDrawable d = (LayerDrawable) progressDrawable;  
  9.                    Drawable progressBar = d.findDrawableByLayerId(R.id.progress );  
  10.                    dx = progressBar.getBounds(). right;  
  11.               } else if (progressDrawable != null) {  
  12.                    dx = progressDrawable.getBounds().right ;  
  13.               }  
  14.   
  15.                //加入offset  
  16.               dx = dx - getIndicatorWidth() / 2 - m_offset + getPaddingLeft();  
  17.   
  18.                // 移动画笔位置  
  19.               canvas.translate(dx, 0);  
  20.                                    // 画出指示器  
  21.                m_indicator .draw(canvas);  
  22.               // 画出进度数字  
  23.               canvas.drawText(  
  24.                          m_formatter != null ? m_formatter .getText(getProgress())  
  25.                                   : Math.round(getScale(getProgress()) * 100.0f)  
  26.                                            + "%" , getIndicatorWidth() / 2,  
  27.                         getIndicatorHeight() / 2 + 1, m_textPaint );  
  28.   
  29.                // restore canvas to original  
  30.               canvas.restore();  
  31.           }  
源码下载:
Github地址:https://github.com/wangjinyu501/SaundProgressBar


  所以、所以我们本次最重要的部分来了,那就是如何自定义一个漂亮ProgressBar。在自定义之前,先看一下系统是如何实现的。Android下ProgressBar的代码量不算多,除去注释估计也就是几百行左右。首先从构造方法看是看,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /**  
  2.    * Create a new progress bar with range 0...100 and initial progress of 0.  
  3.    * @param context the application environment  
  4.    */  
  5.   public ProgressBar(Context context) {  
  6.       this(context, null);  
  7.   }  
  8.    
  9.   public ProgressBar(Context context, AttributeSet attrs) {  
  10.       this(context, attrs, com.android.internal.R.attr.progressBarStyle);  
  11.   }  
  12.   
  13.   public ProgressBar(Context context, AttributeSet attrs, int defStyle) {  
  14.       this(context, attrs, defStyle, 0);  
  15.   }  
  16.   
  17.   /**  
  18.    * @hide  
  19.    */  
  20.   public ProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) {  
  21.       super(context, attrs, defStyle);  
  22.       mUiThreadId = Thread.currentThread().getId();  
  23.       initProgressBar();  
  24.   
  25.       TypedArray a =  
  26.           context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, styleRes);  
  27.        
  28.       mNoInvalidate = true;  
  29.        
  30.       Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);  
  31.       if (drawable != null) {  
  32.           drawable = tileify(drawable, false);  
  33.           // Calling this method can set mMaxHeight, make sure the corresponding  
  34.           // XML attribute for mMaxHeight is read after calling this method  
  35.           setProgressDrawable(drawable);  
  36.       }  
  37.   
  38.   
  39.       mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);  
  40.   
  41.       mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);  
  42.       mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth);  
  43.       mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight);  
  44.       mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight);  
  45.   
  46.       mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);  
  47.   
  48.       final int resID = a.getResourceId(  
  49.               com.android.internal.R.styleable.ProgressBar_interpolator,  
  50.               android.R.anim. linear_interpolator); // default to linear interpolator  
  51.       if (resID > 0) {  
  52.           setInterpolator(context, resID);  
  53.       }  
  54.   
  55.       setMax(a.getInt(R.styleable.ProgressBar_max, mMax));  
  56.   
  57.       setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));  
  58.   
  59.       setSecondaryProgress(  
  60.               a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));  
  61.   
  62.       drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);  
  63.       if (drawable != null) {  
  64.           drawable = tileifyIndeterminate(drawable);  
  65.           setIndeterminateDrawable(drawable);  
  66.       }  
  67.   
  68.       mOnlyIndeterminate = a.getBoolean(  
  69.               R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate);  
  70.   
  71.       mNoInvalidate = false;  
  72.   
  73.       setIndeterminate( mOnlyIndeterminate || a.getBoolean(  
  74.               R.styleable.ProgressBar_indeterminate, mIndeterminate));  
  75.   
  76.       mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);  
  77.   
  78.       a.recycle();  
  79.   }  
  样式文件如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. R.styleable.Progre:  
  2.  <declare-styleable name="ProgressBar">  
  3.         <!-- Defines the maximum value the progress can take. -->  
  4.         <attr name="max" format="integer" />  
  5.         <!-- Defines the default progress value, between 0 and max. -->  
  6.         <attr name="progress" format="integer" />  
  7.         <!-- Defines the secondary progress value, between 0 and max. This progress is drawn between  
  8.              the primary progress and the background.  It can be ideal for media scenarios such as  
  9.              showing the buffering progress while the default progress shows the play progress. -->  
  10.         <attr name="secondaryProgress" format="integer" />  
  11.         <!-- Allows to enable the indeterminate mode. In this mode the progress  
  12.          bar plays an infinite looping animation. -->  
  13.         <attr name="indeterminate" format="boolean" />  
  14.         <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->  
  15.         <attr name="indeterminateOnly" format="boolean" />  
  16.         <!-- Drawable used for the indeterminate mode. -->  
  17.         <attr name="indeterminateDrawable" format="reference" />  
  18.         <!-- Drawable used for the progress mode. -->  
  19.         <attr name="progressDrawable" format="reference" />  
  20.         <!-- Duration of the indeterminate animation. -->  
  21.         <attr name="indeterminateDuration" format="integer" min="1" />  
  22.         <!-- Defines how the indeterminate mode should behave when the progress  
  23.         reaches max. -->  
  24.         <attr name="indeterminateBehavior">  
  25.             <!-- Progress starts over from 0. -->  
  26.             <enum name="repeat" value="1" />  
  27.             <!-- Progress keeps the current value and goes back to 0. -->  
  28.             <enum name="cycle" value="2" />  
  29.         </attr>  
  30.         <attr name="minWidth" format="dimension" />  
  31.         <attr name="maxWidth" />  
  32.         <attr name="minHeight" format="dimension" />  
  33.         <attr name="maxHeight" />  
  34.         <attr name="interpolator" format="reference" />  
  35.         <!-- Timeout between frames of animation in milliseconds  
  36.              {@deprecated Not used by the framework.} -->  
  37.         <attr name="animationResolution" format="integer" />  
  38.     </declare-styleable>  
  ProgressBar把三个构造方法都列出来了,并使用了递归调用的方式,还有一个方式就是分别在每一个构造方法中都调用初始化的代码,个人觉得还是此处比较正规。然后看一下第三个构造方法,在这里主要做了两件事情,一个是从attrs文件中读取设置的属性;一个是initProgressBar()方法,为ProgressBar设置一些默认的属性值。
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void initProgressBar() {  
  2.       mMax = 100;  
  3.       mProgress = 0;  
  4.       mSecondaryProgress = 0;  
  5.       mIndeterminate = false;  
  6.       mOnlyIndeterminate = false;  
  7.       mDuration = 4000;  
  8.       mBehavior = AlphaAnimation.RESTART;  
  9.       mMinWidth = 24;  
  10.       mMaxWidth = 48;  
  11.       mMinHeight = 24;  
  12.       mMaxHeight = 48;  
  13.   }  
  这就是默认的属性值。这在自定义View中算是最基础的了,不多说,不过在这里需要注意两个地方。一是mUiThreadId,他是干嘛的呢,它获取的是当前UI线程的id,然后在更新ProgressBar进度的时候进行一个判断,如果是UI线程,那么直接进行更新,如果不是就post出去,使用Handler等进行更新。二是tileify(drawable, false)方法和tileifyIndeterminate(drawable)方法。这两个方法主要是对Drawable进行一个解析、转换的过程。在这里需要重点强调一下,在ProgressBar中,最重要的部分就是Drawable的使用了,因为不仅是它的背景包括进度等都是使用Drawable来完成的,所以在源码中也可以看到基本上百分之七八十的代码都是和Drawable有关的。因为这一部分篇幅较多,所以就不详细介绍了,下面重点说一下如何绘制ProgressBar,首先看onMeasure()方法,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.    protected synchronized void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {  
  3.        Drawable d = mCurrentDrawable;  
  4.   
  5.        int dw = 0;  
  6.        int dh = 0;  
  7.        if (d != null) {  
  8.            dw = Math. max(mMinWidth , Math.min( mMaxWidth, d.getIntrinsicWidth()));  
  9.            dh = Math. max(mMinHeight , Math.min( mMaxHeight, d.getIntrinsicHeight()));  
  10.        }  
  11.        updateDrawableState();  
  12.        dw += mPaddingLeft + mPaddingRight;  
  13.        dh += mPaddingTop + mPaddingBottom;  
  14.   
  15.        setMeasuredDimension( resolveSizeAndState(dw, widthMeasureSpec, 0),  
  16.                resolveSizeAndState(dh, heightMeasureSpec, 0));  
  17.    }  
  这是测量View大小的方法,也就是ProgressBar的大小,因为每一个ProgressBar默认都会使用Drawable。所以ProgressBar的大小即是Drawable的大小加上Padding的大小,如果没有Padding,那很显然就是Drawable的大小。最后使用setMeasuredDimension()方法设置ProgressBar的大小。
  按照正常的流程,有些朋友可能会想到重写onLayout()方法了,但是这里ProgressBar只是一个View,不需要进行位置的处理。所以直接进入onDraw()方法,在
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.    protected synchronized void onDraw(Canvas canvas) {  
  3.        super.onDraw(canvas);  
  4.   
  5.        Drawable d = mCurrentDrawable;  
  6.        if (d != null) {  
  7.            // Translate canvas so a indeterminate circular progress bar with padding  
  8.            // rotates properly in its animation  
  9.            canvas.save();  
  10.            if(isLayoutRtl() && mMirrorForRtl) {  
  11.                canvas.translate(getWidth() - mPaddingRight, mPaddingTop);  
  12.                canvas.scale(-1.0f, 1.0f);  
  13.            } else {  
  14.                canvas.translate(mPaddingLeft, mPaddingTop);  
  15.            }  
  16.            long time = getDrawingTime();  
  17.            if ( mHasAnimation) {  
  18.                mAnimation.getTransformation(time, mTransformation);  
  19.                float scale = mTransformation.getAlpha();  
  20.                try {  
  21.                    mInDrawing = true;  
  22.                    d.setLevel(( int) (scale * MAX_LEVEL));  
  23.                } finally {  
  24.                    mInDrawing = false;  
  25.                }  
  26.                postInvalidateOnAnimation();  
  27.            }  
  28.            d.draw(canvas);  
  29.            canvas.restore();  
  30.            if ( mShouldStartAnimationDrawable && d instanceof Animatable) {  
  31.                ((Animatable) d).start();  
  32.                mShouldStartAnimationDrawable = false ;  
  33.            }  
  34.        }  
  首先也是先获取当前的Drawable对象,如果不为空就开始绘图,先是一个判断,根据布局的方向来转移画布,isLayoutRtl()是View类的方法,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public boolean isLayoutRtl() {  
  2.       return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);  
  3.   }  
  这个LAYOUT_DIRECTION_RTL是LayoutDirection的一个常量,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package android.util;  
  2.   
  3. /**  
  4. * A class for defining layout directions. A layout direction can be left-to-right (LTR)  
  5. * or right-to-left (RTL). It can also be inherited (from a parent) or deduced from the default  
  6. * language script of a locale.  
  7. */  
  8. public final class LayoutDirection {  
  9.   
  10.     // No instantiation  
  11.     private LayoutDirection() {}  
  12.   
  13.     /**  
  14.      * Horizontal layout direction is from Left to Right.  
  15.      */  
  16.     public static final int LTR = 0;  
  17.   
  18.     /**  
  19.      * Horizontal layout direction is from Right to Left.  
  20.      */  
  21.     public static final int RTL = 1;  
  22.   
  23.     /**  
  24.      * Horizontal layout direction is inherited.  
  25.      */  
  26.     public static final int INHERIT = 2;  
  27.   
  28.     /**  
  29.      * Horizontal layout direction is deduced from the default language script for the locale.  
  30.      */  
  31.     public static final int LOCALE = 3;  
  32. }  
  然后再判断有没有动画,如果有的话,就调用View类的postInvalidateOnAnimation()方法去执行一个动画。最后调用Drawable对象去画出来d.draw(canvas)。
  总的来说,系统的ProgressBar是和Drawable紧密相关的,所以说,如果我们自定义的ProgressBar和Drawable有关,那么完全可以继承于系统的ProgressBar来开发即可。如果你的自定义ProgressBar和Drawable关系不大,比如是这样的,
  其实,就不需要Drawable了,完全可以直接继承于View类开发。
  那下面就从两个方面来自定义ProgressBar,
一、继承于系统ProgressBar
  首先看一下上面给出的进度条其中的一个,
  思路:
  Mini ProgressBar在原生ProgressBar的基础上加入了一个指示器,并且有文字显示。实现的时候可以这样,
  
  也就是说,自定义的ProgressBar包含了两个部分,一部分是默认的;另一部分是新添加的指示器。其实指示器就是一个Drawable和文本的组合,而且直接画在系统ProgressBar的上面即可。接着,关于自定义的ProgressBar的属性也要定义一下,比如Drawable、比如文本、比如间隔等。所以attrs文件可以这样来写了:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <?xml version"1.0" encoding ="utf-8"?>  
  2. <resources>  
  3.   
  4.     <declare-styleable >  
  5.         <attr name"progressIndicator" format="reference" ></attr>  
  6.         <attr name"offset" format ="dimension"></ attr>  
  7.         <attr name"textSize" format ="dimension"></ attr>  
  8.         <attr name"textColor" format="reference|color" ></attr>  
  9.         <attr name"textStyle">  
  10.             <flag name"normal" value ="0" />  
  11.             <flag name"bold" value ="1" />  
  12.             <flag name"italic" value ="2" />  
  13.         </attr>  
  14.         <attr name"textAlign">  
  15.             <flag name"left" value ="0" />  
  16.             <flag name"center" value ="1" />  
  17.             <flag name"right" value ="2" />  
  18.         </attr>  
  19.     </declare-styleable >  
  20.   
  21. </resources>  
  ps:我发现eclipse在写declare-styleable不会自动提示,不清楚什么原因,知道的朋友望告知。
  
  之后我们新建一个类继承于ProgressBar,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /**  
  2.  * @author kince  
  3.  *  
  4.  */  
  5. public class IndicatorProgressBar extends ProgressBar {  
  6.   
  7.      public IndicatorProgressBar(Context context) {  
  8.            this(context, null);  
  9.   
  10.      }  
  11.   
  12.      public IndicatorProgressBar(Context context, AttributeSet attrs) {  
  13.            this(context, attrs, 0);  
  14.   
  15.      }  
  16.   
  17.      public IndicatorProgressBar(Context context, AttributeSet attrs,  
  18.                int defStyle) {  
  19.            super(context, attrs, defStyle);  
  20.   
  21.      }  
  22.   
  23.        
  24. }  
  然后在第三个构造方法中初始化数据,因为用到了文本以及Drawable,所以还需要声明全局变量,初始化完毕后代码如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1.  /**  
  2. *  
  3. */  
  4. package com.example.indicatorprogressbar.widget;  
  5.   
  6. import com.example.indicatorprogressbar.R;  
  7.   
  8. import android.content.Context;  
  9. import android.content.res.TypedArray;  
  10. import android.graphics.Color;  
  11. import android.graphics.Paint;  
  12. import android.graphics.Paint.Align;  
  13. import android.graphics.drawable.Drawable;  
  14. import android.text.TextPaint;  
  15. import android.util.AttributeSet;  
  16. import android.widget.ProgressBar;  
  17.   
  18. /**  
  19. * @author kince  
  20. *  
  21. */  
  22. public class IndicatorProgressBar extends ProgressBar {  
  23.   
  24.       
  25.      private TextPaint mTextPaint;  
  26.      private Drawable mDrawableIndicator;  
  27.      private int offset=5;  
  28.       
  29.       
  30.      public IndicatorProgressBar(Context context) {  
  31.           this(context, null);  
  32.   
  33.      }  
  34.   
  35.      public IndicatorProgressBar(Context context, AttributeSet attrs) {  
  36.           this(context, attrs, 0);  
  37.           mTextPaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);  
  38.           mTextPaint.density=getResources().getDisplayMetrics().density;  
  39.            
  40.           mTextPaint.setColor(Color.WHITE);  
  41.           mTextPaint.setTextSize(10);  
  42.           mTextPaint.setTextAlign(Align.CENTER);  
  43.           mTextPaint.setFakeBoldText(true);  
  44.      }  
  45.   
  46.      public IndicatorProgressBar(Context context, AttributeSet attrs,  
  47.                int defStyle) {  
  48.           super(context, attrs, defStyle);  
  49.   
  50.           TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.IndicatorProgressBar, defStyle, 0);  
  51.           if(array!=null){  
  52.                mDrawableIndicator=array.getDrawable(R.styleable.IndicatorProgressBar_progressIndicator);  
  53.                offset=array.getInt(R.styleable.IndicatorProgressBar_offset, 0);  
  54.                array.recycle();  
  55.           }  
  56.            
  57.      }  
  58.   
  59. }  
  然后,为全局变量设置set、get方法,方便在程序中调用。
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public Drawable getmDrawableIndicator() {  
  2.           return mDrawableIndicator ;  
  3.     }  
  4.   
  5.     public void setmDrawableIndicator(Drawable mDrawableIndicator) {  
  6.           this.mDrawableIndicator = mDrawableIndicator;  
  7.     }  
  8.   
  9.     public int getOffset() {  
  10.           return offset ;  
  11.     }  
  12.   
  13.     public void setOffset(int offset) {  
  14.           this.offset = offset;  
  15.     }  
  接下来,就是重写onMeasure()、onDraw()方法了。在onMeasure()中,需要对进度条计算好具体大小,那根据上面的图示,这个进度条的宽度和系统进度条的宽度是一样的,也就是getMeasuredWidth();高度的话,因为加了一个指示器,所以高度是指示器的高度加上系统进度条的高度。因此在onMeasure()方法中就可以这样来写:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected synchronized void onMeasure(int widthMeasureSpec,  
  3.         int heightMeasureSpec) {  
  4.     // TODO Auto-generated method stub  
  5.     super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  6.     if(mDrawableIndicator!=null){  
  7.         //获取系统进度条的宽度 这个宽度也是自定义进度条的宽度 所以在这里直接赋值  
  8.         final int width=getMeasuredWidth();  
  9.         final int height=getMeasuredHeight()+getIndicatorHeight();  
  10.         setMeasuredDimension(width, height);  
  11.     }  
  12.       
  13. }  
  14.   
  15. /**  
  16.  * @category 获取指示器的高度  
  17.  * @return  
  18.  */  
  19. private int getIndicatorHeight(){  
  20.     if(mDrawableIndicator==null){  
  21.         return 0;  
  22.     }  
  23.     Rect r=mDrawableIndicator.copyBounds();  
  24.     int height=r.height();  
  25.     return height;  
  26. }  
  然后是onDraw()方法,因为在onMeasure()方法中增加了进度条的高度,所以在画的时候需要将系统进度条与指示器分隔开来。在进度条的样式文件中,我们是这样配置的:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <style name="Widget.ProgressBar.RegularProgressBar">  
  2.         <item name="android:indeterminateOnly" >false </item>  
  3.         <item name="android:progressDrawable" >@drawable/progressbar </item>  
  4.         <item name="android:indeterminateDrawable" >@android:drawable/progress_indeterminate_horizontal </item>  
  5.         <item name"android:minHeight">1dip</item >  
  6.         <item name"android:maxHeight">10dip</item >  
  7.     </style >  
  在android:progressDrawable的属性中,使用的drawable是这样的:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >  
  2.   
  3.     <item  
  4.         android:id="@android:id/background"  
  5.         android:drawable="@drawable/progressbar_bg" />  
  6.     <item  
  7.         android:id="@+id/progress"  
  8.         android:drawable="@drawable/progressbar_bar" >  
  9.     </item >  
  10.     <item android:id="@+id/pattern">  
  11.         <bitmap  
  12.             android:src="@drawable/progressbar_pattern"  
  13.             android:tileMode="repeat" />  
  14.     </item >  
  15.   
  16. </layer-list>  
  可以发现,是一个layer类型的drawable,所以在计算大小的时候,需要特别考虑这个情况。代码如下:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. if (m_indicator != null) {  
  2.               if (progressDrawable != null  
  3.                        && progressDrawable instanceof LayerDrawable) {  
  4.                   LayerDrawable d = (LayerDrawable) progressDrawable;  
  5.   
  6.                    for (int i = 0; i < d.getNumberOfLayers(); i++) {  
  7.                        d.getDrawable(i).getBounds(). top = getIndicatorHeight();  
  8.                        d.getDrawable(i).getBounds(). bottom = d.getDrawable(i)  
  9.                                  .getBounds().height()  
  10.                                  + getIndicatorHeight();  
  11.                   }  
  12.              } else if (progressDrawable != null) {  
  13.                   progressDrawable.getBounds(). top = m_indicator  
  14.                             .getIntrinsicHeight();  
  15.                   progressDrawable.getBounds(). bottom = progressDrawable  
  16.                             .getBounds().height() + getIndicatorHeight();  
  17.              }  
  18.          }  
  然后需要更新进度条的位置,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void updateProgressBar () {  
  2.           Drawable progressDrawable = getProgressDrawable();  
  3.   
  4.            if (progressDrawable != null  
  5.                    && progressDrawable instanceof LayerDrawable) {  
  6.               LayerDrawable d = (LayerDrawable) progressDrawable;  
  7.   
  8.                final float scale = getScale(getProgress());  
  9.   
  10.                // 获取进度条 更新它的大小  
  11.               Drawable progressBar = d.findDrawableByLayerId(R.id.progress );  
  12.   
  13.                final int width = d.getBounds(). right - d.getBounds().left ;  
  14.   
  15.                if (progressBar != null) {  
  16.                    Rect progressBarBounds = progressBar.getBounds();  
  17.                    progressBarBounds. right = progressBarBounds.left  
  18.                              + ( int ) (width * scale + 0.5f);  
  19.                    progressBar.setBounds(progressBarBounds);  
  20.               }  
  21.   
  22.                // 获取叠加的图层  
  23.               Drawable patternOverlay = d.findDrawableByLayerId(R.id.pattern );  
  24.   
  25.                if (patternOverlay != null) {  
  26.                     if (progressBar != null) {  
  27.                          // 使叠加图层适应进度条大小  
  28.                         Rect patternOverlayBounds = progressBar.copyBounds();  
  29.                          final int left = patternOverlayBounds.left ;  
  30.                          final int right = patternOverlayBounds.right ;  
  31.   
  32.                         patternOverlayBounds. left = (left + 1 > right) ? left  
  33.                                   : left + 1;  
  34.                         patternOverlayBounds. right = (right > 0) ? right - 1  
  35.                                   : right;  
  36.                         patternOverlay.setBounds(patternOverlayBounds);  
  37.                    } else {  
  38.                          // 没有叠加图层  
  39.                         Rect patternOverlayBounds = patternOverlay.getBounds();  
  40.                         patternOverlayBounds. right = patternOverlayBounds.left  
  41.                                   + ( int ) (width * scale + 0.5f);  
  42.                         patternOverlay.setBounds(patternOverlayBounds);  
  43.                    }  
  44.               }  
  45.           }  
  46.      }  
  最后,需要把指示器画出来,
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. if (m_indicator != null) {  
  2.               canvas.save();  
  3.                int dx = 0;  
  4.   
  5.                // 获取系统进度条最右边的位置 也就是头部的位置  
  6.                if (progressDrawable != null  
  7.                         && progressDrawable instanceof LayerDrawable) {  
  8.                    LayerDrawable d = (LayerDrawable) progressDrawable;  
  9.                    Drawable progressBar = d.findDrawableByLayerId(R.id.progress );  
  10.                    dx = progressBar.getBounds(). right;  
  11.               } else if (progressDrawable != null) {  
  12.                    dx = progressDrawable.getBounds().right ;  
  13.               }  
  14.   
  15.                //加入offset  
  16.               dx = dx - getIndicatorWidth() / 2 - m_offset + getPaddingLeft();  
  17.   
  18.                // 移动画笔位置  
  19.               canvas.translate(dx, 0);  
  20.                                    // 画出指示器  
  21.                m_indicator .draw(canvas);  
  22.               // 画出进度数字  
  23.               canvas.drawText(  
  24.                          m_formatter != null ? m_formatter .getText(getProgress())  
  25.                                   : Math.round(getScale(getProgress()) * 100.0f)  
  26.                                            + "%" , getIndicatorWidth() / 2,  
  27.                         getIndicatorHeight() / 2 + 1, m_textPaint );  
  28.   
  29.                // restore canvas to original  
  30.               canvas.restore();  
  31.           }  
源码下载:
Github地址:https://github.com/wangjinyu501/SaundProgressBar
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 屁股上长了纹路怎么办 手机充电头歪了怎么办 屁股挠烂了化脓怎么办 手机充电那坏了怎么办 孩子在学校被老师冤枉怎么办 初中学校不好我该怎么办 天气太热屁股淹了怎么办 骑车骑的屁股疼怎么办 爬山时屁股摔紫青了怎么办 宝宝不肯脱裤子拉粑粑怎么办 国家对无地农民怎么办 生完孩子骨架变大怎么办 17岁长高很慢怎么办? 出月子腿着凉了怎么办 脚着凉了脚疼怎么办 腿着凉了特别疼怎么办 孩子骨龄大2两年怎么办 和人吃饭很尴尬怎么办 头不自觉向右偏怎么办 靠墙站立腰疼怎么办 小腿酸痛乏力肌肉萎缩怎么办 搬重物后手臂疼怎么办 和尚鹦鹉吃了盐怎么办 刚买鹦鹉不上手怎么办 word的文件时间改了怎么办 图强gps编码丢失怎么办 武统台湾后岛民怎么办 没有你我怎么办是什么歌 ios 12软件闪退怎么办 来大姨妈想吐怎么办 3岁宝宝体重轻怎么办 硕士延期毕业考上博士怎么办 中国人移民欧洲饮食不习惯怎么办 出车祸了报警警察不管怎么办 高中的孩子不好好上学怎么办 和老公消费观念不合拍怎么办 去医院没带现金怎么办 微信读书下架了怎么办 24岁血压有点高怎么办 吃鸡鼠标弹出来怎么办 电脑分辨率调错了怎么办