在本质上,所有的控件和布局都是View类的子类。也就是说明,在理论上,你可以自己手搓一个控件,也就是自定义控件。而且定义自己的方法和图形,界面等。而这都是可行的。

三种自定义控件

自绘控件

自绘控件的意思就是,这个View上所展现的内容全部都是我们自己绘制出来的。这是一个完完全全由自己设计的控件

组合控件

组合控件的意思就是,我们并不需要自己去绘制视图上显示的内容,而只是用系统原生的控件就好了,但我们可以将几个系统原生的控件组合到一起,这样创建出的控件就被称为组合控件。

继承控件

继承控件的意思就是,我们并不需要自己重头去实现一个控件,只需要去继承一个现有的控件,然后在这个控件上增加一些新的功能,就可以形成一个自定义的控件了。这种自定义控件的特点就是不仅能够按照我们的需求加入相应的功能,还可以保留原生控件的所有功能,比如 Android PowerImageView实现,可以播放动画的强大ImageView 这篇文章中介绍的PowerImageView就是一个典型的继承控件。

布局文件转化成view类(三种方式)

1.//context:上下文, resource:要转换成view对象的layout的id, root:一般传null

            view = View.inflate(context, R.layout.frist_layout, null);//将一个布局文件转换成一个view对象

2.//通过LayoutInflater将布局转换成view对象

          view =  LayoutInflater.from(context).inflate(R.layout.frist_layout, null);

3.通过context获取系统服务得到一个LayoutInflater,通过LayoutInflater将一个布局转换为view对象

         LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

         view = layoutInflater.inflate(R.layout.frist_layout, null);

从布局文件转化而成的view类,你就可以通过调用他们的控件然后改变其内部方法,属性和逻辑规则来实现对控件的自定义。

详解LayoutInflater.from(context).inflate()

LayoutInflater.from(context).inflate()

首先LayoutInflater.from(context)他表示会从上下文中获取一个LayoutInflater的实例对象,这个context就是调用该控件的父类。
然后通过这个实例对象调用inflate()方法把布局文件载入为view类。
这个方法有几个重载方法,其中主要使用的参数简单的解释一下。

1 int resource 代表需要加载资源的id

2 ViewGroup root 代表资源需要被添加的地方

3 boolean attachToRoot 是否要被添加到root中

4 XmlPullParser parser 代表xml文件。

他主要有两种方式
第一种是传入资源布局id,第二种是传入xml文件。分别如图所示
1

1
2
3
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}

2

1
2
3
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}

实际上这两种方式调用的都是带有attachToRoot的三个参数的重载方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(
TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")"
);
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}

第三个参数指定成false,表示只让我们在父布局中声明的layout属性生效,但不会为这个
View添加父布局。因为一旦View有了父布局之后,它就不能再添加到ListView中了

视图绑定

有时候为了方便使用,会使用视图绑定
首先在app的gradle目录下,配置

1
2
3
viewBinding{
enabled=true
}

然后在activity中绑定视图

1
2
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

在自定义控件中绑定视图

1
val binding = TitleBinding.inflate(LayoutInflater.from(context), this, true)