简介
在上一章节,我们成功的运行了第一个Android项目,使得我们对整体项目结构有个大概的认识,但这仅仅只是一个开始,接下来本章节将会将Android中的常见的UI组件来进行逐个分析讲解。
常用UI组件速查表
| 类别 | 组件 | 核心用途 |
|---|---|---|
| 文本显示 | TextView 、EditText | 显示或输入文本 |
| 按钮与交互 | Button 、CheckBox... | 触发事件或选择选项 |
| 图片 | ImageView | 显示图片 |
| 列表 | RecyclerView | 高效展示列表 |
| 布局容器 | ConstaintLayout ... | 复杂界面布局 |
| 对话框与提示 | AlertDialog | 用户确认或反馈 |
| 进度指示 | ProgressBar | 显示加载状态 |
1. 文本显示
-
TextView<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".TextViewExampleActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <!-- 1. 基础文本 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="基础文本:Hello World!" android:textSize="18sp" /> <!-- 2. 文本颜色与字体 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="颜色与字体:红色 + 斜体" android:textColor="#FF0000" android:textStyle="italic" android:layout_marginTop="8dp" /> <!-- 3. 背景与边距 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="背景色 + 边距" android:background="#E0E0E0" android:padding="12dp" android:layout_marginTop="8dp" /> <!-- 4. 单行与省略 --> <TextView android:layout_width="100dp" android:layout_height="wrap_content" android:text="超长文本将被截断并显示省略号..." android:singleLine="true" android:ellipsize="end" android:layout_marginTop="8dp" /> <!-- 5. 富文本(HTML) --> <TextView android:id="@+id/tv_html" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" /> <!-- 6. 字体阴影 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="字体阴影效果" android:shadowColor="#80000000" android:shadowDx="2" android:shadowDy="2" android:shadowRadius="4" android:layout_marginTop="8dp" /> <!-- 7. 自动调整文本大小 --> <TextView android:layout_width="200dp" android:layout_height="50dp" android:text="自 动 缩 小 文 本 以 适 应 空 间" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="12sp" android:autoSizeMaxTextSize="20sp" android:background="#BBDEFB" android:gravity="center" android:layout_marginTop="8dp" /> <!-- 8. 链接与点击 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="访问官网:https://www.baidu.com" android:autoLink="web" android:layout_marginTop="8dp" /> <!-- 9. 处理复杂文本 --> <TextView android:id="@+id/tv_spannable" android:layout_width="match_parent" android:layout_height="wrap_content" android:autoLink="web" android:layout_marginTop="8dp" /> </LinearLayout> </ScrollView>class TextViewExampleActivity : AppCompatActivity() { private val textHtml: TextView by lazy { findViewById(R.id.tv_html) } private val tvSpannable: TextView by lazy { findViewById(R.id.tv_spannable) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_text_view_example) // 定义 HTML 格式的字符串,包含段落、加粗、斜体、下划线、颜色、换行和超链接 val htmlText = "<p><--------html文本---------></p>" + // 自定义分隔文字(HTML注释视觉效果) "<p><b>加粗文本</b></p>" + // 加粗文字 "<p><i>斜体文本</i></p>" + // 斜体文字 "<p><u>下划线文本</u></p>" + // 下划线文字 "<p><font color='#FF0000'>红色字体</font></p>" + // 使用 font 标签设置红色字体 "<p>第一行<br>第二行</p>" + // <br> 用于换行 "<p>访问 <a href='https://www.baidu.com'>示例链接</a></p>" + // 带超链接的文字 "<p><--------html文本---------></p>" // 自定义结尾分隔 // 判断系统版本是否 >= Android 7.0 (Nougat) // Html.FROM_HTML_MODE_LEGACY 是 Nougat(API 24)后加入的,提供更好的 HTML 解析支持 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { textHtml.text = Html.fromHtml(htmlText, Html.FROM_HTML_MODE_LEGACY) // 将 HTML 字符串转换为 Spanned 富文本并赋值给 TextView // 注意:链接必须手动设置 movementMethod 才能点击 textHtml.movementMethod = LinkMovementMethod.getInstance() } // 创建一个可富文本操作的 SpannableString val spannable = SpannableString("多彩文本") // 设置前两个字("多彩")为红色 spannable.setSpan( ForegroundColorSpan(Color.RED), // 红色样式 0, 2, // 起始位置 0 到 2,不包含 2 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE // 不包括起止边界的文字扩展 ) // 设置到 TextView 中显示 tvSpannable.text = spannable } }运行上述例子我们可以了解大致
TextView能做到那些文本显示效果,接下来我们来介绍一下TextView的一些基础属性属性 说明 示例值 android:text显示文本的内容 android:text="基础文本:Hello World!"android:textSize文本字体大小 android:textSize="18sp"android:textColor文本颜色 android:textColor="#FF0000"android:textStyle字体样式(粗体 bold、斜体italic等)android:textStyle="italicandroid:maxLine最大行数 android:maxLines="2"android:ellipsize文本溢出时的省略方式(末尾显示省略号: end/开头显示省略号:start/中间显示省略号:middle/超出部门以跑马灯滚动显示:maarquee(需要配合focusable等属性))android:ellipsize="end"android:autoLink自动识别并链接文本(如 URL、邮箱) android:autoLink="web"android:singleLine设置为 true时,TextView的内容将被限制为单行显示,超出部分显示省略号(需配合ellipsize)android:singleLine="true"当然我们也可以在代码中动态的进行设置上述的基础属性,就如上述例子我们去设置
html文本和富文本的操作一样,动态的去设置文本属性可以实现更多有趣的效果,这个需要我们在日后开发中自己去发觉和研究。 -
EditText<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".EditTextExampleActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!-- 1. 普通文本输入框 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="普通文本输入框:" android:textSize="16sp" /> <EditText android:id="@+id/etNormal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:drawable/edit_text" android:hint="请输入普通文本" android:inputType="text" /> <!-- 2. 数字输入框,限制输入数字 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="数字输入框:" android:textSize="16sp" /> <EditText android:id="@+id/etNumber" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:drawable/edit_text" android:hint="请输入数字" android:inputType="number" android:maxLength="5" /> <!-- 3. 密码输入框,输入字符以点显示 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="密码输入框:" android:textSize="16sp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- 密码输入框 --> <EditText android:id="@+id/etPassword" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:drawable/edit_text" android:hint="请输入密码" android:inputType="textPassword" /> <!-- 切换密码显隐的按钮 --> <ImageView android:id="@+id/ivToggle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:contentDescription="密码显示切换" android:layout_marginEnd="40dp" android:src="@drawable/ic_show_password" /> </RelativeLayout> <!-- 4. 多行文本输入框,支持换行 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="多行文本输入框:" android:textSize="16sp" /> <EditText android:id="@+id/etMultiline" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:drawable/edit_text" android:gravity="top|start" android:hint="请输入多行文本,比如留言等..." android:inputType="textMultiLine" android:lines="3" android:minLines="3" /> <!-- 5. 带有颜色设置的输入框 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="自定义文本颜色:" android:textSize="16sp" /> <EditText android:id="@+id/etCustomStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:drawable/edit_text" android:hint="请输入内容" android:inputType="text" android:textColor="@android:color/holo_blue_dark" android:textSize="18sp" /> <!-- 6. 带自定义光标的 EditText --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="带自定义光标的 EditText:" android:textSize="16sp" /> <EditText android:id="@+id/etCustomCursor" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="输入内容" android:textCursorDrawable="@drawable/custom_cursor" android:padding="8dp" android:background="@android:drawable/edit_text" /> </LinearLayout> </ScrollView>class EditTextExampleActivity : AppCompatActivity() { private val etNormal: EditText by lazy { findViewById(R.id.etNormal) } private val etPassword: EditText by lazy { findViewById(R.id.etPassword) } private val ivToggle: ImageView by lazy { findViewById(R.id.ivToggle) } // 密码是否可见的状态变量,默认 false 表示不可见 private var isPasswordVisible = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit_text_example) // 示例:为普通文本输入框设置初始文本 etNormal.setText("初始文本") // 示例:简单校验密码输入框内容 val password = etPassword.text.toString().trim() if (password.isEmpty()) { etPassword.error = "密码不能为空!" } // 默认密码隐藏 etPassword.transformationMethod = PasswordTransformationMethod.getInstance() // 设置切换按钮的点击事件 ivToggle.setOnClickListener { if (isPasswordVisible) { // 当前密码为可见状态,切换为隐藏 etPassword.transformationMethod = PasswordTransformationMethod.getInstance() ivToggle.setImageResource(R.drawable.ic_show_password) isPasswordVisible = false } else { // 当前密码为隐藏状态,切换为显示密码 etPassword.transformationMethod = null ivToggle.setImageResource(R.drawable.ic_hide_password) isPasswordVisible = true } // 移动光标至文本末尾 etPassword.setSelection(etPassword.text.length) } } }ic_show_password<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <!-- 这条 path 定义的是眼睛的外轮廓, 使用 material design 中“visibility”图标的 pathData。 --> <path android:fillColor="?attr/colorControlNormal" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12,17c-2.76,0-5-2.24-5-5s2.24-5 5-5 5,2.24 5,5-2.24,5-5,5zM12,9c-1.66,0-3,1.34-3,3s1.34,3 3,3 3-1.34 3-3-1.34-3-3-3z" /> </vector>ic_hide_password<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <!-- 定义眼睛轮廓和中间瞳孔(隐藏状态下)的部分 --> <path android:fillColor="?attr/colorControlNormal" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12,17c-2.76,0-5-2.24-5-5s2.24-5 5-5 5,2.24 5,5-2.24,5-5,5zM12,9c-1.66,0-3,1.34-3,3s1.34,3 3,3 3-1.34 3-3-1.34-3-3-3z" /> <path android:fillColor="@android:color/transparent" android:pathData="M4,20 L20,4" android:strokeWidth="2" android:strokeColor="?attr/colorControlNormal" android:strokeLineCap="round" android:strokeLineJoin="round" /> </vector>custom_cursor<?xml version="1.0" encoding="utf-8"?> <!-- 定义一个简单的矩形光标样式 --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 设置光标的尺寸,这里定义宽度为 2dp,高度随 EditText 文本行高度而自动适应时也可以不设置高度 --> <size android:width="2dp" /> <!-- 设置光标的颜色 --> <solid android:color="#CC5A5A" /> </shape>通过观察源码我们可以知道,
EditText是TextView派生类,那么TextView有的属性和方法这里就不在赘述了值 说明 示例场景 text普通文本 用户名、昵称 textPassword密码输入(隐藏字符) 密码、验证码 textEmailAddress邮箱地址(键盘显示 @和.com)邮箱输入框 phone电话号码键盘 手机号输入框 number数字键盘(整数) 年龄、数量 numberDecimal数字键盘(允许小数) 价格、金额 textMultiLine允许多行文本输入 评论、个人简介 textCapCharacters自动大写所有字母 验证码输入(全大写) textCapWords每个单词首字母大写 姓名输入 textNoSuggestions关闭输入法自动补全 敏感信息输入
2. 按钮与交互
-
Button<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ButtonExampleActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!-- 基本按钮 --> <Button android:id="@+id/btnBasic" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="基本按钮" android:textColor="#FFFFFF" android:backgroundTint="#2196F3"/> <!-- 带图标的按钮 --> <Button android:id="@+id/btnIcon" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="带图标按钮" android:drawableTop="@android:drawable/ic_dialog_info"/> <!-- 自定义形状按钮 --> <android.widget.Button android:id="@+id/btnCustomShape" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="圆角按钮" android:textColor="@color/white" android:background="@drawable/rounded_button"/> <!-- 禁用按钮 --> <Button android:id="@+id/btnDisabled" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="禁用按钮"/> <!-- 动态操作按钮 --> <Button android:id="@+id/btnDynamic" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="点击改变其他按钮状态"/> <!-- 状态变化按钮 --> <android.widget.Button android:id="@+id/btnState" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="状态变化按钮" android:textColor="@color/white" android:background="@drawable/button_state_selector" /> </LinearLayout> </ScrollView>从布局中,我们可以发现,为什么有些用的是
Button有些用的是android.widget.Button,因为我们的默认主题样式中使用Material3的主题就是
thems文件中<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Base.Theme.HelloWorld" parent="Theme.Material3.DayNight.NoActionBar"> <!-- Customize your light theme here. --> <!-- <item name="colorPrimary">@color/my_light_primary</item> --> </style> <style name="Theme.HelloWorld" parent="Base.Theme.HelloWorld" /> </resources>我们在
AndroidManifest设置了application的主题为上述,所以会出现直接使用Button设置Background或者修改背景颜色无效,按钮的颜色一直是默认的主题颜色,因为当你使用Theme.Material3.*(比如Theme.Material3.DayNight.NoActionBar)时:Button默认使用 Material3 的样式系统(基于MaterialButton),并且不再是简单的android.widget.Button。Material3的按钮会自动应用shapeAppearance,rippleColor,containerColor等属性,并且会忽略你直接设置的android:background。
换句话说,
Material3中的 Button 是“样式接管者”,你需要用新的方式去自定义它。因为设置了这个主题,我们应该去使用Material3风格的UI组件,例如MaterialButton等,这里我们主要是讲UI的使用,具体样式我们也可以用原生的样式去逐步实现,这个因人而异,和因项目而异,读者们可以自己去研究研究这些谷歌官方推荐的UI样式,这里不再赘述,如果在项目中有使用这个主题,可以用上述中的方法去避免,当然,也可以直接修改样式,如下<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Base.Theme.HelloWorld" parent="Theme.MaterialComponents.NoActionBar.Bridge"> <!-- Customize your light theme here. --> <!-- <item name="colorPrimary">@color/my_light_primary</item> --> </style> <style name="Theme.HelloWorld" parent="Base.Theme.HelloWorld" /> </resources>rounded_button圆角样式<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#0080FF"/> <!-- 填充色 --> <corners android:radius="20dp"/> <!-- 圆角角度 --> </shape>button_state_selector状态选择器<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 按下状态 --> <item android:state_pressed="true"> <shape android:shape="rectangle"> <solid android:color="#3700B3" /> <!-- 深紫色 --> <corners android:radius="8dp" /> <!-- 圆角 --> <stroke android:width="1dp" android:color="#6200EE" /> <!-- 边框 --> </shape> </item> <!-- 禁用状态 --> <item android:state_enabled="false"> <shape android:shape="rectangle"> <solid android:color="#CCCCCC" /> <!-- 灰色 --> <corners android:radius="8dp" /> </shape> </item> <!-- 默认状态 --> <item> <shape android:shape="rectangle"> <solid android:color="#6200EE" /> <!-- 紫色 --> <corners android:radius="8dp" /> </shape> </item> </selector>class ButtonExampleActivity : AppCompatActivity() { private val btnBasic: Button by lazy { findViewById(R.id.btnBasic) } private val btnIcon: Button by lazy { findViewById(R.id.btnIcon) } private val btnCustomShape: Button by lazy { findViewById(R.id.btnCustomShape) } private val btnState: Button by lazy { findViewById(R.id.btnState) } private val btnDisabled: Button by lazy { findViewById(R.id.btnDisabled) } private val btnDynamic: Button by lazy { findViewById(R.id.btnDynamic) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_button_example) btnBasic.setOnClickListener { Toast.makeText(this, "这是最普通的按钮", Toast.LENGTH_SHORT).show() } btnIcon.setOnClickListener { Toast.makeText(this, "这是带图标的按钮", Toast.LENGTH_SHORT).show() } btnCustomShape.setOnClickListener { Toast.makeText(this, "这是圆角按钮", Toast.LENGTH_SHORT).show() } btnState.setOnClickListener { Toast.makeText(this, "状态选择器按钮", Toast.LENGTH_SHORT).show() } btnDisabled.setOnClickListener { it.isEnabled = false Toast.makeText(this, "按钮被禁用, 无法点击", Toast.LENGTH_SHORT).show() } btnDynamic.setOnClickListener { btnDisabled.isEnabled = !btnDisabled.isEnabled Toast.makeText(this, "修改禁用按钮状态", Toast.LENGTH_SHORT).show() } } }通过观察源码我们可以知道,
Button也是TextView派生类,并且常用的方法中没有独有的属性和方法, 那么TextView有的属性和方法这里就不在赘述了 -
CheckBox多选按钮<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".CheckBoxExampleActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请选择您喜欢的水果:" android:textSize="20sp" android:layout_marginBottom="16dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:layout_marginBottom="16dp"> <Button android:id="@+id/selectAllButton" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="全选" android:layout_marginEnd="8dp"/> <Button android:id="@+id/invertSelectionButton" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="反选" android:layout_marginStart="8dp"/> </LinearLayout> <android.widget.CheckBox android:id="@+id/appleCheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="苹果" android:textSize="18sp" android:button="@drawable/custom_checkbox" android:layout_marginBottom="8dp"/> <android.widget.CheckBox android:id="@+id/bananaCheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="香蕉" android:textSize="18sp" android:button="@drawable/custom_checkbox" android:layout_marginBottom="8dp"/> <android.widget.CheckBox android:id="@+id/orangeCheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="橙子" android:textSize="18sp" android:button="@drawable/custom_checkbox" android:layout_marginBottom="8dp"/> <android.widget.CheckBox android:id="@+id/watermelonCheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="西瓜" android:textSize="18sp" android:button="@drawable/custom_checkbox" android:layout_marginBottom="16dp"/> <Button android:id="@+id/submitButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="提交选择" android:layout_marginBottom="16dp"/> <TextView android:id="@+id/selectionCountText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="已选择 0 种水果" android:textSize="16sp" android:layout_marginBottom="8dp"/> <TextView android:id="@+id/resultText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="您还没有选择任何水果" android:textSize="18sp"/> </LinearLayout>custom_checkbox<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:drawable="@drawable/ic_checkbox_checked" /> <item android:state_checked="false" android:drawable="@drawable/ic_checkbox_unchecked" /> </selector>ic_checkbox_checked<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="#2196F3" android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-9,14l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z" /> </vector>ic_checkbox_unchecked<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="#B0BEC5" android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14z" /> </vector>class CheckBoxExampleActivity : AppCompatActivity() { private val appleCheckBox: CheckBox by lazy { findViewById(R.id.appleCheckBox) } private val bananaCheckBox: CheckBox by lazy { findViewById(R.id.bananaCheckBox) } private val orangeCheckBox: CheckBox by lazy { findViewById(R.id.orangeCheckBox) } private val watermelonCheckBox: CheckBox by lazy { findViewById(R.id.watermelonCheckBox) } private val selectAllButton: Button by lazy { findViewById(R.id.selectAllButton) } private val invertSelectionButton: Button by lazy { findViewById(R.id.invertSelectionButton) } private val submitButton: Button by lazy { findViewById(R.id.submitButton) } private val selectionCountText: TextView by lazy { findViewById(R.id.selectionCountText) } private val resultText: TextView by lazy { findViewById(R.id.resultText) } private val allCheckBoxes = mutableListOf<CheckBox>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_check_box_example) // 初始化多选按钮集合 initCheckBoxes() // 设置按钮点击事件 setupButtonListeners() // 设置多选按钮监听器 setupCheckBoxListeners() } private fun initCheckBoxes() { allCheckBoxes.apply { add(appleCheckBox) add(bananaCheckBox) add(orangeCheckBox) add(watermelonCheckBox) } } private fun setupButtonListeners() { // 全选按钮 selectAllButton.setOnClickListener { allCheckBoxes.forEach { it.isChecked = true } updateSelectionInfo() } // 反选按钮 invertSelectionButton.setOnClickListener { allCheckBoxes.forEach { it.toggle() } updateSelectionInfo() } // 提交按钮 submitButton.setOnClickListener { val selectedItems = allCheckBoxes .filter { it.isChecked } .map { it.text.toString() } if (selectedItems.isEmpty()) { resultText.text = "您还没有选择任何水果" Toast.makeText(this, "请至少选择一种水果", Toast.LENGTH_SHORT).show() } else { resultText.text = "您选择了: ${selectedItems.joinToString("、")}" Toast.makeText(this, "提交成功! 共选择${selectedItems.size}种水果", Toast.LENGTH_SHORT).show() } } } private fun setupCheckBoxListeners() { allCheckBoxes.forEach { checkBox -> checkBox.setOnCheckedChangeListener { _, _ -> updateSelectionInfo() } } } private fun updateSelectionInfo() { val selectedCount = allCheckBoxes.count { it.isChecked } selectionCountText.text = "已选择 $selectedCount 种水果" // 根据选择状态更新全选按钮文本 selectAllButton.text = if (selectedCount == allCheckBoxes.size) "取消全选" else "全选" } }多选按钮特点:
-
独立选择:每个选项可以单独选择或不选择
-
无互斥:选择某个选项不会影响其他选项
-
批量处理:可以同时获取所有被选中的选项
常用方法
方法 说明 示例 isChecked()检查是否选中 if (checkBox.isChecked)setChecked()设置选中状态 checkBox.setChecked(true)toggle()切换选中状态 checkBox.toggle()setOnCheckedChangeListener()状态变化监听 checkBox.setOnCheckedChangeListener {...} -
-
RadioButton单选按钮<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".RadioButtonExampleActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请选择您的支付方式:" android:textSize="20sp" android:layout_marginBottom="16dp"/> <!-- 单选按钮组 --> <RadioGroup android:id="@+id/paymentRadioGroup" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginBottom="16dp"> <android.widget.RadioButton android:id="@+id/alipayRadio" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="支付宝" android:textSize="18sp" android:button="@drawable/custom_radio_button" android:layout_marginBottom="8dp"/> <android.widget.RadioButton android:id="@+id/wechatRadio" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="微信支付" android:textSize="18sp" android:button="@drawable/custom_radio_button" android:layout_marginBottom="8dp"/> <android.widget.RadioButton android:id="@+id/unionpayRadio" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="银联支付" android:textSize="18sp" android:button="@drawable/custom_radio_button" android:layout_marginBottom="8dp"/> <android.widget.RadioButton android:id="@+id/otherRadio" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="其他方式(2秒后启用)" android:textSize="18sp" android:button="@drawable/custom_radio_button" android:enabled="false"/> </RadioGroup> <!-- 动态操作按钮 --> <Button android:id="@+id/addOptionButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="添加数字货币选项" android:layout_marginBottom="8dp"/> <Button android:id="@+id/removeOptionButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="移除最后一个选项" android:layout_marginBottom="8dp"/> <Button android:id="@+id/confirmButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="确认选择" android:layout_marginBottom="8dp"/> <Button android:id="@+id/resetButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="重置选择" android:layout_marginBottom="16dp"/> <TextView android:id="@+id/selectionInfoText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="当前选项数量: 4" android:textSize="16sp" android:layout_marginBottom="8dp"/> <TextView android:id="@+id/resultText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="您还没有选择支付方式" android:textSize="18sp"/> </LinearLayout> </ScrollView>custom_radio_button<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:drawable="@drawable/ic_radio_checked" /> <item android:state_checked="false" android:drawable="@drawable/ic_radio_unchecked" /> </selector>ic_radio_checked<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="#2196F3" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/> <path android:fillColor="#2196F3" android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z"/> </vector>ic_radio_unchecked<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="#B0BEC5" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> </vector>class RadioButtonExampleActivity : AppCompatActivity() { private val paymentRadioGroup: RadioGroup by lazy { findViewById(R.id.paymentRadioGroup) } private val alipayRadio: RadioButton by lazy { findViewById(R.id.alipayRadio) } private val wechatRadio: RadioButton by lazy { findViewById(R.id.wechatRadio) } private val unionpayRadio: RadioButton by lazy { findViewById(R.id.unionpayRadio) } private val otherRadio: RadioButton by lazy { findViewById(R.id.otherRadio) } private val addOptionButton: Button by lazy { findViewById(R.id.addOptionButton) } private val removeOptionButton: Button by lazy { findViewById(R.id.removeOptionButton) } private val confirmButton: Button by lazy { findViewById(R.id.confirmButton) } private val resetButton: Button by lazy { findViewById(R.id.resetButton) } private val selectionInfoText: TextView by lazy { findViewById(R.id.selectionInfoText) } private val resultText: TextView by lazy { findViewById(R.id.resultText) } private var dynamicOptionCount = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_radio_button_example) // 设置单选按钮组监听 paymentRadioGroup.setOnCheckedChangeListener { group, checkedId -> updateSelectionInfo() when (checkedId) { R.id.alipayRadio -> showToast("选择了支付宝") R.id.wechatRadio -> showToast("选择了微信支付") R.id.unionpayRadio -> showToast("选择了银联支付") R.id.otherRadio -> showToast("选择了其他方式") else -> { if (checkedId != -1) { showToast("选择了动态添加的选项 ${findViewById<RadioButton>(checkedId).text}") } } } } // 添加选项按钮点击事件 addOptionButton.setOnClickListener { addDynamicRadioOption() } // 移除选项按钮点击事件 removeOptionButton.setOnClickListener { removeLastRadioOption() } // 确认按钮点击事件 confirmButton.setOnClickListener { confirmSelection() } // 重置按钮点击事件 resetButton.setOnClickListener { resetSelection() } // 2秒后启用"其他方式"选项并修改样式 otherRadio.postDelayed({ enableOtherOption() }, 2000) } private fun addDynamicRadioOption() { dynamicOptionCount++ val newRadio = RadioButton(this).apply { text = "数字货币支付$dynamicOptionCount" id = View.generateViewId() textSize = 18f setTextColor(Color.parseColor("#9C27B0")) setPaddingRelative(20, 20, 20, 20) } paymentRadioGroup.addView(newRadio) updateSelectionInfo() showToast("已添加选项: ${newRadio.text}") } private fun removeLastRadioOption() { if (paymentRadioGroup.childCount > 4) { // 保留初始的4个选项 val lastIndex = paymentRadioGroup.childCount - 1 val lastOption = paymentRadioGroup.getChildAt(lastIndex) as RadioButton paymentRadioGroup.removeViewAt(lastIndex) updateSelectionInfo() showToast("已移除选项: ${lastOption.text}") } else { showToast("不能移除初始选项") } } private fun enableOtherOption() { otherRadio.isEnabled = true otherRadio.text = "信用卡支付" otherRadio.setTextColor(Color.parseColor("#8BC34A")) updateSelectionInfo() } private fun confirmSelection() { val selectedId = paymentRadioGroup.checkedRadioButtonId if (selectedId == -1) { resultText.text = "请先选择支付方式" showToast("请选择支付方式") } else { val selectedRadio = findViewById<RadioButton>(selectedId) resultText.text = "您选择的支付方式是: ${selectedRadio.text}" showToast("确认选择: ${selectedRadio.text}") } } private fun resetSelection() { paymentRadioGroup.clearCheck() resultText.text = "您还没有选择支付方式" updateSelectionInfo() showToast("已重置选择") } private fun updateSelectionInfo() { selectionInfoText.text = "当前选项数量: ${paymentRadioGroup.childCount}" } private fun showToast(message: String) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show() } }RadioButton属性与方法属性 说明 示例值 android:checked设置初始选中状态 true/falseandroid:buttonTint设置单选按钮圆圈颜色 "#00AAFF"android:button自定义单选按钮图标 @drawable/custom_radio方法 说明 示例 getCheckedRadioButtonId()获取选中按钮ID val id = radioGroup.checkedRadioButtonIdclearCheck()清除所有选择 radioGroup.clearCheck()check(id)选中指定ID的按钮 radioGroup.check(R.id.alipayRadio)setOnCheckedChangeListener()设置组选择变化监听 radioGroup.setOnCheckedChangeListener
3. 图片
-
ImageView<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".ImageViewExampleActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="普通图片" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="centerCrop" android:adjustViewBounds="true" android:src="@drawable/npc_face" app:tint="#80FF0000" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="图片 不缩放,保持原始尺寸" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="center" android:src="@drawable/npc_face" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="图片 等比例缩放,直至完全覆盖 ImageView 区域" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="centerCrop" android:src="@drawable/npc_face" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="图片 等比例缩放,确保完整显示在 ImageView 内" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="centerInside" android:src="@drawable/npc_face" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="图片 等比例缩放,对齐左上角" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="fitStart" android:src="@drawable/npc_face" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="图片 等比例缩放,对齐右下角" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="fitEnd" android:src="@drawable/npc_face" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="图片 等比例缩放,居中显示(默认模式)" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="fitCenter" android:src="@drawable/npc_face" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="图片 非等比例拉伸,填满整个 ImageView" /> <ImageButton android:layout_width="50dp" android:layout_height="50dp" android:scaleType="fitXY" android:src="@drawable/npc_face" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="通过矩阵(Matrix)自定义缩放、平移或旋转(需代码配合)" /> <ImageButton android:id="@+id/imageView" android:layout_width="200dp" android:layout_height="200dp" android:scaleType="matrix" android:src="@drawable/npc_face" /> </LinearLayout> </ScrollView>class ImageViewExampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_image_view_example_activity) val imageView = findViewById<ImageView>(R.id.imageView) val matrix = Matrix() matrix.postScale(0.5f, 0.5f) // 缩放到 50% matrix.postTranslate(50f, 50f) // 向右下平移 50px imageView.imageMatrix = matrix imageView.scaleType = ImageView.ScaleType.MATRIX } }ImageView 独有的核心属性
属性 说明 android:src设置图片资源( @drawable/xxx或@mipmap/xxx)。android:scaleType控制图片缩放和裁剪方式,共 8 种模式(见下文详解)。 android:adjustViewBounds是否根据图片宽高比例自动调整 ImageView的边界(需layout_width或layout_height为wrap_content)。app:tint为图片着色(如 #FF0000红色),支持透明度。android:cropToPadding若为 true,图片裁剪时会考虑padding区域(仅部分scaleType生效)。常用方法
方法 说明 setImageResource(int resId)动态设置图片资源。 setImageDrawable(Drawable drawable)设置 Drawable对象(如动态生成的图形)。setImageBitmap(Bitmap bitmap)直接设置位图(需自行处理内存优化)。 setScaleType(ImageView.ScaleType type)动态设置缩放类型(如 FIT_CENTER)。getDrawable()获取当前显示的 Drawable对象。scaleType详解
android:scaleType是ImageView的核心属性,决定图片如何适应控件尺寸:模式 效果 center居中显示,不缩放,超出部分裁剪。 centerCrop等比例缩放图片,填满控件并居中裁剪多余部分(常用)。 centerInside等比例缩放图片,完整显示在控件内(可能留空白)。 fitStart等比例缩放,对齐左上角。 fitEnd等比例缩放,对齐右下角。 fitCenter等比例缩放,居中显示(默认值)。 fitXY拉伸图片填满控件(可能变形)。 matrix通过矩阵自定义缩放(需配合 setImageMatrix使用)。ImageView 的独有特性
scaleType的精细化控制:ImageView是唯一提供 8 种缩放模式的控件,其他控件(如ImageButton)继承此特性但更侧重交互。adjustViewBounds: 自动根据图片比例调整控件尺寸(需结合wrap_content使用)。cropToPadding: 仅在scaleType为centerCrop等裁剪模式时生效,控制是否在裁剪时考虑padding。tint着色功能: 直接通过 XML 或代码为图片添加颜色遮罩,无需修改原始图片资源。
4. 列表
-
RecyclerView- 定义:
RecyclerView是 Android 中用于高效显示大数据集的滚动列表控件,继承自ViewGroup,是ListView的升级版。 - 核心特性:
- 视图复用机制:自动回收不可见项的视图,大幅优化内存和性能。
- 布局灵活:通过
LayoutManager支持列表、网格、瀑布流等多种布局。 - 高度可定制:支持自定义动画、分割线、点击事件等。
- 四大核心组件:
LayoutManager:控制布局方式(如LinearLayoutManager)。Adapter:管理数据与视图的绑定。ViewHolder:缓存视图引用,避免重复findViewById。ItemDecoration:添加分割线、间距等装饰。
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".RecyclerViewExampleActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp" android:scrollbars="vertical" /> </androidx.constraintlayout.widget.ConstraintLayout>class RecyclerViewExampleActivity : AppCompatActivity() { private val adapter by lazy { MyAdapter() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_recycler_view_example) val recyclerView = findViewById<RecyclerView>(R.id.recyclerView) // 1. 设置布局管理器 recyclerView.layoutManager = LinearLayoutManager(this) // 2. 准备数据 val items = mutableListOf( Item(1, "头部", "头部", 0), Item(1, "标题1", "描述内容1", 1), Item(2, "标题2", "描述内容2", 1), Item(3, "标题3", "描述内容3", 1), Item(4, "标题4", "描述内容4", 1), Item(5, "尾部", "尾部", 2), ) // 3. 设置 Adapter recyclerView.adapter = adapter adapter.setData(items) // 4. 添加分割线(可选) recyclerView.addItemDecoration( DividerItemDecoration(this, DividerItemDecoration.VERTICAL) ) } }Itemdata class Item( var id: Int = 0, var title: String = "", var description: String = "", var model: Int = 0 // 0: 头, 1: 内容, 2: 尾部 )MyAdapterprivate const val TYPE_HEADER = 0 private const val TYPE_FOOTER = 1 private const val TYPE_ITEM = 2 class MyAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private var items: MutableList<Item>? = null // 添加头、尾、数据项到集合 fun setData(items: MutableList<Item>) { this.items = items; notifyDataSetChanged() } override fun getItemViewType(position: Int): Int { // 通过 items 中存储的不同数据类型,Adapter 可以判断当前项是头部、普通项还是尾部: return when (items?.get(position)!!.model) { 0 -> TYPE_HEADER 2 -> TYPE_FOOTER else -> TYPE_ITEM } } // 创建 ViewHolder override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { TYPE_HEADER -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_head, parent, false) HeaderViewHolder(view) } TYPE_FOOTER -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_footer, parent, false) FooterViewHolder(view) } else -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_list, parent, false) // 普通项布局 ItemViewHolder(view) } } } // 绑定数据到视图 override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { // 在 onBindViewHolder 中,根据 items[position] 的内容填充不同布局: when (holder) { is HeaderViewHolder -> { // 绑定头部数据(如设置广告图片) holder.itemView.setOnClickListener { Toast.makeText(holder.itemView.context, "点击头部", Toast.LENGTH_SHORT).show() } } is FooterViewHolder -> { // 绑定尾部数据(如设置加载状态) holder.itemView.setOnClickListener { Toast.makeText(holder.itemView.context, "点击尾部", Toast.LENGTH_SHORT).show() } } is ItemViewHolder -> { val item = items?.get(position) as Item // Item 是数据类 holder.tvTitle.text = item.title holder.tvDesc.text = item.description } } } // 返回数据总数 override fun getItemCount() = items?.size!! // 定义不同 ViewHolder inner class HeaderViewHolder(view: View) : RecyclerView.ViewHolder(view) { val tvHeader: TextView = view.findViewById(R.id.tvHeader) } inner class FooterViewHolder(view: View) : RecyclerView.ViewHolder(view) { val tvFooter: TextView = view.findViewById(R.id.tvFooter) } inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { val tvTitle: TextView = view.findViewById(R.id.item_title) val tvDesc: TextView = view.findViewById(R.id.item_description) } }item_list<?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:orientation="vertical"> <TextView android:id="@+id/item_title" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/item_description" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>item_head<?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="100dp" android:background="#FFA726" android:gravity="center"> <TextView android:id="@+id/tvHeader" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是头部" android:textSize="20sp" android:textColor="#FFFFFF" /> </LinearLayout>item_footer<?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="100dp" android:background="#26FF00" android:gravity="center"> <TextView android:id="@+id/tvFooter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是尾部" android:textSize="20sp" android:textColor="#FFFFFF" /> </LinearLayout>XML 属性:
属性 说明 android:scrollbars滚动条方向( vertical/horizontal)。android:clipToPadding是否允许滚动到 Padding 区域外(默认 true)。核心方法:
方法 说明 setLayoutManager(LayoutManager)设置布局方式(必选项)。 setAdapter(Adapter)绑定数据适配器(必选项)。 addItemDecoration(ItemDecoration)添加分割线或自定义装饰。 smoothScrollToPosition(int)平滑滚动到指定位置。 setItemAnimator(ItemAnimator)自定义项动画(如删除/添加效果)。 RecyclerView是一个十分强大并且在项目中十分常见的组件,具体一些更加精细的功能,在日后的项目开发中会逐步分析讲解。 - 定义:
5. 布局容器
-
LinearLayout线性布局-
定义:
LinearLayout是 Android 中最基础的布局容器之一,继承自ViewGroup,其子视图(View)会按 水平(horizontal) 或 垂直(vertical) 方向依次排列。 -
核心特性:
- 单方向排列:所有子控件按单一方向(水平/垂直)依次排列。
- 权重分配:通过
layout_weight按比例分配剩余空间。 - 简单高效:适合简单布局场景,性能优于嵌套布局。
-
适用场景:表单布局、工具栏按钮排列、列表项布局等。
垂直布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".data.LinearLayoutExampleActivity"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="用户名" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="密码" android:inputType="textPassword" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="登录" /> <!-- 顶部外边距 --> </LinearLayout>效果:
-
三个子控件(输入框、输入框、按钮)垂直排列。
-
输入框宽度填满父布局,按钮位于底部。
-
控件间显示自定义分割线。
水平布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".data.LinearLayoutExampleActivity"> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="首页" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="搜索" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="我的" /> </LinearLayout>效果:
-
三个按钮水平排列,宽度按权重等分父布局。
-
每个按钮宽度 = 父布局剩余空间 ÷ 总权重(1+1+1=3)。
LinearLayout 的独有属性
属性 说明 android:orientation排列方向: vertical(垂直)或horizontal(水平)(必填属性)。android:gravity子控件的对齐方式(如 center、right、bottom)。android:layout_weight子控件的权重(需设置对应宽/高为 0dp生效)。android:weightSum总权重值(可选,默认按子控件权重总和计算)。 android:divider设置分割线 Drawable(需配合 showDividers使用)。android:showDividers分割线显示位置: none、beginning、middle、end。android:baselineAligned是否基线对齐(默认 true,影响文本对齐)。常用方法
方法 说明 setOrientation(int orientation)动态设置排列方向( LinearLayout.VERTICAL或HORIZONTAL)。setGravity(int gravity)动态设置子控件的对齐方式。 setWeightSum(float weightSum)动态设置总权重。 getOrientation()获取当前排列方向。 通过灵活使用
LinearLayout的方向、权重和对齐属性,可以快速构建高效且美观的界面。 -
-
RelativeLayout相对布局- 定义:
RelativeLayout是 Android 中一种基于相对位置的布局容器,继承自ViewGroup,允许子视图(View)通过相对关系(如对齐父布局边缘、相对于其他视图的位置)进行排列。 - 核心特性:
- 灵活定位:子控件的位置依赖于父布局或其他子控件。
- 减少嵌套:通过相对关系替代多层
LinearLayout嵌套。 - 动态适配:适合复杂但无需绝对定位的界面。
- 适用场景:用户资料页、卡片式布局、需要多控件联动的界面。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="200dp" android:padding="16dp" tools:context=".RelativeLayoutExampleActivity"> <!-- 头像(左上角) --> <ImageView android:id="@+id/iv_avatar" android:layout_width="80dp" android:layout_height="80dp" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:src="@drawable/npc_face" /> <!-- 用户名(头像右侧,顶部对齐) --> <TextView android:id="@+id/tv_username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@id/iv_avatar" android:layout_marginStart="16dp" android:layout_toEndOf="@id/iv_avatar" android:text="人生游戏牛马NPC1号" android:textSize="18sp" /> <!-- 个人简介(用户名下方,右侧与父布局对齐) --> <TextView android:id="@+id/tv_bio" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/tv_username" android:layout_alignStart="@id/tv_username" android:layout_marginTop="8dp" android:text="Android 开发者 | 热爱技术分享" android:textColor="#666666" /> <!-- 关注按钮(右下角) --> <Button android:layout_width="100dp" android:layout_height="40dp" android:layout_alignParentEnd="true" android:layout_alignParentBottom="true" android:layout_marginEnd="16dp" android:text="关注" /> </RelativeLayout>效果:
-
头像位于左上角,用户名在其右侧顶部对齐。
-
个人简介在用户名下方,左侧对齐用户名。
-
关注按钮固定在右下角。
-
所有控件通过相对关系定位,无需嵌套布局。
RelativeLayout 的独有属性(子控件专用)
属性 说明 layout_alignParent[Start/End/Top/Bottom]对齐父布局的某一边缘( true/false)。layout_centerInParent在父布局中居中(水平和垂直方向)。 layout_centerHorizontal水平居中。 layout_centerVertical垂直居中。 layout_to[Start/End]Of位于某控件的左侧或右侧(值为其他控件的 @id)。layout_above位于某控件的上方。 layout_below位于某控件的下方。 layout_align[Start/End/Top/Bottom]与某控件的某一边对齐(值为其他控件的 @id)。layout_alignBaseline与其他控件的文本基线对齐(常用于 TextView)。RelativeLayout 容器属性
属性 说明 android:gravity子控件的默认对齐方式(如 center、right)。android:ignoreGravity指定某个子控件不受 gravity影响(值为控件@id)。常用方法
方法 说明 addRule(int verb, int anchor)动态为子控件添加相对规则(如 ALIGN_PARENT_TOP)。removeRule(int verb)移除子控件的相对规则。 getRule(int verb)获取子控件的相对规则。 通过熟练掌握
RelativeLayout的相对规则,可以高效实现复杂但灵活的界面布局,尤其适合需要动态调整控件位置的场景。 - 定义:
-
ConstraintLayout约束布局- 定义:
ConstraintLayout是 Android 中基于约束关系的布局容器,继承自ViewGroup,旨在通过扁平化布局层级实现复杂界面设计。 - 核心特性:
- 灵活定位:子视图通过约束(Constraint)与其他视图或父布局关联。
- 高性能:减少嵌套层级,优化测量和布局速度。
- 可视化编辑:Android Studio 提供直观的拖拽布局设计器。
- 适用场景:响应式布局、复杂界面(如仪表盘、动态表单)、多屏幕适配。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ConstraintLayoutExampleActivity"> <!-- 头像(左侧,垂直居中) --> <ImageView android:id="@+id/iv_avatar" android:layout_width="80dp" android:layout_height="80dp" android:src="@drawable/npc_face" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> <!-- 用户名(头像右侧,顶部对齐) --> <TextView android:id="@+id/tv_name" android:layout_width="0dp" android:layout_height="wrap_content" android:text="人生游戏牛马NPC1号" android:textSize="20sp" app:layout_constraintStart_toEndOf="@id/iv_avatar" app:layout_constraintEnd_toStartOf="@id/btn_follow" app:layout_constraintTop_toTopOf="@id/iv_avatar" android:layout_marginStart="16dp" /> <!-- 关注按钮(右侧,垂直居中) --> <Button android:id="@+id/btn_follow" android:layout_width="100dp" android:layout_height="40dp" android:text="关注" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> <!-- 个人简介(用户名下方,撑满剩余宽度) --> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:text="Android 开发者 | 技术博主" app:layout_constraintStart_toStartOf="@id/tv_name" app:layout_constraintEnd_toEndOf="@id/btn_follow" app:layout_constraintTop_toBottomOf="@id/tv_name" android:layout_marginTop="8dp" /> <!-- 分割线(底部对齐) --> <View android:layout_width="0dp" android:layout_height="1dp" android:background="#E0E0E0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>效果:
- 标题居中,输入框水平撑满,登录按钮居中。
- 忘记密码左下角,注册按钮右下角,两者底部对齐。
- 所有控件通过约束关联,无嵌套布局。
ConstraintLayout 独有属性(子控件专用)
属性(前缀 app:)说明 layout_constraint[Start/End/Left/Right]_to[Start/End/Left/Right]Of控件的某边对齐到目标控件的某边(如 layout_constraintStart_toEndOf)。layout_constraintTop/Bottom_toTop/BottomOf控件的顶部/底部对齐到目标控件的顶部/底部。 layout_constraintBaseline_toBaselineOf文本基线对齐(多用于 TextView)。layout_constraintCircle以目标控件为中心,通过半径和角度定位(圆形定位)。 layout_constraintWidth/Height_default约束宽度/高度模式: spread(默认)、wrap、percent。layout_constraintWidth/Height_percent按父容器百分比设置宽高(需 default="percent")。layout_constraintHorizontal/Vertical_bias偏移比例(0~1),用于调整约束内的位置。 layout_constraintDimensionRatio宽高比例(如 H,16:9表示高是宽的 16/9)。layout_constraintHorizontal/Vertical_chainStyle链条样式: spread、spread_inside、packed。容器级属性(ConstraintLayout 独有)
属性 说明 app:barrierDirection创建屏障(Barrier),自动对齐多个控件边缘( left/right/top/bottom)。app:guideline创建引导线(Guideline),辅助定位(垂直或水平,固定位置或百分比)。 常用方法
方法 说明 ConstraintSet类动态修改约束(代码中替换 XML 约束): clone()、applyTo()、connect()。setVisibility(int visibility)控制控件可见性(自动调整约束关系)。 getViewById(int id)快速获取子控件实例。 通过掌握
ConstraintLayout的约束系统与高级功能(如链条、屏障),可以轻松构建高性能的复杂界面,是现代化 Android 开发的核心布局方案。 - 定义:
-
FrameLayout帧布局FrameLayout是 Android 中最简单的布局容器之一,它的核心特性是 层叠显示子视图(后添加的子视图会覆盖在前面的子视图之上)。常用于以下场景:- 叠加显示多个视图(如图片+文字)
- 单页面内切换不同视图(如 Fragment 容器)
- 实现悬浮按钮、进度条覆盖层等效果
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".FrameLayoutExampleActivity"> <!-- 底层图片 --> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/ic_launcher_background" android:scaleType="centerCrop"/> <!-- 上层文字 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello FrameLayout!" android:textSize="24sp" android:textColor="#FFFFFF" android:layout_gravity="center" android:padding="16dp" android:background="#66000000"/> </FrameLayout>效果说明:
- ImageView 作为底层显示图片
- TextView 居中叠加在图片上方,带有半透明背景
- 后添加的 TextView 会覆盖在 ImageView 之上
通过合理使用
FrameLayout,可以实现高效的视图叠加效果,特别适合需要动态控制视图层级的场景。
6. 对话框与提示
-
AlertDialog对话框AlertDialog是 Android 中用于显示模态对话框的核心组件,常用于以下场景:- 提示关键信息(如警告、错误)
- 确认用户操作(如删除确认)
- 选择选项(单选/多选列表)
- 自定义输入表单
在对应地方显示弹窗
class AlertDialogExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_alert_dialog_example)
findViewById<Button>(R.id.btnAlertDialog).setOnClickListener {
// 在 Activity 或 Fragment 中调用
MaterialAlertDialogBuilder(this)
.setTitle("删除确认")
.setMessage("确定要删除此文件吗?")
.setIcon(R.drawable.npc_face)
.setPositiveButton("确定") { dialog, _ ->
// 执行删除操作
Toast.makeText(this, "点击了确认", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
.setNegativeButton("取消") { dialog, _ ->
Toast.makeText(this, "点击了取消", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
.setNeutralButton("稍后处理") { dialog, _ ->
Toast.makeText(this, "稍后处理", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
.show()
}
}
}
效果说明:
- 标题 + 图标 + 消息内容
- 底部双操作按钮(Material Design 风格)
核心属性配置
- 基础配置方法(Builder)
| 方法 | 说明 |
|---|---|
setTitle(CharSequence) | 设置对话框标题 |
setMessage(CharSequence) | 设置消息内容 |
setIcon(int) | 设置标题栏图标 |
setView(View) | 添加自定义布局 |
setCancelable(boolean) | 是否允许点击外部关闭 |
这里只是简单介绍一下这这个组件,详细的效果在之后的项目中会一一展示出来