自定义 View 绘制顺序 HenCoder-5 [MD]

2021-05-29 11:03

阅读:622

标签:mon   触摸   and   项目   使用   private   好的   col   目标   

博文地址

我的GitHub 我的博客 我的微信 我的邮箱
baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目录

目录
  • 目录
  • 扔物线自定义 View 系列教程总结-5
  • 绘制顺序
    • 总结
    • drawBackground 绘制背景
    • onDraw 绘制主体
      • 写在 super.onDraw 下面
      • 写在 super.onDraw 上面
    • dispatchDraw 绘制子 View
      • 写在 super.dispatchDraw 下面
      • 写在 super.dispatchDraw 上面
    • onDrawForeground 绘制前景
      • 写在 super.onDrawForeground 下面
      • 写在 super.onDrawForeground 上面
    • draw 总调度方法
      • 写在 super.draw 下面
      • 写在 super.draw 上面
    • 注意事项

扔物线自定义 View 系列教程总结-5

全文整理自 扔物线(HenCoder)自定义 View 系列文章

重新整理的目标:

  • 内容压缩:去除活跃气氛的段子、图片,去除无意义的解释、代码,去除不刚兴趣的内容,压缩比至少 50%
  • 排版优化:更清晰的结构,更精简的标题,更规范的缩进、标点符号、代码格式,好的结构才能更好的吸收
  • MarkDown:以标准的 MarkDown 格式重新编排,纯文本更易迭代维护

扔物线自定义 View 系列教程分绘制布局触摸反馈三部分内容。

绘制顺序

  • 绘制顺序
  • 练习项目

总结

Android 里面的绘制都是按顺序的,先绘制的内容会被后绘制的盖住,所以控制好绘制顺序是非常重要的。

一个完整的绘制过程会依次绘制以下几个内容:

  • 绘制背景drawBackground()
  • 绘制主体onDraw()
  • 绘制子 ViewdispatchDraw()
  • 绘制滑动边缘渐变和滑动条以及前景onDrawForeground()

技术图片

技术图片

drawBackground 绘制背景

这个方法是 private 的,不能重写,也就是说不能自定义绘制。可以通过android:background属性或View.setBackgroundXxx()方法设置背景。

onDraw 绘制主体

onDraw() 是负责自身主体内容绘制的。在 View 中,onDraw() 是空实现,所以直接继承 View 的类,它们的 super.onDraw() 什么也不会做。

写在 super.onDraw 下面

绘制内容就会盖住控件原来的内容,可以为控件增加点缀性内容。

在 Debug 模式下绘制出 ImageView 的图像尺寸信息:

技术图片

写在 super.onDraw 上面

绘制的内容会被控件的原内容盖住,这种用法的场景相对来说会少一些。

通过在文字的下层绘制纯色矩形来作为「强调色」:

技术图片

dispatchDraw 绘制子 View

在绘制过程中,每个 View 和 ViewGroup 都会先调用 onDraw() 方法来绘制主体,再调用 dispatchDraw() 方法来绘制子 View。

如果你继承了一个 LinearLayout,重写了它的 onDraw() 方法,在 super.onDraw() 后插入了你自己的绘制代码,以便使它能够在内部绘制一些斑点作为点缀。但是你会发现,当你添加了子 View 之后,你的斑点不见了。造成这种情况的原因是:在绘制过程中,每一个 ViewGroup 会先调用自己的 onDraw() 来绘制完自己的主体之后再去绘制它的子 View,那么在子 View 绘制完成之后,先前绘制的斑点就被子 View 盖住了。

注:由于 View 没有子 View,所以一般来说这个方法只对 ViewGroup 有意义。

写在 super.dispatchDraw 下面

绘制的内容发生在子 View 的绘制之后,所以绘制内容会盖住子 View

上面说的 LinearLayout 案例,就应该放在 super.dispatchDraw 下面

写在 super.dispatchDraw 上面

这其实和前面讲的把绘制代码写在 super.onDraw() 之后的效果是一样的。

onDrawForeground 绘制前景

  • Android 6.0(API 23)之前,仅FrameLayout支持前景,6.0 后所有View都支持
  • 滑动边缘渐变和滑动条可以通过android:scrollbarXXX系列属性或View.setXXXScrollbarXXX()系列方法来设置
  • 前景可以通过android:foreground属性或View.setForeground()方法来设置

虽然这三部分是依次绘制的,但它们被一起写进了这个方法里,所以不能在它们之间插入自定义绘制

写在 super.onDrawForeground 下面

绘制代码会在滑动边缘渐变、滑动条和前景之后被执行,所以绘制内容会盖住滑动边缘渐变、滑动条和前景

在 ImageView 左上角添加「New」标签,要求不能被遮罩(foreground)盖住

技术图片

写在 super.onDrawForeground 上面

这种写法,和前面讲的把绘制代码写在 super.dispatchDraw() 的下面的效果是一样的。

在 ImageView 左上角添加「New」标签,要求需要被遮罩(foreground)盖住

技术图片

draw 总调度方法

draw() 是绘制过程的总调度方法,一个 View 的整个绘制过程都发生在 draw() 方法里。前面讲到的背景、主体、子 View 、滑动相关以及前景的绘制,它们其实都是在 draw() 方法里调用的。

// View.java 的 draw() 方法的简化版大致结构
public void draw(Canvas canvas) {
    drawBackground(Canvas); // 绘制背景(不能重写)
    onDraw(Canvas); // 绘制主体
    dispatchDraw(Canvas); // 绘制子 View
    onDrawForeground(Canvas); // 绘制滑动相关和前景
}

所以也可以通过重写 draw() 方法来做自定义的绘制。

写在 super.draw 下面

绘制的内容会在其他所有绘制完成之后再执行,也就是说,它的绘制内容会盖住其他的所有绘制内容

它的效果和把绘制代码写在 super.onDrawForeground() 下面时的效果是一样的,都会盖住其他的所有内容。

写在 super.draw 上面

绘制的内容会在其他所有绘制之前被执行,所以这部分绘制内容会被其他所有的内容盖住,包括背景。

是不是觉得没用?觉得怎么可能会有谁想要在背景的下面绘制内容?别这么想,有的时候它还真的有用。

例如 EditText,它下面的那条横线是它的背景(background)

技术图片

如果我想给这个 EditText 加一个绿色的底,我不能使用给它设置绿色背景色android:background="#66BB6A"的方式,因为这就相当于是把它的背景替换掉,从而会导致下面的那条横线消失

技术图片

在这种时候,你就可以重写它的 draw() 方法,然后在 super.draw() 的上方插入代码,以此来在所有内容的底部涂上一片绿色:

public void draw(Canvas canvas) {
    canvas.drawColor(Color.parseColor("#66BB6A")); // 涂上绿色
    super.draw(canvas);
}

技术图片

注意事项

关于绘制方法,有两点需要注意:

1、在 ViewGroup 的子类中重写除 dispatchDraw() 以外的绘制方法时,可能需要调用 setWillNotDraw(false)

出于效率的考虑, ViewGroup 默认会绕过 draw() 方法,换而直接执行 dispatchDraw(),以此来简化绘制流程。所以如果你自定义了某个 ViewGroup 的子类并且需要在它的除 dispatchDraw() 以外的任何一个绘制方法内绘制内容,你 可能 会需要调用 View.setWillNotDraw(false) 这行代码来切换到完整的绘制流程。是「可能」而不是「必须」的原因是,有些 ViewGroup 是已经调用过 setWillNotDraw(false) 了的,例如 ScrollView

2、在重写的方法有多个选择时,优先选择 onDraw()

一般情况,一段绘制代码写在不同的绘制方法中效果是一样的,但有一个例外:如果绘制代码既可以写在 onDraw() 里,也可以写在其他绘制方法里,那么优先写在 onDraw()。因为 Android 有相关的优化,可以在不需要重绘的时候自动跳过 onDraw() 的重复执行,以提升开发效率。享受这种优化的只有 onDraw() 一个方法。

2021-5-7

自定义 View 绘制顺序 HenCoder-5 [MD]

标签:mon   触摸   and   项目   使用   private   好的   col   目标   

原文地址:https://www.cnblogs.com/baiqiantao/p/14743101.html


评论


亲,登录后才可以留言!