Android Spinner二级联动

本文最后更新于:2024年3月18日 晚上

前言

遇到一个需求是两个Spinner的联动,第二个Spinner根据第一个Spinner选择的数据来修改内容。更常见的例子就是省市的选择,选中一个省份,再根据选中的省份显示对应的城市列表。
我这里则是名称和型号的联动选择,首先选中一个产品名称,再根据选中的产品名显示对应的型号列表。在网上看到的例子中的数据都是程序中直接定义的,而我这里的数据是从数据库中查询得到的,因此又多了一个子线程的内容。

1. Spinner的设置

也没啥好设置的,就是xml文件中放置两个Spinner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<TextView  
android:id="@+id/textViewNameLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/material_fragment_name"
android:textAppearance="@style/font_family_pingfang_black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/guideline47"
app:layout_constraintStart_toStartOf="@+id/guideline60"
app:layout_constraintTop_toTopOf="@+id/guideline47" />

<Spinner
android:id="@+id/spinnerName"
android:layout_width="0dp"
android:layout_height="30dp"
app:layout_constraintBottom_toTopOf="@+id/guideline47"
app:layout_constraintEnd_toStartOf="@+id/guideline61"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toTopOf="@+id/guideline47"
tools:ignore="TouchTargetSizeCheck" />

<TextView
android:id="@+id/textViewModelLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/material_fragment_model"
android:textAppearance="@style/font_family_pingfang_black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/guideline72"
app:layout_constraintStart_toStartOf="@+id/guideline60"
app:layout_constraintTop_toTopOf="@+id/guideline72" />

<Spinner
android:id="@+id/spinnerModel"
style="@style/mySpinnerItemStyle"
android:layout_width="0dp"
android:layout_height="30dp"
android:theme="@style/mySpinnerItemStyle"
app:layout_constraintBottom_toTopOf="@+id/guideline72"
app:layout_constraintEnd_toStartOf="@+id/guideline61"
app:layout_constraintStart_toEndOf="@+id/textView11"
app:layout_constraintTop_toTopOf="@+id/guideline72"
tools:ignore="TouchTargetSizeCheck" />

显示效果如下

2. 查询名称列表

因为安卓中不能在主线程中访问数据库,所以新开一个线程来进行数据的查询,首先查询名称列表

1
2
3
4
5
6
new Thread(new Runnable() {  
@Override
public void run() {
productNameList = productViewModel.getProductNameList();
}
}).start();

在程序初始化中即查询名称列表,并赋值给提前声明的变量。

3. 填充名称Spinner

1
2
3
4
5
6
spinnerName = v.findViewById(R.id.spinnerName);  
spinnerModel = v.findViewById(R.id.spinnerModel);

ArrayAdapter<String> nameAdapter = new ArrayAdapter<String>(getContext(), com.lihang.R.layout.support_simple_spinner_dropdown_item,productNameList);

spinnerName.setAdapter(nameAdapter);

由于型号对应的下拉框还没有数据,所以我们只声明了其Adapter,先不进行填充

4. 填充型号Spinner

首先给名称Spinner添加一个选中项变化的观察者,在观察者中,每当选中项发生变化,便根据名称查询型号,并填充到型号Spinner中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spinnerName.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {  
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
materialModelList.clear();
new Thread(new Runnable() {
@Override
public void run() {
String name = spinnerName.getSelectedItem().toString();
productModelList = productViewModel.getProductModelListByName(name);
ArrayAdapter<String> modelAdapter = new ArrayAdapter<String>(getContext(), com.lihang.R.layout.support_simple_spinner_dropdown_item,modelList);
spinnerModel.setAdapter(modelAdapter);
}
}).start();
}

@Override
public void onNothingSelected(AdapterView<?> adapterView) { }
});

一开始是这么写的,但是系统报了一个错误,Only the original thread that created a view hierarchy can touch its views.,即不能在子线程中操作UI,这里我在子线程中使用SpinnerModel.setAdapter()方法直接操作了UI,所以会报错,于是就需要一个Handler,在Handler中对UI进行操作,而在子线程中则进行查询操作,查询完毕后通过Message将数据传递给Handler,由Handler重新填充型号下拉框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Handler mHandler = new Handler(Looper.myLooper()){  
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if(msg.what == 1){
List<String> modelList = msg.getData().getStringArrayList("modelList");
ArrayAdapter<String> modelAdapter = new ArrayAdapter<String>(getContext(), com.lihang.R.layout.support_simple_spinner_dropdown_item,modelList);
spinnerModel.setAdapter(modelAdapter);
}
}
};
spinnerName.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
productModelList.clear();
new Thread(new Runnable() {
@Override
public void run() {
String name = spinnerName.getSelectedItem().toString();
productModelList = productViewModel.getProductModelListByName(name);
Message message = Message.obtain();
message.what = 1;;
Bundle bundle = new Bundle();
bundle.putStringArrayList("modelList", (ArrayList<String>) productModelList);
message.setData(bundle);
mHandler.sendMessage(message);
}
}).start();
}

Android Spinner二级联动
http://starnight.top/2022/05/04/Android Spinner二级联动/
作者
Cardy Xie
发布于
2022年5月4日
许可协议