Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android官方開發文檔Training系列課程中文版:創建自定義View之View的繪制

Android官方開發文檔Training系列課程中文版:創建自定義View之View的繪制

編輯:關於Android編程

自定義View最重要的部分就是它的樣子了。自定義View的繪制根據應用的需要或者簡單亦或者復雜。這節課的內容涵蓋了大多數通用的知識點。

重寫onDraw()方法

繪制自定義View很重要的一個步驟就是重寫它的onDraw()方法。該方法含有一個Canvas對象作為參數,用來使View繪制它本身的內容。Canvas類定義了用於繪制文本,線條,位圖以及許多其它基礎的物理圖形的方法。你可以在onDraw()方法中使用這些方法來創建屬於你自己的UI效果。

在開始任何繪制之前,必須先創建一個Paint對象。後面的部分會討論Paint的相關知識。

創建繪制對象

android.graphics框架將繪制分為了兩塊區域:

要繪制什麼,由Canvas控制 如何繪制,由Paint控制

舉個例子,Canvas提供了用於繪制線條的方法,而Paint則定義了線條的顏色。Canvas擁有繪制矩形的方法,而Paint則定義了是否要使顏色填充這個矩形。簡而言之,Canvas定義了你繪制在屏幕上的形狀,而Paint則定義了這些形狀的顏色、風格以及字體等等。

所以,在開始繪制任何事物之前,你需要先創建一個或多個Paint對象。示例PieChart將這些工作放在了一個名為init的方法中,它由構造方法調用:

private void init() {
   mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   mTextPaint.setColor(mTextColor);
   if (mTextHeight == 0) {
       mTextHeight = mTextPaint.getTextSize();
   } else {
       mTextPaint.setTextSize(mTextHeight);
   }
   mPiePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   mPiePaint.setStyle(Paint.Style.FILL);
   mPiePaint.setTextSize(mTextHeight);
   mShadowPaint = new Paint(0);
   mShadowPaint.setColor(0xff101010);
   mShadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));
   ...

提前創建對象是一項非常重要的優化手段。View會很頻繁的繪制,創建繪制對象的開銷十分高昂。在onDraw()方法中創建繪制對象會明顯的降低應用的性能,並可能會導致UI出現停頓。

處理布局事件

為了可以正確的繪制View,首先需要知道View的尺寸。復雜一點的View經常需要執行多次布局計算。你永遠不要假定View的尺寸,就算是只有一款應用使用了你的View。因為APP需要處理不同的屏幕尺寸,不同的屏幕密度,以及在垂直模式及水平模式下不同的高寬比。

盡管View擁有很多測量尺寸的方法,但是絕大多數是不需要重寫的。如果View不需要特別控制它的尺寸,你只需要重寫一個方法:onSizeChanged().

onSizeChanged()方法會在首次分配尺寸的時候調用,如果尺寸再次變更時則會再次調用。我們在onSizeChanged()方法中計算View的位置、尺寸以及其它任何與尺寸相關的值,而不是在每次繪制的時候重新計算它們。在示例PieChart中,onSizeChanged()方法內部計算了餅圖的矩形邊框以及文本和其它可視元素的相對位置。

當View被分配了一個尺寸時,布局管理器會假設該尺寸包含了View所有的內邊距。所以在計算View的尺寸時要將View的內邊距計算在內。下面的代碼段展示了PieChart.onSizeChanged()是如何做的:

       // Account for padding
       float xpad = (float)(getPaddingLeft() + getPaddingRight());
       float ypad = (float)(getPaddingTop() + getPaddingBottom());
       // Account for the label
       if (mShowText) xpad += mTextWidth;
       float ww = (float)w - xpad;
       float hh = (float)h - ypad;
       // Figure out how big we can make the pie.
       float diameter = Math.min(ww, hh);

如果你想更精細的控制View布局的參數,請實現onMeasure()方法。這個方法的兩個參數都為View.MeasureSpec。它們用於告訴View的父布局希望View是多大,這個View的尺寸是最大值還是只是一個建議值等等。隨著優化,這些值被存放在一個被包裝後的整型值內,你必須使用View.MeasureSpec的一個靜態方法來提取存儲在這個整型值內的信息。

下面是onMeasure()方法的一個示例。在這個實現中,PieCart嘗試將自己的區域擴大到內部標簽的大小。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   // Try for a width based on our minimum
   int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
   int w = resolveSizeAndState(minw, widthMeasureSpec, 1);
   // Whatever the width ends up being, ask for a height that would let the pie
   // get as big as it can
   int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
   int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);
   setMeasuredDimension(w, h);
}

這裡有三個非常重要的點需要關注:

這個公式將View的內邊距一並計算在內。正如前面所說的,這是View本身的職責。 輔助方法resolveSizeAndState()用於創建最終的寬度值與高度值。這個輔助方法通過比較View的期望值與onMeasure()回調的spec值,最後返回一個適當的View.MeasureSpec值。 onMeasure()沒有返回值。相反的,該方法通過調用setMeasuredDimension()方法與外界交流。調用這個方法是強制要求的。如果你忽略了這個調用,那麼View類會拋出一個運行時異常。

繪制

完成對象創建代碼與尺寸測量代碼之後,接下來就可以實現onDraw()方法了。每個View的onDraw()方法都不相同,但是它們還是有一些相同特點存在的:

使用drawText()方法繪制文本。通過setTypeface()方法指定字體類型,通過setColor()方法設置文本顏色。 通過drawRect()、drawOval()和drawArc()繪制基礎圖形。通過setStyle()更改圖形是填充模式還是繪制輪廓模式,或者兩者都不是。 通過Path類來繪制更加復雜的圖形。通過給Path對象添加線條及曲線來定義形狀,然後通過drawPath()方法繪制這些形狀。只是這些基礎形狀可以通過setStyle()方法來定義它們的Path風格。 通過創建LinearGradient對象可以定義梯度填充模式。調用setShader()方法來填充形狀。 通過drawBitmap()方法繪制位圖。

舉個例子,下面的代碼用來繪制PieChart。它混合使用了文本、線條以及形狀。

protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);
   // Draw the shadow
   canvas.drawOval(
           mShadowBounds,
           mShadowPaint
   );
   // Draw the label text
   canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);
   // Draw the pie slices
   for (int i = 0; i < mData.size(); ++i) {
       Item it = mData.get(i);
       mPiePaint.setShader(it.mShader);
       canvas.drawArc(mBounds,
               360 - it.mEndAngle,
               it.mEndAngle - it.mStartAngle,
               true, mPiePaint);
   }
   // Draw the pointer
   canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
   canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved