Android 自定义View 加 lifecycle 简单使用
前言
本文是自定义view中最简单的使用方法,分别进行 ‘onMeasure’、‘onDraw’、‘自定义样式’、‘lifecycle’的简单使用,了解自定义view的使用。
通过lifecycle来控制 动画的状态

一、onMeasure做了什么?
在onMeasure中获取view 的宽和高 是 ‘0’
测量View的宽 / 高
- 在某些情况下,需要多次测量
(measure)才能确定View最终的宽/高; - 该情况下,
measure过程后得到的宽 / 高可能不准确; - 此处建议:在
layout过程中onLayout()去获取最终的宽 / 高
必须要了解 MeasureSpec 作用
测量规格(MeasureSpec)是由测量模式(mode)和测量大小(size)组成,共32位(int类型),其中:
- 测量模式(mode):占测量规格(MeasureSpec)的高2位;
- 测量大小(size):占测量规格(MeasureSpec)的低30位。

MeasureSpec类用一个变量封装了测量模式(mode)和测量大小(size):通过使用二进制,将测量模式(mode)和测量大小(size)打包成一个int值,并提供了打包和解包的方法,这样的做法是为了减少对象内存分配和提高存取效率。具体使用如下所示:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val widthModel = MeasureSpec.getMode(widthMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val heightModel = MeasureSpec.getMode(heightMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)
// @TODO 在 onMeasure 中获取view的 宽高 获取到是 0Log.e(TAG, "onMeasure: ${widthSize}-${width}__${heightSize}__${height}")val defWidth = 400val defHeight = 400
// @TODO MeasureSpec.AT_MOST:wrap_content ; MeasureSpec.EXACTLY:match_parent ;if (widthModel == MeasureSpec.AT_MOST && heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, defHeight)} else if (widthModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, heightSize)} else if (heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, defHeight)}}
2、onLayout 做了什么
计算位置,里面包含子view 的情况下才会用到这个函数
一般继承自viewGroup或者重新写layout布局

3、onDraw 做了什么
绘制View自身,设置padding 时要在onDraw中计算
1. 绘制view背景 2. 绘制view内容 3. 绘制子View 4. 绘制装饰(渐变框,滑动条等等)

override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)canvas?.let {val pL = paddingLeftval pR = paddingRightval pT = paddingTopval pB = paddingBottomvar mHeight = height - pT - pBvar mWidth = width - pL - pRval cy = pT.plus(pB).div(2) + mHeight.div(2).toFloat()val cx = pL.plus(pR).div(2) + mWidth.div(2).toFloat()val cc = Math.min(mHeight, mWidth).div(2).toFloat()it.drawCircle(cx,cy,cc,mPaint)}}
4、lifecycle控制动画的状态
自定义view 继承 DefaultLifecycleObserver 类 然后实现 生命周期=中的方法override fun onStart(owner: LifecycleOwner) {super.onStart(owner)animSetColor.start()}override fun onDestroy(owner: LifecycleOwner) {super.onDestroy(owner)animSetColor.cancel()}override fun onPause(owner: LifecycleOwner) {super.onPause(owner)animSetColor.pause()}override fun onResume(owner: LifecycleOwner) {super.onResume(owner)animSetColor.resume()}在Act中 进行生命周期监听的绑定lifecycle.addObserver(customView)
5、代码示例
自定义View代码
/*** @TODO 自定义view***/
class MyView(context: Context?, attrs: AttributeSet?) :View(context, attrs), DefaultLifecycleObserver {private val mPaint by lazy { Paint() }private val TAG = "MyView"private var i = 0// @TODO 动画实现改变颜色 然后 通过 lifecycle 控制动画的状态:开始、暂停、恢复、取消private val animSetColor by lazy {ValueAnimator.ofInt(0, 100).apply {addListener(object : AnimatorListener {override fun onAnimationStart(animation: Animator) {}override fun onAnimationEnd(animation: Animator) {}override fun onAnimationCancel(animation: Animator) {}override fun onAnimationRepeat(animation: Animator) {i++if (i % 2 == 0) {mPaint.color = android.graphics.Color.BLUE}mPaint.color = when (i % 5) {0 -> android.graphics.Color.BLUE1 -> android.graphics.Color.YELLOW2 -> android.graphics.Color.CYAN3 -> android.graphics.Color.MAGENTA4 -> android.graphics.Color.LTGRAYelse -> android.graphics.Color.TRANSPARENT}
// @TODO 每次设置颜色后 调用postInvalidate 重新绘制ViewpostInvalidate()}})
// 动画无线循环执行repeatCount = ValueAnimator.INFINITE
// 间隔一秒执行一次duration = 1000}}init {mPaint.color = Color.Blue.hashCode()mPaint.style = Paint.Style.FILLmPaint.strokeWidth = 20fcontext?.obtainStyledAttributes(attrs, R.styleable.MyView)?.apply {mPaint.color = getColor(R.styleable.MyView_circlr_color, android.graphics.Color.GREEN)recycle()}}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val widthModel = MeasureSpec.getMode(widthMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val heightModel = MeasureSpec.getMode(heightMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)
// @TODO 在 onMeasure 中获取view的 宽高 获取到是 0Log.e(TAG, "onMeasure: ${widthSize}-${width}__${heightSize}__${height}")val defWidth = 400val defHeight = 400
// @TODO MeasureSpec.AT_MOST:wrap_content ; MeasureSpec.EXACTLY:match_parent ;if (widthModel == MeasureSpec.AT_MOST && heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, defHeight)} else if (widthModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, heightSize)} else if (heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, defHeight)}}
//挂在到Act上时
// override fun onAttachedToWindow() {
// super.onAttachedToWindow()
// Log.e(TAG, "onAttachedToWindow: ")
// anim.start()
// }//在Act 销毁时
// override fun onDetachedFromWindow() {
// super.onDetachedFromWindow()
// Log.e(TAG, "onDetachedFromWindow: ")
// anim.cancel()
//
// }override fun onStart(owner: LifecycleOwner) {super.onStart(owner)animSetColor.start()}override fun onDestroy(owner: LifecycleOwner) {super.onDestroy(owner)animSetColor.cancel()}override fun onPause(owner: LifecycleOwner) {super.onPause(owner)animSetColor.pause()}override fun onResume(owner: LifecycleOwner) {super.onResume(owner)animSetColor.resume()}override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {super.onLayout(changed, left, top, right, bottom)Log.e(TAG, "onLayout: ")}/*** 作用:根据给定的 Canvas 自动渲染View包括其所有子 View)。* 绘制过程:* 1. 绘制view背景* 2. 绘制view内容* 3. 绘制子View* 4. 绘制装饰(渐变框,滑动条等等)* 注:* a. 在调用该方法之前必须要完成 layout 过程* b. 所有的视图最终都是调用 View 的 draw()绘制视图( ViewGroup 没有复写此方法)* c. 在自定义View时,不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘制* d. 若自定义的视图确实要复写该方法,那么需先调用 super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制*/override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)canvas?.let {val pL = paddingLeftval pR = paddingRightval pT = paddingTopval pB = paddingBottomvar mHeight = height - pT - pBvar mWidth = width - pL - pRval cy = pT.plus(pB).div(2) + mHeight.div(2).toFloat()val cx = pL.plus(pR).div(2) + mWidth.div(2).toFloat()val cc = Math.min(mHeight, mWidth).div(2).toFloat()it.drawCircle(cx,cy,cc,mPaint)}}
}
自定义View的xml样式文件
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="MyView"><attr name="circlr_color" format="color"/></declare-styleable>
</resources>
layout布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#11008811"tools:context=".CustomViewActivity"><com.andriod.police.view.MyViewandroid:id="@+id/customView"android:layout_width="wrap_content"android:layout_height="130dp"android:background="#11f08811"app:circlr_color="@color/cardview_light_background"android:padding="20dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>
Act
class CustomViewActivity : AppCompatActivity() {private val customView: MyView by lazy { findViewById(R.id.customView) }override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_custom_view)
// @TODO 通过 lifecycle 控制动画的状态:开始、暂停、恢复、取消lifecycle.addObserver(customView)}
}
总结
在自定义View中了解在 onMeasure中进行view 的测量,在onLayout中进行对view位置的控制,在onDraw中进行view的绘制。
通过 lifecycle控制view的生命周期,防止出现内存泄露问题如在相应的生命周期中操作动画的执行状态
相关文章:
Android 自定义View 加 lifecycle 简单使用
前言 本文是自定义view中最简单的使用方法,分别进行 ‘onMeasure’、‘onDraw’、‘自定义样式’、‘lifecycle’的简单使用,了解自定义view的使用。 通过lifecycle来控制 动画的状态 一、onMeasure做了什么? 在onMeasure中获取view 的宽和…...
在K8S中,svc底层是如何实现的?
在Kubernetes中,Service是集群内部的一个抽象层,用于定义一组Pod的逻辑分组,并提供统一的访问入口点,同时还可以对这些Pod提供负载均衡和网络代理功能。Service底层的实现主要包括以下几个关键组件和技术: 标签选择器…...
Python pyqt小技巧:默认打开某文件(即自动加载某文件)
文章目录 前言 前言 有的时候需要界面自动加载某文件。不需要人为在打开选择。 import os #自带 import sys # 获取该程序当前文件目录dir_name os.path.dirname(os.path.realpath(sys.argv[0])) f1 os.path.join(dir_name, 题目调度规程.xls) # 拼接路径 文件必须和程序在…...
vue2实现组件库的自动按需引入,unplugin-auto-import,unplugin-vue-components
1.使用ant-design-vue或者element-ui时,如何每个组件都去import导入组件,大大降低了开发效率,如果全局一次性注册会增加项目体积,那么如何实现既不局部引入,也不全局注册? 2.在element-plus官网看到有说明…...
C++第十节:map和set的介绍与使用
【本节要点】 1.关联式容器2.键值对3.map介绍与使用4.set介绍与使用5.multimap与multisedd的介绍与使用 一、关联式容器:数据管理的核心利器 关联式容器是STL中用于高效存储和检索键值对(key-value pair)的数据结构,其底层基于红黑…...
线性代数笔记28--奇异值分解(SVD)
1. 奇异值分解 假设矩阵 A A A有 m m m行 n n n列 奇异值分解就是在 A A A的行向量上选取若干对标准正交基,对它作 A A A矩阵变化并投射到了 A A A的列空间上的正交基的若干倍数。 A v → u → σ u → ∈ R m v → ∈ R n A\overrightarrow{v}\overrightarrow{u…...
【从零开始学习计算机科学】硬件设计与FPGA原理
硬件设计 硬件设计流程 在设计硬件电路之前,首先要把大的框架和架构要搞清楚,这要求我们搞清楚要实现什么功能,然后找找有否能实现同样或相似功能的参考电路板(要懂得尽量利用他人的成果,越是有经验的工程师越会懂得借鉴他人的成果)。如果你找到了的参考设计,最好还是…...
项目中同时使用Redis(lettuce)和Redisson的报错
温馨提示:图片有点小,可以放大页面进行查看... 问题1:版本冲突 直接上图,这个错表示依赖版本不匹配问题,我本地SpringBoot用的是2.7,但是Redisson版本用的3.32.5。 我们通过点击 artifactId跟进去 发现它…...
leetcode-数组
26. 删除有序数组中的重复项 已解答 简单 相关标签 相关企业 提示 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 n…...
人工智能里的深度学习指的是什么?
深度学习(Deep Learning, 简称DL)是机器学习领域的一个重要分支,它通过构建和训练深层神经网络模型,从大量数据中自动学习和提取特征,以实现复杂任务的自动化处理和决策。以下是关于深度学习的详细介绍: 一…...
docker本地部署ollama
启动ollama容器 1.使用该命令启动CPU版运行本地AI模型 docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 2.此命令用于启动GPU版本运行AI模型 前提是笔记本已配置NVIDIA的GPU驱动,可在shell中输入nvidia-smi查看详细情况…...
LangChain构建语言模型驱动应用的强大框架
LangChain 核心功能与组件链(Chains)记忆(Memory)提示模板(Prompts)代理(Agents)数据检索(Indexes) 应用场景文档问答自动化工作流知识管理系统 发展历程总结…...
2025-03-08 学习记录--C/C++-PTA 习题10-2 递归求阶乘和
合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。💪🏻 一、题目描述 ⭐️ 二、代码(C语言)⭐️ #include <stdio.h>double fact( int n ); double facts…...
浅谈 DeepSeek 对 DBA 的影响
引言: 在人工智能技术飞速发展的背景下,DeepSeek 作为一款基于混合专家模型(MoE)和强化学习技术的大语言模型,正在重塑传统数据库管理(DBA)的工作模式。通过结合其强大的自然语言处理能力、推理…...
AI如何重塑运维体系
AI大模型的引入正在从被动响应到主动预防、从经验驱动到数据智能全面重构运维体系。 一、颠覆传统运维模式的技术革新 故障预测:从“救火”到“防火” AI大模型通过整合历史日志、硬件状态、网络流量等多模态数据,结合时间序列分析(如LSTM&am…...
linux 内网下载 yum 依赖问题
1.上传系统镜像 创建系统目录,用户存放镜像,如下: mkdir /mnt/iso上传 iso 文件到 /mnt/iso 文件夹下。 2.挂载系统镜像 安装镜像至 /mnt/cdrom 目录中 mount -o loop /mnt/iso/CentOS-7-x86_64-Minimal-xx.iso /mnt/cdrom3.修改yum源配…...
mapbox开发小技巧
自定义图标 // 1、单个图标 const url ./static/assets/symbols/code24x24/VIDEO.png // 图标路径 map.loadImage(url ,(error, image) > {if (error) throw errormap.addImage(video-icon, image) })// 2、雪碧图利用canvas // json和png图片 function getStyleImage(fil…...
DeepSeek×博云AIOS:突破算力桎梏,开启AI普惠新纪元
背景 在全球人工智能技术高速迭代的背景下,算力成本高企、异构资源适配复杂、模型部署效率低下等问题,始终是制约企业AI规模化应用的关键。 DeepSeek以创新技术直击产业痛点,而博云先进算力管理平台AIOS的全面适配,则为这一技术…...
Java高频面试之集合-07
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:ArrayList 和 Vector 的区别是什么? ArrayList 与 Vector 的区别详解 ArrayList 和 Vector 都是 Java 中基于…...
Redis- 切片集群
切片集群 切片集群什么是Redis Cluster吗?为什么需要切片集群?Redis Cluster的数据分片机制是怎样的?哈希槽的算法是什么基本算法流程 待填坑 切片集群 什么是Redis Cluster吗?为什么需要切片集群? Redis Cluster是R…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
