如何制作一个带有初始文本“选择一个”的Android Spinner?
如何制作一个带有初始文本“选择一个”的Android Spinner?
我想使用一个Spinner,初始时(当用户还没有进行选择时)显示文本“选择一个”。当用户点击Spinner时,会显示项目列表,用户选择其中一个选项。用户选择了一个选项后,选中的项目将显示在Spinner中,而不是“选择一个”。\n我有以下代码来创建一个Spinner:\n
String[] items = new String[] {"One", "Two", "Three"}; Spinner spinner = (Spinner) findViewById(R.id.mySpinner); ArrayAdapteradapter = new ArrayAdapter (this, android.R.layout.simple_spinner_item, items); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter);
\n使用这段代码,最初显示的是“One”项。我可以将一个新项“选择一个”添加到项目中,但是“选择一个”也会作为第一项显示在下拉列表中,这不是我想要的。\n我该如何解决这个问题?
如何创建一个带有初始文本"Select One"的Android Spinner?
问题出现的原因:
我尝试使用Spinner来实现这个功能,但是发现Spinner的样式和行为都不太容易自定义。于是我决定使用一个Button来代替Spinner,并通过自定义Button的行为来达到相同的效果。
解决方法:
首先,像通常一样创建一个Adapter:
String[] items = new String[] {"One", "Two", "Three"};
ArrayAdapter
android.R.layout.simple_spinner_dropdown_item, items);
注意,我在这里使用了simple_spinner_dropdown_item作为布局id。这将在创建警告对话框时帮助创建更好的外观。
在我的Button的onClick处理程序中,我有以下代码:
public void onClick(View w) {
new AlertDialog.Builder(this)
.setTitle("the prompt")
.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 用户自定义操作
dialog.dismiss();
}
}).create().show();
}
就是这样!
我同意上面的回答。此外,与Spinner相比,按钮更容易进行样式定制。
我已经按照您建议的方法进行了实现,但是之前选择的项没有被高亮显示(意思是单选按钮应该被中间的绿点高亮显示)。在onClick()方法中,如何实现这一点呢?请帮助我,谢谢。
这个Button的布局是完美的:
如何在Android Spinner中添加初始文本“Select One”?
问题的出现原因:
- Spinner默认会自动选择列表中的第一个条目,而不是显示“Select One”这个提示文本。
解决方法:
- 创建一个自定义的Spinner类,命名为NoDefaultSpinner。
- 重写setAdapter()方法,在方法内部设置初始位置为-1,并使用代理模式显示提示文本。
- 使用反射调用私有方法setNextSelectedPositionInt()和setSelectedPositionInt()来设置初始位置为-1。
- 创建一个SpinnerAdapterProxy类,拦截getView()方法,在位置小于0时显示提示文本。
- 使用NoDefaultSpinner类替代原始的Spinner类,并在布局文件中添加android:prompt属性来显示提示文本。
以下是完整的解决方案代码:
/** * A modified Spinner that doesn't automatically select the first entry in the list. * * Shows the prompt if nothing is selected. * * Limitations: does not display prompt if the entry list is empty. */ public class NoDefaultSpinner extends Spinner { public NoDefaultSpinner(Context context) { super(context); } public NoDefaultSpinner(Context context, AttributeSet attrs) { super(context, attrs); } public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setAdapter(SpinnerAdapter orig ) { final SpinnerAdapter adapter = newProxy(orig); super.setAdapter(adapter); try { final Method m = AdapterView.class.getDeclaredMethod("setNextSelectedPositionInt", int.class); m.setAccessible(true); m.invoke(this, -1); final Method n = AdapterView.class.getDeclaredMethod("setSelectedPositionInt", int.class); n.setAccessible(true); n.invoke(this, -1); } catch (Exception e) { throw new RuntimeException(e); } } protected SpinnerAdapter newProxy(SpinnerAdapter obj) { return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), new Class[] { SpinnerAdapter.class }, new SpinnerAdapterProxy(obj)); } protected class SpinnerAdapterProxy implements InvocationHandler { protected SpinnerAdapter obj; protected Method getView; protected SpinnerAdapterProxy(SpinnerAdapter obj) { this.obj = obj; try { this.getView = SpinnerAdapter.class.getMethod("getView", int.class, View.class, ViewGroup.class); } catch (Exception e) { throw new RuntimeException(e); } } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { try { return m.equals(getView) && (Integer)(args[0]) < 0 ? getView((Integer)args[0], (View)args[1], (ViewGroup)args[2]) : m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException(e); } } protected View getView(int position, View convertView, ViewGroup parent) throws IllegalAccessException { if (position < 0) { final TextView v = (TextView) ((LayoutInflater)getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE)).inflate( android.R.layout.simple_spinner_item, parent, false); v.setText(getPrompt()); return v; } return obj.getView(position, convertView, parent); } } }
以上是一个解决Android Spinner初始文本问题的通用解决方案。通过重写Spinner类的setAdapter()方法,设置初始位置为-1,并使用代理模式显示提示文本。这个解决方案已经在Android 1.5到4.2上进行了测试,但是由于使用了反射调用私有方法,不能保证在未来的OS更新中能够正常工作。这个解决方案已经得到了广泛应用,并被认为是一个合理的解决方案。
问题的原因是Android的Spinner组件默认不支持显示初始文本,而作者想要实现一个带有初始文本"Select One"的Spinner。
为了解决这个问题,作者创建了一个名为NothingSelectedSpinnerAdapter的装饰器适配器,用于在Spinner中显示初始文本。这个适配器可以包装任何SpinnerAdapter,并在第一个位置显示一个特定的View。
作者提供了一个工作示例,包括使用示例代码和布局文件。使用这个示例,开发人员可以自定义初始文本和布局,以及使用自定义适配器。
该解决方法非常优雅,代码可以直接复制到项目中使用,并且不需要使用反射。该解决方案还可以处理Spinner的其他常见问题,如默认选择第一个选项等。
虽然该解决方案非常好,但有些用户报告了在Android 5.0及更高版本上出现问题。作者在后续的回答中更新了代码,以解决这些问题。
总之,虽然Android的Spinner组件默认不支持显示初始文本,但是使用NothingSelectedSpinnerAdapter可以轻松实现这一功能。这个解决方案非常优雅,并且适用于大多数Android版本。