在 Android 开发中,基础控件是构建用户界面的基石。无论是简单的文本展示、按钮交互,还是复杂的列表展示,都离不开TextViewButtonListView等基础控件。

一、TextView:文本展示的核心控件

TextView是 Android 中最基础的控件之一,主要用于显示文本内容,支持文本样式、图文混排等功能。

1. 核心功能与常用属性

TextView的核心是展示文本,通过 XML 属性或代码可配置文本内容、样式、对齐方式等。

常用属性作用示例
android:text设置文本内容android:text="Hello World"
android:textSize文本大小(单位 sp,支持缩放)android:textSize="16sp"
android:textColor文本颜色android:textColor="#FF0000"@color/red
android:textStyle文本风格normal(默认)、bold(粗体)、italic(斜体)
android:gravity文本在控件内的对齐方式centerleftrighttopbottom
android:layout_width/layout_height控件宽高match_parentwrap_content100dp
android:maxLines最大行数(超出部分默认截断)android:maxLines="2"
android:ellipsize文本超出时的省略方式end(末尾省略)、start(开头省略)、middle(中间省略)
android:drawableLeft文本左侧添加图标android:drawableLeft="@mipmap/ic_launcher"

2. 实战示例:基础文本展示


3. 高级用法:图文混排与富文本

(1)通过SpannableString实现富文本

SpannableString可实现文本中部分内容的样式修改(如高亮、下划线、超链接等):

TextView tvRichText = findViewById(R.id.tv_rich_text);
String text = "这是一段富文本:红色粗体、下划线、点击跳转";
SpannableString spannable = new SpannableString(text);
// 1. 红色粗体(0-5字符)
spannable.setSpan(
    new StyleSpan(Typeface.BOLD),
    7, 11, // 起始和结束索引("红色粗体")
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
spannable.setSpan(
    new ForegroundColorSpan(Color.RED),
    7, 11,
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
// 2. 下划线(12-14字符)
spannable.setSpan(
    new UnderlineSpan(),
    12, 14, // "下划线"
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
// 3. 点击跳转(15-19字符)
ClickableSpan clickableSpan = new ClickableSpan() {
    @Override
    public void onClick(@NonNull View widget) {
        Toast.makeText(MainActivity.this, "点击了跳转", Toast.LENGTH_SHORT).show();
    }
    // 去除默认下划线
    @Override
    public void updateDrawState(@NonNull TextPaint ds) {
        super.updateDrawState(ds);
        ds.setUnderlineText(false);
    }
};
spannable.setSpan(
    clickableSpan,
    15, 19, // "点击跳转"
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
tvRichText.setText(spannable);
// 必须设置MovementMethod才能触发点击事件
tvRichText.setMovementMethod(LinkMovementMethod.getInstance());
(2)图文混排(文本中插入图片)
TextView tvImageText = findViewById(R.id.tv_image_text);
SpannableString imageText = new SpannableString("文本前的图片: ");
// 添加图片(使用ImageSpan)
Drawable drawable = ContextCompat.getDrawable(this, R.mipmap.ic_launcher);
if (drawable != null) {
    drawable.setBounds(0, 0, 40, 40); // 设置图片宽高
    imageText.setSpan(
        new ImageSpan(drawable),
        0, 1, // 插入到第一个字符位置
        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
    );
}
tvImageText.setText(imageText);

4. 适用场景

  • 展示静态文本(如标题、描述、标签);
  • 富文本展示(如带高亮的搜索结果、带链接的文本);
  • 简单的图文混排(如左侧图标 + 右侧文本)。

二、Button:用户交互的核心控件

Button继承自TextView,在文本展示的基础上增加了点击交互能力,是用户触发操作的主要入口。

1. 核心功能与常用属性

Button继承了TextView的所有属性,同时增加了点击状态样式等特性。

常用属性作用示例
android:onClick点击事件回调方法(XML 中直接绑定)android:onClick="onButtonClick"
android:background背景(可设置 selector 实现状态变化)android:background="@drawable/btn_selector"
android:textAllCaps是否自动大写文本(默认 true,需关闭)android:textAllCaps="false"

2. 点击事件的三种实现方式

(1)XML 中直接绑定(简单场景)

Java 代码中实现方法:

// 方法名必须与XML中一致,参数为View
public void onBtnClick(View view) {
    Toast.makeText(this, "XML绑定的点击事件", Toast.LENGTH_SHORT).show();
}
(2)匿名内部类(常用)
Button btnAnonymous = findViewById(R.id.btn_anonymous);
btnAnonymous.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(MainActivity.this, "匿名内部类点击事件", Toast.LENGTH_SHORT).show();
    }
});
(3)实现 OnClickListener 接口(多个按钮时推荐)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn1 = findViewById(R.id.btn1);
        Button btn2 = findViewById(R.id.btn2);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn1:
                Toast.makeText(this, "按钮1被点击", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn2:
                Toast.makeText(this, "按钮2被点击", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

3. 自定义按钮样式(selector)

通过selector资源实现按钮在不同状态(正常、点击、禁用)下的样式变化:

  1. 创建背景选择器res/drawable/btn_selector.xml


    
    
        
             
             
        
    
    
    
        
             
            
        
    
    
    
        
             
            
        
    
  1. 在 Button 中引用:

4. 适用场景

  • 触发操作(如提交表单、跳转页面、删除数据);
  • 状态切换(如播放 / 暂停、展开 / 折叠);
  • 对话框 / 底部弹窗中的确认 / 取消按钮。

三、ListView:传统列表展示控件

ListView是用于展示大量数据列表的传统控件,通过视图复用机制高效展示数据。尽管现在更推荐RecyclerView,但ListView仍是面试和 legacy 项目中的常见考点。

1. 核心功能与工作原理

ListView的核心是视图复用:仅创建屏幕可见范围内的 item 视图,当列表滚动时,复用已滑出屏幕的 item 视图(通过convertView),减少内存占用。

核心组件:
  • Adapter:数据与视图的桥梁(如ArrayAdapterSimpleAdapter、自定义 Adapter);
  • ViewHolder:缓存 item 视图中的控件,避免频繁findViewById(优化性能)。

2. 基本使用:自定义 Adapter

(1)Item 布局(res/layout/item_list.xml

    
    
(2)自定义 Adapter(使用 ViewHolder)
public class MyListAdapter extends BaseAdapter {
    private List mData;
    private LayoutInflater mInflater;
    public MyListAdapter(Context context, List data) {
        mData = data;
        mInflater = LayoutInflater.from(context);
    }
    // 数据总数
    @Override
    public int getCount() {
        return mData == null ? 0 : mData.size();
    }
    // 获取指定位置的数据
    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }
    // 获取item的id(通常为position)
    @Override
    public long getItemId(int position) {
        return position;
    }
    // 核心方法:返回item视图
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        // 1. 复用convertView(视图复用)
        if (convertView == null) {
            // 加载item布局
            convertView = mInflater.inflate(R.layout.item_list, parent, false);
            // 2. 创建ViewHolder缓存控件
            holder = new ViewHolder();
            holder.ivIcon = convertView.findViewById(R.id.iv_icon);
            holder.tvTitle = convertView.findViewById(R.id.tv_title);
            // 将ViewHolder存储到convertView中
            convertView.setTag(holder);
        } else {
            // 复用已有的ViewHolder
            holder = (ViewHolder) convertView.getTag();
        }
        // 3. 绑定数据
        String title = mData.get(position);
        holder.tvTitle.setText(title);
        return convertView;
    }
    // ViewHolder:缓存item中的控件
    static class ViewHolder {
        ImageView ivIcon;
        TextView tvTitle;
    }
}
(3)在 Activity 中使用 ListView
public class ListViewActivity extends AppCompatActivity {
    private ListView mListView;
    private List mData;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);
        mListView = findViewById(R.id.list_view);
        // 准备数据
        mData = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            mData.add("ListView Item " + (i + 1));
        }
        // 设置Adapter
        mListView.setAdapter(new MyListAdapter(this, mData));
        // 点击事件
        mListView.setOnItemClickListener((parent, view, position, id) -> {
            Toast.makeText(this, "点击了第" + (position + 1) + "项", Toast.LENGTH_SHORT).show();
        });
        // 长按事件
        mListView.setOnItemLongClickListener((parent, view, position, id) -> {
            Toast.makeText(this, "长按了第" + (position + 1) + "项", Toast.LENGTH_SHORT).show();
            return true; // 消费事件,避免触发点击
        });
    }
}

3. 性能优化技巧

  • 复用 convertView:避免每次getView都创建新视图;
  • 使用 ViewHolder:缓存控件引用,减少findViewById(该方法耗时);
  • 减少 item 布局层级:避免嵌套过多导致测量耗时;
  • 图片异步加载:列表中的图片需异步加载(如用 Glide),避免阻塞 UI 线程;
  • 分页加载:大量数据时分页加载,避免一次性加载过多数据。

4. 局限性(为何被 RecyclerView 替代)

  • 布局单一:仅支持线性布局,网格布局需用GridView
  • 缓存机制简单:仅一级缓存(convertView),复用效率低于RecyclerView
  • 动画支持差:item 添加 / 删除动画需手动实现;
  • 扩展性弱:无法自定义布局管理器,难以实现复杂交互(如滑动删除)。

四、其他常用基础控件

除了上述核心控件,还有一些高频使用的基础控件:

1. EditText:文本输入控件

用于接收用户输入(如账号、密码、搜索内容),常用属性:

监听输入变化:

EditText etUsername = findViewById(R.id.et_username);
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) {
        // 输入变化时回调(s为当前文本)
        Log.d("EditText", "输入内容:" + s.toString());
    }
    @Override
    public void afterTextChanged(Editable s) {}
});

2. ImageView:图片展示控件

用于展示图片(本地资源、网络图片),常用属性:

加载网络图片(需配合 Glide 等库):

// 需添加Glide依赖:implementation 'com.github.bumptech.glide:glide:4.15.1'
ImageView ivNetwork = findViewById(R.id.iv_network);
Glide.with(this)
    .load("https://example.com/image.jpg") // 网络图片URL
    .placeholder(R.mipmap.ic_launcher) // 加载中占位图
    .error(R.mipmap.ic_error) // 加载失败图
    .into(ivNetwork);

3. CheckBox 与 RadioButton:选择控件

  • CheckBox:多选控件(如兴趣标签);
  • RadioButton:单选控件(需配合RadioGroup使用,如性别选择)。

示例(RadioButton):


     
    

监听选择变化:

RadioGroup rgGender = findViewById(R.id.rg_gender);
rgGender.setOnCheckedChangeListener((group, checkedId) -> {
    if (checkedId == R.id.rb_male) {
        Toast.makeText(this, "选择了男", Toast.LENGTH_SHORT).show();
    } else if (checkedId == R.id.rb_female) {
        Toast.makeText(this, "选择了女", Toast.LENGTH_SHORT).show();
    }
});

五、高频面试题(含答案)

1. TextView 如何实现富文本效果?有哪些常用的 Span?

  • 实现方式:通过SpannableStringSpannableStringBuilder结合Span接口实现,Span用于修改文本的部分样式。
  • 常用 Span
    • ForegroundColorSpan:文本颜色;
    • BackgroundColorSpan:背景颜色;
    • StyleSpan:文本风格(粗体、斜体);
    • UnderlineSpan:下划线;
    • StrikethroughSpan:删除线;
    • ClickableSpan:可点击文本;
    • ImageSpan:文本中插入图片。

2. Button 的点击事件有几种实现方式?各有什么优缺点?

  • XML 绑定(android:onClick
    • 优点:简单直观,无需在代码中 findViewById;
    • 缺点:方法必须是 public,且参数固定为 View,灵活性低,不适合复杂逻辑。
  • 匿名内部类
    • 优点:代码集中,适合简单点击逻辑;
    • 缺点:多个按钮时代码冗余,不易维护。
  • 实现 OnClickListener 接口
    • 优点:统一管理多个按钮的点击事件,代码清晰,适合复杂场景;
    • 缺点:需通过switch-case区分按钮,略繁琐。

3. ListView 的优化方案有哪些?ViewHolder 的作用是什么?

  • 优化方案
    1. 复用convertView:避免每次getView创建新视图;
    2. 使用ViewHolder:缓存 item 中的控件,减少findViewById调用;
    3. 减少 item 布局层级:避免嵌套过多导致测量耗时;
    4. 图片异步加载:避免阻塞 UI 线程;
    5. 分页加载:大量数据时分批加载。
  • ViewHolder 作用:通过convertView.setTag(holder)缓存 item 中的控件引用,使每次getView无需重新调用findViewById(该方法通过遍历视图树查找控件,耗时较高),提升列表滑动流畅度。

4. ListView 与 RecyclerView 的核心区别是什么?

对比维度ListViewRecyclerView
布局支持仅线性布局(网格需用 GridView)支持线性、网格、瀑布流(通过 LayoutManager)
缓存机制一级缓存(convertView)四级缓存(mAttachedScrap、mCachedViews 等)
复用机制需手动实现 ViewHolder强制使用 ViewHolder,复用更彻底
动画支持无内置动画,需手动实现内置 item 添加 / 删除动画,支持自定义
事件处理自带 onItemClickListener需自定义点击事件
扩展性低(无法自定义布局管理器)高(可自定义 LayoutManager、ItemDecoration 等)

5. EditText 如何限制输入内容?例如只允许输入数字或手机号格式。

  • 通过android:inputType限制输入类型
    • 数字:android:inputType="number"
    • 手机号:android:inputType="phone"
    • 密码:android:inputType="textPassword"
  • 通过InputFilter自定义过滤规则(如限制长度、禁止特殊字符):
  • EditText etPhone = findViewById(R.id.et_phone);
    // 限制长度为11位(手机号)
    etPhone.setFilters(new InputFilter[]{
        new InputFilter.LengthFilter(11)
    });
    // 自定义过滤规则(仅允许数字)
    etPhone.setFilters(new InputFilter[]{
        (source, start, end, dest, dstart, dend) -> {
            for (int i = start; i < end; i++) {
                if (!Character.isDigit(source.charAt(i))) {
                    return ""; // 过滤非数字字符
                }
            }
            return null; // 允许输入
        }
    });

6. ImageView 的 scaleType 有哪些?各自的效果是什么?

scaleType用于控制图片的缩放和显示方式,常用值:

  • center:图片居中显示,不缩放(超出部分裁剪);
  • centerCrop:等比例缩放,填满 ImageView,超出部分裁剪(常用,保持比例且充满控件);
  • centerInside:等比例缩放,图片完全显示在 ImageView 内(可能留有空白);
  • fitXY:拉伸图片填满 ImageView(不保持比例,可能变形);
  • fitStart:等比例缩放,图片靠上 / 左显示;
  • fitCenter:等比例缩放,图片居中显示(默认值);
  • fitEnd:等比例缩放,图片靠下 / 右显示。

7. 如何实现 Button 的点击状态变化(如背景色、文字色)?

通过selector 资源实现,步骤:

  1. res/drawable下创建 selector 文件(如btn_selector.xml);
  2. 定义不同状态(state_pressedstate_enabled等)对应的样式(背景、颜色等);
  3. 在 Button 的android:backgroundandroid:textColor中引用该 selector。

示例(背景色变化):



    
    

8. TextView 的ellipsize属性有哪些值?如何实现多行文本末尾省略?

  • ellipsizeend(末尾省略)、start(开头省略)、middle(中间省略)、marquee(滚动显示)。
  • 多行文本末尾省略:需同时设置maxLinesellipsize
  •  // 末尾省略

9. ListView 的getView方法中,convertView 的作用是什么?为什么要复用它?

  • 作用convertView是已滑出屏幕的 item 视图缓存,用于复用,避免每次创建新视图。
  • 复用原因
    • 减少内存占用:若列表有 1000 项,屏幕可见 10 项,复用后只需创建 10 个视图,而非 1000 个;
    • 提升性能:避免频繁创建和销毁视图(视图创建涉及 XML 解析、对象实例化,耗时较高),减少 GC(垃圾回收)频率,使列表滑动更流畅。

10. 如何解决 ListView 滑动时图片加载错位的问题?

图片加载错位通常是因为视图复用导致的(异步加载的图片绑定到了复用的 item 上),解决方案:

  • 为图片设置 Tag:将图片 URL 作为 Tag 绑定到 ImageView,加载完成后校验 Tag 是否匹配当前 item 的 URL:

  1. @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // ... 省略ViewHolder初始化 ...
        String imageUrl = mData.get(position).getImageUrl();
        // 设置Tag为当前URL
        holder.ivIcon.setTag(imageUrl);
        // 异步加载图片
        loadImageAsync(imageUrl, new ImageLoadCallback() {
            @Override
            public void onSuccess(Bitmap bitmap) {
                // 校验Tag是否匹配(避免错位)
                if (imageUrl.equals(holder.ivIcon.getTag())) {
                    holder.ivIcon.setImageBitmap(bitmap);
                }
            }
        });
        return convertView;
    }
  2. 使用成熟的图片加载库(如 Glide、Picasso):内部已处理复用问题,自动绑定图片到正确的 item。

TextViewButtonListView等基础控件是 Android UI 开发的基石,掌握它们的核心用法、属性配置和性能优化技巧,是构建高质量界面的前提。尽管RecyclerView已逐渐替代ListView,但ListView的视图复用思想和优化方案仍具有重要参考价值。