Android自定义View教程(二)

来源:互联网 发布:金山数据恢复手机版 编辑:程序博客网 时间:2024/04/30 10:45

自定义绘图

自定义View最重要的一部分就是它的外观了。自定义绘图可以使简单的,也可以是复杂的,这取决于你的应用需求。这篇教程将包含一些最常用的操作。

覆写 onDraw()

绘制自定义View最重要的一步就是覆写onDraw()方法。Ondraw()方法的参数是一个可以用于绘制自身的canvas对象。Canvas定义了很多绘制方法,比如:Text, lines, bitmaps,还有一些其他的显示基类。你能够在onDraw()中使用这些方法来创建你的自定义UI。

在你调用任何一个绘制方法之前,你必须创建一个Paint对象。下一个章节将会更加详细地讨论Paint

创建绘制对象

android.graphics框架被分为了两部分:

  • 绘制什么,由Canvas来操作
  • 怎样绘制,由Paint来操作

具体来说,Canvas提供绘制line的方法,而Paint来提供line的颜色定义。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));

提前创建对象是一种很重要的优化方式。Views会经常重绘,并且很多绘制对象需要很繁重的初始化。在onDraw()中创建绘制对象会显著地降低性能,让你的UI变得很缓慢。

处理Layout事件

为了适当地绘制你的自定义view,你需要知道它的size有多大。复杂的自定义view经常会需要执行多次layout计算,者取决于它们屏幕区域的大小,形状。你永远也不要去假设你的view在屏幕上的大小,即使只有一个APP使用你的view。这个APP需要处理不同的屏幕大小,不同的屏幕密度,以及各种多样的比例和landscape模式。

即使View有很多方法来处理测量过程,它们其中的绝大部分不需要覆写。加入你的view不需要特别去控制它的大小,你只需要覆写一个方法onSizeChanged()

onSizeChanged()在你的view第一次被分配大小的时候调用,并且在你的view大小因为任何原因发生了变化的时候再一次调用。你可以在onSizeChanged()中计算你的view的位置,尺寸以及其他任何和你的view大小有关系的值。,不同于在你每次绘制的时候重新计算,在PieChart这个例子中,onSizeChanged()才是计算这些的地方。

当你的view被分配了大小的时候,Layout管理器会假定大小包含了所有的view的padding值。你必须处理你的padding值,在你计算你的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的Layout参数,那么实现onMeasure()方法。这个方法的参数是View.MeasureSpec,这是用来告诉你,你的view的父类希望你的view是多大,和这个大小是真正的最大值还是建议值。作为一种优化,这些值被打包储存在了integer之中,你需要使用静态方法View.MeasureSpec去解包出存储在每一个Integer中的信息。

以下是一个实现了onMeasure()的例子,在这个实现中,PieChart 企图让它的大小足够大以此让pie达到和它的标签中所描述的设定的大小:

@Overrideprotected 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的padding值,就和前面所提到的那样,这是一个view的责任。
  • 辅助类resolveSizeAndState()用来创建最终的宽,高值。这个辅助类通过比较view在onMeasure()中想要的大小返回了一个适当的View.MeasureSpec值。
  • onMeasure()并没有返回值,相对地,这个方法调用了setMeasuredDimension()以用来传递它的结果值。这个方法的调用时强制性的,如果你忽略了这个调用,那么,view类将会抛出运行时异常。

Draw!

一旦你定义好了你的对象的创建以及测量代码,你就能实现 onDraw().每个view的onDraw()实现都不相同,但是在它们之间还是有一些相同的操作的,绝大部分view都会这么做:

  • 使用drawText()来绘制text.用setTypeface()来指定字体,用setColor()来指定text的颜色。
  • 原始的形状绘制使用drawRect(),drawOval(),drawArc()。用setStyle()来改变形状是否填充,轮廓。
  • LinearGradient对象来定义梯度值,调用setShader()来使用你定义的梯度值到你定义的形状中去。
  • drawBitmap()来绘制Bitmap。

举个例子吧,以下的代码是用来进行绘制的:

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);}
0 0
原创粉丝点击