Android开发基础——自定义控件

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时,返回按钮和编辑按钮的点击事件就已经自动实现了,也就省去了重复编写代码的工作。

猜你喜欢

转载自blog.csdn.net/SAKURASANN/article/details/126918564
今日推荐