如何制作带有初始文本“选择一个”的 Android Spinner?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/867518/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make an Android Spinner with initial text "Select One"?
提问by Pieter Kuijpers
I want to use a Spinner that initially (when the user has not made a selection yet) displays the text "Select One". When the user clicks the spinner, the list of items is displayed and the user selects one of the options. After the user has made a selection, the selected item is displayed in the Spinner instead of "Select One".
我想使用最初(当用户尚未进行选择时)显示文本“选择一个”的微调器。当用户单击微调器时,将显示项目列表并且用户选择其中一个选项。用户做出选择后,所选项目将显示在微调器中,而不是“选择一个”。
I have the following code to create a Spinner:
我有以下代码来创建 Spinner:
String[] items = new String[] {"One", "Two", "Three"};
Spinner spinner = (Spinner) findViewById(R.id.mySpinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
With this code, initially the item "One" is displayed. I could just add a new item "Select One" to the items, but then "Select One" would also be displayed in the dropdown list as first item, which is not what I want.
使用此代码,最初显示项目“一”。我可以在项目中添加一个新项目“选择一个”,但是“选择一个”也将作为第一项显示在下拉列表中,这不是我想要的。
How can I fix this problem?
我该如何解决这个问题?
采纳答案by emmby
Here's a general solution that overrides the Spinner
view. It overrides setAdapter()
to set the initial position to -1, and proxies the supplied SpinnerAdapter
to display the prompt string for position less than 0.
这是覆盖Spinner
视图的通用解决方案。它覆盖setAdapter()
以将初始位置设置为 -1,并代理提供SpinnerAdapter
的显示位置小于 0 的提示字符串。
This has been tested on Android 1.5 through 4.2, but buyer beware! Because this solution relies on reflection to call the private AdapterView.setNextSelectedPositionInt()
and AdapterView.setSelectedPositionInt()
, it's not guaranteed to work in future OS updates. It seems likely that it will, but it is by no means guaranteed.
这已经在 Android 1.5 到 4.2 上进行了测试,但买家要注意!由于此解决方案依赖反射来调用私有AdapterView.setNextSelectedPositionInt()
和AdapterView.setSelectedPositionInt()
,因此不能保证在未来的操作系统更新中工作。看起来很可能会,但绝不是保证。
Normally I wouldn't condone something like this, but this question has been asked enough times and it seems like a reasonable enough request that I thought I would post my solution.
通常我不会宽恕这样的事情,但是这个问题已经被问到足够多的次数了,这似乎是一个足够合理的要求,我想我会发布我的解决方案。
/**
* 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);
}
@Override
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));
}
/**
* Intercepts getView() to display the prompt if position < 0
*/
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);
}
}
}
回答by aaronvargas
What you can do is decorate your SpinnerAdapter
with one that presents a 'Select Option...' View initially for the Spinner to display with nothing selected.
您可以做的是SpinnerAdapter
用一个显示“选择选项...”的视图来装饰您的初始视图,以便 Spinner 显示而没有选择任何内容。
Here is a working example tested for Android 2.3, and 4.0 (it uses nothing in the compatibility library, so it should be fine for awhile) Since it's a decorator, it should be easy to retrofit existing code and it works fine with CursorLoader
s also. (Swap cursor on the wrapped cursorAdapter
of course...)
这是一个针对 Android 2.3 和 4.0 测试的工作示例(它在兼容性库中不使用任何内容,因此应该可以使用一段时间)由于它是一个装饰器,因此应该很容易改造现有代码并且它也适用于CursorLoader
s。(cursorAdapter
当然,在包裹上交换光标......)
There is an Android bug that makes this a little tougher to re-use views. (So you have to use the setTag
or something else to ensure your convertView
is correct.) Spinner does not support multiple view types
有一个 Android 错误使重用视图变得有点困难。(因此您必须使用setTag
或其他东西来确保您convertView
的正确。)Spinner 不支持多种视图类型
Code notes: 2 constructors
代码注释:2个构造函数
This allows you to use a standard prompt or define your own 'nothing selected' as the first row, or both, or none. (Note: Some themes show a DropDown for a Spinner instead of a dialog. The Dropdown doesn't normally show the prompt)
这允许您使用标准提示或将您自己的“未选择的内容”定义为第一行,或两者兼而有之,或无。(注意:某些主题显示 Spinner 的 DropDown 而不是对话框。Dropdown 通常不显示提示)
You define a layout to 'look' like a prompt, for example, grayed out...
您将布局定义为“看起来”像一个提示,例如,灰显...
Using a standard prompt (notice that nothing is selected):
使用标准提示(注意没有选择任何内容):
Or with a prompt and something dynamic (could have had no prompt also):
或者有一个提示和一些动态的东西(也可能没有提示):
Usage in above example
上面例子中的用法
Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.planets_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setPrompt("Select your favorite Planet!");
spinner.setAdapter(
new NothingSelectedSpinnerAdapter(
adapter,
R.layout.contact_spinner_row_nothing_selected,
// R.layout.contact_spinner_nothing_selected_dropdown, // Optional
this));
contact_spinner_row_nothing_selected.xml
contact_spinner_row_nothing_selected.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerItemStyle"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:textSize="18sp"
android:textColor="#808080"
android:text="[Select a Planet...]" />
NothingSelectedSpinnerAdapter.java
NothingSelectedSpinnerAdapter.java
import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;
/**
* Decorator Adapter to allow a Spinner to show a 'Nothing Selected...' initially
* displayed instead of the first choice in the Adapter.
*/
public class NothingSelectedSpinnerAdapter implements SpinnerAdapter, ListAdapter {
protected static final int EXTRA = 1;
protected SpinnerAdapter adapter;
protected Context context;
protected int nothingSelectedLayout;
protected int nothingSelectedDropdownLayout;
protected LayoutInflater layoutInflater;
/**
* Use this constructor to have NO 'Select One...' item, instead use
* the standard prompt or nothing at all.
* @param spinnerAdapter wrapped Adapter.
* @param nothingSelectedLayout layout for nothing selected, perhaps
* you want text grayed out like a prompt...
* @param context
*/
public NothingSelectedSpinnerAdapter(
SpinnerAdapter spinnerAdapter,
int nothingSelectedLayout, Context context) {
this(spinnerAdapter, nothingSelectedLayout, -1, context);
}
/**
* Use this constructor to Define your 'Select One...' layout as the first
* row in the returned choices.
* If you do this, you probably don't want a prompt on your spinner or it'll
* have two 'Select' rows.
* @param spinnerAdapter wrapped Adapter. Should probably return false for isEnabled(0)
* @param nothingSelectedLayout layout for nothing selected, perhaps you want
* text grayed out like a prompt...
* @param nothingSelectedDropdownLayout layout for your 'Select an Item...' in
* the dropdown.
* @param context
*/
public NothingSelectedSpinnerAdapter(SpinnerAdapter spinnerAdapter,
int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context) {
this.adapter = spinnerAdapter;
this.context = context;
this.nothingSelectedLayout = nothingSelectedLayout;
this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout;
layoutInflater = LayoutInflater.from(context);
}
@Override
public final View getView(int position, View convertView, ViewGroup parent) {
// This provides the View for the Selected Item in the Spinner, not
// the dropdown (unless dropdownView is not set).
if (position == 0) {
return getNothingSelectedView(parent);
}
return adapter.getView(position - EXTRA, null, parent); // Could re-use
// the convertView if possible.
}
/**
* View to show in Spinner with Nothing Selected
* Override this to do something dynamic... e.g. "37 Options Found"
* @param parent
* @return
*/
protected View getNothingSelectedView(ViewGroup parent) {
return layoutInflater.inflate(nothingSelectedLayout, parent, false);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// Android BUG! http://code.google.com/p/android/issues/detail?id=17128 -
// Spinner does not support multiple view types
if (position == 0) {
return nothingSelectedDropdownLayout == -1 ?
new View(context) :
getNothingSelectedDropdownView(parent);
}
// Could re-use the convertView if possible, use setTag...
return adapter.getDropDownView(position - EXTRA, null, parent);
}
/**
* Override this to do something dynamic... For example, "Pick your favorite
* of these 37".
* @param parent
* @return
*/
protected View getNothingSelectedDropdownView(ViewGroup parent) {
return layoutInflater.inflate(nothingSelectedDropdownLayout, parent, false);
}
@Override
public int getCount() {
int count = adapter.getCount();
return count == 0 ? 0 : count + EXTRA;
}
@Override
public Object getItem(int position) {
return position == 0 ? null : adapter.getItem(position - EXTRA);
}
@Override
public int getItemViewType(int position) {
return 0;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int position) {
return position >= EXTRA ? adapter.getItemId(position - EXTRA) : position - EXTRA;
}
@Override
public boolean hasStableIds() {
return adapter.hasStableIds();
}
@Override
public boolean isEmpty() {
return adapter.isEmpty();
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
adapter.registerDataSetObserver(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
adapter.unregisterDataSetObserver(observer);
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
return position != 0; // Don't allow the 'nothing selected'
// item to be picked.
}
}
回答by HRJ
I ended up using a Button
instead. While a Button
is not a Spinner
, the behavior is easy to customize.
我最终使用了 aButton
代替。虽然 aButton
不是 a Spinner
,但行为很容易定制。
First create the Adapter as usual:
首先像往常一样创建适配器:
String[] items = new String[] {"One", "Two", "Three"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_dropdown_item, items);
Note that I am using the simple_spinner_dropdown_item
as the layout id. This will help create a better look when creating the alert dialog.
请注意,我使用的simple_spinner_dropdown_item
作为布局 ID。这将有助于在创建警报对话框时创建更好的外观。
In the onClick handler for my Button I have:
在我的 Button 的 onClick 处理程序中,我有:
public void onClick(View w) {
new AlertDialog.Builder(this)
.setTitle("the prompt")
.setAdapter(adapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO: user specific action
dialog.dismiss();
}
}).create().show();
}
And that's it!
就是这样!
回答by Casey
First, you might be interested in the prompt
attribute of the Spinner
class. See the picture below, "Choose a Planet" is the prompt that can be set in the XML with android:prompt=""
.
首先,您可能prompt
对Spinner
类的属性感兴趣。如下图,“Choose a Planet”是可以在XML中设置的提示 android:prompt=""
。
I was going to suggest subclassing Spinner
, where you could maintain two adapters internally. One adapter that has the "Select One" option, and the other realadapter (with the actual options), then using the OnClickListener
to switch the adapters before the choices dialog is shown. However, after trying implement that idea I've come to the conclusion you cannot receive OnClick
events for the widget itself.
我打算建议 subclassing Spinner
,你可以在内部维护两个适配器。一个具有“选择一个”选项的适配器和另一个真实的适配器(具有实际选项),然后OnClickListener
在显示选择对话框之前使用切换适配器。但是,在尝试实施该想法后,我得出的结论是您无法接收OnClick
小部件本身的事件。
You could wrap the spinner in a different view, intercept the clicks on the view, and then tell your CustomSpinner
to switch the adapter, but seems like an awful hack.
您可以将微调器包装在不同的视图中,拦截视图上的点击,然后告诉您CustomSpinner
切换适配器,但这似乎是一个可怕的黑客。
Do you really need to show "Select One"?
你真的需要显示“选择一个”吗?
回答by Manos
This code has been tested and works on Android 4.4
此代码已经过测试,可在 Android 4.4 上运行
Spinner spinner = (Spinner) activity.findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity, android.R.layout.simple_spinner_dropdown_item) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
if (position == getCount()) {
((TextView)v.findViewById(android.R.id.text1)).setText("");
((TextView)v.findViewById(android.R.id.text1)).setHint(getItem(getCount())); //"Hint to be displayed"
}
return v;
}
@Override
public int getCount() {
return super.getCount()-1; // you dont display last item. It is used as hint.
}
};
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
adapter.add("Daily");
adapter.add("Two Days");
adapter.add("Weekly");
adapter.add("Monthly");
adapter.add("Three Months");
adapter.add("HINT_TEXT_HERE"); //This is the text that will be displayed as hint.
spinner.setAdapter(adapter);
spinner.setSelection(adapter.getCount()); //set the hint the default selection so it appears on launch.
spinner.setOnItemSelectedListener(this);
回答by Marco da Gualdo
I found this solution:
我找到了这个解决方案:
String[] items = new String[] {"Select One", "Two", "Three"};
Spinner spinner = (Spinner) findViewById(R.id.mySpinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) {
items[0] = "One";
selectedItem = items[position];
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
Just change the array[0] with "Select One" and then in the onItemSelected, rename it to "One".
只需使用“Select One”更改数组[0],然后在onItemSelected中将其重命名为“One”。
Not a classy solution, but it works :D
不是一个经典的解决方案,但它有效:D
回答by Yakiv Mospan
There is no default API to set hint on Spinner. To add it we need a small workaround with out that not safety reflection implementation
没有在 Spinner 上设置提示的默认 API。要添加它,我们需要一个小的解决方法,而不是安全反射实现
List<Object> objects = new ArrayList<Object>();
objects.add(firstItem);
objects.add(secondItem);
// add hint as last item
objects.add(hint);
HintAdapter adapter = new HintAdapter(context, objects, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Spinner spinnerFilmType = (Spinner) findViewById(R.id.spinner);
spinner.setAdapter(adapter);
// show hint
spinner.setSelection(adapter.getCount());
Adapter source:
适配器来源:
public class HintAdapter
extends ArrayAdapter<Objects> {
public HintAdapter(Context theContext, List<Object> objects) {
super(theContext, android.R.id.text1, android.R.id.text1, objects);
}
public HintAdapter(Context theContext, List<Object> objects, int theLayoutResId) {
super(theContext, theLayoutResId, android.R.id.text1, objects);
}
@Override
public int getCount() {
// don't display last item. It is used as hint.
int count = super.getCount();
return count > 0 ? count - 1 : count;
}
}
回答by mjancola
Lots of answers here but I'm surprised no one suggested a simple solution: Place a TextView on top of the Spinner. Set a click listener on the TextView which hides the TextView shows the Spinner, and calls spinner.performClick().
这里有很多答案,但我很惊讶没有人提出一个简单的解决方案:在 Spinner 顶部放置一个 TextView。在隐藏 TextView 的 TextView 上设置一个单击侦听器,显示 Spinner,并调用 spinner.performClick()。
回答by Rajasekhar
I got the same problem for spinner, with an empty selection, and I found a better solution. Have a look at this simple code.
我在微调器上遇到了同样的问题,选择了空,我找到了更好的解决方案。看看这个简单的代码。
Spinner lCreditOrDebit = (Spinner)lCardPayView.findViewById(R.id.CARD_TYPE);
spinneradapter lAdapter =
new spinneradapter(
BillPayScreen.this,
ndroid.R.layout.simple_spinner_item,getResources().getStringArray(R.array.creditordebit));
lAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
lCreditOrDebit.setAdapter(lAdapter);
Here spinneradapter is a small customization for arrayadapter. It looks like this:
这里的 spinneradapter 是对 arrayadapter 的一个小定制。它看起来像这样:
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
public class spinneradapter extends ArrayAdapter<String>{
private Context m_cContext;
public spinneradapter(Context context,int textViewResourceId, String[] objects) {
super(context, textViewResourceId, objects);
this.m_cContext = context;
}
boolean firsttime = true;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(firsttime){
firsttime = false;
//Just return some empty view
return new ImageView(m_cContext);
}
//Let the array adapter take care of it this time.
return super.getView(position, convertView, parent);
}
}
回答by Christian Vielma
You can change it to a Text View and use this:
您可以将其更改为文本视图并使用:
android:style="@android:style/Widget.DeviceDefault.Light.Spinner"
and then define the android:text
property.
然后定义android:text
属性。