Android ProgressBar详解以及自定义
- Widget.ProgressBar.Horizontal
- Widget.ProgressBar.Small
- Widget.ProgressBar.Large
- Widget.ProgressBar.Inverse
- Widget.ProgressBar.Small.Inverse
- Widget.ProgressBar.Large.Inverse
- 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:id="@+id/progressbar" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:secondaryProgress="50" />虽然没有设置android:indeterminateDrawable,但是样式Widget.ProgressBar.Horizontal已经帮我们设置好了。查看源码如下:
<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>
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2008 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <shape> <corners android:radius="5dip" /> <gradient android:startColor="#ff9d9e9d" android:centerColor="#ff5a5d5a" android:centerY="0.75" android:endColor="#ff747674" android:angle="270" /> </shape> </item> <item android:id="@android:id/secondaryProgress"> <clip> <shape> <corners android:radius="5dip" /> <gradient android:startColor="#80ffd300" android:centerColor="#80ffb600" android:centerY="0.75" android:endColor="#a0ffcb00" android:angle="270" /> </shape> </clip> </item> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="5dip" /> <gradient android:startColor="#ffffd300" android:centerColor="#ffffb600" android:centerY="0.75" android:endColor="#ffffcb00" android:angle="270" /> </shape> </clip> </item> </layer-list>
所以、所以我们本次最重要的部分来了,那就是如何自定义一个漂亮ProgressBar。在自定义之前,先看一下系统是如何实现的。Android下ProgressBar的代码量不算多,除去注释估计也就是几百行左右。首先从构造方法看是看,
/** * 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(); }样式文件如下:
R.styleable.Progre: <declare-styleable name="ProgressBar"> <!-- Defines the maximum value the progress can take. --> <attr name="max" format="integer" /> <!-- Defines the default progress value, between 0 and max. --> <attr name="progress" format="integer" /> <!-- 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. --> <attr name="secondaryProgress" format="integer" /> <!-- Allows to enable the indeterminate mode. In this mode the progress bar plays an infinite looping animation. --> <attr name="indeterminate" format="boolean" /> <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). --> <attr name="indeterminateOnly" format="boolean" /> <!-- Drawable used for the indeterminate mode. --> <attr name="indeterminateDrawable" format="reference" /> <!-- Drawable used for the progress mode. --> <attr name="progressDrawable" format="reference" /> <!-- Duration of the indeterminate animation. --> <attr name="indeterminateDuration" format="integer" min="1" /> <!-- Defines how the indeterminate mode should behave when the progress reaches max. --> <attr name="indeterminateBehavior"> <!-- 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" /> </attr> <attr name="minWidth" format="dimension" /> <attr name="maxWidth" /> <attr name="minHeight" format="dimension" /> <attr name="maxHeight" /> <attr name="interpolator" format="reference" /> <!-- Timeout between frames of animation in milliseconds {@deprecated Not used by the framework.} --> <attr name="animationResolution" format="integer" /> </declare-styleable>
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; }
@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的大小。
@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 ; } }
public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); }
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; }
<?xml version= "1.0" encoding ="utf-8"?> <resources> <declare-styleable > <attr name= "progressIndicator" format="reference" ></attr> <attr name= "offset" format ="dimension"></ attr> <attr name= "textSize" format ="dimension"></ attr> <attr name= "textColor" format="reference|color" ></attr> <attr name= "textStyle"> <flag name= "normal" value ="0" /> <flag name= "bold" value ="1" /> <flag name= "italic" value ="2" /> </attr> <attr name= "textAlign"> <flag name= "left" value ="0" /> <flag name= "center" value ="1" /> <flag name= "right" value ="2" /> </attr> </declare-styleable > </resources>
/** * @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); } }
/** * */ 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(); } } }
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; }
@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; }
<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 >
<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" /> </item > </layer-list>可以发现,是一个layer类型的drawable,所以在计算大小的时候,需要特别考虑这个情况。代码如下:
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(); } }
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); } } } }
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(); }源码下载:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。