原文地址:http://android.xsoftlab.net/training/custom-views/custom-drawing.html#draw

自定义View最重要的部分就是它的样子了。自定义View的绘制根据应用的需要或者简单亦或者复杂。这节课的内容涵盖了大多数通用的知识点。

重写onDraw()方法

绘制自定义View很重要的一个步骤就是重写它的onDraw()方法。该方法含有一个Canvas对象作为参数,用来使View绘制它本身的内容。Canvas类定义了用于绘制文本,线条,位图以及许多其它基础的物理图形的方法。你可以在onDraw()方法中使用这些方法来创建属于你自己的UI效果。

在开始任何绘制之前,必须先创建一个Paint对象。后面的部分会讨论Paint的相关知识。

创建绘制对象

android.graphics框架将绘制分为了两块区域:

  • 要绘制什么,由Canvas控制
  • 如何绘制,由Paint控制

举个例子,Canvas提供了用于绘制线条的方法,而Paint则定义了线条的颜色。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));...

提前创建对象是一项非常重要的优化手段。View会很频繁的绘制,创建绘制对象的开销十分高昂。在onDraw()方法中创建绘制对象会明显的降低应用的性能,并可能会导致UI出现停顿。

处理布局事件

为了可以正确的绘制View,首先需要知道View的尺寸。复杂一点的View经常需要执行多次布局计算。你永远不要假定View的尺寸,就算是只有一款应用使用了你的View。因为APP需要处理不同的屏幕尺寸,不同的屏幕密度,以及在垂直模式及水平模式下不同的高宽比。

尽管View拥有很多测量尺寸的方法,但是绝大多数是不需要重写的。如果View不需要特别控制它的尺寸,你只需要重写一个方法:onSizeChanged().

onSizeChanged()方法会在首次分配尺寸的时候调用,如果尺寸再次变更时则会再次调用。我们在onSizeChanged()方法中计算View的位置、尺寸以及其它任何与尺寸相关的值,而不是在每次绘制的时候重新计算它们。在示例PieChart中,onSizeChanged()方法内部计算了饼图的矩形边框以及文本和其它可视元素的相对位置。

当View被分配了一个尺寸时,布局管理器会假设该尺寸包含了View所有的内边距。所以在计算View的尺寸时要将View的内边距计算在内。下面的代码段展示了PieChart.onSizeChanged()是如何做的:

       // Account for paddingfloat xpad = (float)(getPaddingLeft() + getPaddingRight());float ypad = (float)(getPaddingTop() + getPaddingBottom());// Account for the labelif (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的尺寸是最大值还是只是一个建议值等等。随着优化,这些值被存放在一个被包装后的整型值内,你必须使用View.MeasureSpec的一个静态方法来提取存储在这个整型值内的信息。

下面是onMeasure()方法的一个示例。在这个实现中,PieCart尝试将自己的区域扩大到内部标签的大小。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// Try for a width based on our minimumint 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 canint minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);setMeasuredDimension(w, h);
}

这里有三个非常重要的点需要关注:

  • 这个公式将View的内边距一并计算在内。正如前面所说的,这是View本身的职责。
  • 辅助方法resolveSizeAndState()用于创建最终的宽度值与高度值。这个辅助方法通过比较View的期望值与onMeasure()回调的spec值,最后返回一个适当的View.MeasureSpec值。
  • onMeasure()没有返回值。相反的,该方法通过调用setMeasuredDimension()方法与外界交流。调用这个方法是强制要求的。如果你忽略了这个调用,那么View类会抛出一个运行时异常。

绘制

完成对象创建代码与尺寸测量代码之后,接下来就可以实现onDraw()方法了。每个View的onDraw()方法都不相同,但是它们还是有一些相同特点存在的:

  • 使用drawText()方法绘制文本。通过setTypeface()方法指定字体类型,通过setColor()方法设置文本颜色。
  • 通过drawRect()、drawOval()和drawArc()绘制基础图形。通过setStyle()更改图形是填充模式还是绘制轮廓模式,或者两者都不是。
  • 通过Path类来绘制更加复杂的图形。通过给Path对象添加线条及曲线来定义形状,然后通过drawPath()方法绘制这些形状。只是这些基础形状可以通过setStyle()方法来定义它们的Path风格。
  • 通过创建LinearGradient对象可以定义梯度填充模式。调用setShader()方法来填充形状。
  • 通过drawBitmap()方法绘制位图。

举个例子,下面的代码用来绘制PieChart。它混合使用了文本、线条以及形状。

protected void onDraw(Canvas canvas) {super.onDraw(canvas);// Draw the shadowcanvas.drawOval(mShadowBounds,mShadowPaint);// Draw the label textcanvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);// Draw the pie slicesfor (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 pointercanvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
}

Android官方开发文档Training系列课程中文版:创建自定义View之View的绘制相关推荐

  1. Android官方开发文档Training系列课程中文版:后台服务之IntentService的创建

    原文地址:http://android.xsoftlab.net/training/run-background-service/index.html 引言 除非特别指定,否则所有的操作都是在UI线程 ...

  2. Android官方开发文档Training系列课程中文版:目录

    原文地址 : http://android.xsoftlab.net/training/index.html 引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻 ...

  3. Android官方开发文档Training系列课程中文版:创建自定义View之View的创建

    原文地址:http://android.xsoftlab.net/training/custom-views/index.html 引言 Android框架含有大量的View类,这些类用来显示各式各样 ...

  4. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形绘制

    原文地址:http://android.xsoftlab.net/training/graphics/opengl/draw.html 如果你还不清楚如何定义图形及坐标系统,请移步:Android官方 ...

  5. Android官方开发文档Training系列课程中文版:使用Fragment构建动态UI之Fragment创建

    原文地址:http://android.xsoftlab.net/training/basics/fragments/index.html 导言 为了在Android中创建动态的多面板用户界面,你需要 ...

  6. Android官方开发文档Training系列课程中文版:打印内容之HTML文档打印

    原文地址:http://android.xsoftlab.net/training/printing/html-docs.html 在Android中打印内容要比打印照片要复杂一些,它要求将文本与图像 ...

  7. Android官方开发文档Training系列课程中文版:动画视图之转场框架介绍

    原文地址:http://android.xsoftlab.net/training/transitions/index.html 引言 Activity所呈现的UI经常会由用户的输入或者其它事件而发生 ...

  8. Android官方开发文档Training系列课程中文版:调用相机之控制相机

    原文地址:http://android.xsoftlab.net/training/camera/cameradirect.html 在这节课,我们会讨论如何使用Android框架API来直接控制相机 ...

  9. Android官方开发文档Training系列课程中文版:Android的安全建议

    原文地址:http://android.xsoftlab.net/training/articles/security-tips.html Android系统内置的安全策略可以有效的降低应用程序的安全 ...

  10. Android官方开发文档Training系列课程中文版:后台加载数据之使用CursorLoader进行查询

    原文地址:http://android.xsoftlab.net/training/load-data-background/index.html 引言 在ContentProvider中查询数据是需 ...

最新文章

  1. 设计模式中的六大基本原则
  2. 如何使用JPA注解标注多对多的关系
  3. android多媒体图文混排,干货!!!Android富文本实现图文混排
  4. 【电路】KiCad-Pcbnew-建BGA形式的Footprint
  5. 子弹短信新发布,支付宝即将入驻
  6. python中单下划线(_)和双下划线(__)的特殊用法 还等什么
  7. Firefox不支持input手动填写后的getAttribute(value),只能用.value(Firefox 3.5.5 Windows)。bug?...
  8. 没想到你是这样的Nginx!
  9. jqGrid colModel 参数(来自中文手册)
  10. Jquery源码中的Javascript基础知识(三)
  11. 2d shader unity 阴影_【Unity Shader】平面阴影(Planar Shadow)
  12. 像素,色彩,分辨率什么的
  13. net导出到excel数字变为科学技术法
  14. 自动驾驶-MPC控制器
  15. 蓝宝石rx470d原版bios_狼神矿卡烤机89°C!强刷蓝宝石RX570超白金显卡BIOS降温75°教程...
  16. MonthCalendar 的使用
  17. 嵌入式:ARM系列处理器详解与性能对比
  18. 众筹网站项目第五天之用户的增、删、改
  19. 初学容器:Docker
  20. Python爬虫开发与项目实战pdf下载

热门文章

  1. UDT源码剖析(六):UDT::socket()过程代码注释
  2. 16进制数组转字符串
  3. STM32f103 —— 内部flash读写
  4. seir模型matlab_疫情专题 | 传染病的经典数学模型
  5. python历史波动率_历史波动率计算(旧文)
  6. android sdk 封装html5,Android平台以WebView方式集成HTML5+SDK方法
  7. 图像分类_02神经网络(NN)简介:定义+ 感知机+历史
  8. 卷积神经网络(高级篇) Inception Moudel
  9. Chapter7-13_Dialogue State Tracking (as Question Answering)
  10. LeetCode 1253. 重构 2 行二进制矩阵(贪心)