从零开始学习自定义view【0】View是什么

序🙉

对于很多初中级的android开发人员来说,自定义view是提升自己能力的必须跨过的一道坎儿,想学习,却总是无从下手。
因为我感觉,网上的碎片化的知识总是让我们头大,还不知道view到底是啥呢,就onMeasure,onLayout,onDraw。如果没有一定基础的话,还真不知道这是view的生命周期的一部分呢,这不是只有activity,service才有的么?
花了时间看了源码,结果还是云里雾里。很多问题,离开了校园的我们,绝大多数只有网上搜又是一大堆有的没的,更不用说有时候搜一些资料,出来一堆广告,这里单走一个6字!反正对于我们这些新手小白很不友好。

Notes

本文及接下来的系列文章均来自与包括但不限于网络,书本,博客,AI以及个人的理解,如有侵权,请联系删除。
如果有不对的地方,烦请各位看官们及时指出,我们共同学习。

View 简介

  1. 在Android中,View是界面上所有用户可见元素的基础类,包括TextView、Button、ImageView等。它们都是View类的子类,并且具有View的所有特性和功能。

通俗来讲,我们经常用button来设置onClick事件,难道就不可以用TextView来设置onClick事件吗?因为他们本质上都是view,继承自view。所以我们自定义view的话,也可以直接设置它的onClick事件哦,前提是自定义的view最终继承的是Android View类。

  1. View是屏幕上的一个矩形区域,可以显示一些内容,并且可以接受用户的交互。每个View都有一个唯一的ID,通过该ID可以对View进行操作和控制。View还有很多属性,比如位置、大小、背景色、边框等,这些属性可以通过代码或xml文件来设置。

  2. 除了提供可视化的UI组件,View还提供了一些事件处理方法,例如onClick()、onLongClick()、onTouchEvent()等。这些方法可以被重写,用于处理用户的触摸事件、单击事件等交互事件。

  3. 在布局过程中,View会按照一定的规则计算出自己的尺寸和位置,并且根据父容器的大小和布局规则进行排列。如果需要定制自己的View,可以继承View类或其子类,然后重写相应的方法来实现自定义功能。

综上所述,View是Android开发中非常基础和重要的概念,理解View的使用方法和生命周期对于Android应用程序的开发和设计至关重要。

实践一下

光说不练假把式。
这个系列使用kotlin编程语言,其实和java差不多,大家再学习view的同时也顺便理一下kotlin吧,毕竟是Google官方推的,上手不难。

这里我们先在脑海里想一下我们写之前写TextView,Button的时候的代码场景,是不是都得现在xml里布个位置,然后再在activity通过id找到它,然后对它进行一顿操作(设置颜色啊,设置点击事件啊),最后就把程序跑起来一顿点是吧,哈哈哈哈😃😃😃😃。

本质上来讲,TextView,Button这些组件就是Google 开发者们自定义的view,我们直接用。这些View还是比较复杂的,看源码一堆的方法,恐怖如斯。因此我们先自己试一下简单的吧。

上菜:🥘

package xxxxxx

import android.content.Context
import android.util.AttributeSet
import android.view.View

class MyView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}

简单吧,小白觉得简单,高级Android开发也觉得简单。看山是山,看山还是山。

剖析一下:
A: kotlin 语法部分:

  1. @JvmOverloads

是将Kotlin中定义的有默认参数值的构造函数生成多个对应的Java构造函数,每个Java构造函数都对应不同数量的参数。这样,就能够从Java代码中方便地访问这些构造函数了。

也就是说在上面的例子里 @JvmOverloads constructor重载构造函数,这一个方法,相当于java写了三个构造方法(带一个参数的,带两个参数的,带三个参数的)。

  1. constructor 是Kotlin中用于定义构造函数的关键字。

在Kotlin中,每个类都可以拥有一个或多个构造函数,用于创建该类的对象。与Java不同的是,Kotlin中的构造函数可以包含初始化代码,而且还支持默认参数、命名参数、可变参数等特性。

Kotlin中的构造函数有两种类型:主构造函数和次构造函数。主构造函数是类头的一部分,它位于类名后面,并可以带有参数。而次构造函数则是类体中的函数,它使用constructor关键字声明,可以有自己的参数列表。

通过构造函数,我们可以为Kotlin类提供初始化代码,并为使用该类的开发者提供更加方便的创建对象的方式。

以下是一个简单的Kotlin类,其中包含主构造函数和次构造函数:

// 类名Sample 后面的constructor是主构造
class Sample constructor(param: String) {

	// 每次调用主构造都会执行我
	// 如果要调用次构造的话,先执行我然后再执行次构造
	// 一般用来初始化一些数据,看我的名字就知道啦,比如初始自定义view的画笔Paint
    init {
        println("init")
    }

	// 我是次构造
    constructor(param1: String, param2: String)
            : this(param1) {
        println("次构造")
    }
}

val s = Sample("pa1")
// log: init

val s2 = Sample("pa1", "pa2")
// log: init
// log: 次构造
  1. 冒号
    在Java中,我们使用extends关键字来指定继承关系,而在Kotlin中则使用冒号 😃。
  2. 问号

在 Kotlin 中,问号(?)表示可为null,也就是说一个变量可以存储一个非空值或者一个空值(null)。这个特性称为“空安全”(null safety),是 Kotlin的一大特色。

在 Java 中,如果一个变量没有被初始化,它的初始值将会是null。这很容易导致编程错误,因为程序可能会在不知道变量是否为空的情况下进行操作。

而在 Kotlin 中,所有的变量默认都是不可为 null 的。

所以如果我们需要一个允许为空的变量,可以在类型名称后面加上 ? 来声明:

// 允许为空的字符串
var s: String? = null 

在上述代码中,我们定义了一个名为 s 的字符串变量,并使用 ? 将其声明为可为 null。这意味着我们可以将 null 赋值给变量s,并且我们必须在使用 s 之前检查它是否为 null。

例如,我们可以使用 if 表达式来检查变量是否为 null:

if (s != null) {
    // 变量 s 不为空,可以进行的操作
} else {
    // 变量 s 为空,不能进行的操作
}

另外,在使用可为 null 的变量时,我们还可以使用安全调用运算符 ?. 来避免空指针异常:

val length = s?.length // 如果 s 不为空,则返回字符串长度;否则返回 null

在上述代码中,当 s 为 null 时,length 变量也会被赋值为 null,而不会抛出空指针异常。

总之,问号(?)是 Kotlin 中的一种语法特性,用来表示可为 null 的变量,帮助我们更好地处理空值情况,避免程序运行时出现异常。

B: View 参数部分:

1. Context ,上下文,详细理解的话要花老长时间了,不过理解context所花的时间是值得的!!!大家可以浏览其他csdn上面讲得详细的文章。

在Android应用程序中,Context是一个非常重要的概念,它代表了当前应用环境的上下文信息。简单地说,Context就是连接应用程序与操作系统之间的一个桥梁,它提供了很多与应用程序相关的系统级资源和服务。

Context主要有以下几个作用:

提供应用程序访问应用资源(如字符串、布局文件等)的途径。
充当应用程序与系统服务(如ActivityManager、WindowManager等)进行交互的媒介。
为应用程序提供一些基本操作,例如:启动新的Activity、发送广播、创建数据库等。

在自定义 View 中传入 Context 参数是因为它可以使我们的自定义 View 访问应用程序的资源,例如字符串、图片、颜色等。这些资源通常存储在应用程序的 res/ 目录下,因此需要通过 Context 对象才能访问到这些资源。

另外,Context 还可以用于启动新的 Activity、获取系统服务等操作。例如,我们可以使用
context.startActivity(Intent) 方法启动一个新的 Activity,或者使用
context.getSystemService(Context.VIBRATOR_SERVICE) 方法获取系统震动服务。

在自定义 View 时,我们经常需要使用到这些系统级资源和服务,因此需要通过传递 Context
参数来获取相应的内容。一般情况下我们会将构造函数中传入的 Context 对象保存起来,并在需要时使用它来访问资源或者执行其他操作。

总之,在自定义 View 中传入 Context 参数是非常重要的,它可以帮助我们完成很多与应用程序相关的操作。

2. AttributeSet 属性集合

在自定义 View 中,如果我们需要在 XML 布局文件中为 View 设置属性,那么就需要通过 AttributeSet
接口来获取这些属性值。AttributeSet 是一个接口,它包含了一系列用于获取 XML 布局文件中定义的 View 属性的方法。

当我们在 XML 文件中使用自定义 View 时,可以使用自定义属性来配置该 View,例如:

<com.example.myapp.MyView
    android:id="@+id/my_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:my_custom_title_color="#FFFFFF"
    app:my_custom_title_text=""xixi" />

在这个例子中,我们使用了一个名为 MyView 的自定义 View(也就是我们上面的菜),并给它设置了两个自定义属性
my_custom_title_color和 my_custom_title_text,这些属性是我们自定义的,系统并不知道它们的含义和作用。虽然我们自己能够理解,第一个是我们自定义的标题颜色,第二个是我们自定义的标题内容,假想的哈都是。

因此,在实现自定义 View 时,我们需要解析和处理这些自定义属性,才能正确地设置 View 的状态和样式。而 AttributeSet
就提供了一种方便的方式来获取这些属性值,并将其应用于 View 对象上。

具体来说,当我们在自定义 View 的构造函数中传入 AttributeSet 参数时,系统会解析 XML
布局文件,并将其中定义的所有属性封装成 AttributeSet 对象。我们可以通过 AttributeSet
对象的方法来获取这些属性值,然后根据需要来处理这些属性值。

下面是一个简单的例子,展示了如何在自定义 View 中使用 AttributeSet(把菜再炒一次):

package xxxxxx

import android.content.Context
import android.util.AttributeSet
import android.view.View

class MyView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
	init {
        // 获取自定义属性的值
        val titleColor = attrs?.getAttributeValue(null, "my_custom_title_color")
        val titleText = attrs?.getAttributeValue(null, "my_custom_title_text")
        ...
        // 根据需要处理属性值
        ...
    }
}

通过上述代码,我们可以获取 XML 布局文件中定义的自定义属性值,并根据需要来处理它们,这样就能够正确地设置自定义 View 的状态和样式了。

因此,AttributeSet 在自定义 View 中是非常重要的,它为我们提供了一种方便的方式来获取 XML 布局文件中定义的所有属性,并将其应用于 View 上。

  1. defStyleAttr🚀

在自定义 View 中,defStyleAttr 是一个用来指定自定义 View 的默认样式的属性。它表示在布局文件中使用该自定义 View时,可以通过设置该 View 的 style 属性来为该 View 指定一个样式。如果没有指定样式,则该 View 将采用默认的样式。

这里需要注意的是,defStyleAttr 并不是必须的属性,也就是说,我们可以在自定义 View中省略该属性的声明。但是,如果我们希望支持样式属性,那么建议将 defStyleAttr 属性包含在内。

当我们在 XML 布局文件中创建自定义 View 时,可以使用 style 属性来设置该 View 的样式。例如:

<com.example.myapp.MyView
    android:id="@+id/my_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/MyViewStyle" />

在上述代码中,我们使用了 style 属性来为自定义 View 指定样式。其中@style/MyViewStyle 是一个样式资源的引用,它被定义在 styles.xml 文件中。

而在自定义 View 中,我们可以通过在构造函数中获取该 View 的 AttributeSet 参数,并调用其 obtainStyledAttributes() 方法来获取样式属性:

package xxxxxx

import android.content.Context
import android.util.AttributeSet
import android.view.View

class MyView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
	init {
        // 获取样式属性
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView, defStyleAttr, 0)
        
        // TODO: 解析样式属性
        
        // 记得回收 TypedArray
        typedArray.recycle()
    }

    // ...
}

在上述代码中,我们调用了 context.obtainStyledAttributes() 方法来获取样式属性,并将它们存储在一个名为 typedArray 的 TypedArray 对象中。其中,R.styleable.MyView 表示我们自定义 View 所支持的属性集合,而 defStyleAttr 则表示样式属性的默认值。

接着,我们可以通过 TypedArray 对象来读取和解析样式属性。需要注意的是,最后必须调用 typedArray.recycle()来回收该对象。

总结来看,defStyleAttr 是一个用来指定自定义 View 默认样式的属性。如果我们希望支持样式属性,建议将该属性包含在内,并在构造函数中获取并解析样式属性。

下课

耐着性子,静下心来,很难得,好了好了,感谢阅读, 我们下次见。🌹🌹🌹🌹

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jiet_h

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值