赞
踩
Android中常用控件和布局的继承结构如下图所示:
从上面可以看出,所有控件都是直接或间接继承自View的,所用的所有布局都是直接或间接继承自ViewGroup的。View是Android中最基本的一种UI组件,其可以在屏幕上绘制一块矩形区域,并能够响应这块区域的各种事件,因此,用户使用的各种控件其实就是在View的基础上又添加了各自特有的功能。而ViewGroup则是一种特殊的View,其可以包含很多子View和子ViewGroup,是一个用于放置控件和布局的容器。
除此之外,也可以创建自定义控件。
这里创建一个自定义的标题栏。
首先在layout目录下新建一个title.xml布局:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/title_bg">
-
- <Button
- android:id="@+id/titleBack"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_margin="5dp"
- android:background="@drawable/back_bg"
- android:text="Back"
- android:textColor="#fff"/>
-
- <TextView
- android:id="@+id/titleText"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="1"
- android:gravity="center"
- android:text="Title Text"
- android:textColor="#fff"
- android:textSize="24sp"/>
-
- <Button
- android:id="@+id/titleEdit"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_margin="5dp"
- android:background="@drawable/edit_bg"
- android:text="Edit"
- android:textColor="#fff"/>
-
- </LinearLayout>
上面的代码中,在LinearLayout中加入了两个Button和一个TextView,左侧的Button用于返回,右侧的Button用于编辑,中间的TextView用于显示标题文本。
android:background属性用于为布局或控件指定一个背景,可以使用颜色或图片来进行填充。android:margin属性可以指定控件在上下左右方向上的间距,也可以使用android:layout_marginLeft或android:layout_marginRight等属性来单独指定控件在某个方向上的间距。
然后修改activity_main.xml中的代码:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <include layout="@layout/title"/>
-
- </LinearLayout>
再隐藏系统自带的标题栏:
- package com.example.uicustomviews
-
- import androidx.appcompat.app.AppCompatActivity
- import android.os.Bundle
-
- class MainActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- supportActionBar?.hide()
- }
- }
上面使用getSupportActionBar方法获取ActionBar的实例,然后调用其hide方法将标题栏隐藏。程序运行结果为:
使用这种方法,不管有多少布局需要添加标题栏,只需要一行include语句就可以了。
引入布局可以解决重复编写布局代码的问题,但是如果布局中有一些控件要求能够响应事件,还是需要在每个activity中为这些控件单独编写一次事件注册的代码。比如标题栏中的返回按钮,其实不管是在哪一个Acitvity中,这个按钮的功能都是相同的,即销毁当前Activity。而如果每一个Activity都需要重新注册该事件,就会增加很多重复代码,该情况最好使用自定义控件的方式来解决。
新建TitleLayout继承自LinearLayout,使其称为自定义的标题栏控件:
- package com.example.uicustomviews
-
- import android.content.Context
- import android.util.AttributeSet
- import android.view.LayoutInflater
- import android.widget.LinearLayout
-
- class TitleLayout(context: Context, attrs:AttributeSet):LinearLayout(context, attrs) {
- init {
- LayoutInflater.from(context).inflate(R.layout.title, this)
- }
- }
在TitleLayout的主构造函数中声明了Context和AttributeSet这两个参数,在布局中引入TitleLayout控件时就会调用该构造函数,然后在init结构体中需要对标题栏布局进行动态加载,这就要借助LayoutInflater来实现。通过LayoutInflater的from方法可以构建出一个LayoutInflater对象,然后调用inflate方法就可以动态加载一个布局文件。inflate方法接收两个参数,第一个参数是需要加载的布局文件的id,这里是R.layout.title,第二个参数是给加载好的布局再添加一个父布局,这里指定为TitleLayout,也就是this。
然后在布局文件中添加该自定义控件:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.example.uicustomviews.TitleLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
添加自定义控件和添加普通控件的方式基本类似,只不过在添加自定义控件的时候,需要指明控件的完整类名,包名不可忽略。
然后为标题栏中的按钮注册点击事件:
- package com.example.uicustomviews
-
- import android.app.Activity
- import android.content.Context
- import android.util.AttributeSet
- import android.view.LayoutInflater
- import android.widget.LinearLayout
- import android.widget.Toast
- import kotlinx.android.synthetic.main.title.view.*
-
- class TitleLayout(context: Context, attrs:AttributeSet):LinearLayout(context, attrs) {
- init {
- LayoutInflater.from(context).inflate(R.layout.title, this)
-
- titleBack.setOnClickListener {
- val activity = context as Activity
- activity.finish()
- }
-
- titleEdit.setOnClickListener {
- Toast.makeText(context,"You clicked Edit button", Toast.LENGTH_SHORT).show()
- }
- }
- }
上面的代码其实也好理解,不过需要注意的是TitleLayout中接收的context实际上是Activity的实例,在返回按钮的点击事件中,需要先将之转换为Activity类型,然后调用finish方法销毁当前的Activity。Kotlin中的类型强制转换使用的是关键字as。
程序运行结果为:
这样也就实现了自定义控件,同时每当在布局中引入TitleLayout时,返回按钮和编辑按钮的点击事件就已经自动实现了,也就省去了重复编写代码的工作。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。