<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>