android 2d绘图函数,Android View 的绘制(2D)

View 是 Android 界面里最基本的组件,一个 View 占据屏幕上一块方形区域,我们平时手机上看到的文字、图片、列表等都是 View,可以说 Android 手机屏幕上能看到的所有东西都是 View。正是这些形形色色的 View,组成了我们手机上千变万化的界面。做为一名 Android 开发人员,我们有必要了解下如此重要的一个界面组件是怎样呈现在手机屏幕上的。首先,我们不妨想想,在现实中,如果我们自己要画一幅画该怎么做

第一,我们要准备笔墨纸砚吧,不过在在手机上,只要这个手机正确安装了 Android 系统,那么系统就已经为我们准备好这些东西啦

第二,我们应该要知道画的这个东西有多大吧?不然下笔的范围、轻重都无法掌握啊

第三,应该把这个东西画在纸上的哪个位置必须要清楚,这样这块位置画什么,那块位置画什么才能有效的组织起来

第四,经过前面几步的准备,最后我们当然要开画了,至于用什么颜色的笔、笔的粗细就看我们自己的风格了

不错,Android 一个 View 的呈现大致也正是需要上面的这些步骤,好了,废话不多说了,让我们开始探索 View 的神秘世界吧!

整体绘制流程图

21ac038a7deb

android_view_draw_flow.jpg

测量

View 要想显示在手机屏幕上,必须放在容器 ViewGroup 类或其子类中,View 的测量是在 View 的 onMeasure 回调方法中进行的,这个方法有两个父容器传给 View 的宽、高的整型参数,这两个参数并不是表面上看起来那么简单,它们是由 父容器约束模式 和 大小 组成的一个 32 位整型值,高 2 位代表模式,低 30 位代表大小,你如果想指定一个 View 的大小,一定要覆写这个方法并最好遵守一些父容器对你这个 View 的约束,至于需要遵守怎样的约束,请看下面 MeasureSpec 类介绍

MeasureSpec

首先,我们先了解下 MeasureSpec 这个类是干嘛的,我们知道在 View 的 onMeasure 回调方法中有两个 32 位的整型的参数 widthMeasureSpec 和 heightMeasureSpec 整型规格参数,如果我们要获取这个 32 位整数的约束模式部分,是不是有点头大?不用担心,MeasureSpec 这个类的作用就是提供了一些方法用来辅助操作这个 32 位整型数的,比如获取模式的 getMode 方法、获取大小的 getSize 方法等。之所以选择一整型而不用一个对象来表示,主要是为了避免对象的分配

MeasureSpec 类为我们提供了 3 种父容器对子 View 的约束模式:

模式

约束

UNSPECIFIED

0 << 30

父容器不对当前 View 大小做任何限制,子 View 要多大给多大,一般一些滑动控件会用到,例如 ScrollView 等

EXACTLY

1 << 30

父容器已经测量出了当前子 View 所需的大小,希望子 View 按照这个大小来设置,对应 match_parent 或具体大小值

AT_MOST

2 << 30

View 要多大就多大,但 View 大小不能大于父容器,对应 wrap_content

注意下,上面给出的规则只是常规情况下的一种期望,也就是说这样写你的 View 的宽高绝对不会超出父容器的范围,其实这三种限制模式是限制不了你的 View 要多大的,只是一种建议,比如,父容器给你的高是 300,你的 View 就不能大于或小于这个数了吗?不是的,你可以是任意大小,只要你能通过某种方式能显示出你这个 View 的全部内容就行,比如改变 View 坐标、外边距、内边距等

示例代码

/**

* 此方法结尾处需调用 setMeasuredDimension 方法使自定义的宽、高生效

* 不调用会抛出异常

*/

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

setMeasuredDimension(getSuitableSize(suggestedMinimumWidth,widthMeasureSpec),

getSuitableSize(suggestedMinimumHeight,heightMeasureSpec))

// 推荐使用 resolveSize 和 resolveSizeAndState 方法来获取 View 的合适大小

setMeasuredDimension(resolveSize(suggestedMinimumWidth,widthMeasureSpec),

resolveSize(suggestedMinimumHeight,heightMeasureSpec))

}

/**

* @param desiredSize 你期望这个 View 多大

* @param parenConstraintSpec 父容器约束这个 View 的大小规格

*/

private fun getSuitableSize(desiredSize:Int,parenConstraintSpec:Int):Int{

val mode = MeasureSpec.getMode(parenConstraintSpec)

val size = MeasureSpec.getSize(parenConstraintSpec)

return when (mode){

MeasureSpec.UNSPECIFIED -> desiredSize

MeasureSpec.EXACTLY -> size

MeasureSpec.AT_MOST -> Math.min(desiredSize,size)

else -> desiredSize

}

}

/**

* 官方示例

*/

private fun measureView(widthMeasureSpec: Int, heightMeasureSpec: Int){

// Try for a width based on our minimum

val minw: Int = paddingLeft + paddingRight + suggestedMinimumWidth

val w: Int = View.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

val minh: Int = View.MeasureSpec.getSize(w) - desiredSize + paddingBottom + paddingTop

// 第二个参数传 0 即可满足大多数情况

val h: Int = View.resolveSizeAndState(minh, heightMeasureSpec, 0)

setMeasuredDimension(w, h)

}

相关方法

方法名

所在类

备注

measure

View

measureChild

ViewGroup

measureChildren

ViewGroup

getChildMeasureSpec

ViewGroup

getMode

MeasureSpec

getSize

MeasureSpec

makeMeasureSpec

MeasureSpec

根据约束模式和大小生成 32 位整型规格参数,区别于 resolveSize 和 resolveSizeAndState 方法

toString

MeasureSpec

调试时很有用,可以将模式和大小组合成一个字符串返回

getMeasuredWidthAndState

View

getMeasuredState

View

combineMeasuredStates

View

getMeasuredWidth

View

高类似

getSuggestedMinimumWidth

View

没有提供 set 方法

getMinimumWidth

View

高类似,有提供 set 方法

onSizeChanged

View

布局

View 通过调用 layout 方法来确定自己的位置,ViewGroup 及其子类需要覆写 onlayout 方法并在里面确定每个子 View 的摆放位置,至于摆放规则,需要自己指定了,这也是自定义 ViewGroup 比自定义 View 要难的原因之一,当然,系统也为我们提供了很多实现好的 ViewGroup 子类,比如常用的 LinearLayout、RelativeLayout 等,如果我们能直接继承这些子类就能满足需求的话当然能更省事

相关方法

方法名

所在类

备注

onLayout

View

回调方法,在方法中调用 layout 方法指定子 View 的大小和位置

注意下这个方法的 changed 参数是用来判断当前 View 目前的位置是否相对于上次位置发生了变化,如果是 ViewGroup 里的子 View 位置发生了变化,ViewGroup 的 changed 参数是不会变的

layout

View

指定当前 View 的大小和位置

requestLayout

View

重新回调 onLayout 方法对 View 进行布局

bringToFront

View

通过改变 View 的 z轴 坐标来达到界面 View 排列顺序的变化

绘制

经过了前面两步的准备工作,终于到最后一步绘制啦,关于绘制阶段,有两个重要的类,一个是 Canvas,一个是 Paint,它们分别决定了当前 View 要画什么和怎么画,比如,前者提供了一个 drawRect 方法用于画方形,后者提供了一个 setStyle 方法规定了你是只需要画要有边的方形呢、还是要实心的方形呢、还是即要有边又要实心的方法

相关方法

方法名

所在类

备注

onDraw

View

绘制具体图形的回调方法,注意不要在此方法中执行太耗时的操作,对象的创建也不要放在此方法中,因为绘制期间,此方法回调会很频繁

invalidate

View

重绘,onDraw 方法会被回调,此方法必须在主线程中调用

postInvalidate

View

onDraw 方法会被回调,此方法即可在主线程也可在子线程中调用

setColor

Paint

设置画笔颜色

setTypeface

Paint

绘制文字时,设置字体,黑体、斜体等

setStyle

Paint

设置绘制方式,画边、填充等

setShader

Paint

图形是否需要渐变

drawPoint

Canvas

drawLine

Canvas

drawText

Canvas

drawRect

Canvas

drawRoundRect

Canvas

drawOval

Canvas

drawArc

Canvas

drawPath

Canvas

根据路径画图形,一般用于绘制复杂的图形

drawBitmap

Canvas

drawCircle

Canvas

其它相关方法

方法名

所在类

备注

onFinishInflate

View

onVisibilityChanged

View

onAttachedToWindow

View

onDetachedFromWindow

View

setTouchDelegate

View

扩展 View 的点击区域,用法见这里

总结

了解 View 的绘制机制对自定义我们自己的 View 尤为重要,当然这只是自定义 View 需要掌握的重要知识之一,希望大家都能掌握,还有就是这篇文章是基于本人目前的能力所写,难免会有错误与不足之处,我也会持续修正和补充,希望大家批评指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值