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…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...