編輯:關於Android編程
雖然沒有設置android:indeterminateDrawable,但是樣式Widget.ProgressBar.Horizontal已經幫我們設置好了。查看源碼如下:
先看一下progress_horizontal,源碼如下:
可以看到,系統使用的是圖層方式,以覆蓋的方式進行的。所以如果需要其他的樣式的話,改變系統默認的值即可,或者參考一下系統自帶的樣式設置就行了。
/** * 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:ProgressBar把三個構造方法都列出來了,並使用了遞歸調用的方式,還有一個方式就是分別在每一個構造方法中都調用初始化的代碼,個人覺得還是此處比較正規。然後看一下第三個構造方法,在這裡主要做了兩件事情,一個是從attrs文件中讀取設置的屬性;一個是initProgressBar()方法,為ProgressBar設置一些默認的屬性值。
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()方法,
@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()方法,在
@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類的方法,
public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); }這個LAYOUT_DIRECTION_RTL是LayoutDirection的一個常量,
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文件可以這樣來寫了:
ps:我發現eclipse在寫declare-styleable不會自動提示,不清楚什麼原因,知道的朋友望告知。 之後我們新建一個類繼承於ProgressBar,attr> attr>
/** * @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,所以還需要聲明全局變量,初始化完畢後代碼如下:
/** * */ 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方法,方便在程序中調用。
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()方法中就可以這樣來寫:
@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()方法中增加了進度條的高度,所以在畫的時候需要將系統進度條與指示器分隔開來。在進度條的樣式文件中,我們是這樣配置的:
在android:progressDrawable的屬性中,使用的drawable是這樣的:
可以發現,是一個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(); }源碼下載:
AChartEngine是一個很強大的圖表引擎,我在上學的時候就接觸過,並且利用它做了一個傳感器的應用,想想現在也很久遠了,今天就把這個app的源碼貼出來供其他人研究這款
Android使用Handler進行實例化(new)時, 如: private Handler handler = new Handler(); 會報錯Ha
之前想要給statusbar和toolbar實現這樣的效果:為使得statusbar變為透明,在自定義theme中給statusbar添加了以下屬性: &l
轉帖請注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273),請尊重他人的