首页 > 代码库 > Android 开发 Tip 6 -- Spinner
Android 开发 Tip 6 -- Spinner
转载请注明出处:http://blog.csdn.net/crazy1235/article/details/70903974
设置Spinner 文字居中
默认情况下,Spinner控件的效果是这样的:
想让文字居中显示怎么办???
在布局文件中设置
android:gravity="center"
也不起作用!!
源码走读
先来看 Spinner 的构造函数
public Spinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes, int mode,
Theme popupTheme) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.Spinner, defStyleAttr, defStyleRes);
// 省略代码
if (mode == MODE_THEME) {
mode = a.getInt(R.styleable.Spinner_spinnerMode, MODE_DIALOG);
}
// 判断弹出模式 dialog or dropdown
switch (mode) {
case MODE_DIALOG: {
mPopup = new DialogPopup(); // DialogPopup
mPopup.setPromptText(a.getString(R.styleable.Spinner_prompt));
break;
}
case MODE_DROPDOWN: {
final DropdownPopup popup = new DropdownPopup(
mPopupContext, attrs, defStyleAttr, defStyleRes); // DropdownPopup
// 省略代码
break;
}
}
// ...
a.recycle();
// 设置adapter
if (mTempAdapter != null) {
setAdapter(mTempAdapter);
mTempAdapter = null;
}
}
当mTempAdapter 不为空时,调用了setAdapter() 设置适配器!
但是我们如果在xml中设置了entries属性,并没有设置adapter
android:entries="@array/date_spinner_items"
上图的列表是怎么出来的呢?!
来看父类~~
AbsSpinner
public AbsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initAbsSpinner();
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.AbsSpinner, defStyleAttr, defStyleRes);
final CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries);
if (entries != null) {
final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
context, R.layout.simple_spinner_item, entries);
adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
setAdapter(adapter);
}
a.recycle();
}
从构造器函数中看出,当entries属性不为空时,调用了 setAdapter() 函数!
注意这里,用到的是 ArrayAdapter 适配器 。还有两个重要的布局文件:
simple_spinner_item
simple_spinner_dropdown_item
子类Spinner重写了setAdapter 函数
@Override
public void setAdapter(SpinnerAdapter adapter) {
// ...
super.setAdapter(adapter);
// ...
mPopup.setAdapter(new DropDownAdapter(adapter, popupContext.getTheme()));
}
mPopup 是一个接口对象,里面封装了 设置适配器、显示列表、关闭列表等操作!
不管是Spinner是 dialog 形式还是 dropdown 形式,都实现了该接口!
private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener
private class DropdownPopup extends ListPopupWindow implements SpinnerPopup
OK,现在来看 DropDownAdapter
private static class DropDownAdapter implements ListAdapter, SpinnerAdapter {
private SpinnerAdapter mAdapter;
private ListAdapter mListAdapter;
public DropDownAdapter(@Nullable SpinnerAdapter adapter,
@Nullable Resources.Theme dropDownTheme) {
mAdapter = adapter; // 注意这里!!!
// 省略代吗
}
public int getCount() {
return mAdapter == null ? 0 : mAdapter.getCount();
}
public Object getItem(int position) {
return mAdapter == null ? null : mAdapter.getItem(position);
}
public long getItemId(int position) {
return mAdapter == null ? -1 : mAdapter.getItemId(position);
}
public View getView(int position, View convertView, ViewGroup parent) {
return getDropDownView(position, convertView, parent);
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return (mAdapter == null) ? null : mAdapter.getDropDownView(position, convertView, parent);
}
// 省略代吗
}
弹出来的列表每个item的View渲染通过 getDropDownView 函数!
而mAdapter是通过构造函数传进来的!
再回到这里:
mPopup.setAdapter(new DropDownAdapter(adapter, popupContext.getTheme()));
这时adapter是Spinner父类AbsSpinner构造函数中new出来的 ArrayAdapter
然后看 ArrayAdapter 类中的 getDropDownView 函数
@Override
public View getDropDownView(int position, @Nullable View convertView,
@NonNull ViewGroup parent) {
final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
}
private @NonNull View createViewFromResource(@NonNull LayoutInflater inflater, int position,
@Nullable View convertView, @NonNull ViewGroup parent, int resource) {
final View view;
final TextView text;
if (convertView == null) {
view = inflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
text = (TextView) view;
} else {
text = (TextView) view.findViewById(mFieldId);
if (text == null) {
throw new RuntimeException("Failed to find view with ID "
+ mContext.getResources().getResourceName(mFieldId)
+ " in item layout");
}
}
} catch (ClassCastException e) {
Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"ArrayAdapter requires the resource ID to be a TextView", e);
}
// 省略代码!!!
return view;
}
源码看到这里就能发现,通过映射 mDropDownResource这个布局文件,来得到Spinner列表的item布局!
而,恰恰在AbsSpinner 的构造函数中设置了这一布局文件
adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
所以,现在想要改变Spinner的文字居中显示!则需要设置相应的adapter!
OK。现在就来看这个布局文件
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerDropDownItemStyle"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="?android:attr/dropdownListPreferredItemHeight"
android:ellipsize="marquee"/>
可以看出item布局文件只是一个 CheckedTextView
我们现在想要把这个列表的文字居中显示!
跟踪style文件发现设置的gravity属性是 center_vertical
<item name="android:gravity">center_vertical</item>
此时尝试把这个布局文件拿出来重写。
simple_spinner_dropdown_item.xml
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerDropDownItemStyle"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:ellipsize="marquee"
android:gravity="center" // !!!
android:maxLines="1" />
然后在Activity中对Spinner对象设置适配器!
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, dates);
arrayAdapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(arrayAdapter);
运行之后,发现并没有居中!
现在,回过头来看看 mPopup.show() 显示列表的函数
加入我们选择的是dropdown模式!
DropdownPopup.show()
public void show(int textDirection, int textAlignment) {
final boolean wasShowing = isShowing();
computeContentWidth();
setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
super.show();
final ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setTextDirection(textDirection);
listView.setTextAlignment(textAlignment);
setSelection(Spinner.this.getSelectedItemPosition());
// ... 省略代码
}
可以看出,弹出来的视图就是一个ListView
针对listview设置了 direction 和 textAligment 两个属性!!!
Android 开发 Tip 6 -- Spinner