07.15 控件-Spinner

2023/5/28 Android 开发基础

Spinner 列表选择框控件,可让用户从一组数据中快速选择一个值。默认状态下,列表选择框控件显示其当前所选的值。轻触可显示列表选择框,其中列有所有可用值,用户可从中选择任意一个数据。

列表选择框控件有两种风格的选择模式,通过spinnerMode属性设置为dropdown,下拉选择框模式;SpinnerMode属性设置为dialog,是在页面中部以对话框形式展示列表。

# 基本属性

  • android:dropDownHorizontalOffset:设置下拉列表框的水平偏移距离
  • android:dropDownVerticalOffset:设置下拉列表框的垂直偏移距离
  • android:dropDownSelector:下拉列表框被选中时的背景
  • android:dropDownWidth:设置下拉列表框的宽度
  • android:gravity:设置里面组件的对其方式
  • android:popupBackground:设置列表框的背景
  • android:prompt:设置对话框模式的列表框的提示信息(标题),只能够引用 string.xml 中的资源id,而不能直接设置字符串(在代码中可以设置资源id或者字符串)
  • android:spinnerMode:列表框的模式,有两个可选值: dialog:对话框风格的窗口 dropdown:下拉菜单风格的窗口(默认)
  • android:entries:使用数组资源设置列表选择框中的列表选择项目
  • android:overlapAnchor:dropdown样式情况下,列表框是否覆盖控件显示

# 相关方法

  • setAdapter:设置适配器
  • setSelection:设置当前选中哪项,注意该方法要在 setAdapter 方法之后调用
  • setOnItemSelectedListener:设置下拉列表的选择监听器,接收 OnItemSelectedListener,onNothingSelected 通知

# 基本使用

基本使用示例,使用最简单的ArrayAdapter显示一行文字选项的列表选择框。

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

    <!-- 下拉选择样式列表选择框 -->
    <Spinner
        android:id="@+id/sp_dropdown"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:overlapAnchor="false"
        android:spinnerMode="dropdown" />

    <!-- 弹窗选择样式列表选择框 -->
    <Spinner
        android:id="@+id/sp_dialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:spinnerMode="dialog" />

    <!-- 使用entries属性指定资源id对应的列表数据 -->
    <Spinner
        android:id="@+id/sp_entries"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:entries="@array/sp_data"
        android:overlapAnchor="false"
        android:spinnerMode="dropdown" />
</LinearLayout>

示例代码:

// 数组数据
String[] data = {"羽毛球", "乒乓球", "橄榄球", "篮球", "排球", "网球", "足球"};
// 数组适配器
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
        android.R.layout.simple_spinner_dropdown_item,
        data);

// 下拉列表选择框
Spinner spDropDown = findViewById(R.id.sp_dropdown);
// 设置适配器
spDropDown.setAdapter(adapter);
// 设置选择项为数据项中的第一项
spDropDown.setSelection(0);
// 设置选项监听器,监听选中项
spDropDown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        Log.i(TAG, "onItemSelected: spDropDown, position = " + position);
        Toast.makeText(getApplicationContext(),
                "下拉列表选择框:选择了" + data[position],
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        Log.i(TAG, "onItemSelected: spDropDown");
    }
});

// 对话弹窗列表选择框
Spinner spDialog = findViewById(R.id.sp_dialog);
// 设置弹窗的提示标题
spDialog.setPrompt("请选择一种感兴趣的球类:");
spDialog.setAdapter(adapter);
spDialog.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        Log.i(TAG, "onItemSelected: spDialog, position = " + position);
        Toast.makeText(getApplicationContext(),
                "弹窗列表选择框:选择了" + data[position],
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        Log.i(TAG, "onNothingSelected: spDialog");
    }
});

// 通过entries属性指定资源id对应的列表数据的列表选择框
Spinner spEntries = findViewById(R.id.sp_entries);
spEntries.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        Log.i(TAG, "onItemSelected: spEntries, position = " + position);
        Toast.makeText(getApplicationContext(),
                "Entries数据列表选择框:选择了" + data[position],
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        Log.i(TAG, "onNothingSelected: spEntries");
    }
});

效果图:

Spinner 基本使用效果图

# 自定义

自定义 Item 布局和自定义 BaseAdapter。

  1. 自定义列表项View布局layout_spinner_item.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="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="50dp"
        android:layout_height="50dp" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:paddingStart="10dp"
        android:paddingEnd="20dp"
        android:textSize="22sp" />

</LinearLayout>
  1. 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:gravity="center_horizontal"
    android:orientation="vertical">

    <!-- 下拉选择样式列表选择框 -->
    <Spinner
        android:id="@+id/sp_dropdown"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:overlapAnchor="false"
        android:spinnerMode="dropdown" />

    <!-- 弹窗选择样式列表选择框 -->
    <Spinner
        android:id="@+id/sp_dialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:spinnerMode="dialog" />

</LinearLayout>
  1. 自定义适配器 CustomAdapter
/**
    * 自定义适配器,继承自BaseAdapter,实现其必要的适配方案.
    */
public class CustomAdapter extends BaseAdapter {
    private String[] data;

    public CustomAdapter(String[] data) {
        // 初始化数据
        this.data = data;
    }

    @Override
    public int getCount() {
        // 实现获取item数量
        return data == null ? 0 : data.length;
    }

    @Override
    public String getItem(int position) {
        // 实现获取指定位置的Item数据
        return data == null ? null : data[position];
    }

    @Override
    public long getItemId(int position) {
        // 实现获取指定位置的ItemId,一般返回其位置,也可以根据需要实现
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 实现获取指定位置的View对象
        View view = LayoutInflater
                .from(parent.getContext())
                .inflate(R.layout.layout_spinner_item, parent, false);
        ImageView ivIcon = view.findViewById(R.id.iv_icon);
        TextView tvContent = view.findViewById(R.id.tv_content);
        if (position % 2 == 0) {
            ivIcon.setBackgroundResource(R.drawable.rating_off);
        } else {
            ivIcon.setBackgroundResource(R.drawable.rating_on);
        }
        tvContent.setText(getItem(position));
        return view;
    }
}
  1. 示例代码
// 数据列表
String[] data = {"羽毛球", "乒乓球", "橄榄球", "篮球", "排球", "网球", "足球"};
// 自定义适配器
CustomAdapter customAdapter = new CustomAdapter(data);

// 下拉列表选择框
Spinner spDropDown = findViewById(R.id.sp_dropdown);
// 设置自定义适配器
spDropDown.setAdapter(customAdapter);
// 设置选择项为数据项中的第一项
spDropDown.setSelection(0);
spDropDown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        Log.i(TAG, "onItemSelected: spDropDown, position = " + position);
        Toast.makeText(getApplicationContext(),
                "下拉列表选择框:选择了" + data[position],
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        Log.i(TAG, "onItemSelected: spDropDown");
    }
});

// 对话弹窗列表选择框
Spinner spDialog = findViewById(R.id.sp_dialog);
// 设置弹窗的提示标题
spDialog.setPrompt("请选择一种感兴趣的球类:");
spDialog.setAdapter(customAdapter);
spDialog.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        Log.i(TAG, "onItemSelected: spDialog, position = " + position);
        Toast.makeText(getApplicationContext(),
                "弹窗列表选择框:选择了" + data[position],
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        Log.i(TAG, "onNothingSelected: spDialog");
    }
});
  1. 效果图

Spinner 自定义示例效果图

提示:

ArrayAdapter 也可以实现自定义布局,但是只能绑定单个布局中的 TextView 文本控件进行文本的适配绑定。如果有多个元素的控件则无法进行默认适配,需要自定义 BaseAdapter 来实现。

ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
        @IdRes int textViewResourceId, @NonNull List<T> objects)
  • resource:自定义布局的资源id

  • textViewResourceId:自定义布局中需要绑定数据的文本控件

  • objects:数据

如上面自定义示例只绑定文本,则可以这样使用:

ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getApplicationContext(),
                R.layout.layout_spinner_item, // 自定义布局
                R.id.tv_content, // 自定义自定义布局中需要绑定数据的TextView的Id
                data); // 数据列表
spDropDown.setAdapter(arrayAdapter);

# 结束

本节介绍了 Android 中默认的列表选择控件(Spinner)常用属性以及相关使用方法,自定义实现的布局和适配器的方法,更多属性和用法请参考:Spinner (opens new window)