<h2>自定義繪製</h2> <p>自定義視圖的最重要部分是它的外觀。自定義繪圖根據您的應用程序的須要能夠簡單或複雜。本節課將介紹一些最多見的操做。</p> <p> </p> <h3>重寫 onDraw()</h3> <p>在繪製自定義視圖中的最重要步驟是重寫 <a href="http://developer.android.com/reference/android/view/View.html#onDraw(android.graphics.Canvas)" target="_blank">onDraw()</a> 方法。<a href="http://developer.android.com/reference/android/view/View.html#onDraw(android.graphics.Canvas)" target="_blank">OnDraw()</a> 的參數是一個視圖能夠使用來繪製自身的畫布對象。<a href="http://developer.android.com/reference/android/graphics/Canvas.html" target="_blank">Canvas</a> 類定義用於繪製文本、 線條、 位圖和許多其餘圖形基元的方法。你能夠在 <a href="http://developer.android.com/reference/android/view/View.html#onDraw(android.graphics.Canvas)" target="_blank">onDraw()</a> 中使用這些方法,以建立您的自定義用戶界面 (UI)。</p> <p>您調用任何繪圖方法以前,有必要建立一個 <a href="http://developer.android.com/reference/android/graphics/Paint.html" target="_blank">Paint</a> 對象。下一節中更詳細地討論它。</p> <p> </p> <h3>建立繪圖對象</h3> <p><a href="http://developer.android.com/reference/android/graphics/package-summary.html" target="_blank">android.graphics</a> 框架將繪圖分爲兩個方面:</p> <ul> <li><a href="http://developer.android.com/reference/android/graphics/Canvas.html" target="_blank">Canvas</a> 用於處理畫什麼 </li> <li><a href="http://developer.android.com/reference/android/graphics/Paint.html" target="_blank">Paint</a> 用於處理怎麼畫 </li> </ul> <p>例如,<a href="http://developer.android.com/reference/android/graphics/Canvas.html" target="_blank">Canvas</a> 提供了一個方法用於畫線,<a href="http://developer.android.com/reference/android/graphics/Paint.html" target="_blank">Paint</a> 提供了方法指定線的顏色。<a href="http://developer.android.com/reference/android/graphics/Canvas.html" target="_blank">Canvas</a> 有一個方法畫矩形,<a href="http://developer.android.com/reference/android/graphics/Paint.html" target="_blank">Paint</a> 定義填充矩形的顏色或不填充。簡單的說, <a href="http://developer.android.com/reference/android/graphics/Canvas.html" target="_blank">Canvas</a> 定義您能在屏幕在繪製的圖形, <a href="http://developer.android.com/reference/android/graphics/Paint.html" target="_blank">Paint</a> 定義每一個圖形的顏色,樣式,字體等。</p> <p>因此在您繪製任何內容以前,須要建立一個或多個 <a href="http://developer.android.com/reference/android/graphics/Paint.html" target="_blank">Paint</a> 對象。PieChart 示例中由構造方法調用的一個 init 方法:</p> <div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre><span style="color: #0000ff">private</span> <span style="color: #0000ff">void</span> init() { mTextPaint = <span style="color: #0000ff">new</span> Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setColor(mTextColor); <span style="color: #0000ff">if</span> (mTextHeight == 0) { mTextHeight = mTextPaint.getTextSize(); } <span style="color: #0000ff">else</span> { mTextPaint.setTextSize(mTextHeight); }html
mPiePaint = <span style="color: #0000ff">new</span> Paint(Paint.ANTI_ALIAS_FLAG); mPiePaint.setStyle(Paint.Style.FILL); mPiePaint.setTextSize(mTextHeight);android
mShadowPaint = <span style="color: #0000ff">new</span> Paint(0); mShadowPaint.setColor(0xff101010); mShadowPaint.setMaskFilter(<span style="color: #0000ff">new</span> BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));canvas
...</pre>框架
</div>ide
<br />佈局
<p>在構造時建立對象是重要的優化。視圖重畫至關頻繁,許多繪圖對象的初始化性能低下。在 <a href="http://developer.android.com/reference/android/view/View.html#onDraw(android.graphics.Canvas)" target="_blank">onDraw()</a> 方法中建立繪圖對象會下降性能,可能命名您的 UI 緩慢。</p>性能
<p> </p>字體
<h3>處理佈局事件</h3>優化
<p>要正確地繪製您自定義的視圖,您須要知道它是什麼尺寸。複雜的自定義視圖一般須要在屏幕上執行多個佈局計算根據大小和其區域的形狀。您永遠不該假設您在屏幕上的視圖的大小。即便只有一個應用程序使用您的視圖,該應用程序須要處理不一樣的屏幕大小、 多個屏幕密度和在縱向和橫向模式下的各類長寬比。</p>spa
<p>雖然 <a href="http://developer.android.com/reference/android/view/View.html" target="_blank">View</a> 有不少方法處理測量,大多時候都不須要重寫。若是您的視圖並不須要特別控制它的大小,您只須要重寫一個方法: <a href="http://developer.android.com/reference/android/view/View.html#onSizeChanged(int, int, int, int)" target="_blank">onSizeChanged()</a>。</p>
<p><a href="http://developer.android.com/reference/android/view/View.html#onSizeChanged(int, int, int, int)" target="_blank">onSizeChanged()</a> 在您的視圖首次分配大小,又或您的視圖由於任何緣由更改大小時被調用。應該在此方法中計算座標、尺寸和其它相關值,而不是在繪製時計算。在 PicChart 示例中, <a href="http://developer.android.com/reference/android/view/View.html#onSizeChanged(int, int, int, int)" target="_blank">onSizeChanged()</a> 內將計算餅圖圖表的邊框和文本標籤和其餘可視元素的相對位置。當您查看分配大小時,佈局管理器假定該大小包括全部視圖的填充。當您計算您的視圖大小,您必須處理的空白值。這裏是從 <font color="#006600">PieChart.onSizeChanged()</font>,它演示如何執行此操做的代碼段:</p>
<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre> <span style="color: #008000">// 計算空白空間</span> <span style="color: #0000ff">float</span> xpad = (<span style="color: #0000ff">float</span>)(getPaddingLeft() + getPaddingRight()); <span style="color: #0000ff">float</span> ypad = (<span style="color: #0000ff">float</span>)(getPaddingTop() + getPaddingBottom());
<span style="color: #008000">// 計算標籤位置</span> <span style="color: #0000ff">if</span> (mShowText) xpad += mTextWidth; <span style="color: #0000ff">float</span> ww = (<span style="color: #0000ff">float</span>)w - xpad; <span style="color: #0000ff">float</span> hh = (<span style="color: #0000ff">float</span>)h - ypad; <span style="color: #008000">// 計算可顯示餅圖尺寸</span> <span style="color: #0000ff">float</span> diameter = Math.min(ww, hh);</pre>
</div>
<p>若是您須要更好地控制您的視圖佈局參數,執行 <a href="http://developer.android.com/reference/android/view/View.html#onMeasure(int, int)" target="_blank">onMeasure()</a>。此方法參數 <a href="http://developer.android.com/reference/android/view/View.MeasureSpec.html" target="_blank">View.MeasureSpec</a> 值告訴您父視圖但願您的視圖的大小,並告訴您是最大值仍是建議值。做爲一種優化,這些值打包爲一個整數,並使用 <a href="http://developer.android.com/reference/android/view/View.MeasureSpec.html" target="_blank">View.MeasureSpec</a> 的靜態方法解包其中的每一個整數值。</p>
<p>這裏是 <a href="http://developer.android.com/reference/android/view/View.html#onMeasure(int, int)" target="_blank">onMeasure()</a> 的示例實現。在此實現中,<font color="#006600">PieChart</font> 嘗試使其區域足夠大以顯示它的標籤:</p>
<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre>@Override <span style="color: #0000ff">protected</span> <span style="color: #0000ff">void</span> onMeasure(<span style="color: #0000ff">int</span> widthMeasureSpec, <span style="color: #0000ff">int</span> heightMeasureSpec) { <span style="color: #008000">// 嘗試肯定最小寬度</span> <span style="color: #0000ff">int</span> minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth(); <span style="color: #0000ff">int</span> w = resolveSizeAndState(minw, widthMeasureSpec, 1);
<span style="color: #008000">// 經過肯定的寬度,計算能完整顯示最大餅圖的高度</span> <span style="color: #0000ff">int</span> minh = MeasureSpec.getSize(w) - (<span style="color: #0000ff">int</span>)mTextWidth + getPaddingBottom() + getPaddingTop(); <span style="color: #0000ff">int</span> h = resolveSizeAndState(MeasureSpec.getSize(w) - (<span style="color: #0000ff">int</span>)mTextWidth, heightMeasureSpec, 0);
setMeasuredDimension(w, h); }</pre>
</div>
<p>在此代碼中有三件重要的事情要注意:</p>
<ul> <li>計算考慮到視圖的填充。如前所述,這是視圖的責任。 </li>
<li>幫助器方法 <a href="http://developer.android.com/reference/android/view/View.html#resolveSizeAndState(int, int, int)" target="_blank">resolveSizeAndState()</a> 用來建立最終的寬度和高度值。此幫助器返回一個適當的 <a href="http://developer.android.com/reference/android/view/View.MeasureSpec.html" target="_blank">View.MeasureSpec</a> 值經過比較對傳遞到 <a href="http://developer.android.com/reference/android/view/View.html#onMeasure(int, int)" target="_blank">onMeasure()</a> 的規範視圖所需的大小。 </li>
<li><a href="http://developer.android.com/reference/android/view/View.html#onMeasure(int, int)" target="_blank">onMeasure()</a> 沒有返回值。該方法經過調用 <a href="http://developer.android.com/reference/android/view/View.html#setMeasuredDimension(int, int)" target="_blank">setMeasuredDimension()</a> 設置結果,調用此方法是強制性的。若是您省略此調用,視圖類將引起運行時異常。
<br /></li>
</ul>
<h3>繪製!</h3>
<p>一旦您建立對象和測量定義的代碼,您能夠實現 onDraw()。每一個視圖以不一樣的方式,實現 onDraw(),但有一些常見的操做大多數視圖相同:</p>
<ul> <li>繪製文本使用 <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawText(char[], int, int, float, float, android.graphics.Paint)" target="_blank">drawText()</a>。經過調用 <a href="http://developer.android.com/reference/android/graphics/Paint.html#setTypeface(android.graphics.Typeface)" target="_blank">setTypeface()</a> 指定字樣,<a href="http://developer.android.com/reference/android/graphics/Paint.html#setColor(int)" target="_blank">setColor()</a> 指定文本顏色。 </li>
<li>繪製使用 <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawRect(android.graphics.Rect, android.graphics.Paint)" target="_blank">drawRect()</a>、 <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawOval(android.graphics.RectF, android.graphics.Paint)" target="_blank">drawOval()</a> 和 <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)" target="_blank">drawArc()</a> 的原始形狀。更改是否填充形狀、線框或二者經過調用 <a href="http://developer.android.com/reference/android/graphics/Paint.html#setStyle(android.graphics.Paint.Style)" target="_blank">setStyle()</a> 。 </li>
<li>繪製更復雜的形狀,使用 <a href="http://developer.android.com/reference/android/graphics/Path.html" target="_blank">Path</a> 類。經過將直線和曲線添加到路徑對象,定義一個形狀,而後繪製使用 <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawPath(android.graphics.Path, android.graphics.Paint)" target="_blank">drawPath()</a> 的形狀。正如與原始形狀,路徑能夠更改是否填充形狀、線框或二者經過調用 <a href="http://developer.android.com/reference/android/graphics/Paint.html#setStyle(android.graphics.Paint.Style)" target="_blank">setStyle()</a>。 </li>
<li>經過建立 <a href="http://developer.android.com/reference/android/graphics/LinearGradient.html" target="_blank">LinearGradient</a> 對象定義漸變填充。調用 <a href="http://developer.android.com/reference/android/graphics/Paint.html#setShader(android.graphics.Shader)" target="_blank">setShader()</a> 來使用您的 <a href="http://developer.android.com/reference/android/graphics/LinearGradient.html" target="_blank">LinearGradient</a> 填充的形狀。 </li>
<li>繪製位圖使用 <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint)" target="_blank">drawBitmap()</a>。 </li> </ul>
<p>例如,下面是 <font color="#006600">PieChart</font> 的繪製代碼。它使用的文本、 線條和形狀的組合。</p>
<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre><span style="color: #0000ff">protected</span> <span style="color: #0000ff">void</span> onDraw(Canvas canvas) { <span style="color: #0000ff">super</span>.onDraw(canvas);
<span style="color: #008000">// 繪製投影</span> canvas.drawOval( mShadowBounds, mShadowPaint );
<span style="color: #008000">// 繪製標籤文本</span> canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);
<span style="color: #008000">// 繪製餅分段</span> <span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> 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, <span style="color: #0000ff">true</span>, mPiePaint); }
<span style="color: #008000">// 繪製指針</span> canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint); canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint); }</pre>
</div>