Spinner和AppCompatSpinner是实现下拉菜单的一种方式,本章将详细讲解下它的使用。两者其实是一个东西,只是在不同的包下罢了,本章就针对Spinner讲解了。
点击Spinner控件,可以弹出下拉列表,选择列表中的某项即为选中状态。
最简单的Spinner是使用entries
适配列表数据,代码如下:
<Spinner
android:id="@+id/spinner"
android:layout_marginTop="10dp"
android:layout_width="200dp"
android:layout_height="50dp"
android:entries="@array/spinner_values" />
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="spinner_values">
<item>嫦娥</item>
<item>上官婉儿</item>
<item>猪八戒</item>
<item>司马懿</item>
<item>盘古</item>
<item>伽罗</item>
<item>艾琳</item>
<item>韩信</item>
</array>
</resources>
效果如下:

Spinner有一个父类:AbsSpinner,AbsSpinner是一个抽象类,是不可以直接拿来使用的,但是它有一个直观重要的属性:AbsSpinner_entries

这个属性就是以上使用的entries
属性。
当然,Spinner
的setAdapter
方法可以配置列表值,setAdapter
d的形参是SpinnerAdapter
对象,SpinnerAdapter
是一个接口,所以这里实际adapter必须实现SpinnerAdapter
接口。
现在已知实现SpinnerAdapter
接口,并且用public修饰的非内部类的adapter有BaseRecipientAdapter
、SuggestedLocaleAdapter
、MenuAdapter
、AccountViewAdapter
、ActionsAdapter
、ArrayAdapter
、RemoteViewsAdapter
、MenuAdapter
、SimpleAdapter
、OverflowMenuAdapter
、FakeAdapter
、PreferenceGroupAdapter
、ItemAdapter
、TimeZoneResultAdapter
、TimeZoneFilterTypeAdapter
,这些都是SDK自带的,当然,也可以自定义一个实现SpinnerAdapter
接口的Adapter。
SDK自带的Adapter有很多,但是暂时无法选择,那么就先自定义一个Adapter
来举例吧。
自定义Adapter如下:
public class MyAdapter extends BaseAdapter {
private List<String> dataList;
private Context mContext;
public MyAdapter(Context mContext, List<String> list){
this.mContext = mContext;
dataList = list;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.layout_spinner, parent, false);
TextView textView = view.findViewById(R.id.textview);
textView.setText(dataList.get(position));
return view;
}
@Override
public int getCount() {
return dataList == null ? 0 : dataList.size();
}
@Override
public String getItem(int position) {
return dataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
}
MyAdapter继承了BaseAdapter,BaseAdapter实现了SpinnerAdapter接口,符合Spinner的Adapter参数类型。
使用方法如下:
dataList.add("嫦娥");
dataList.add("上官婉儿");
dataList.add("猪八戒");
dataList.add("司马懿");
dataList.add("盘古");
dataList.add("伽罗");
dataList.add("艾琳");
dataList.add("韩信");
spinner.setAdapter(new MyAdapter(MainActivity.this, dataList));
Item布局文件如下:
<?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"
android:padding="20dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试"
android:textSize="20sp"
android:textStyle="bold"
android:layout_gravity="center"
android:layout_marginLeft="30dp"
android:textColor="#92C457"/>
</LinearLayout>
效果如下:

我们继续分析setAdapter源码,源码如下:
@Override
public void setAdapter(SpinnerAdapter adapter) {
// The super constructor may call setAdapter before we're prepared.
// Postpone doing anything until we've finished construction.
if (mPopup == null) {
mTempAdapter = adapter;
return;
}
super.setAdapter(adapter);
mRecycler.clear();
final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
if (targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP
&& adapter != null && adapter.getViewTypeCount() != 1) {
throw new IllegalArgumentException("Spinner adapter view type count must be 1");
}
final Context popupContext = mPopupContext == null ? mContext : mPopupContext;
mPopup.setAdapter(new DropDownAdapter(adapter, popupContext.getTheme()));
}
可以看到,adapter被DropDownAdapter所装饰(装饰设计模式),继续看一下DropDownAdapter源码
public DropDownAdapter(@Nullable SpinnerAdapter adapter,
@Nullable Resources.Theme dropDownTheme) {
mAdapter = adapter;
if (adapter instanceof ListAdapter) {
mListAdapter = (ListAdapter) adapter;
}
if (dropDownTheme != null && adapter instanceof ThemedSpinnerAdapter) {
final ThemedSpinnerAdapter themedAdapter = (ThemedSpinnerAdapter) adapter;
if (themedAdapter.getDropDownViewTheme() == null) {
themedAdapter.setDropDownViewTheme(dropDownTheme);
}
}
}
当popup设置了主题,并且adapter直接或间接是ThemedSpinnerAdapter
的子类,那么才可以真正成功设置主题。

如图,ThemedSpinnerAdapter的父类是SpinnerAdapter ,上面已经指出,直接或间接实现SpinnerAdapter 接口的Adapter有:BaseRecipientAdapter
、SuggestedLocaleAdapter
、MenuAdapter
、AccountViewAdapter
、ActionsAdapter
、ArrayAdapter
、RemoteViewsAdapter
、MenuAdapter
、SimpleAdapter
、OverflowMenuAdapter
、FakeAdapter
、PreferenceGroupAdapter
、ItemAdapter
、TimeZoneResultAdapter
、TimeZoneFilterTypeAdapter
,那么,这些Adapter中直接或间接实现ThemedSpinnerAdapter接口的有哪些呢?
只有两个:分别是SimpleAdapter
和ArrayAdapter
,这两个Adapter也是Spinner常用的Adapter了。
ArrayAdapter
使用比较简单,根据它的构造方法传递参数即可,构造方法如下:
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a TextView to use when
* instantiating views.
* @param objects The objects to represent in the ListView.
*/
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
@NonNull List<T> objects) {
this(context, resource, 0, objects);
}
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a layout to use when
* instantiating views.
* @param textViewResourceId The id of the TextView within the layout resource to be populated
* @param objects The objects to represent in the ListView.
*/
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
@IdRes int textViewResourceId, @NonNull List<T> objects) {
this(context, resource, textViewResourceId, objects, false);
}
ArrayAdapter
的构造方法大致分为两种,一种需要指定TextView的ID,另一种不需要指定TextView的ID。
构造方法有几个参数需要说明一下:
objects:存放数据的数组,一般是字符串。
resource:资源文件
textViewResourceId:指定textview的ID
如果需要指定textview的ID,那么资源布局可能是这样的
<?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"
android:padding="20dp">
<ImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试"
android:textSize="20sp"
android:textStyle="bold"
android:layout_gravity="center"
android:layout_marginLeft="30dp"
android:textColor="#92C457"/>
</LinearLayout>
如果不需要指定textview的ID,那么资源布局必然是这样的
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
ArrayAdapter
的存在大大说短了构造Adapter的时间,但是它只能适配文本列表,而其它数据(比如图片)只能默认写死。
SimpleAdapter
可以解决ArrayAdapter
数据单一的弊端,它的使用方法大致如下:
List<HashMap<String, ?>> list = new ArrayList<>();
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("文字", "嫦娥");
hashMap.put("图片", R.mipmap.ic_launcher);
list.add(hashMap);
hashMap = (HashMap<String, Object>) hashMap.clone();
hashMap.put("文字", "上官婉儿");
hashMap.put("图片", R.mipmap.ic_launcher_round);
list.add(hashMap);
hashMap = (HashMap<String, Object>) hashMap.clone();
hashMap.put("文字", "猪八戒");
hashMap.put("图片", R.mipmap.ic_launcher_round);
list.add(hashMap);
hashMap = (HashMap<String, Object>) hashMap.clone();
hashMap.put("文字", "司马懿");
hashMap.put("图片", R.mipmap.ic_launcher);
list.add(hashMap);
hashMap = (HashMap<String, Object>) hashMap.clone();
hashMap.put("文字", "盘古");
hashMap.put("图片", R.mipmap.ic_launcher_round);
list.add(hashMap);
hashMap = (HashMap<String, Object>) hashMap.clone();
hashMap.put("文字", "伽罗");
hashMap.put("图片", R.mipmap.ic_launcher);
list.add(hashMap);
hashMap = (HashMap<String, Object>) hashMap.clone();
hashMap.put("文字", "艾琳");
hashMap.put("图片", R.mipmap.ic_launcher_round);
list.add(hashMap);
hashMap = (HashMap<String, Object>) hashMap.clone();
hashMap.put("文字", "韩信");
hashMap.put("图片", R.mipmap.ic_launcher);
list.add(hashMap);
spinner.setAdapter(new SimpleAdapter(MainActivity.this, list, R.layout.layout_spinner, new String[]{"图片", "文字"}, new int[]{R.id.imageview, R.id.textview}));
SimpleAdapter
可以传递多个HashMap,使数据的多样化,解析SimpleAdapter的终点在于其构造方法。
public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,
@LayoutRes int resource, String[] from, @IdRes int[] to) {
mData = data;
mResource = mDropDownResource = resource;
mFrom = from;
mTo = to;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
resource:是资源文件
data:是数据
from:指定Map的key,传递key的数组
to:指定那些key所对应组件的id
展示效果如下:

Adapter适配总结:
综上所述,Spinner数据的适配有三种方式:
- 自定义Adapter,这种方法是一个比较好的方法,但是构造Adapter比较繁琐;
- ArrayAdapter,这种方式可以快速的适配文本列表,即使布局中可以添加其他组件,但是其他组件只能固定写死,而唯一可以设置不同值的就只有文本了;
- SimpleAdapter,这是Spinner数据适配的亮点,可以任意配置各种类型的数据,以及数据和组件之间一一对应的关系。(最方便)
最后,为了完善这篇文章,Spinner固有的属性还是要提一下的。
Spinner有哪些属性呢?从源码中可以获取。

Spinner弹出的列表本质上是一个PopupWindow或者AlertDialog。
- dropDownSelector
点击列表上某数据时显示的颜色。(这个设置之后发现没效果)
- dropDownWidth
设置popupWindow的宽度,如这个布局
<Spinner
android:id="@+id/spinner"
android:layout_marginTop="10dp"
android:layout_width="200dp"
android:dropDownWidth="150dp"
android:layout_height="wrap_content"
android:entries="@array/spinner_values" />
Spinner宽度本身是200dp,默认情况下,popupWindow的宽度和Spinner宽度一样,如果将popupWindow的宽度设置成150dp,那么效果图如下:

- popupBackground
设置popupWindow的背景色

- spinnerMode
设置Spinner模式,有两种模式:dropdown和dialog,默认是dropdown模式。
dropdown模式下,Spinner弹出的列表本质上是PopupWindow;
dialog模式下,Spinner弹出的列表本质上是AlertDialog;
- prompt
设置标题提示,目测在dropdown不生效,在dialog模式下,AlertDialog顶端会有一个提示,如图:

[本章完...]
网友评论