自定义绘图

本文翻译自android开发指导文档Custom Drawing

自定义绘图

     自定义view最重要的部分是它的外观。自定义绘图可以根据你的外观需要或简单或复杂。这篇文档包含了一些重要操作。

覆写onDraw()

     画自定义view最重要的一步是覆写onDraw()方法。onDraw()的参数是一个Canvas对象,view可以用它来画自己的内容。Canvas类定义了画文字、线、bitmap还有很多其他图形的方法。你可以在onDraw()中用这些方法来创建你的view的用户界面。
     在你调用绘画方法之前,你有必要创建一个Paint对象。接下来详细讨论Paint。

创建绘画对象

     android.graphics框架将绘画分为两个方面:
  • 画什么,由Canvas控制
  • 怎么画,由Paint控制
     例如,Canvas提供了一个画线的方法,然而Paint提供了定义线颜色的方法。Canvas有一个画矩形的方法,而Paint定义了是否用一种颜色来填充矩形或者不填充。简单讲,Canvas定义了你可以在屏幕上绘画的形状,Paint定义了颜色,样式,字体。所以,在你画任何东西之前,你需要创建一个或多个Piant对象。PieChar例子中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在屏幕上的大小做出假设。即使只有一个app使用你的view,应用程序需要处理不同的屏幕尺寸,多样的屏幕密度,还有各种横屏、竖屏模式的比例。
     尽管视图有多种方法来处理测量,但大部分不需要覆写。如果你的view不需要指定控制它的大小,你仅仅需要覆写一个方法:onSizeChanged()。onSizeChanged()在view第一次被分配大小时调用,并且在view大小改变时再次调用。在onSizeChanged()中计算位置、尺寸还有任何其它和view大小有关的值,而不是每次在你画的时候重新计算。
     当给你的view分配大小时,布局管理器假设view的大小包括所有的padding。当计算view的大小时,你必须处理padding值。下面有一个代码片段告诉你怎么做:
   // 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.MeasureSpec的静态方法来取出存储在每一个整数中的信息。
     这有一个例子来实现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的期望大小和spec返回一个合适View.MeasureSpec值传进onMeasure()。
  • onMeasure()没有返回值。相反,这个方法通过调用setMeasuredDimension()来传达它的值。该方法的调用时强制的。如果你忽略了,view类会抛出异常。

绘制!

     一旦你完成了对象创建和测量代码定义,你就可以实现onDraw()方法了。每一个view实现onDraw()方法不相同,但大部分view共享一些相同的操作:
  • 用drawText()绘制文字。通过调用setTypeface()指定字体,setColor()指定文字颜色。
  • 用drawRect(),drawOval(),drawArc()绘制简单图形。通过调用setStyle()改变形状是否填充,是否有轮廓,或者两个都有。
  • 用Path类绘制更复杂的图形。通过添加直线和曲线Path对象定义形状,然后用drawPath()绘制。像简单图像一样,paths也可以指定填充或者轮廓。
  • 通过创建LinearGradient定义渐变填充。调用setShader()用你的LinearGradient填充图形。
  • 用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、付费专栏及课程。

余额充值