5、常用UI控件(上):TextView文本控件、Button按钮与点击事件、EditText输入框、ImageView图片显示、ProgressBar进度条
各位同学,今天我们来聊聊Android开发里最基础、也最常用的一批UI控件。说实话,不管你现在做的是社交App、电商App还是工具类应用,这几个控件你几乎天天都要打交道。我刚开始学Android那会儿,就是靠这几个控件搭出了第一个能用的界面——虽然丑了点,但那种成就感,嗯,至今还记得。
本章核心知识点一览:
- TextView —— 显示文本,最基础的输出控件
- Button —— 可点击的按钮,用户交互的入口
- EditText —— 接收用户输入,与用户对话的窗口
- ImageView —— 展示图片,让界面活起来
- ProgressBar —— 告诉用户“正在加载,别急”
5.1 TextView —— 最朴实的文本展示者
TextView是Android里最基础的控件,没有之一。它的任务很简单:把一段文字显示在屏幕上。但你想想看,就这么一个简单的控件,Google给它配了几十个属性——说明什么?说明“显示文字”这件事,其实有很多讲究。
我个人习惯在布局文件里这样定义一个TextView:
<TextView
android:id="@+id/tv_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你好,Android!"
android:textSize="18sp"
android:textColor="#333333"
android:textStyle="bold"
android:gravity="center"
android:padding="12dp" />
这里有几个属性我想重点说一下:
- textSize:单位用sp,别用dp。为什么?因为sp会跟随系统字体缩放,照顾视力不好的用户。我在项目里见过有人用dp写死,结果用户把系统字体调大后,界面直接崩了布局。
- gravity:控制文字在控件内部的对齐方式。注意它和layout_gravity的区别——前者是内部对齐,后者是控件在父容器里的对齐。初学者经常搞混。
- maxLines / ellipsize:限制行数,超出部分用省略号。做新闻列表时这个很常用。
小技巧:如果你想让TextView实现跑马灯效果(文字滚动显示),需要同时设置:android:singleLine="true"、android:ellipsize="marquee"、android:marqueeRepeatLimit="marquee_forever",并且TextView必须获取焦点。我曾经踩过这个坑——忘了让控件获取焦点,跑马灯死活不滚。
5.2 Button与点击事件 —— 用户交互的第一步
Button本质上就是TextView加了一个点击背景。但它的核心价值不在于外观,而在于——点击事件。说白了,用户点一下按钮,App得知道该干什么。
在XML里定义一个Button很简单:
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="提交"
android:textAllCaps="false" />
注意textAllCaps="false"这个属性。Android 5.0之后,Button默认会把所有字母转成大写。如果你不想这样,记得加上这个属性。我当年第一次做App时,明明写的是“Submit”,运行出来变成“SUBMIT”,还以为是手机坏了……
处理点击事件,目前最推荐的方式是用Lambda表达式:
Button btnSubmit = findViewById(R.id.btn_submit);
btnSubmit.setOnClickListener(v -> {
// 在这里写点击后的逻辑
Toast.makeText(this, "按钮被点击了", Toast.LENGTH_SHORT).show();
});
为什么推荐Lambda?因为它简洁。你想想看,如果用匿名内部类,得写一大坨:
btnSubmit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 逻辑代码
}
});
嗯,两种写法都能跑。但我建议你从现在开始就用Lambda,代码看起来清爽很多。
注意:千万不要在XML里用android:onClick="方法名"这种方式。虽然它也能用,但会让代码耦合严重,而且方法签名必须严格匹配——我曾经因为这个方式导致一个线上bug,排查了半天才发现是方法名拼写错了。
5.3 EditText —— 让用户输入内容
EditText是用户和App对话的窗口。用户打字进来,App读取处理。看似简单,但坑不少。
先看一个标准的输入框定义:
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="请输入用户名"
android:inputType="text"
android:maxLines="1"
android:background="@drawable/bg_edittext" />
几个关键点:
- hint:提示文本,用户输入后自动消失。别用text属性来当提示,那不会消失。
- inputType:控制键盘类型。输入密码用
textPassword,输入数字用number,输入邮箱用textEmailAddress。这个属性直接影响用户体验——你总不想让用户输数字时弹出全键盘吧? - maxLines:限制行数。单行输入设成1,多行输入可以设成3或5。
获取用户输入的内容,代码是这样:
EditText etUsername = findViewById(R.id.et_username);
String username = etUsername.getText().toString().trim();
注意一定要调toString(),因为getText()返回的是Editable对象,不是String。另外trim()去掉首尾空格——用户有时候会不小心多打空格,这个处理能省很多麻烦。
监听输入变化:如果你想实时知道用户输入了什么(比如搜索框的自动补全),可以用addTextChangedListener。我记得有一次做登录界面,需要实时检测密码长度是否达标,就是用这个监听器实现的。
5.4 ImageView —— 一图胜千言
一个没有图片的App,就像一本没有插图的教科书——干巴巴的。ImageView就是用来展示图片的。
最基础的使用方式:
<ImageView
android:id="@+id/iv_avatar"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/ic_avatar"
android:scaleType="centerCrop" />
scaleType这个属性,我建议你花点时间搞明白。它决定了图片怎么适配到ImageView的尺寸里:
| scaleType值 | 效果 | 适用场景 |
|---|---|---|
| centerCrop | 按比例缩放,裁剪多余部分,填满控件 | 头像、封面图(最常用) |
| fitCenter | 按比例缩放,完整显示,可能有留白 | 展示完整图片 |
| fitXY | 拉伸填满,不保持比例 | 背景图(但会变形,慎用) |
| center | 不缩放,居中显示 | 小图标 |
在代码里动态设置图片也很常见:
ImageView ivAvatar = findViewById(R.id.iv_avatar);
ivAvatar.setImageResource(R.drawable.ic_avatar);
// 或者从网络加载(需要Glide或Picasso等库)
Glide.with(this).load("https://example.com/avatar.jpg").into(ivAvatar);
内存警告:直接加载大图到ImageView,很容易OOM(内存溢出)。我曾经在一个列表里直接加载了高清壁纸,结果App在低端机上秒崩。解决方案是用Glide或Picasso这类图片加载库,它们会自动做压缩和缓存。
5.5 ProgressBar —— 给用户一点耐心
网络请求、文件下载、数据加载……这些操作都需要时间。如果界面一动不动,用户会以为App卡死了。ProgressBar就是用来告诉用户:“别急,正在干活呢。”
Android提供了两种进度条样式:
- 圆形(不确定进度):不知道还要等多久,就用它。比如加载数据时。
- 水平(确定进度):知道具体进度百分比,比如下载文件时。
XML定义:
<!-- 圆形进度条 -->
<ProgressBar
android:id="@+id/progress_circular"
android:layout_width="48dp"
android:layout_height="48dp"
android:visibility="gone" />
<!-- 水平进度条 -->
<ProgressBar
android:id="@+id/progress_horizontal"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="12dp"
android:max="100"
android:progress="0"
android:visibility="gone" />
在代码里控制进度条:
// 显示/隐藏圆形进度条
ProgressBar progressCircular = findViewById(R.id.progress_circular);
progressCircular.setVisibility(View.VISIBLE); // 显示
progressCircular.setVisibility(View.GONE); // 隐藏
// 更新水平进度条
ProgressBar progressHorizontal = findViewById(R.id.progress_horizontal);
progressHorizontal.setProgress(50); // 设置到50%
使用建议:我个人习惯在请求网络数据时,先显示圆形进度条,数据回来后再隐藏。如果是文件下载,就用水平进度条实时更新进度。另外,别忘了设置android:visibility="gone"——默认隐藏,需要时再显示。不然一进页面就看到一个转圈圈,用户会懵的。
5.6 综合小案例:登录界面
学完了这几个控件,我们来把它们串起来。做一个简单的登录界面,包含:用户名输入框、密码输入框、登录按钮、加载进度条。
XML布局:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
android:gravity="center">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/ic_logo"
android:layout_marginBottom="32dp" />
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="请输入用户名"
android:inputType="text"
android:maxLines="1"
android:background="@android:drawable/editbox_background"
android:layout_marginBottom="16dp" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="请输入密码"
android:inputType="textPassword"
android:maxLines="1"
android:background="@android:drawable/editbox_background"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="登录"
android:textAllCaps="false" />
<ProgressBar
android:id="@+id/progress_loading"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginTop="16dp"
android:visibility="gone" />
</LinearLayout>
Java代码:
public class LoginActivity extends AppCompatActivity {
private EditText etUsername, etPassword;
private Button btnLogin;
private ProgressBar progressLoading;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
etUsername = findViewById(R.id.et_username);
etPassword = findViewById(R.id.et_password);
btnLogin = findViewById(R.id.btn_login);
progressLoading = findViewById(R.id.progress_loading);
btnLogin.setOnClickListener(v -> {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "用户名和密码不能为空", Toast.LENGTH_SHORT).show();
return;
}
// 显示加载进度条
progressLoading.setVisibility(View.VISIBLE);
btnLogin.setEnabled(false);
// 模拟网络请求
new Handler().postDelayed(() -> {
progressLoading.setVisibility(View.GONE);
btnLogin.setEnabled(true);
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
}, 2000);
});
}
}
这个案例虽然简单,但包含了本章所有控件的使用。你想想看,实际项目中的登录界面,无非就是在这个基础上加一些校验、加密、网络请求的封装。核心逻辑是一样的。
本章总结:TextView负责展示文字,Button负责接收点击,EditText负责收集输入,ImageView负责展示图片,ProgressBar负责反馈进度。这五个控件组合起来,已经能搭建出大部分常见的界面了。我建议你动手写一遍上面的登录案例,把每个控件的属性都调一调、改一改,看看效果变化。实践出真知,光看代码是记不住的。
公众号:蓝海资料掘金营,微信deep3321