写一个自定义View你都需要注意什么

news2024/11/15 15:49:06

本文主要是记录一下继承子View,所需要实现的方法,以及对自己的知识做一下梳理和记录,其中不少内容觉得自己应该是会的,但是实际写起来,还是遇到不少阻碍

构造方法

首先构造先了解一下构造方法,一般来说,继承自View,需要实现四个构造方法,如下列代码:

public SVGView(Context context) {
    this(context, null);
}

public SVGView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public SVGView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public SVGView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
    super(context, attrs, defStyleAttr, defStyleRes);

}

第一个构造方法

只有在Java中去new一个新的控件的时候才会使用,比如去new一个TextView

    val textView = TextView(this)
    val text = "Hello Word"
    val layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT)
    textView.layoutParams = layoutParams
    textView.text = text
    layoutBox.addView(textView)

效果如图所示:

第二个构造方法

适用与在XML中去加入这个控件,其中attrs这个参数,就是存储在XML中,效果和之前是一样的

<TextView
    android:text="Hello Word"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

第三个构造方法

defStyleAttr是主题,一个Theme中的属性,如果在主题中定制了文字的颜色,大小,可以使用此函数,现在我们去Theme去添加一些主题的属性

<style name="Base.Theme.SVGDemo" parent="Theme.AppCompat.DayNight.NoActionBar">
    <item name="android:textViewStyle">@style/MyTextViewStyle</item>
</style>

<style name="MyTextViewStyle" parent="Widget.AppCompat.TextView">
    <item name="android:textColor">@color/red</item>
</style>

这里需要注意只有系统的View,比如TextView,ImageView之类才可以使用此方式,进行集体定义,因为因为textViewStyle是Android Framework中定义的特定样式项,用于设置TextView的默认样式,如果是继承自TextView的一个自定义View,也是不能直接使用的,但是可以这样:

constructor(context: Context) : super(context, null)

constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, android.R.attr.textViewStyle)

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

对于自定义View,只会走两个构造方法,第一个和第二个,如果你设置了类似android:textViewStyle进行了集体修改,并且希望自定义View也按照此规则进行变化,则需要单独设置

第四个构造方法

第四个,只有在SDK21及以上才可以使用,作用是什么呢,就比如在Theme设置了主要文字颜色为黑色,但是这个View很特殊,它得是红色,所以,你可以额外给它单独设置一个主题

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr,R.style.MyTextViewStyle)

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)

如图所示,没有做修改,在第四个构造方法内传入了一个defStyleRes的Int类型参数,就实现了变化

View绘制流程

这里只是简述一个View的绘制流程,不牵扯其他的,这里加上代码

打印结果如图所示:

这里来解释以下这三个函数的意义:

  1. onMeasure:在测量阶段调用,用于确定 View 的大小。在该方法中,您可以通过设置 View 的 MeasuredWidthMeasuredHeight 来指定视图的测量尺寸。这个方法通常被重写以确保 View 在布局中获得适当的大小。
  2. onLayout:在布局阶段调用,用于确定 View 在父容器中的位置。在该方法中,您可以通过设置 View 的 lefttoprightbottom 来指定视图在布局中的位置。这个方法通常在自定义 ViewGroup 中重写,用于摆放子视图的位置。
  3. onDraw:在绘制阶段调用,用于绘制 View 的内容。在该方法中,您可以使用绘图工具(Canvas)绘制各种形状、文本、图像等内容。这个方法通常在自定义 View 中重写,用于自定义视图的外观和样式。

onMeasure

这里主要确定View的大小,通过调用 MeasureSpec.getMode(widthMeasureSpec) 可以获取测量模式,通过调用 MeasureSpec.getSize(widthMeasureSpec) 可以获取测量大小。

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        Log.e("MyView","onMeasure")
    }

然后通过这个View本身需要加载的内容来计算出内容大小,通过在new View的设置的wrap_content或者match_parent,或者多少DP配合来设置View的实际显示大小

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getSize(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

其中widthMode,heightMode,有以下几种测量模式

  1. MeasureSpec.EXACTLY:表示视图的大小已经确定,要么是通过固定的宽度和高度值,要么是通过 match_parent 或指定的具体大小。在这种情况下,您可以直接使用
  2. MeasureSpec.AT_MOST:表示视图的大小可以是一个限制范围内的值,例如父容器提供的可用空间大小。在这种情况下,您需要根据测量规格和视图的内容来计算一个合适的测量尺寸。
  3. MeasureSpec.UNSPECIFIED:表示视图的大小没有限制,可以根据需要自由扩展。 计算完成后设置这个View的真正大小,调用setMeasuredDimension(measuredWidth, measuredHeight)来设置

onLayout

当 Android 系统需要安排视图在父容器中的位置时,会调用视图的 onLayout 方法。在 onLayout 方法中,您可以指定视图的左上角 (lefttop) 和右下角 (rightbottom) 的位置,从而布局视图在父容器中的位置,先看一下方法:

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        Log.e("MyView","onLayout")
    }
  • changed:一个布尔值,表示视图的布局是否发生了变化。当视图的布局发生变化时,该参数值为 true;否则为 false
  • lefttoprightbottom:这四个参数表示视图的边界框(矩形),指定了视图在父容器中的位置。 也就是是根据ViewGroup传来的lefttoprightbottom来设置这个View的位置大小 可以直接通 = 的方法 比如 this.left = left

onDraw

这个是自定义View中最重要的方法,你的View显示什么内容全靠它来设置,那么如何设置,通过画布和画笔

  • onDraw 方法中,您可以使用传入的 Canvas 对象进行绘制操作。Canvas 是一个画布对象,它提供了一系列的绘制方法,如绘制线条、矩形、文本、位图等。
  • onDraw 方法中,您可以使用 Canvas 对象绘制视图的各个部分,例如背景、文本、图标等。可以根据需要使用 drawRect()drawText()drawBitmap() 等方法进行绘制。

比如画一个矩形:

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    paint.color = Color.RED
    val rect = Rect(0, 0, width, height)
    canvas?.drawRect(rect, paint)
}

或者去画一下其他比较复杂的东西。这里记录一下sin,cos,tan,为什么记录,在计算比较复杂的图形中,这些是必不可少,还有贝塞尔曲线什么之类的,总的来说,嗯,数学很重要

  • sin 对边与斜边的比值
  • cos 邻边与斜边的比值
  • tan 对边与邻边的比值

页面刷新

如果你的自定义View是一个会变化的页面

  • invalidate 会进行onDraw的重新绘制,一般用于View大小不变,内容有了变化,需要更新视图
  • requestLayout 会重新进行布局 ,调用measure() -> onLayout

requestLayout是不会自己主动调用onDraw,如果需要布局发生变化后立即触发onDraw的话,需要自己手动调用invalidate

XML增加参数

如果希望你的View可以像系统的View可以在Xml中添加比较多的定义,这里需要在theme或者attr中去定义方法名字和接受的参数类型,比如:

<declare-styleable name="MyView">
    <attr name="MyViewColor" format="color"/>
</declare-styleable>

这里定义了一个MyViewColor的,然后使用需要添加一个Color,那么我如何把它拿出来使用呢

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, R.style.MyTextViewStyle) {
    var typedArray: TypedArray? = null
    try {
        typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView)
        val color = typedArray.getColor(R.styleable.MyView_MyViewColor,Color.BLACK)
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        typedArray?.recycle()
    }
}

这样就完成就可以了

事件分发

事件分发流程

AndroidView的事件分发是一个U字型设计,其中主要有三个方法dispatchTouchEventonInterceptTouchEvent,onTouchEvent,分别作用于事件分发,事件拦截,事件消费,在此之前先看一般的流程图:

这个图是我当初学习事件分发copy下来的,这里也记录一下 可以看到三个方法的不同作用,其中onInterceptTouchEventViewGroup特有的,

事件类型

事件的类型主要有三种:

override fun onTouchEvent(event: MotionEvent?): Boolean {
   when(event?.action){
       MotionEvent.ACTION_DOWN -> {
           //按下
       }
       MotionEvent.ACTION_MOVE ->{
           //移动
       }
       MotionEvent.ACTION_UP ->{
           //抬起
       }
   }
   return super.onTouchEvent(event)
}

可以根据不同的情况来处理,最后如果此事件消费了,不想让他继续往下面传递就return true,如果想让他继续往下面船体就return false

Android 学习笔录

Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap

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

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

相关文章

和悦未来社区:助力共同富裕,三思打造智慧社区新样板

“共同富裕是社会主义的本质要求&#xff0c;是中国式现代化的重要特征&#xff0c;是人民群众的共同期盼。” 2021年5月20日&#xff0c;《中共中央 国务院关于支持浙江高质量发展建设共同富裕示范区的意见》正式发布。 浙江省作为共同富裕先行示范省份&#xff0c;行而不辍…

SpringCloud microservice-student-consumer-80服务消费者项目建立(四)

新建一个服务器提供者module子模块&#xff0c;类似前面建的common公共模块&#xff0c;名称是 microservice-student-consumer-1001 pom.xml修改&#xff1a; <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSc…

01 UML概述

UML概述 (1) 规约系统的制品–UML适用于对所有重要的分析、设 计和实现决策进行详细描述 (2) 构造系统的制品–UML描述的模型可与各种编程语言直接相关联 UML应用范围 (1)可用于对象方法和构件方法&#xff1b; (2)可用于 ●所有应用领域(例如&#xff0c;航空航天、财政、通…

重生之我测阿里云U1实例(通用算力型实例)

官方福利&#xff01;&#xff01;&#xff01;&#xff01;大厂羊毛你确定不薅&#xff1f;&#xff1f;&#xff1f; 参与ECSU实例评测&#xff0c;申请免费体验机会&#xff1a;https://developer.aliyun.com/mission/review/ecsu 参与ECSU实例评测&#xff0c;申请免费体验…

CVPR 2023 | 基于金字塔模型的异常检测方法

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/pdf/2211.11317 0.背景&#xff1a; 工业异常检测旨在发现产品的异常区域&#xff0c;在工业质量检测中发挥着重要作用。在工业场景中&#xff0c;很容易获得大量的正…

阿里云RAM凭据插件应用纪实

官方传送 官方文档传送门 官方源码传送门 记录日期 2023-06-13 背景简介 项目中主要使用了OSS&#xff0c;本文记录在OSS SDK中的使用方法 引入依赖 <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactI…

一波未平一波又起!加密市场静候利率决议!美联储将会如何演绎?

过去一周&#xff0c;加密市场主要根据SECvs币安&Coinbase的诉讼案件做出反应&#xff0c;但本周市场行情或将以宏观事件为主导&#xff0c;市场短期内仍看不到明确的方向。 因通胀意外反弹&#xff0c;曾暂停加息的澳大利亚央行、加拿大央行近期宣布继续加息&#xff0c;让…

企业软文投稿流程是怎样的,投稿有什么要求?

随着互联网的快速发展&#xff0c;传统的线下推广已经不再是企业宣传的唯一选择。越来越多的企业开始关注线上平台&#xff0c;尤其是软文投稿。那么&#xff0c;企业软文投稿流程是怎样的&#xff1f;投稿有什么要求呢&#xff1f;本文伯乐网络传媒将为大家详细介绍。 1.确定投…

GORM---高级查询

文章目录 构建Demo数据模型定义数据表 高级查询子查询选择字段排序数量偏移总数Group & Having连接 Pluck扫描 构建Demo数据 模型定义 type Users struct {Id uint64 gorm:"primary_key;NOT NULL"EmailId uint64 gorm:"foreignKey:email_id;referenc…

python + pytest 接口自动化 —— 参数关联

什么是参数关联&#xff1f; 参数关联&#xff0c;也叫接口关联&#xff0c;即接口之间存在参数的联系或依赖。在完成某一功能业务时&#xff0c;有时需要按顺序请求多个接口&#xff0c;此时在某些接口之间可能会存在关联关系。 比如&#xff1a;B接口的某个或某些请求参数是…

高级数据结构-线段树

线段树 线段树树基于分治思想的二叉树&#xff0c;用来维护区间信息(区间和、区间最大值、区间最小值等等)。可以在 O ( l o g n ) O(logn) O(logn)的时间内完成区间信息的查询和修改。 线段树中每个叶子结点存储元素本身&#xff0c;非叶子结点存储区间内元素的统计值 节点数组…

C#多线程Task常见问题(二)

1 多线程临时变量 2 线程安全和锁lock 3 线程安全策略总结 线程安全和锁lock 线程安全问题&#xff1a;一段程序逻辑在单线程中执行和多线程中执行&#xff0c;结果一致说明线程是安全的&#xff1b;如果结果不同说明线程不安全。 同样先看一个例子&#xff1a;分别用主线程…

唯品会宕机惨案,损失超亿元!故障来时如何迅速应对?

01 事件回顾 对于IT工程师来说&#xff0c;宕机并非新鲜话题&#xff0c;经历过一次服务器宕机&#xff0c;职业生涯才“完整”。但如果事故超过 12 小时&#xff0c;或许会直接造成职业生涯“宕机”。 3月29日发生的突发事件&#xff0c;#唯品会App崩了 冲上热搜&#xff0…

最全整理,完整一套WEB/APP/接口测试测试流程,全面覆盖...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 web测试流程 需求…

Java --- springboot3整合redis

目录​​​​​​​ 一、整合redis 1.1、导入pom依赖 1.2、修改springboot配置文件 1.3、代码测试 二、测试访问redis五大常用数据类型 三、自动配置原理 四、定制化 4.1、解决redis存储序列化乱码问题 4.2、redis客户端使用jedis 一、整合redis 1.1、导入pom依赖 …

【考试】2023年5月软件设计师考试感受

前言❤️ 由于考试地点距离住的地方很远&#xff0c;一个南面一个北面&#xff0c;所以BZ选择了提前一天去到考试地点附近&#xff0c;住在考点附近。吃了晚饭后。到住的地方大概9点多&#xff0c;洗漱完&#xff0c;10点左右开始考前过知识点&#xff0c;复习到凌晨3点左右。…

改进的粒子滤波算法及其应用研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【博客660】prometheus默认5min的lookback机制带来查询的影响

prometheus默认5min的lookback机制带来查询的影响 1、prometheus staleness机制 我发过的&#xff1a;【博客616】prometheus staleness对PromQL查询的影响 官方staleness解析 相关文章&#xff1a;do-prometheus-metrics-have-some-sort-of-freshness-lifetime 相关issue…

全面解析数据治理

摘要 数据治理并不是一种简单的操作行为&#xff0c;而是对数据资产管理行使权力和控制的活动集合&#xff0c;是一种管理和保护数据的方法&#xff0c;是确保准确性、完整性、安全性、可靠性和一致性的关键。 通过数据治理&#xff0c;企业可以更好地掌握现有数据&#xff0…

C语言---认识动态内存管理并实现一个动态通讯录:静态通讯录别来沾边

文章目录 前言&#x1f31f;一、为什么存在动态内存分配&#x1f31f;二、动态内存函数的介绍&#x1f30f;2.1.malloc函数free函数&#x1f30f;2.2.calloc函数free函数&#x1f30f;2.3.realloc函数 &#x1f31f;三、常见的动态内存错误&#x1f30f;3.1.对NULL指针的解引用…