如何制作带有初始文本“选择一个”的 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:28:29  来源:igfitidea点击:

How to make an Android Spinner with initial text "Select One"?

androidandroid-spinner

提问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 Spinnerview. It overrides setAdapter()to set the initial position to -1, and proxies the supplied SpinnerAdapterto 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 SpinnerAdapterwith 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 CursorLoaders also. (Swap cursor on the wrapped cursorAdapterof course...)

这是一个针对 Android 2.3 和 4.0 测试的工作示例(它在兼容性库中不使用任何内容,因此应该可以使用一段时间)由于它是一个装饰器,因此应该很容易改造现有代码并且它也适用于CursorLoaders。(cursorAdapter当然,在包裹上交换光标......)

There is an Android bug that makes this a little tougher to re-use views. (So you have to use the setTagor something else to ensure your convertViewis 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...

您将布局定义为“看起来”像一个提示,例如,灰显...

Initial nothing selected

初始未选择

Using a standard prompt (notice that nothing is selected):

使用标准提示(注意没有选择任何内容):

With a standard prompt

带有标准提示

Or with a prompt and something dynamic (could have had no prompt also):

或者有一个提示和一些动态的东西(也可能没有提示):

Prompt and nothing selected row

提示并没有选择行

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 Buttoninstead. While a Buttonis 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_itemas 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 promptattribute of the Spinnerclass. See the picture below, "Choose a Planet" is the prompt that can be set in the XML with android:prompt="".

首先,您可能promptSpinner类的属性感兴趣。如下图,“Choose a Planet”是可以在XML中设置的提示 android:prompt=""

enter image description here

在此处输入图片说明

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 OnClickListenerto switch the adapters before the choices dialog is shown. However, after trying implement that idea I've come to the conclusion you cannot receive OnClickevents 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 CustomSpinnerto 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 上运行

enter image description here

在此处输入图片说明

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;
    }
}

Original source

原始来源

回答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:textproperty.

然后定义android:text属性。