如何使用初始文本“select一个”来制作Android微调器
在Android中,我想使用最初(当用户还没有做出select时)显示文本“select一个”的微调。 当用户点击微调器时,显示项目列表并且用户select其中一个选项。 用户做出select后,所选项目将显示在微调器中而不是“select一个”。
我有以下代码来创build一个微调:
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);
使用此代码,最初显示项目“一”。 我可以只添加一个新的项目“select一个”的项目,但“select一个”也将显示在下拉列表作为第一项,这是不是我想要的。
我该如何解决这个问题?
这是覆盖Spinner
视图的一般解决scheme。 它重写setAdapter()
以将初始位置设置为-1,并代理提供的SpinnerAdapter
以显示小于0的位置的提示string。
这已经在Android 1.5到4.2testing过了,但买家要小心! 由于此解决scheme依靠reflection来调用专用AdapterView.setNextSelectedPositionInt()
和AdapterView.setSelectedPositionInt()
,因此无法保证在将来的操作系统更新中能够正常工作。 这似乎是可能的,但这决不是保证。
通常我不会宽恕这样的事情,但这个问题已经被问了足够多的时间,似乎是一个足够合理的请求,我想我会张贴我的解决scheme。
/** * 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); } } }
你可以做的是装饰你的SpinnerAdapter
的一个呈现'select选项…'查看最初为微调显示没有select。
这里是一个testingAndroid 2.3和4.0(它在兼容性库中没有使用任何东西,因此它应该是罚款一段时间)的一个工作示例因为它是一个装饰,应该很容易翻新现有的代码,它也可以很好地工作与CursorLoader
也。 (当然,在包装的cursorAdapter
上交换游标…)
有一个Android的错误,使这个有点难以重新使用的意见。 (所以你必须使用setTag
或其他东西来确保你的convertView
是正确的。) 微调不支持多种视图types
代码注释:2个构造函数
这使您可以使用标准提示或将自己的“没有select”作为第一行,或两者都定义,或者没有。 (注:有些主题显示的是一个Spinner的DropDown,而不是对话框,下拉菜单通常不会显示提示)
你定义一个布局来看起来像一个提示,例如,灰色的…
使用标准提示(注意没有select任何东西):
或者提示和dynamic的东西(也可能没有提示):
在上面的例子中使用
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
<?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
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... eg "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. } }
我结束了使用一个Button
。 虽然Button
不是Spinner
,但行为很容易定制。
首先像往常一样创build适配器:
String[] items = new String[] {"One", "Two", "Three"}; ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, items);
请注意,我使用simple_spinner_dropdown_item
作为布局ID。 这将有助于在创build警报对话框时创build更好的外观。
在我的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(); }
而就是这样!
我知道这个问题有很多答案,但我find了最简单,最简单的方法。
此解决scheme独立于API级别,它将适用于所有API级别 。
想法是将微调器的最后一项设置为默认的一个..
spinner.setSelection(lastIndex);//index starts from 0.so if spinner has 5 item the lastIndex is 4
最后指数的项目应该是你的旋转头像“select国家”
并且,在填充微调器时减less一个项目的计数。// //计数从1开始到总项目。
@Override public int getCount() { // don't display last item. It is used as hint. int count = super.getCount(); return count > 0 ? count - 1 : count; }
所以你的代码stream将是这样的
List<String> objects = new ArrayList<String>(); objects.add("India"); objects.add("Pakistan"); objects.add("China"); // add hint as last item objects.add("Select Country"); 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());
HintAdapter.java
public class HintAdapter extends ArrayAdapter<Objects> { public HintAdapter(Context theContext, List<Object> objects, int theLayoutResId) { super(theContext, theLayoutResId, 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; } }
微调标题 微调项目
编辑:错字
首先,您可能对Spinner
类的prompt
属性感兴趣。 看到下面的图片,“select一个星球”是可以用android:prompt=""
在XML中设置的android:prompt=""
。
我打算build议子类Spinner
,在那里你可以在内部维护两个适配器。 一个适配器具有“select一个”选项,另一个适配器(具有实际选项),然后在显示select对话框之前使用OnClickListener
切换适配器。 然而,在尝试实现这个想法之后,我已经得出结论,您无法接收窗口小部件本身的OnClick
事件。
你可以在不同的视图中包装微调,拦截视图上的点击,然后告诉你的CustomSpinner
切换适配器,但似乎是一个可怕的黑客。
你真的需要展示“select一个”吗?
此代码已经过testing,并在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);
我发现这个解决scheme:
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) { } });
只需更改数组[0]与“select一个”,然后在onItemSelected,将其重命名为“一”。
不是一个优雅的解决scheme,但它的工作原理:D
Spinner没有默认的API来设置提示。 要添加它,我们需要一个小的解决方法,而不是安全reflection的实现
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());
适配器来源:
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; } }
原始来源
对于微调,我也遇到了同样的问题,只有一个空的select,我find了一个更好的解决scheme。 看看这个简单的代码。
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);
这里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); } }
XML文件:
<Spinner android:id="@+id/locationSpinner" android:layout_width="fill_parent" android:layout_height="wrap_content" android:prompt="@string/select_location" />
活动:
private Spinner featuresSelection; private ArrayAdapter<CharSequence> featuresAdapter; private List<CharSequence> featuresList;
的onCreate:
featuresList = new ArrayList<CharSequence>(); featuresAdapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, featuresList); featuresAdapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item); featuresSelection = ((Spinner) yourActivity.this .findViewById(R.id.locationSpinner)); featuresSelection.setAdapter(featuresAdapter); featuresSelection.setOnItemSelectedListener( new MyOnItemSelectedListener());
某些function(以编程方式向适配器添加内容)>
featuresAdapter.add("some string");
现在你有一个空的微调,你可以编写代码来打开对话框,如果是空的。 或者他们可以按回去。 但是你也可以在运行时用函数或其他列表填充它。
很多的答案在这里,但我很惊讶没有人build议一个简单的解决scheme:放在一个TextView顶部的微调。 在隐藏TextView的TextView上设置一个点击监听器来显示Spinner,并调用spinner.performClick()。
我已经尝试了如下。 采取一个button,并给它点击事件。 通过改变button的背景,它似乎是一个微调。
声明为全局variablesalertdialog和默认值..
AlertDialog d; static int default_value = 0; final Button btn = (Button) findViewById(R.id.button1); btn .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //c.show(); final CharSequence str[] = {"Android","Black Berry","Iphone"}; AlertDialog.Builder builder = new AlertDialog.Builder(TestGalleryActivity.this).setSingleChoiceItems( str, default_value,new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int position) { Toast.makeText(TestGalleryActivity.this, "" + position, Toast.LENGTH_SHORT).show(); default_value = position; btn.setText(str[position]); if(d.isShowing()) d.dismiss(); } }).setTitle("Select Any"); d = builder.create(); d.show(); } });
您可以将其更改为文本视图并使用此:
android:style="@android:style/Widget.DeviceDefault.Light.Spinner"
然后定义android:text
属性。
看一看应用程序的一个通用的解决scheme添加到列表顶部的元素。 特别是,如果您使用的是CursorAdapter,请查看扩展该定义的TracksAdapter.java ,以提供一个“setHasAllItem”方法和相关代码来pipe理列表计数,以处理顶部的额外项目。
使用自定义适配器,您可以将文本设置为“select一个”或其他您可能想要的顶级项目。
我在我的main.xml中有一个微调,它的ID是@+id/spinner1
这是我写在我的OnCreate函数:
spinner1 = (Spinner)this.findViewById(R.id.spinner1); final String[] groupes = new String[] {"A", "B", "C", "D", "E", "F", "G", "H"}; ArrayAdapter<CharSequence> featuresAdapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, new ArrayList<CharSequence>()); featuresAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner1.setAdapter(featuresAdapter); for (String s : groupes) featuresAdapter.add(s); spinner1.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) { // Here go your instructions when the user chose something Toast.makeText(getBaseContext(), groupes[position], 0).show(); } public void onNothingSelected(AdapterView<?> arg0) { } });
它不需要任何类的实现。
我认为最简单的方法是在索引0上创build一个虚拟项目,select一个,然后保存,也许检查select不是0。
所以这是我最后一个“全押”的例子
在activity_my_form.xml中
<Button android:id="@+id/btnSpinnerPlanets" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="left|center_vertical" android:singleLine="true" android:text="@string/selectAPlanet" android:textSize="10sp" android:background="@android:drawable/btn_dropdown"> </Button>
在strings.xml中
<string name="selectAPlanet">Select planet…</string> <string-array name="planets__entries"> <item>The Sun with a name very long so long long long long longThe Sun with a name very long so long long long long longThe Sun with a name very long so long long long long long</item> <item>Mercury</item> <item>Venus</item> <item>Earth</item> <item>Mars</item> <item>Jupiter</item> <item>Saturn</item> <item>Uranus</item> <item>Neptune</item> </string-array>
在MyFormActivity.java中
public class MyFormActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { ((Button) findViewById(R.id.btnSpinnerPlanets)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { final String[] items = view.getResources().getStringArray(R.array.planets__entries); ArrayAdapter<String> adapter = new ArrayAdapter<String>(MyFormActivity.this, android.R.layout.simple_spinner_dropdown_item, items); new AlertDialog.Builder(MyFormActivity.this).setTitle("the prompt").setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ((Button) findViewById(R.id.btnSpinnerPlanets)).setText(items[which]); dialog.dismiss(); } }).create().show(); } }); } }
最后,我获得了一个字体大小可configuration没有第一个项目可选button微调! 感谢HRJ
我发现了很多很好的解决scheme。 大多数工作是通过将一个项目添加到适配器的结尾,而不要在下拉列表中显示最后一个项目。 对我来说,最大的问题是微调下拉列表将从列表的底部开始。 因此,用户在第一次触摸微调器后,会看到最后一个项目,而不是第一个项目(如果有多个项目要显示)。
所以我把提示项目放在列表的开头。 并将第一项隐藏在下拉列表中。
private void loadSpinner(){ HintArrayAdapter hintAdapter = new HintArrayAdapter<String>(context, 0); hintAdapter.add("Hint to be displayed"); hintAdapter.add("Item 1"); hintAdapter.add("Item 2"); . . hintAdapter.add("Item 30"); spinner1.setAdapter(hintAdapter); //spinner1.setSelection(0); //display hint. Actually you can ignore it, because the default is already 0 //spinner1.setSelection(0, false); //use this if don't want to onItemClick called for the hint spinner1.setOnItemSelectedListener(yourListener); } private class HintArrayAdapter<T> extends ArrayAdapter<T> { Context mContext; public HintArrayAdapter(Context context, int resource) { super(context, resource); this.mContext = context } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(android.R.layout.simple_spinner_item, parent, false); TextView texview = (TextView) view.findViewById(android.R.id.text1); if(position == 0) { texview.setText(""); texview.setHint(getItem(position).toString()); //"Hint to be displayed" } else { texview.setText(getItem(position).toString()); } return view; } @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); View view; if(position == 0){ view = inflater.inflate(R.layout.spinner_hint_list_item_layout, parent, false); // Hide first row } else { view = inflater.inflate(android.R.layout.simple_spinner_dropdown_item, parent, false); TextView texview = (TextView) view.findViewById(android.R.id.text1); texview.setText(getItem(position).toString()); } return view; } }
set the below layout in @Override getDropDownView() when position is 0, to hide the first hint row.
R.layout.spinner_hint_list_item_layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" > </LinearLayout>
When extending SpinnerAdapter
, you override two View
-producing methods, getView(int, View, ViewGroup)
and getDropDownView(int, View, ViewGroup)
. The first one supplies the View
inserted into the Spinner
itself; the second supplies the View
in the drop-down list (as the name suggests). You can override the getView(...)
so that, until an item has been selected, it displays a TextView
containing a prompt; then, when you detect an item has been selected, you change it to display a TextView
corresponding to that.
public class PromptingAdapter extends SpinnerAdapter { //... various code ... private boolean selectionmade = false; //call this method from the OnItemSelectedListener for your Spinner public setSelectionState(boolean b) { selectionmade = b; } @Override public View getView(int position, View recycle, ViewGroup container) { if(selectionmade) { //your existing code to supply a View for the Spinner //you could even put "return getDropDownView(position, recycle, container);" } else { View output; if(recycle instanceof TextView) { output = recycle; } else { output = new TextView(); //and layout stuff } output.setText(R.string.please_select_one); //put a string "please_select_one" in res/values/strings.xml return output; } } //... }
For those using Xamarin, here is the C# equivalent to aaronvargas's answer above.
using Android.Content; using Android.Database; using Android.Views; using Android.Widget; using Java.Lang; namespace MyNamespace.Droid { public class NothingSelectedSpinnerAdapter : BaseAdapter, ISpinnerAdapter, IListAdapter { protected static readonly int EXTRA = 1; protected ISpinnerAdapter adapter; protected Context context; protected int nothingSelectedLayout; protected int nothingSelectedDropdownLayout; protected LayoutInflater layoutInflater; public NothingSelectedSpinnerAdapter(ISpinnerAdapter spinnerAdapter, int nothingSelectedLayout, Context context) : this(spinnerAdapter, nothingSelectedLayout, -1, context) { } public NothingSelectedSpinnerAdapter(ISpinnerAdapter spinnerAdapter, int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context) { this.adapter = spinnerAdapter; this.context = context; this.nothingSelectedLayout = nothingSelectedLayout; this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout; layoutInflater = LayoutInflater.From(context); } protected View GetNothingSelectedView(ViewGroup parent) { return layoutInflater.Inflate(nothingSelectedLayout, parent, false); } protected View GetNothingSelectedDropdownView(ViewGroup parent) { return layoutInflater.Inflate(nothingSelectedDropdownLayout, parent, false); } public override Object GetItem(int position) { return position == 0 ? null : adapter.GetItem(position - EXTRA); } public override long GetItemId(int position) { return position >= EXTRA ? adapter.GetItemId(position - EXTRA) : position - EXTRA; } public override 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); } // Could re-use the convertView if possible. return this.adapter.GetView(position - EXTRA, null, parent); } public override int Count { get { int count = this.adapter.Count; return count == 0 ? 0 : count + EXTRA; } } public override 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); } public override int GetItemViewType(int position) { return 0; } public override int ViewTypeCount => 1; public override bool HasStableIds => this.adapter.HasStableIds; public override bool IsEmpty => this.adapter.IsEmpty; public override void RegisterDataSetObserver(DataSetObserver observer) { adapter.RegisterDataSetObserver(observer); } public override void UnregisterDataSetObserver(DataSetObserver observer) { adapter.UnregisterDataSetObserver(observer); } public override bool AreAllItemsEnabled() { return false; } public override bool IsEnabled(int position) { return position > 0; } } }
I'd just use a RadioGroup with RadioButtons if you only have three choices, you can make them all unchecked at first.
None of the previously submitted answers really worked the way I wanted to solve this issue. To me the ideal solution would provide the “Select One” (or whatever initial text) when the spinner is first displayed. When the user taps the spinner, the initial text shouldn't be a part of the drop down that is displayed.
To further complicate my particular situation, my spinner data is coming form a cursor that is loaded via the LoaderManager callbacks.
After considerable experimentation I came up with the following solution:
public class MyFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{ private static final String SPINNER_INIT_VALUE = "Select A Widget"; private Spinner mSpinner; private int mSpinnerPosition; private boolean mSpinnerDropDownShowing = false; private View mSpinnerDropDown; private MyCursorAdapter mCursorAdapter; ... @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ ... mCursorAdapter = new MyCursorAdapter(getActivity()); mSpinner = (Spinner) rootView.findViewById(R.id.theSpinner); mSpinner.setOnTouchListener(mSpinnerTouchListener); mSpinner.setAdapter(mCursorAdapter); ... } //Capture the touch events to toggle the spinner's dropdown visibility private OnTouchListener mSpinnerTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if(mSpinnerDropDown != null && mSpinnerDropDownShowing == false){ mSpinnerDropDownShowing = true; mSpinnerDropDown.setVisibility(View.VISIBLE); } return false; } }; //Capture the click event on the spinner drop down items protected OnClickListener spinnerItemClick = new OnClickListener(){ @Override public void onClick(View view) { String widget = ((TextView) view.findViewById(android.R.id.text1)).getText().toString(); if(!widget.equals(SPINNER_INIT_VALUE)){ if(mCursorAdapter != null){ Cursor cursor = mCursorAdapter.getCursor(); if(cursor.moveToFirst()){ while(!cursor.isAfterLast()){ if(widget.equals(cursor.getString(WidgetQuery.WIDGET_NAME))){ ... //Set the spinner to the correct item mSpinnerPosition = cursor.getPosition() + 1; mSpinner.setSelection(mSpinnerPosition); break; } cursor.moveToNext(); } } } } //Hide the drop down. Not the most elegent solution but it is the only way I could hide/dismiss the drop down mSpinnerDropDown = view.getRootView(); mSpinnerDropDownShowing = false; mSpinnerDropDown.setVisibility(View.GONE); } }; private class MyCursorAdapter extends CursorAdapter { private final int DISPLACEMENT = 1; private final int DEFAULT_ITEM_ID = Integer.MAX_VALUE; private Activity mActivity; public MyCursorAdapter(Activity activity) { super(activity, null, false); mActivity = activity; } //When loading the regular views, inject the defualt item @Override public View getView(int position, View convertView, ViewGroup parent) { if(position == 0){ if(convertView == null){ convertView = mActivity.getLayoutInflater().inflate(R.layout.list_item_widget, parent, false); } return getDefaultItem(convertView); } return super.getView(position - DISPLACEMENT, convertView, parent); } //When loading the drop down views, set the onClickListener for each view @Override public View getDropDownView(int position, View convertView, ViewGroup parent){ View view = super.getDropDownView(position, convertView, parent); view.setOnClickListener(spinnerItemClick); return view; } //The special default item that is being injected private View getDefaultItem(View convertView){ TextView text = (TextView) convertView.findViewById(android.R.id.text1); text.setText(SPINNER_INIT_VALUE); return convertView; } @Override public long getItemId(int position) { if (position == 0) { return DEFAULT_ITEM_ID; } return super.getItemId(position - DISPLACEMENT); } @Override public boolean isEnabled(int position) { return position == 0 ? true : super.isEnabled(position - DISPLACEMENT); } @Override public int getViewTypeCount() { return super.getViewTypeCount() + DISPLACEMENT; } @Override public int getItemViewType(int position) { if (position == 0) { return super.getViewTypeCount(); } return super.getItemViewType(position - DISPLACEMENT); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return mActivity.getLayoutInflater().inflate(R.layout.list_item_widget, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor){ if(cursor.isAfterLast()){ return; } TextView text = (TextView) view.findViewById(android.R.id.text1); String WidgetName = cursor.getString(WidgetQuery.WIDGET_NAME); text.setText(WidgetName); } } }
I handle this by using a button instead of a Spinner. I have the sample project up on GitHub.
In the project, i'm displaying both the Spinner and button to show that they indeed look identical. Except the button you can set the initial text to whatever you want.
Here's what the activity looks like:
package com.stevebergamini.spinnerbutton; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Spinner; public class MainActivity extends Activity { Spinner spinner1; Button button1; AlertDialog ad; String[] countries; int selected = -1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); spinner1 = (Spinner) findViewById(R.id.spinner1); button1 = (Button) findViewById(R.id.button1); countries = getResources().getStringArray(R.array.country_names); // You can also use an adapter for the allert dialog if you'd like // ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, countries); ad = new AlertDialog.Builder(MainActivity.this).setSingleChoiceItems(countries, selected, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { button1.setText(countries[which]); selected = which; ad.dismiss(); }}).setTitle(R.string.select_country).create(); button1.setOnClickListener( new OnClickListener(){ @Override public void onClick(View v) { ad.getListView().setSelection(selected); ad.show(); }}); } }
NOTE: Yes, I realize that this is dependent on the applied Theme and the look will be slightly different if using Theme.Holo. However, if you're using one of the legacy themes such as Theme.Black, you're good to go.
if you are facing this issue when your items are populates from database-cursor ,
the simplest solution that I found in this SO answer:
use UNION in your cursor adapter query and add the additional item with id= -1 to the query result, without really adding it to the DB:
就像是:
db.rawQuery("SELECT iWorkerId as _id, nvLastName as name FROM Worker w UNION SELECT -1 as _id , '' as name",null);
if the item selected is -1, then it's the default value. Otherwise it's a record from the table.
Seems a banal solution but I usually put simply a TextView in the front of the spinner. The whole Xml looks like this. (hey guys, don't shoot me, I know that some of you don't like this kind of marriage):
<FrameLayout android:id="@+id/selectTypesLinear" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <Spinner android:id="@+id/spinnerExercises" android:layout_width="match_parent" android:layout_height="match_parent" android:entries="@array/exercise_spinner_entries" android:prompt="@string/exercise_spinner_prompt" /> <TextView android:id="@+id/spinnerSelectText" android:layout_width="match_parent" android:layout_height="match_parent" android:text="Hey! Select this guy!" android:gravity="center" android:background="#FF000000" /> </FrameLayout>
Then I hide the TextView when an Item was selected. Obviously the background color of the TextView should be the same as the Spinner. Works on Android 4.0. Don't know on older versions.
是。 Because the Spinner calls setOnItemSelectedListener at the beginning, the hiding of the textview could be a little bit tricky, but can be done this way:
Boolean controlTouched; exerciseSpinner.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { controlTouched = true; // I touched it but but not yet selected an Item. return false; } }); exerciseSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { if (controlTouched) { // Are you sure that I touched it with my fingers and not someone else ? spinnerSelText.setVisibility(View.GONE); } } @Override public void onNothingSelected(AdapterView<?> arg0) { } });
for me it worked something like this. has the improvement that only changes the text in SOME options, not in all.
First i take the names of the spinner and create the arrayadapter with a customize view, but it doesn't matter now, the key is override the getView, and inside change the values u need to change. In my case was only the first one, the rest i leave the original
public void rellenarSpinnerCompeticiones(){ spinnerArrayCompeticiones = new ArrayList<String>(); for(Competicion c: ((Controlador)getApplication()).getCompeticiones()){ spinnerArrayCompeticiones.add(c.getNombre()); } //ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this,R.layout.spinner_item_competicion,spinnerArrayCompeticiones); ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this, R.layout.spinner_item_competicion, spinnerArrayCompeticiones){ @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); final View v = vi.inflate(R.layout.spinner_item_competicion, null); final TextView t = (TextView)v.findViewById(R.id.tvCompeticion); if(spinnerCompeticion.getSelectedItemPosition()>0){ t.setText(spinnerArrayCompeticiones.get(spinnerCompeticion.getSelectedItemPosition())); }else{ t.setText("Competiciones"); } return v; } }; spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerCompeticion.setAdapter(spinnerArrayAdapter); }
This is my way:
List<String> list = new ArrayList<String>(); list.add("string1"); list.add("string2"); list.add("string3"); list.add("[Select one]"); final int listsize = list.size() - 1; ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, list) { @Override public int getCount() { return(listsize); // Truncate the list } }; dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mySpinner.setAdapter(dataAdapter); mySpinner.setSelection(listsize); // Hidden item to appear in the spinner
here's a simple one
private boolean isFirst = true; private void setAdapter() { final ArrayList<String> spinnerArray = new ArrayList<String>(); spinnerArray.add("Select your option"); spinnerArray.add("Option 1"); spinnerArray.add("Option 2"); spin.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) { TextView tv = (TextView)selectedItemView; String res = tv.getText().toString().trim(); if (res.equals("Option 1")) { //do Something } else if (res.equals("Option 2")) { //do Something else } } @Override public void onNothingSelected(AdapterView<?> parentView) { } }); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.my_spinner_style,spinnerArray) { public View getView(int position, View convertView, ViewGroup parent) { View v = super.getView(position, convertView, parent); int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics()); ((TextView) v).setTypeface(tf2); ((TextView) v).getLayoutParams().height = height; ((TextView) v).setGravity(Gravity.CENTER); ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_SP, 19); ((TextView) v).setTextColor(Color.WHITE); return v; } public View getDropDownView(int position, View convertView, ViewGroup parent) { if (isFirst) { isFirst = false; spinnerArray.remove(0); } View v = super.getDropDownView(position, convertView, parent); ((TextView) v).setTextColor(Color.argb(255, 70, 70, 70)); ((TextView) v).setTypeface(tf2); ((TextView) v).setGravity(Gravity.CENTER); return v; } }; spin.setAdapter(adapter); }
Refer to one of the above answers: https://stackoverflow.com/a/23005376/1312796
I added my code to fix a little bug. That where no data retrieved..How to show the prompt text..!
Here is my Trick…It works fine with me. !
Try to put your spinner in a Relative_layoutand align a Textview with your spinner and play with the visibility of the Textview (SHOW/HIDE) whenever the adapter of the spinner loaded or empty..Like this:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="20dp" android:background="#ededed" android:orientation="vertical"> <TextView android:id="@+id/txt_prompt_from" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:textColor="@color/gray" android:textSize="16sp" android:layout_alignStart="@+id/sp_from" android:text="From" android:visibility="gone"/> <Spinner android:id="@+id/sp_from" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" />
这里是代码:
txt__from = (TextView) rootView.findViewById(R.id.txt_prompt_from);
call this method after and before spinner adapter loaded and empty.
setPromptTextViewVisibility (); //True or fales public void setPromptTextViewVisibility (boolean visible ) { if (visible) { txt_from.setVisibility(View.VISIBLE); } else { txt_from.setVisibility(View.INVISIBLE); } }
to make it easy just like this:
String[] listAges = getResources().getStringArray(R.array.ages); // Creating adapter for spinner ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, listAges); // Drop down layout style - list view with radio button dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // attaching data adapter to spinner spinner_age.getBackground().setColorFilter(ContextCompat.getColor(this, R.color.spinner_icon), PorterDuff.Mode.SRC_ATOP); spinner_age.setAdapter(dataAdapter); spinner_age.setSelection(0); spinner_age.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { String item = parent.getItemAtPosition(position).toString(); if(position > 0){ // get spinner value Toast.makeText(parent.getContext(), "Age..." + item, Toast.LENGTH_SHORT).show(); }else{ // show toast select gender Toast.makeText(parent.getContext(), "none" + item, Toast.LENGTH_SHORT).show(); } } @Override public void onNothingSelected(AdapterView<?> parent) { } });
- 如何确定从后台堆栈中恢复的碎片
- DownloadManager.Request.setNotificationVisibility失败,并显示jSecurityException:无效值:2
- 在android中定义button事件的最佳做法
- 如何把jar放在Eclipse的classpath中?
- 在Eclipse中进行Android NDKdebugging – 如何停止只在使用本机代码时才会出现的segfaults / SIGILL
- 如何在android中的datepicker对话框中设置最大date?
- 任何方式来发现您的networking上的Android设备?
- 如何在打开应用程序时dynamicselect要启动的活动
- 使用GridLayoutManager和RecyclerView更改列数