Android ProgressBar詳解以及自定義


版本:1.0日期:2014.5.16版權:© 2014 kince 轉載注明出處
這一次主要說一下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.HorizontalWidget.ProgressBar.SmallWidget.ProgressBar.LargeWidget.ProgressBar.InverseWidget.ProgressBar.Small.InverseWidget.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的話,

      ?

      1

      <progressbar android:id="@+id/progressbar" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:secondaryprogress="50"></progressbar>

      雖然沒有設置android:indeterminateDrawable,但是樣式Widget.ProgressBar.Horizontal已經幫我們設置好了。查看源碼如下:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      <style name="Widget.ProgressBar.Horizontal">

      <item name="android:indeterminateOnly">false</item>

      <item name="android:progressDrawable">@android:drawable/progress_horizontal</item>

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

      <item name="android:minHeight">20dip</item>

      <item name="android:maxHeight">20dip</item>

      <item name="android:mirrorForRtl">true</item>

      </style>

      先看一下progress_horizontal,源碼如下:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      <!--?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">

      </gradient></corners></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">

      </gradient></corners></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">

      </gradient></corners></shape>

      </clip>

      </item>

      </layer-list>

      可以看到,系統使用的是圖層方式,以覆蓋的方式進行的。所以如果需要其他的樣式的話,改變系統默認的值即可,或者參考一下系統自帶的樣式設置就行了。
      緊接着,說一下ProgressBar的方法,總體來說,可以分為兩個部分。一是和自身屬性相關的,比如獲取進度、設置進度的最大值、設置插入器等等。二是和繪制相關的部分,如圖所示:\
      所以、所以我們本次最重要的部分來了,那就是如何自定義一個漂亮ProgressBar。在自定義之前,先看一下系統是如何實現的。Android下ProgressBar的代碼量不算多,除去注釋估計也就是幾百行左右。首先從構造方法看是看,

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      /**

      * Create a new progress bar with range 0...100 and initial progress of 0.

      * @param context the application environment

      */

      public ProgressBar(Context context) {

      this(context, null);

      }

      public ProgressBar(Context context, AttributeSet attrs) {

      this(context, attrs, com.android.internal.R.attr.progressBarStyle);

      }

      public ProgressBar(Context context, AttributeSet attrs, int defStyle) {

      this(context, attrs, defStyle, 0);

      }

      /**

      * @hide

      */

      public ProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) {

      super(context, attrs, defStyle);

      mUiThreadId = Thread.currentThread().getId();

      initProgressBar();

      TypedArray a =

      context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, styleRes);

      mNoInvalidate = true;

      Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);

      if (drawable != null) {

      drawable = tileify(drawable, false);

      // Calling this method can set mMaxHeight, make sure the corresponding

      // XML attribute for mMaxHeight is read after calling this method

      setProgressDrawable(drawable);

      }

      mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);

      mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);

      mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth);

      mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight);

      mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight);

      mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);

      final int resID = a.getResourceId(

      com.android.internal.R.styleable.ProgressBar_interpolator,

      android.R.anim. linear_interpolator); // default to linear interpolator

      if (resID > 0) {

      setInterpolator(context, resID);

      }

      setMax(a.getInt(R.styleable.ProgressBar_max, mMax));

      setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));

      setSecondaryProgress(

      a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));

      drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);

      if (drawable != null) {

      drawable = tileifyIndeterminate(drawable);

      setIndeterminateDrawable(drawable);

      }

      mOnlyIndeterminate = a.getBoolean(

      R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate);

      mNoInvalidate = false;

      setIndeterminate( mOnlyIndeterminate || a.getBoolean(

      R.styleable.ProgressBar_indeterminate, mIndeterminate));

      mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);

      a.recycle();

      }

      樣式文件如下:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      R.styleable.Progre:

      <declare-styleable name="ProgressBar">

      <!-- Defines the maximum value the progress can take. -->

      <!-- Defines the default progress value, between 0 and max. -->

      <!-- Defines the secondary progress value, between 0 and max. This progress is drawn between

      the primary progress and the background.  It can be ideal for media scenarios such as

      showing the buffering progress while the default progress shows the play progress. -->

      <!-- Allows to enable the indeterminate mode. In this mode the progress

      bar plays an infinite looping animation. -->

      <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->

      <!-- Drawable used for the indeterminate mode. -->

      <!-- Drawable used for the progress mode. -->

      <!-- Duration of the indeterminate animation. -->

      <!-- Defines how the indeterminate mode should behave when the progress

      reaches max. -->

      <!-- Progress starts over from 0. -->

      <enum name="repeat" value="1">

      <!-- Progress keeps the current value and goes back to 0. -->

      <enum name="cycle" value="2">

      </enum></enum></attr>

      <!-- Timeout between frames of animation in milliseconds

      {@deprecated Not used by the framework.} -->

      </attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></declare-styleable>

      ProgressBar把三個構造方法都列出來了,並使用了遞歸調用的方式,還有一個方式就是分別在每一個構造方法中都調用初始化的代碼,個人覺得還是此處比較正規。然后看一下第三個構造方法,在這里主要做了兩件事情,一個是從attrs文件中讀取設置的屬性;一個是initProgressBar()方法,為ProgressBar設置一些默認的屬性值。

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      private void initProgressBar() {

      mMax = 100;

      mProgress = 0;

      mSecondaryProgress = 0;

      mIndeterminate = false;

      mOnlyIndeterminate = false;

      mDuration = 4000;

      mBehavior = AlphaAnimation.RESTART;

      mMinWidth = 24;

      mMaxWidth = 48;

      mMinHeight = 24;

      mMaxHeight = 48;

      }

      這就是默認的屬性值。這在自定義View中算是最基礎的了,不多說,不過在這里需要注意兩個地方。一是mUiThreadId,他是干嘛的呢,它獲取的是當前UI線程的id,然后在更新ProgressBar進度的時候進行一個判斷,如果是UI線程,那么直接進行更新,如果不是就post出去,使用Handler等進行更新。二是tileify(drawable, false)方法和tileifyIndeterminate(drawable)方法。這兩個方法主要是對Drawable進行一個解析、轉換的過程。在這里需要重點強調一下,在ProgressBar中,最重要的部分就是Drawable的使用了,因為不僅是它的背景包括進度等都是使用Drawable來完成的,所以在源碼中也可以看到基本上百分之七八十的代碼都是和Drawable有關的。因為這一部分篇幅較多,所以就不詳細介紹了,下面重點說一下如何繪制ProgressBar,首先看onMeasure()方法,

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      @Override

      protected synchronized void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {

      Drawable d = mCurrentDrawable;

      int dw = 0;

      int dh = 0;

      if (d != null) {

      dw = Math. max(mMinWidth , Math.min( mMaxWidth, d.getIntrinsicWidth()));

      dh = Math. max(mMinHeight , Math.min( mMaxHeight, d.getIntrinsicHeight()));

      }

      updateDrawableState();

      dw += mPaddingLeft + mPaddingRight;

      dh += mPaddingTop + mPaddingBottom;

      setMeasuredDimension( resolveSizeAndState(dw, widthMeasureSpec, 0),

      resolveSizeAndState(dh, heightMeasureSpec, 0));

      }

      這是測量View大小的方法,也就是ProgressBar的大小,因為每一個ProgressBar默認都會使用Drawable。所以ProgressBar的大小即是Drawable的大小加上Padding的大小,如果沒有Padding,那很顯然就是Drawable的大小。最后使用setMeasuredDimension()方法設置ProgressBar的大小。 按照正常的流程,有些朋友可能會想到重寫onLayout()方法了,但是這里ProgressBar只是一個View,不需要進行位置的處理。所以直接進入onDraw()方法,在

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      @Override

      protected synchronized void onDraw(Canvas canvas) {

      super.onDraw(canvas);

      Drawable d = mCurrentDrawable;

      if (d != null) {

      // Translate canvas so a indeterminate circular progress bar with padding

      // rotates properly in its animation

      canvas.save();

      if(isLayoutRtl() && mMirrorForRtl) {

      canvas.translate(getWidth() - mPaddingRight, mPaddingTop);

      canvas.scale(-1.0f, 1.0f);

      } else {

      canvas.translate(mPaddingLeft, mPaddingTop);

      }

      long time = getDrawingTime();

      if ( mHasAnimation) {

      mAnimation.getTransformation(time, mTransformation);

      float scale = mTransformation.getAlpha();

      try {

      mInDrawing = true;

      d.setLevel(( int) (scale * MAX_LEVEL));

      } finally {

      mInDrawing = false;

      }

      postInvalidateOnAnimation();

      }

      d.draw(canvas);

      canvas.restore();

      if ( mShouldStartAnimationDrawable && d instanceof Animatable) {

      ((Animatable) d).start();

      mShouldStartAnimationDrawable = false ;

      }

      }

      首先也是先獲取當前的Drawable對象,如果不為空就開始繪圖,先是一個判斷,根據布局的方向來轉移畫布,isLayoutRtl()是View類的方法,

      ?

      1

      2

      3

      public boolean isLayoutRtl() {

      return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);

      }

      這個LAYOUT_DIRECTION_RTL是LayoutDirection的一個常量,

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      package android.util;

      /**

      * A class for defining layout directions. A layout direction can be left-to-right (LTR)

      * or right-to-left (RTL). It can also be inherited (from a parent) or deduced from the default

      * language script of a locale.

      */

      public final class LayoutDirection {

      // No instantiation

      private LayoutDirection() {}

      /**

      * Horizontal layout direction is from Left to Right.

      */

      public static final int LTR = 0;

      /**

      * Horizontal layout direction is from Right to Left.

      */

      public static final int RTL = 1;

      /**

      * Horizontal layout direction is inherited.

      */

      public static final int INHERIT = 2;

      /**

      * Horizontal layout direction is deduced from the default language script for the locale.

      */

      public static final int LOCALE = 3;

      }

      然后再判斷有沒有動畫,如果有的話,就調用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文件可以這樣來寫了:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      <!--?xml version= "1.0" encoding ="utf-8"?-->

      <resources>

      <declare-styleable>

      </attr>

      <!-- attr-->

      <!-- attr-->

      </attr>

      <flag name="normal" value="0">

      <flag name="bold" value="1">

      <flag name="italic" value="2">

      </flag></flag></flag></attr>

      <flag name="left" value="0">

      <flag name="center" value="1">

      <flag name="right" value="2">

      </flag></flag></flag></attr>

      </attr></attr></declare-styleable>

      </resources>

      ps:我發現eclipse在寫declare-styleable不會自動提示,不清楚什么原因,知道的朋友望告知。 之后我們新建一個類繼承於ProgressBar,

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      /**

      * @author kince

      *

      */

      public class IndicatorProgressBar extends ProgressBar {

      public IndicatorProgressBar(Context context) {

      this(context, null);

      }

      public IndicatorProgressBar(Context context, AttributeSet attrs) {

      this(context, attrs, 0);

      }

      public IndicatorProgressBar(Context context, AttributeSet attrs,

      int defStyle) {

      super(context, attrs, defStyle);

      }

      }

      然后在第三個構造方法中初始化數據,因為用到了文本以及Drawable,所以還需要聲明全局變量,初始化完畢后代碼如下:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      /**

      *

      */

      package com.example.indicatorprogressbar.widget;

      import com.example.indicatorprogressbar.R;

      import android.content.Context;

      import android.content.res.TypedArray;

      import android.graphics.Color;

      import android.graphics.Paint;

      import android.graphics.Paint.Align;

      import android.graphics.drawable.Drawable;

      import android.text.TextPaint;

      import android.util.AttributeSet;

      import android.widget.ProgressBar;

      /**

      * @author kince

      *

      */

      public class IndicatorProgressBar extends ProgressBar {

      private TextPaint mTextPaint;

      private Drawable mDrawableIndicator;

      private int offset=5;

      public IndicatorProgressBar(Context context) {

      this(context, null);

      }

      public IndicatorProgressBar(Context context, AttributeSet attrs) {

      this(context, attrs, 0);

      mTextPaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);

      mTextPaint.density=getResources().getDisplayMetrics().density;

      mTextPaint.setColor(Color.WHITE);

      mTextPaint.setTextSize(10);

      mTextPaint.setTextAlign(Align.CENTER);

      mTextPaint.setFakeBoldText(true);

      }

      public IndicatorProgressBar(Context context, AttributeSet attrs,

      int defStyle) {

      super(context, attrs, defStyle);

      TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.IndicatorProgressBar, defStyle, 0);

      if(array!=null){

      mDrawableIndicator=array.getDrawable(R.styleable.IndicatorProgressBar_progressIndicator);

      offset=array.getInt(R.styleable.IndicatorProgressBar_offset, 0);

      array.recycle();

      }

      }

      }

      然后,為全局變量設置set、get方法,方便在程序中調用。

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      public Drawable getmDrawableIndicator() {

      return mDrawableIndicator ;

      }

      public void setmDrawableIndicator(Drawable mDrawableIndicator) {

      this.mDrawableIndicator = mDrawableIndicator;

      }

      public int getOffset() {

      return offset ;

      }

      public void setOffset(int offset) {

      this.offset = offset;

      }

      接下來,就是重寫onMeasure()、onDraw()方法了。在onMeasure()中,需要對進度條計算好具體大小,那根據上面的圖示,這個進度條的寬度和系統進度條的寬度是一樣的,也就是getMeasuredWidth();高度的話,因為加了一個指示器,所以高度是指示器的高度加上系統進度條的高度。因此在onMeasure()方法中就可以這樣來寫:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      @Override

      protected synchronized void onMeasure(int widthMeasureSpec,

      int heightMeasureSpec) {

      // TODO Auto-generated method stub

      super.onMeasure(widthMeasureSpec, heightMeasureSpec);

      if(mDrawableIndicator!=null){

      //獲取系統進度條的寬度 這個寬度也是自定義進度條的寬度 所以在這里直接賦值

      final int width=getMeasuredWidth();

      final int height=getMeasuredHeight()+getIndicatorHeight();

      setMeasuredDimension(width, height);

      }

      }

      /**

      * @category 獲取指示器的高度

      * @return

      */

      private int getIndicatorHeight(){

      if(mDrawableIndicator==null){

      return 0;

      }

      Rect r=mDrawableIndicator.copyBounds();

      int height=r.height();

      return height;

      }

      然后是onDraw()方法,因為在onMeasure()方法中增加了進度條的高度,所以在畫的時候需要將系統進度條與指示器分隔開來。在進度條的樣式文件中,我們是這樣配置的:

      ?

      1

      2

      3

      4

      5

      6

      7

      <style name="Widget.ProgressBar.RegularProgressBar">

      <item name="android:indeterminateOnly" >false </item>

      <item name="android:progressDrawable" >@drawable/progressbar </item>

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

      <item name= "android:minHeight">1dip</item >

      <item name= "android:maxHeight">10dip</item >

      </style>

      在android:progressDrawable的屬性中,使用的drawable是這樣的:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

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

      <item android:id="@android:id/background" android:drawable="@drawable/progressbar_bg">

      <item android:id="@+id/progress" android:drawable="@drawable/progressbar_bar">

      </item>

      <item android:id="@+id/pattern">

      <bitmap android:src="@drawable/progressbar_pattern" android:tilemode="repeat">

      </bitmap></item>

      </item></layer-list>

      可以發現,是一個layer類型的drawable,所以在計算大小的時候,需要特別考慮這個情況。代碼如下:

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      if (m_indicator != null) {

      if (progressDrawable != null

      && progressDrawable instanceof LayerDrawable) {

      LayerDrawable d = (LayerDrawable) progressDrawable;

      for (int i = 0; i < d.getNumberOfLayers(); i++) {

      d.getDrawable(i).getBounds(). top = getIndicatorHeight();

      d.getDrawable(i).getBounds(). bottom = d.getDrawable(i)

      .getBounds().height()

      + getIndicatorHeight();

      }

      } else if (progressDrawable != null) {

      progressDrawable.getBounds(). top = m_indicator

      .getIntrinsicHeight();

      progressDrawable.getBounds(). bottom = progressDrawable

      .getBounds().height() + getIndicatorHeight();

      }

      }

      然后需要更新進度條的位置,

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      private void updateProgressBar () {

      Drawable progressDrawable = getProgressDrawable();

      if (progressDrawable != null

      && progressDrawable instanceof LayerDrawable) {

      LayerDrawable d = (LayerDrawable) progressDrawable;

      final float scale = getScale(getProgress());

      // 獲取進度條 更新它的大小

      Drawable progressBar = d.findDrawableByLayerId(R.id.progress );

      final int width = d.getBounds(). right - d.getBounds().left ;

      if (progressBar != null) {

      Rect progressBarBounds = progressBar.getBounds();

      progressBarBounds. right = progressBarBounds.left

      + ( int ) (width * scale + 0.5f);

      progressBar.setBounds(progressBarBounds);

      }

      // 獲取疊加的圖層

      Drawable patternOverlay = d.findDrawableByLayerId(R.id.pattern );

      if (patternOverlay != null) {

      if (progressBar != null) {

      // 使疊加圖層適應進度條大小

      Rect patternOverlayBounds = progressBar.copyBounds();

      final int left = patternOverlayBounds.left ;

      final int right = patternOverlayBounds.right ;

      patternOverlayBounds. left = (left + 1 > right) ? left

      : left + 1;

      patternOverlayBounds. right = (right > 0) ? right - 1

      : right;

      patternOverlay.setBounds(patternOverlayBounds);

      } else {

      // 沒有疊加圖層

      Rect patternOverlayBounds = patternOverlay.getBounds();

      patternOverlayBounds. right = patternOverlayBounds.left

      + ( int ) (width * scale + 0.5f);

      patternOverlay.setBounds(patternOverlayBounds);

      }

      }

      }

      }

      最后,需要把指示器畫出來,

      ?

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      if (m_indicator != null) {

      canvas.save();

      int dx = 0;

      // 獲取系統進度條最右邊的位置 也就是頭部的位置

      if (progressDrawable != null

      && progressDrawable instanceof LayerDrawable) {

      LayerDrawable d = (LayerDrawable) progressDrawable;

      Drawable progressBar = d.findDrawableByLayerId(R.id.progress );

      dx = progressBar.getBounds(). right;

      } else if (progressDrawable != null) {

      dx = progressDrawable.getBounds().right ;

      }

      //加入offset

      dx = dx - getIndicatorWidth() / 2 - m_offset + getPaddingLeft();

      // 移動畫筆位置

      canvas.translate(dx, 0);

      // 畫出指示器

      m_indicator .draw(canvas);

      // 畫出進度數字

      canvas.drawText(

      m_formatter != null ? m_formatter .getText(getProgress())

      : Math.round(getScale(getProgress()) * 100.0f)

      + "%" , getIndicatorWidth() / 2,

      getIndicatorHeight() / 2 + 1, m_textPaint );

      // restore canvas to original

      canvas.restore();

      }

      源碼下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM