Android自定义View教程(二)

自定义绘图

自定义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达到和它的标签中所描述的设定的大小:

@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的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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值