Android视图状态以及重绘

一、视图状态(View States)

1. 五种核心状态
状态作用修改方法特点
enabled视图是否响应交互setEnabled(boolean)禁用状态下不响应onTouch事件
focused视图是否获得焦点requestFocus()需同时满足focusable和focusableInTouchMode
window_focused视图所在窗口是否在前台系统自动维护应用无法直接修改
selected视图是否被选中setSelected(boolean)同一界面允许多个视图同时选中
pressed视图是否被按下setPressed(boolean)通常由系统自动设置点击状态
2. 状态变更响应流程

关键源码解析

  1. 状态变更入口

    // View.java
    protected void drawableStateChanged() {Drawable d = mBackground;if (d != null && d.isStateful()) {d.setState(getDrawableState()); // 传递新状态给Drawable}
    }
  2. 状态匹配原理

    // StateListDrawable.java
    protected boolean onStateChange(int[] stateSet) {int idx = findStateIndex(stateSet); // 匹配selector中对应的itemreturn selectDrawable(idx); // 切换Drawable
    }
  3. 触发重绘

    // StateListDrawable.java
    public boolean selectDrawable(int idx) {// ...更新DrawableinvalidateSelf(); // 关键重绘触发
    }

二、重绘机制(View Invalidation)

1. 两种重绘方式对比
方法触发流程应用场景性能影响
invalidate()仅重走draw()流程内容变化但尺寸不变(如文字/颜色)低开销,局部刷新
requestLayout()完整measure-layout-draw视图尺寸/结构变化(如添加子View)高开销,全局重新布局
2. invalidate() 核心流程

源码关键路径

// ViewRootImpl.java
void scheduleTraversals() {sendEmptyMessage(DO_TRAVERSAL); // 发送异步消息
}public void handleMessage(Message msg) {if (msg.what == DO_TRAVERSAL) {performTraversals(); // 最终入口}
}private void performTraversals() {// 根据标记位决定流程if (!mLayoutRequested) {// 仅执行draw流程performDraw();}
}
3. 性能优化要点
  1. 减少重绘范围

    // 只刷新局部区域
    public void invalidate(Rect dirty) {// 计算脏区域并传递
    }
  2. 避免过度重绘

    • 使用View.setWillNotDraw(true)跳过无内容视图

    • 合并状态变更(避免连续多次invalidate)


三、常见问题

Q1:按下按钮时背景图切换的完整流程?

A

  1. 状态变更View.setPressed(true)更新状态数组

  2. 通知DrawabledrawableStateChanged()调用StateListDrawable.setState()

  3. 匹配资源StateListDrawable.onStateChange()查找对应状态图片

  4. 触发重绘selectDrawable() → invalidateSelf() → View.invalidate()

  5. 绘制执行:递归至ViewRootImpl.scheduleTraversals() → 下一帧触发draw()流程

Q2:invalidate() 和 requestLayout() 的本质区别?

A

  • invalidate()

    • 仅设置DIRTY标记 → 触发draw()流程

    • 不重新测量/布局 → 适用于内容变化但尺寸不变场景

  • requestLayout()

    • 设置FORCE_LAYOUT标记 → 触发完整measure-layout-draw

    • 向父视图递归 → 可能引发全局重新布局

Q3:为什么StateListDrawable能自动切换图片?

A:核心机制是状态匹配+重绘触发

  1. res/drawable中定义<selector>状态映射

  2. onStateChange()用状态数组匹配最佳item下标

  3. selectDrawable()切换当前Drawable并调用invalidateSelf()

Q4:自定义View如何优化重绘性能?

A:三级优化策略:

  1. 减少区域

    // 只刷新变化区域
    invalidate(dirtyRect);
  2. 避免过度绘制

    • 覆写hasOverlappingRendering()返回false

    • 使用canvas.clipRect()限制绘制区域

  3. 复用资源

    • 预初始化Paint/Path等对象

    • 使用View.setLayerType(LAYER_TYPE_HARDWARE)启用硬件加速

Q5:解释下 scheduleTraversals() 中发送异步消息的意义?是否在主线程执行?

A
核心是通过异步消息+同步屏障确保UI更新的及时性

  1. 异步消息DO_TRAVERSAL 消息被标记为异步类型,优先于普通消息处理

  2. 同步屏障

    • 在消息队列插入屏障,阻塞后续同步消息

    • 仅允许异步的UI更新消息通过

  3. 主线程执行

    • 消息最终由 ViewRootImpl 的 Handler 在主线程处理

    • 调用 performTraversals() 执行完整的视图树遍历

  4. 设计目的

    • 解决UI更新被业务消息阻塞的问题

    • 保证16ms内完成绘制(60Hz刷新率)

使用代码证明主线程执行
在 performTraversals() 中可检查线程:

void performTraversals() {if (Thread.currentThread() != mThread) {throw new RuntimeException("Must be on UI thread!");}// ...measure/layout/draw...
}

其中 mThread 即 ViewRootImpl 创建时的主线程。

Q6:View.postInvalidate() 和 invalidate() 区别?

A

维度invalidate()postInvalidate()
调用线程仅UI线程任意线程
内部实现直接操作视图树通过Handler转发到UI线程
适用场景视图内部状态变更后台线程触发的UI更新

四、总结

Q:请解释Android视图状态变更如何触发界面更新?

A
整个过程分为四个关键阶段:

  1. 状态变更

    • 调用setPressed()/setSelected()等方法改变视图状态

    • 更新视图内部的mDrawableState状态数组

  2. Drawable响应

    • 触发drawableStateChanged()回调

    • StateListDrawable通过onStateChange()匹配新状态对应的Drawable资源

  3. 重绘调度

    • 调用invalidateSelf() → 触发View.invalidate()

    • 通过ViewParent链递归至ViewRootImpl

    • 通过scheduleTraversals()异步调度重绘

  4. 绘制执行

    • 下一帧触发performTraversals()

    • 根据标记位仅执行draw流程(measure/layout跳过)

    • 调用View.draw() → Drawable.draw()渲染新状态对应的图片

性能优化要点

  • 优先使用invalidate(Rect)局部刷新

  • 复杂动画启用硬件加速(LAYER_TYPE_HARDWARE

  • 避免在draw()中创建对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/92190.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92190.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3接收SSE流数据进行实时渲染日志

后端使用的是 Spring Boot WebFlux&#xff08;响应式编程框架&#xff09;&#xff0c;并且返回的是 Server-Sent Events (SSE) 流式数据&#xff0c;那么在 Vue3 中&#xff0c;需要使用 EventSource API 或 fetch 流式读取 来正确获取响应内容。方案 1&#xff1a;使用 Eve…

每日五个pyecharts可视化图表-bars(6)

探索pyecharts库中条形图的高级用法与定制技巧 在数据可视化中&#xff0c;条形图是最常用的图表类型之一&#xff0c;它能够清晰地展示不同类别之间的数量对比。今天&#xff0c;我们将继续学习如何使用pyecharts创建5种不同风格的条形图。pyecahts源码 图表1&#xff1a;带…

【C语言】文件操作全解析

文章目录一、为什么需要文件操作&#xff1f;二、认识文件&#xff1a;不止是磁盘上的存储2.1 程序文件2.2 数据文件2.3 文件名的构成三、文本文件与二进制文件&#xff1a;数据的两种形态3.1 存储方式差异3.2 实例对比&#xff1a;整数10000的存储3.3 二进制文件操作示例四、文…

C结构体的几种定义形式 + typedef结合使用的好处

struct 语句定义了一个包含多个成员的新的数据类型&#xff0c;struct 语句的格式如下&#xff1a; struct tag { member-list member-list member-list ... } variable-…

SPICE电容矩阵

SPICE电容矩阵: 如果有许多条传输线,就可以用下标来标记每一条线。例如,如果有5条线,就用1~5分别标记,依惯例把返回路径导体标记为导线0。图10.6给出了5条导线和一个公共返回平面的横截面图。首先研究电容器元件,下一节再讨论电感器元件。 在这个线的集合中,每对导线之间…

【Java】栈和队列

文章目录1.栈1.1 栈的定义1.2 栈的使用1.3 栈的模拟实现2.队列2.1 队列的定义2.2 队列的使用2.3 队列的模拟实现3.循环队列3.1 循环队列的概念3.2 循环队列判断空和满4.双端队列Deque1.栈 1.1 栈的定义 栈是一种特殊的线性表&#xff0c;其只允许在固定的一段进行数据的插入或…

【性能测试】---测试工具篇(jmeter)

目录 1、安装并启动jemeter 2、重点组件 2.1、线程组&#xff1a; 2.2、HTTP取样器​编辑 2.3、查看结果树 2.4、HTTP请求默认值 2.5、HTTP信息头管理器 2.6、JSON提取器 2.7、JSON断言 2.8、同步定时器 2.9、CSV数据文件设置 2.10、HTTP Cookie管理器 3、测试报告…

机器学习(12):拉索回归Lasso

- 拉索回归可以将一些权重压缩到零&#xff0c;从而实现特征选择。这意味着模型最终可能只包含一部分特征。 - 适用于特征数量远大于样本数量的情况&#xff0c;或者当特征间存在相关性时&#xff0c;可以从中选择最相关的特征。 - 拉索回归产生的模型可能更简单&#xff0c;因…

Redis持久化存储

Redis持久化存储详解 一、核心持久化机制 Redis提供两种主要持久化方式&#xff1a;RDB&#xff08;快照&#xff09; 和 AOF&#xff08;追加文件&#xff09;&#xff0c;以及两者的混合模式。 RDB&#xff08;Redis Database&#xff09;快照持久化 工作原理 RDB通过创建数据…

python学智能算法(三十四)|SVM-KKT条件回顾

【1】引言 前序学习进程中&#xff0c;对软边界拉格朗日方程进行了初步构建。 其中约定了两个拉格朗日乘子要非负&#xff0c;其本质是要满足KKT条件。 今天就乘此次机会&#xff0c;在回顾一下KKT条件。 【2】定义 当问题无约束的时候&#xff0c;只要让函数梯度为零&#…

【网络基础】计算机网络发展背景及传输数据过程介绍

本文旨在帮助初学者建立起计算机网络的基础认知&#xff0c;从网络的发展背景到网络协议的分层模型&#xff0c;再到IP与MAC地址的基本概念&#xff0c;全面覆盖第一阶段学习重点。 &#x1f4cc; 本节重点 了解计算机网络的发展背景&#xff0c;掌握局域网&#xff08;LAN&am…

阿里云-通义灵码:解锁云原生智能开发新能力,让云开发更“灵”~

免责声明&#xff1a;此篇文章所有内容皆是本人实验&#xff0c;并非广告推广&#xff0c;并非抄袭&#xff0c;如有侵权&#xff0c;请联系笔者。 每日一句 信念其实就是相信未来&#xff0c; 相信内在&#xff0c; 以及坦然美好的心境。 目录 每日一句 一. 引言 二.通义…

lesson33:Python协程详解:从原理到实战的异步编程指南

目录 一、协程核心概念&#xff1a;轻量级并发的本质 1.1 什么是协程&#xff1f; 1.2 协程与线程/进程的对比 二、协程工作原理&#xff1a;事件循环与协作式调度 2.1 事件循环&#xff08;Event Loop&#xff09;&#xff1a;协程的"调度中心" 2.2 协作式调度…

深入理解C++模板进阶:非类型参数、特化与分离编译

前言C模板是泛型编程的核心&#xff0c;它允许我们编写与类型无关的代码。在掌握了模板的基础知识后&#xff0c;我们需要进一步了解模板的高级特性&#xff0c;以便更灵活地使用它们。本文将深入探讨三个重要的模板进阶主题&#xff1a;非类型模板参数、模板特化以及模板的分离…

使用winsw把SpringBoot项目注册成window服务

目录 一、使用winsw注册 1.1、项目打jar包 1.2、下载winsw 1.3、把 WinSW.NET4.exe 重新命名 1.4、编写m配置文件用于配置注册信息 1.5、创建文件夹存放你的文件 1.6、安装服务 1.7、启动服务 1.8、卸载服务 1.8、停止服务 一、使用winsw注册 1.1、项目打jar包 例如项目jar包名…

进阶向:Python开发简易QQ聊天机器人

数字化时代的聊天机器人应用在当今数字化时代&#xff0c;聊天机器人已经成为日常生活和商业活动中不可或缺的一部分。根据市场研究数据显示&#xff0c;全球聊天机器人市场规模预计将在2026年达到102亿美元&#xff0c;年复合增长率达到34.75%。这些智能助手正广泛应用于以下场…

基于开源链动2+1模式AI智能名片S2B2C商城小程序的用户留存策略研究

摘要&#xff1a;在数字化商业竞争白热化的当下&#xff0c;用户留存成为企业可持续发展的核心命题。本文聚焦开源链动21模式AI智能名片S2B2C商城小程序这一创新技术组合&#xff0c;通过分析其技术架构、模式创新与生态闭环的协同效应&#xff0c;揭示其在降低用户决策成本、提…

单词的划分(动态规划)

题目描述有一个很长的由小写字母组成字符串。为了便于对这个字符串进行分析&#xff0c;需要将它划分成若干个部分&#xff0c;每个部分称为一个单词。出于减少分析量的目的&#xff0c;我们希望划分出的单词数越少越好。你就是来完成这一划分工作的。输入第一行&#xff0c;一…

C语言学习笔记——文件

目录1 文件的概念2 程序文件和数据文件3 二进制文件和文本文件4 流4.1 流的概念4.2 标准流5 文件信息区和文件指针6 处理文件的库函数6.1 fopen6.2 fclose6.3 fgetc6.4 fputc6.5 fgets6.6 fputs6.7 fscanf6.8 fprintf6.9 fread6.10 fwrite6.11 fseek6.12 ftell6.13 rewind6.14 …

C++语法与面向对象特性(2)

一.inline函数1.inline的基本特性被inline修饰的函数被称为内联函数。inline函数设计的初衷是为了优化宏的功能&#xff0c;编译器会在编译阶段对inline函数进行展开。然而需要注意的是&#xff0c;inline对于编译器而言是一种建议&#xff0c;它通常会展开一些简短的&#xff…