目录
下拉框
Spinner 是 Android 中常用的下拉选择控件,允许用户从一组选项中选择一个值,它的功能类似于单选按钮的组合。它的展示方式有两种,一种是在下拉框的正下方弹出列表框,一种是在页面中弹出对话框。这两种方式的选择由Spinner的属性spinnerMode设置。当属性的取值为dropdown则为第一种,取值为dialog则为第二种。在Java代码中Spinner类有几个重要的方法,它们的介绍如下:
setPrompt:设置标题文字。标题只有在对话框模式才会显示。
setAdapter:设置列表项的数据适配器。
setSelection:设置当前选中哪一项。该方法需要在方法setAdapter调用后才可以使用,没有数据就没有可选项。
setOnItemSelectedListener:设置下拉框列表的选择监听器,该监听器需要实现接口OnItemSelectedListener。
示例代码如下,布局文件中添加了两个Spinner分别设置为dropdown以及dialog,文本视图。
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".SeniorWidget.SpinnerActivity">
<Spinner
android:id="@+id/spinner_Dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Spinner
android:id="@+id/spinner_Dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
部分Java代码,定义了一个ArrayAdapter作为数据适配器,后给两个Spinner设置适配器并设置监听器。在获取ArrayAdapter示例中的构造函数的第二个参数是一个布局文件用于定义下拉框项的布局。因为下拉框中的每一项可以看做一个文本视图,需要定义这个视图的各项属性,第三个参数时所要展示的数据内容,如下面代码的数据是一个整型数组。
java
public class SpinnerActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private Spinner spinner_Dropdown, spinner_Dialog;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner);
spinner_Dropdown = findViewById(R.id.spinner_Dropdown);
spinner_Dialog = findViewById(R.id.spinner_Dialog);
textView = findViewById(R.id.textView);
ArrayAdapter adapter = new ArrayAdapter<Integer>(this, R.layout.item_layout, new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
spinner_Dropdown.setAdapter(adapter);
spinner_Dropdown.setSelection(1);
spinner_Dialog.setAdapter(adapter);
spinner_Dialog.setPrompt("请选择...");
spinner_Dialog.setSelection(2);
spinner_Dropdown.setOnItemSelectedListener(this);
spinner_Dialog.setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if (adapterView.getId() == R.id.spinner_Dropdown) {
textView.setText(String.format("列表框选择了第%d项", i));
} else if (adapterView.getId() == R.id.spinner_Dialog) {
textView.setText(String.format("弹出框选择了第%d项", i));
}
}
// 未选择时的处理方法,通常无需关注
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
item_layout.xml中的代码如下。
TypeScript
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="50dp"
android:singleLine="true"
android:gravity="center"
android:textSize="17sp"
android:textColor="#1269db"
android:text="@string/app_name"/>
效果图如下,依次点击两个下拉框可以看到两种不同模式的区别。点击下拉框的其中一项可以看到它们的序号是从0开始的。



简单适配器SimpleAdapter
上面使用的适配器ArrayAdapter主要用于每行列表只展示文本内容,但在实际使用中有可能需要添加图片之类的,ArrayAdapter是不够用的。这就需要用到另一种适配器SimpleAdapter,它允许在列表项中同时显示图片与文本。示例代码如下,页面的布局文件不变。在Java代码中有两个数组,一个存放图片资源的id,一个存放要展示的文本。一个列表List存放图片与文本的映射Map。SimpleAdapter的构造函数中的第二个参数为一个列表实例,第三个参数为布局文件,第四个参数为Map中的每一项关键词组成的数组,第五个参数为第四个参数数组每一项对应的图片资源id的数组。如第一个为1对应的图片资源id为R.drawable.one。
java
public class SpinnerActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private Spinner spinner_Dropdown, spinner_Dialog;
private TextView textView;
private int[] image_id = {R.drawable.one, R.drawable.two, R.drawable.three, R.drawable.four, R.drawable.five,
R.drawable.six, R.drawable.seven, R.drawable.eight, R.drawable.nine, R.drawable.zero};
private Integer[] number = {1,2,3,4,5,6,7,8,9,0};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner);
spinner_Dropdown = findViewById(R.id.spinner_Dropdown);
spinner_Dialog = findViewById(R.id.spinner_Dialog);
textView = findViewById(R.id.textView);
ArrayAdapter adapter = new ArrayAdapter<Integer>(this, R.layout.item_layout, number);
spinner_Dropdown.setAdapter(adapter);
spinner_Dropdown.setSelection(1);
List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
for (int i = 0; i < number.length; i++){
Map<String,Object> map = new HashMap<String,Object>();
map.put("image",image_id[i]);
map.put("number",number[i]);
mapList.add(map);
}
SimpleAdapter simpleAdapter = new SimpleAdapter(this,mapList , R.layout.item_simple, new String[]{"image", "number"}, new int[]{R.id.imageView, R.id.textView});
spinner_Dialog.setAdapter(simpleAdapter);
spinner_Dialog.setPrompt("请选择...");
spinner_Dialog.setSelection(2);
spinner_Dropdown.setOnItemSelectedListener(this);
spinner_Dialog.setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if (adapterView.getId() == R.id.spinner_Dropdown) {
textView.setText(String.format("列表框选择了第%d项", i));
} else if (adapterView.getId() == R.id.spinner_Dialog) {
textView.setText(String.format("弹出框选择了第%d项", i));
}
}
// 未选择时的处理方法,通常无需关注
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
下面为item_simple.xml的代码,其中线性布局中有一个图片视图以及一个文本视图。
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:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/image_3"/>
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:singleLine="true"
android:text="@string/app_name"
android:textColor="#1269db"
android:textSize="17sp" />
</LinearLayout>
效果图如下,可以看到与第一个下拉框相比第二个多了图片,在视觉效果上会比第一个要好一些。


基本适配器BaseAdapter
前面说讲的两种适配器只适用于一些简单的数据,若每个列表项需要三个以上的控件,虽然简单适配器可以实现但是消耗较大且不易扩展。对此Android提供了另一种适用性更强的基本适配器BaseAdapter,该适配器允许在别的代码中编写操作代码,大大提升了代码的可读性以及可维护性。由于基本适配器是一个抽象类,因此需要重新定义一个由它派生而来的新类。在派生类中需要实现以下五个方法:
构造函数:指定适配器需要处理的数据集合。
getCount:获取列表项的个数。
getItem:获取列表项的数据。
getItemId:获取列表项的编号。
getView:获取每项的展示视图实例并对每一项的内部控件进行业务处理。
示例代码如下,在布局文件中有三个Spinner,分别用于三个适配器。
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".SeniorWidget.SpinnerActivity">
<Spinner
android:id="@+id/spinner_Dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dropdown"/>
<Spinner
android:id="@+id/spinner_Dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dialog"/>
<Spinner
android:id="@+id/spinner_BaseAdapter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dialog"/>
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
列表项的布局文件item_base.xml的代码。
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/image_3"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical">
<TextView
android:id="@+id/textView_1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:textSize="17sp"
android:textColor="#1269db"
android:text="@string/app_name"/>
<TextView
android:id="@+id/textView_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="17sp"
android:text="@string/app_name"/>
</LinearLayout>
</LinearLayout>
部分Java代码如下,主要增加了一个数组number_english,一个BaseAdapter的派生类MyBaseAdapter,一个NumberInfo类,一个获取数据列表的方法getLit,在MyBaseAdapter中有一个类ViewHolder,这是用来保存列表项中的视图实例,这样就不需要次次调用findViewById方法获取实例,减少消耗。
java
public class SpinnerActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private Spinner spinner_Dropdown, spinner_Dialog, spinner_BaseAdapter;
private TextView textView;
private int[] image_id = {R.drawable.one, R.drawable.two, R.drawable.three, R.drawable.four, R.drawable.five,
R.drawable.six, R.drawable.seven, R.drawable.eight, R.drawable.nine, R.drawable.zero};
private Integer[] number = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
private String[] number_english = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "zero"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner);
spinner_Dropdown = findViewById(R.id.spinner_Dropdown);
spinner_Dialog = findViewById(R.id.spinner_Dialog);
spinner_BaseAdapter = findViewById(R.id.spinner_BaseAdapter);
textView = findViewById(R.id.textView);
Log.e(TAG, "onCreate: ");
ArrayAdapter adapter = new ArrayAdapter<Integer>(this, R.layout.item_layout, number);
spinner_Dropdown.setAdapter(adapter);
spinner_Dropdown.setSelection(1);
List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
for (int i = 0; i < number.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("image", image_id[i]);
map.put("number", number[i]);
mapList.add(map);
}
SimpleAdapter simpleAdapter = new SimpleAdapter(this, mapList, R.layout.item_simple, new String[]{"image", "number"}, new int[]{R.id.imageView, R.id.textView});
spinner_Dialog.setAdapter(simpleAdapter);
spinner_Dialog.setPrompt("请选择...");
spinner_Dialog.setSelection(2);
MyBaseAdapter myBaseAdapter = new MyBaseAdapter(this, getList());
spinner_BaseAdapter.setAdapter(myBaseAdapter);
spinner_BaseAdapter.setSelection(0);
spinner_BaseAdapter.setPrompt("Please Choose...");
spinner_Dropdown.setOnItemSelectedListener(this);
spinner_Dialog.setOnItemSelectedListener(this);
spinner_BaseAdapter.setOnItemSelectedListener(this);
}
public List<NumberInfo> getList() {
List<NumberInfo> numberInfoList = new ArrayList<NumberInfo>();
for (int i = 0; i < number.length; i++) {
NumberInfo numberInfo = new NumberInfo();
numberInfo.setImage_id(image_id[i]);
numberInfo.setNumber(number[i]);
numberInfo.setNumber_english(number_english[i]);
numberInfoList.add(numberInfo);
}
Log.e(TAG, "getList: ");
return numberInfoList;
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if (adapterView.getId() == R.id.spinner_Dropdown) {
textView.setText(String.format("列表框选择了第%d项", i));
} else if (adapterView.getId() == R.id.spinner_Dialog) {
textView.setText(String.format("弹出框选择了第%d项", i));
} else if (adapterView.getId() == R.id.spinner_BaseAdapter) {
textView.setText(String.format("弹出框选择了第%d项", i));
}
}
// 未选择时的处理方法,通常无需关注
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
class NumberInfo {
private int image_id;
private int number;
private String number_english;
public int getImage_id() {
return image_id;
}
public void setImage_id(int image_id) {
this.image_id = image_id;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getNumber_english() {
return number_english;
}
public void setNumber_english(String number_english) {
this.number_english = number_english;
}
}
class MyBaseAdapter extends BaseAdapter {
private List<NumberInfo> numberInfoList;
private Context mcontext;
public MyBaseAdapter(Context context, List<NumberInfo> list) {
super();
numberInfoList = list;
mcontext = context;
}
@Override
public int getCount() {
return numberInfoList.size();
}
@Override
public Object getItem(int i) {
return numberInfoList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (view == null) {
viewHolder = new ViewHolder();
view = LayoutInflater.from(mcontext).inflate(R.layout.item_base, null);
viewHolder.imageView = view.findViewById(R.id.imageView);
viewHolder.textView_1 = view.findViewById(R.id.textView_1);
viewHolder.textView_2 = view.findViewById(R.id.textView_2);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
NumberInfo numberInfo = numberInfoList.get(i);
viewHolder.imageView.setImageResource(numberInfo.getImage_id());
viewHolder.textView_1.setText(String.format("%d",numberInfo.getNumber()));
viewHolder.textView_2.setText(numberInfo.getNumber_english());
viewHolder.imageView.requestFocus();
return view;
}
final class ViewHolder {
public ImageView imageView;
public TextView textView_1;
public TextView textView_2;
}
}
}
效果图如下,前两个下拉框与效果与前面一样,主要看第三个下拉框。第三张图可以看到它的内容会比前两个要多。

