Android UI入门:XML与常用控件的使用

该如何编写程序界面

在过去,Android 应用的界面主要是通过编写 XML 文件完成的。XML 可以让我们了解界面背后的实现原理,并且能够帮助我们构建出具有良好的屏幕适配性的界面。

不过 Google 推出了 ConstraintLayout 这一种强大的布局方式,它既支持在可视化编辑器中通过拖拽组件来构建界面,也支持直接编写 XML 文件来构建。

本文专注于通过编写 XML 的传统方式来开发程序界面,我们先来看看常见的几种控件以及其使用方法。

常用控件的使用方法

我们新建一个名为 UIWidgetTest 的 Empty Views Activity 项目。

TextView

TextView(文本控件)可以在界面上显示一段文字信息。

我们修改 activity_main.xml 布局文件,往里面添加一个文本控件:

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"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is TextView" />

</LinearLayout>

我们先只看 TextView 控件,忽略外部的 LinearLayout 布局。

在 TextView 中,我们通过 android:id 属性给当前控件设置了唯一标识符,然后通过 android:layout_widthandroid:layout_height 属性设置了控件的宽和高。

Android 中所有的控件都具有这两个属性,可选的值有三种,分别是:match_parentwrap_content 和固定值。

  • match_parent 表示让当前控件的尺寸和父布局的可用空间相匹配。

  • wrap_content 表示让当前控件的大小恰好能显示其内部的内容。

  • 固定值就是给当前控件指定一个固定的尺寸,单位一般是 dp(Density-independent Pixels,密度无关像素),因为这是一种与屏幕密度无关的尺寸单位,能让我们在不同分辨率的手机上显示的效果接近一致。

而上述代码中,我们让 TextView 控件的宽度和父布局一样宽,也就是手机的宽度,高度仅仅包裹住文字内容。运行程序,效果如图所示:

虽然当前文本控件在横向上占满了屏幕宽度,但是从效果上你是看不出的,因为文字内容默认向左上角对齐。所以修改文字内容的对齐方式为居中对齐,并且给文本组件添加背景颜色:

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"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00BCD4"
        android:gravity="center"
        android:text="This is TextView" />

</LinearLayout>

我们通过 android:gravity 属性来指定文字在文本组件中的对齐方式。可选值有top、bottom、start、end、center_vertical、center_horizontal、center 等,并且可以使用 | 符号来同时指定多种对齐方式,比如这里的 "center",等同于 "center_vertical|center_horizontal",表示在水平和垂直方向都居中。

并且通过 android:background 属性指定了文本控件的背景颜色,它的值可以是预设值,比如 "@android:color/darker_gray",也可以是颜色码的十六进制形式,比如上述中的 "#00BCD4"

现在重新运行程序,效果如图:

这也说明了 TextView 的宽度确实是和屏幕宽度一样的。

另外,我们还可以对 TextView 中文字的大小和颜色进行修改,比如:

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"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/darker_gray"
        android:gravity="center_vertical|center_horizontal"
        android:text="This is TextView"
        android:textColor="#00BCD4"
        android:textSize="24sp" />

</LinearLayout>

在上述代码中,我们通过 android:textColor 属性指定了文字内容的颜色,通过 android:textSize 属性指定了文字的大小。文字大小的单位是 sp(Scale-independent Pixels,可缩放像素),它会跟随系统设置的字体大小进行缩放,常用于文本。比如当用户在系统设置中修改了字体大小时,应用中的文字大小也会跟着变化。

重新运行程序,效果如图:

TextView 还有更多的属性,但就不一一看了,当需要用到时,可以查阅官方文档:TextView XML attributes

Button

Button(按钮控件)可用于与用户进行交互,它的 XML 配置和 TextView 是差不多的,我们往界面中加入一个按钮:

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"
    android:orientation="vertical">

    ...

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>

加入了按钮后的界面:

我们可以在 MainActivity.kt 中为按钮注册点击监听器,这里依然使用的是 ViewBinding 来进行视图绑定,与 findViewById 方法相比,具有类型安全、空安全和编译时编译器还会检查的优点。

首先你要在 build.gradle.kts(:app) 配置文件中启用 ViewBinding:

arduino 复制代码
android {
    // ...
    buildFeatures {
        viewBinding true
    }
}

然后在 MainActivity.kt 中使用:

kotlin 复制代码
class MainActivity : AppCompatActivity() {

    private lateinit var viewBinding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 使用 ViewBinding 进行视图绑定
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        val root = viewBinding.root
        setContentView(root)

        viewBinding.button.setOnClickListener {
            // 添加点击逻辑
            // 这里弹出一个 Toast 通知
            Toast.makeText(this, "Button Clicked!", Toast.LENGTH_SHORT).show()
        }

    }
}

这里是通过 setOnClickListener() 方法和 Lambda 表示式来监听按钮的点击事件的,不过是利用是 Java 单抽象方法接口的特性,简化了代码。我们可以直接在方法的尾 Lambda 表达式中写点击的逻辑代码。

除了使用 Lambda 表达式的函数式API的方式来注册外,也可以通过让 MainActivity 实现接口的方式进行注册:

kotlin 复制代码
class MainActivity : AppCompatActivity(),View.OnClickListener {

    private lateinit var viewBinding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 使用 ViewBinding 进行视图绑定
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        val root = viewBinding.root
        setContentView(root)
        
        // 注册点击事件
        viewBinding.button.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when(v?.id){
            R.id.button -> {
                // 添加点击逻辑
            }
        }
    }
}

这种方式下,我们让 MainActivity 实现了 View.OnClickListener 接口,并重写 onClick 方法,最后调用按钮的 setOnClickListener 方法,并将当前 MainActivity 的实例传入即可。

onClick 方法中,我们是通过传入的 View 对象的 id 来区分是哪个控件触发了点击事件。

EditText

EditText(输入框)允许用户输入和编辑内容,我们可以在代码中对这些内容进行处理。

我们往界面中添加一个 EditText:

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"
    android:orientation="vertical">

    ...

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

重新运行程序,效果如图所示:

我们可以给输入框添加提示性的文字(hint),当用户不输入任何内容时,提示文本会出现;而一旦用户开始输入了,提示性的文本就会自动消失。

我们通过 android:hint 属性指定提示性文本的内容:

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"
    android:orientation="vertical">

    ...

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Type something here" />

</LinearLayout>

运行结果:

但输入的内容太多时,EditText 会被拉伸为了显示出所有行,因为它的高度是适应内容的(我们把 layout_height 设置为 wrap_content):

我们可以通过 android:maxLines 属性来解决这个问题,它可以限制 EditText 显示的最大行数,或者你也可以通过 android:lines 属性来指定 EditText 的固定行数:

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"
    android:orientation="vertical">

    ...

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Type something here"
        android:maxLines="2" />

</LinearLayout>

我们指定了输入框显示的最大行数为两行。这样当输入的内容超过两行后,文本会向上滚动,而 EditText 不会被拉伸。

前面说过,我们还可以在代码中对用户输入的内容进行处理。比如,我们在按钮的点击事件中,获取 EditText 的内容,并且通过 Toast 弹出提示:

kotlin 复制代码
// 注册点击事件
viewBinding.button.setOnClickListener {
    val text = viewBinding.editText.text.toString()
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
}

其中输入内容的获取,看似好像是访问了 EditText 的 text 属性,实际上是调用了 EditText 的 getText 方法。这是 Kotlin 中的属性访问语法糖。

现在重新运行程序,在输入框输入 "Are you Ok?",然后点击按钮,运行效果:

可以看到下方弹出了 "Are you Ok?" 内容的 Toast。

ImageView

ImageView(图片控件)用于在界面中展示图片。

你需要准备两张图片资源(我这里准备了 white_dog.png, red_moon.png)。

Android 会根据设备的像素密度从最适合的 drawable-<density> 目录中加载资源,现在主流手机的分辨率是 xxhdpi,所以我们将这两张图片放在 res/drawable-xxhdpi 目录下(没有的话,要新建)。

接下来在界面中添加图片控件:

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"
    android:orientation="vertical">

    ...

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/white_dog"
        />

</LinearLayout>

我们通过 android:src 属性来指定了图片资源。由于不知道图片的切确尺寸,就将 ImageView 控件的layout_widthlayout_height 属性设为 wrap_content,这样不管图片的尺寸是多少,ImageView 会自动调整尺寸以完整地展示图片。

现在重新运行程序,运行效果:

我们还可以通过代码动态更改 ImageView 中的图片:

kotlin 复制代码
// 在 MainActivity 的内部
private var isShowingWhiteDog = true 

// 在 MainActivity 的 onCreate 方法中
viewBinding.button.setOnClickListener {
    isShowingWhiteDog = !isShowingWhiteDog
    if (isShowingWhiteDog) {
        viewBinding.imageView.setImageResource(R.drawable.white_dog)
    } else {
        viewBinding.imageView.setImageResource(R.drawable.red_moon)
    }
}

现在点击按钮后,会切换 ImageView 中显示的图片:

ProgressBar

ProgressBar(进度条控件)用于在界面中显示一个进度条,比如表示当前正在加载数据或是执行耗时的操作。

我们往界面中添加一个 ProgressBar:

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"
    android:orientation="vertical">

    ...

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

运行效果(这里把界面中的 IamgeView 控件移除了):

可以看到有一个圆形进度条在旋转,并且它会一直旋转。

那数据加载完毕后,该怎么让进度条消失?答案是设置控件的可见(visibility)属性。

所有 Android 控件都有这个属性,属性名是 android:visibility,可选的值有三种,分别是:visible、invisible 和 gone。

  • visible 是默认值,表示控件可见。

  • invisible 表示控件不可见,虽然不可见,但是控件仍然占着原来的位置和大小,你可以理解为控件只是变得透明了。

  • gone 也表示控件不可见,只不过控件不再占用任何布局空间了。

我们可以在代码中通过 setVisibility() 方法设置控件的可见性,传入 View.VISIBLEView.INVISIBLE View.GONE

现在我们来实现一种效果:点击按钮,进度条会消失,再次点击,进度条会显现。

代码如下:

kotlin 复制代码
// 注册点击事件
viewBinding.button.setOnClickListener {
    val progressBar = viewBinding.progressBar
    if (progressBar.visibility == View.VISIBLE) {
        progressBar.visibility = View.GONE
    } else {
        progressBar.visibility = View.VISIBLE
    }
}

在按钮的点击事件中,我们通过 getVisibility() 方法来判断当前 ProgressBar 是否可见,如果可见就将 ProgressBar 隐藏(GONE),如果不可见就将 ProgressBar 显示出来。

重新运行程序,然后点击按钮,你会发现进度条在显示和隐藏之间来回切换了。

另外我们还可以给进度条指定不同的样式。之前是圆形进度条(默认),现在我们通过 style 属性将它变为水平进度条,并设置最大值:

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"
    android:orientation="vertical">

    ...

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100" />

</LinearLayout>

我们通过 android:max 属性给进度条设置进度的最大值。然后在代码中动态地改变进度条的当前进度。

kotlin 复制代码
// 注册点击事件
viewBinding.button.setOnClickListener {
    val progressBar = viewBinding.progressBar

    val progress = run {
        var currentProgress = progressBar.progress
        currentProgress += 10
        val max = progressBar.max
        if (currentProgress > max) {
            max
        } else {
            currentProgress
        }
    }

    progressBar.progress = progress
}

这样每点击一次按钮,就会增加进度条现有的进度,直到达到最大值。

运行效果:

AlertDialog

AlertDialog(警告对话框)可以在界面上弹出一个对话框,这个对话框位于所有界面元素的最顶层。它出现时,会屏蔽应用内其他控件的交互能力,所以 AlertDialog 一般用于显示重要消息、提示警告信息、进行确认操作(在删除某个内容时,弹出确认删除的对话框)。

比如,我们在按钮的点击事件中弹出一个 AlertDialog,询问用户是否确认:

kotlin 复制代码
viewBinding.button.setOnClickListener {
    AlertDialog.Builder(this).apply {
        setTitle("This is Dialog")
        setMessage("Something important.")
        setCancelable(false)
        setPositiveButton("OK") { dialog, which ->
        }
        setNegativeButton("Cancel") { dialog, which ->
        }
        show()
    }
}

在代码中,我们先通过 AlertDialog.Builder(this) 构建了一个对话框的构建器,然后设置了标题、内容、是否可用返回键或是点击对话框外部关闭对话框。然后调用 setPositiveButton() 方法为对话框设置了确定按钮以及 点击事件,调用 setNegativeButton() 方法设置了取消按钮以及 点击事件,最后调用 show() 方法将对话框显示出来。

点击按钮后的运行效果:

总结

最后,Android 中常用控件的使用就这么多了,当然,Android 提供的控件远不止这些,你也不要想一时就学会所有控件的使用,学习是一个漫长的过程。你可以通过查阅官方文档、优秀的教程和以及实践来学习更多控件的更多用法。

相关推荐
coderlin_1 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
2501_915918411 小时前
Fiddler中文版全面评测:功能亮点、使用场景与中文网资源整合指南
android·ios·小程序·https·uni-app·iphone·webview
wen's3 小时前
React Native安卓刘海屏适配终极方案:仅需修改 AndroidManifest.xml!
android·xml·react native
编程乐学4 小时前
网络资源模板--基于Android Studio 实现的聊天App
android·android studio·大作业·移动端开发·安卓移动开发·聊天app
没有了遇见6 小时前
Android 通过 SO 库安全存储敏感数据,解决接口劫持问题
android
hsx6666 小时前
使用一个 RecyclerView 构建复杂多类型布局
android
hsx6666 小时前
利用 onMeasure、onLayout、onDraw 创建自定义 View
android
守城小轩6 小时前
Chromium 136 编译指南 - Android 篇:开发工具安装(三)
android·数据库·redis
whysqwhw6 小时前
OkHttp平台抽象机制分析
android
hsx6667 小时前
Android 内存泄漏避坑
android