6、常用UI控件:TextView、Button、EditText、ImageView、CheckBox、RadioButton的使用与事件监听

说实话,Android开发入门最绕不开的就是这几个基础控件。我刚开始带团队时,发现很多新人上来就追求花哨的自定义View,结果连最基本的TextView属性都没吃透。嗯,今天我们就踏踏实实把这六个控件的用法捋一遍。

Android 基础控件 TextView 文本展示 Button 点击操作 EditText 输入框 ImageView 图片展示 CheckBox 多选 RadioButton 单选(需RadioGroup) setOnClickListener setOnCheckedChangeListener TextWatcher 核心:掌握每个控件的属性配置与事件回调

6.1 TextView — 不只是显示文字

TextView是Android里最基础的控件,没有之一。说白了,它就是用来展示文本的。但你别小看它,我见过太多人连基本的跑马灯效果都写不对。

我个人习惯在布局里这样配置TextView:

<TextView
    android:id="@+id/tvHello"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="你好,Android!"
    android:textSize="16sp"
    android:textColor="#333333"
    android:gravity="center"
    android:padding="12dp"/>
小提示:textSize一定要用sp单位,别用dp。为什么?因为sp会跟随系统字体缩放,对用户更友好。我在项目中吃过这个亏,用户反馈字体调大了界面就乱了,后来统一改成sp才解决。

TextView的事件监听其实很简单,但它默认是不可点击的。如果你想让它响应点击,需要设置android:clickable="true",或者直接在代码里设setOnClickListener

TextView tvHello = findViewById(R.id.tvHello);
tvHello.setOnClickListener(v -> {
    Toast.makeText(this, "TextView被点击了", Toast.LENGTH_SHORT).show();
});

6.2 Button — 点击事件的灵魂

Button继承自TextView,所以TextView的属性它基本都有。但Button自带一个默认的背景样式,不同Android版本长得还不一样。你想想看,这多坑?

我建议你这样做:

<Button
    android:id="@+id/btnSubmit"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:text="提交"
    android:textAllCaps="false"
    android:background="@drawable/btn_bg_rounded"/>
注意:Button默认会把英文字母全部转大写!如果你不想这样,记得加android:textAllCaps="false"。我曾经有个项目上线后,用户反馈按钮文字怎么全是大写的,就是忘了加这个属性。

事件监听最常用的就是点击和长按:

Button btnSubmit = findViewById(R.id.btnSubmit);
btnSubmit.setOnClickListener(v -> {
    // 处理点击事件
    Log.d("TAG", "按钮被点击了");
});

btnSubmit.setOnLongClickListener(v -> {
    // 处理长按事件
    Toast.makeText(this, "长按触发", Toast.LENGTH_SHORT).show();
    return true; // 返回true表示消费事件
});

6.3 EditText — 用户输入的入口

EditText是用户和App交互的重要通道。我见过很多新手直接把EditText扔上去,既不设输入类型,也不做限制,结果用户什么乱七八糟的内容都能输进去。

来看一个比较规范的配置:

<EditText
    android:id="@+id/etPhone"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:hint="请输入手机号"
    android:inputType="phone"
    android:maxLength="11"
    android:singleLine="true"
    android:background="@drawable/edit_bg"/>

EditText的事件监听主要有两种:

  • 文本变化监听 — 用TextWatcher接口
  • 焦点变化监听 — 用setOnFocusChangeListener
EditText etPhone = findViewById(R.id.etPhone);
etPhone.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // 文本变化前调用
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // 文本变化中调用
        String input = s.toString();
        // 实时校验输入
    }

    @Override
    public void afterTextChanged(Editable s) {
        // 文本变化后调用
    }
});
核心要点:TextWatcher的三个方法调用顺序是固定的:beforeTextChanged → onTextChanged → afterTextChanged。别搞混了,我刚开始写的时候就在这上面栽过跟头。

6.4 ImageView — 图片展示的利器

ImageView用来显示图片,支持本地资源、网络图片、Bitmap等多种来源。我个人习惯用android:scaleType来控制图片的缩放方式,这个属性太重要了。

<ImageView
    android:id="@+id/ivAvatar"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:src="@drawable/ic_default_avatar"
    android:scaleType="centerCrop"
    android:contentDescription="用户头像"/>
scaleType值 效果说明
center 居中显示,不缩放
centerCrop 按比例缩放,裁剪多余部分(最常用)
fitXY 拉伸填满,会变形
fitCenter 按比例缩放,完整显示

ImageView的点击监听和Button一样:

ImageView ivAvatar = findViewById(R.id.ivAvatar);
ivAvatar.setOnClickListener(v -> {
    // 点击头像,跳转到个人主页
    startActivity(new Intent(this, ProfileActivity.class));
});

6.5 CheckBox — 多选场景的标配

CheckBox用于多选场景,比如用户选择兴趣爱好。它继承自CompoundButton,所以有选中状态的概念。

<CheckBox
    android:id="@+id/cbAgree"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="我已阅读并同意用户协议"
    android:checked="false"/>

监听选中状态变化:

CheckBox cbAgree = findViewById(R.id.cbAgree);
cbAgree.setOnCheckedChangeListener((buttonView, isChecked) -> {
    if (isChecked) {
        // 用户勾选了
        btnSubmit.setEnabled(true);
    } else {
        // 用户取消了勾选
        btnSubmit.setEnabled(false);
    }
});
经验之谈:CheckBox的setOnCheckedChangeListenersetOnClickListener不要混用。如果你同时设置了这两个监听器,点击时它们都会触发,但执行顺序可能不是你想要的。我一般只用setOnCheckedChangeListener

6.6 RadioButton — 单选组的实现

RadioButton必须配合RadioGroup使用,才能实现单选效果。你想想看,如果不用RadioGroup,那每个RadioButton都是独立的,用户就能全选了,那还叫单选吗?

<RadioGroup
    android:id="@+id/rgGender"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <RadioButton
        android:id="@+id/rbMale"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="男"
        android:checked="true"/>

    <RadioButton
        android:id="@+id/rbFemale"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="女"/>
</RadioGroup>

监听RadioGroup的选择变化:

RadioGroup rgGender = findViewById(R.id.rgGender);
rgGender.setOnCheckedChangeListener((group, checkedId) -> {
    if (checkedId == R.id.rbMale) {
        // 选择了男
        Log.d("TAG", "性别:男");
    } else if (checkedId == R.id.rbFemale) {
        // 选择了女
        Log.d("TAG", "性别:女");
    }
});
注意:RadioGroup的setOnCheckedChangeListener和RadioButton的setOnCheckedChangeListener是两回事。RadioGroup的监听器会告诉你哪个RadioButton被选中了,而RadioButton自己的监听器会在它自身状态变化时触发。我建议你只用RadioGroup的监听器,逻辑更清晰。

6.7 综合实战:一个简单的注册表单

光说不练假把式。我们把这些控件组合起来,做一个简单的注册表单。这个例子我在实际项目中用过类似的。

// 布局文件 activity_register.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="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="用户名"
        android:textSize="14sp"
        android:textColor="#666"/>

    <EditText
        android:id="@+id/etUsername"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:hint="请输入用户名"
        android:inputType="text"
        android:maxLength="20"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="性别"
        android:textSize="14sp"
        android:textColor="#666"
        android:layout_marginTop="12dp"/>

    <RadioGroup
        android:id="@+id/rgGender"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/rbMale"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="男"
            android:checked="true"/>

        <RadioButton
            android:id="@+id/rbFemale"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="女"/>
    </RadioGroup>

    <CheckBox
        android:id="@+id/cbAgree"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我已阅读并同意用户协议"
        android:layout_marginTop="12dp"/>

    <Button
        android:id="@+id/btnRegister"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:text="注册"
        android:textAllCaps="false"
        android:enabled="false"
        android:layout_marginTop="24dp"/>

    <ImageView
        android:id="@+id/ivLogo"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/ic_logo"
        android:scaleType="centerCrop"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="24dp"/>
</LinearLayout>
// 注册逻辑 RegisterActivity.java
public class RegisterActivity extends AppCompatActivity {

    private EditText etUsername;
    private CheckBox cbAgree;
    private Button btnRegister;
    private RadioGroup rgGender;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);

        etUsername = findViewById(R.id.etUsername);
        cbAgree = findViewById(R.id.cbAgree);
        btnRegister = findViewById(R.id.btnRegister);
        rgGender = findViewById(R.id.rgGender);

        // 监听协议勾选状态
        cbAgree.setOnCheckedChangeListener((buttonView, isChecked) -> {
            btnRegister.setEnabled(isChecked);
        });

        // 监听用户名输入
        etUsername.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // 实时校验用户名长度
                if (s.length() > 0 && s.length() < 3) {
                    etUsername.setError("用户名至少3个字符");
                }
            }

            @Override
            public void afterTextChanged(Editable s) {}
        });

        // 注册按钮点击
        btnRegister.setOnClickListener(v -> {
            String username = etUsername.getText().toString().trim();
            int selectedGenderId = rgGender.getCheckedRadioButtonId();
            RadioButton selectedGender = findViewById(selectedGenderId);
            String gender = selectedGender.getText().toString();

            Toast.makeText(this, 
                "注册成功!用户名:" + username + ",性别:" + gender, 
                Toast.LENGTH_SHORT).show();
        });

        // 点击Logo跳转
        ImageView ivLogo = findViewById(R.id.ivLogo);
        ivLogo.setOnClickListener(v -> {
            Toast.makeText(this, "欢迎使用本应用", Toast.LENGTH_SHORT).show();
        });
    }
}

这个例子涵盖了今天讲的所有控件。你仔细看,每个控件都发挥了它该有的作用。TextView展示标签,EditText接收输入,RadioGroup做性别选择,CheckBox控制协议同意,Button执行注册,ImageView展示Logo。各司其职,互不干扰。

好了,这六个控件的基本用法和事件监听就讲到这里。记住一点:基础控件虽然简单,但用好了能解决80%的UI需求。别总想着炫技,先把基础打扎实了。


公众号:蓝海资料掘金营,微信deep3321