74

我试图让 Spinner 加载没有选定的值。一旦用户选择了一个值,它就会将它们带到另一个页面。

这被证明是一个问题,因为目前,页面只是在用户获得选择之前立即加载。

我的微调器类的设置方式与谷歌相同:http: //developer.android.com/resources/tutorials/views/hello-spinner.html

所以基本上,是否有可能加载一个没有选择任何内容的微调器,因为目前它加载了我的字符串数组中的第一项。

4

11 回答 11

89

是否有可能有一个加载时未选择任何内容的微调器

只有在没有数据的情况下。如果您在 1+ 项目中SpinnerAdapterSpinner将始终有一个选择。

Spinners并非设计为命令小部件。用户不会期望 a 中的选择Spinner来启动活动。请考虑使用其他东西,例如 a ListViewor GridView,而不是 a Spinner


编辑

顺便说一句,我忘了提——你总是可以在你的适配器中添加一个额外的条目来表示“无选择”,并使其成为Spinner.

于 2011-01-18T16:47:39.557 回答
31

或者,您可以覆盖微调器适配器,并在 getView 方法中为位置 0 提供一个空视图,并在该getDropDownView方法中提供一个高度为 0dp 的视图。

这样,您将在首次加载微调器时显示一个初始文本,例如“选择一个选项...”,但这不是用户选择的选项(从技术上讲,它是,但因为高度为 0 ,他们看不到它)。

于 2012-02-14T23:22:24.347 回答
29

这是Paul Bourdeaux 想法getView()的完整实现,即在位置 0 处返回一个特殊的初始视图(或空视图) 。

它对我有用,而且相对简单。您可能会考虑这种方法,特别是如果您已经为 Spinner 定制了适配器。(在我的例子中,我使用自定义适配器来轻松自定义项目的布局,每个项目都有几个 TextView。)

适配器将是这样的:

public class MySpinnerAdapter extends ArrayAdapter<MyModel> {

    public MySpinnerAdapter(Context context, List<MyModel> items) {
        super(context, R.layout.my_spinner_row, items);
    }

    @Override
    public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
        if (position == 0) {
            return initialSelection(true);
        }
        return getCustomView(position, convertView, parent);
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, @NonNull ViewGroup parent) {
        if (position == 0) {
            return initialSelection(false);
        }
        return getCustomView(position, convertView, parent);
    }


    @Override
    public int getCount() {
        return super.getCount() + 1; // Adjust for initial selection item
    }

    private View initialSelection(boolean dropdown) {
        // Just an example using a simple TextView. Create whatever default view 
        // to suit your needs, inflating a separate layout if it's cleaner.
        TextView view = new TextView(getContext());
        view.setText(R.string.select_one);
        int spacing = getContext().getResources().getDimensionPixelSize(R.dimen.spacing_smaller);
        view.setPadding(0, spacing, 0, spacing);

        if (dropdown) { // Hidden when the dropdown is opened
            view.setHeight(0);
        }

        return view;
    }

    private View getCustomView(int position, View convertView, ViewGroup parent) {
        // Distinguish "real" spinner items (that can be reused) from initial selection item
        View row = convertView != null && !(convertView instanceof TextView) 
        ? convertView :
        LayoutInflater.from(getContext()).inflate(R.layout.my_spinner_row, parent, false);

        position = position - 1; // Adjust for initial selection item
        MyModel item = getItem(position);

        // ... Resolve views & populate with data ...

        return row;
    }

}

而已。请注意,如果您将 aOnItemSelectedListener与 Spinner 一起使用,onItemSelected()您还必须进行调整position以将默认项目考虑在内,例如:

if (position == 0) {
    return;
} else {
    position = position - 1;
}
MyModel selected = items.get(position);
于 2017-01-13T15:02:12.650 回答
5

使用这样的自定义微调器布局:

<?xml version="1.0" encoding="utf-8"?>
<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/spinnerTarget"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:textSize="14dp"         
          android:textColor="#000000"/>

在活动中:

    // populate the list
    ArrayList<String> dataList = new ArrayList<String>();
    for (int i = 0; i < 4; i++) {
        dataList.add("Item");
    }

    // set custom layout spinner_layout.xml and adapter
    Spinner spinnerObject = (Spinner) findViewById(R.id.spinnerObject);
    ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, R.drawable.spinner_layout, dataList);
    dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinnerObject.setAdapter(dataAdapter);
    spinnerObject.setOnTouchListener(new View.OnTouchListener() { 

        public boolean onTouch(View v, MotionEvent event) {
            // to set value of first selection, because setOnItemSelectedListener will not dispatch if the user selects first element
            TextView spinnerTarget = (TextView)v.findViewById(R.id.spinnerTarget);
            spinnerTarget.setText(spinnerObject.getSelectedItem().toString());

            return false;
        }

    });
    spinnerObject.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                private boolean selectionControl = true;

                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                    // just the first time
                    if(selectionControl){

                        // find TextView in layout 
                        TextView spinnerTarget = (TextView)parent.findViewById(R.id.spinnerTarget);
                        // set spinner text empty
                        spinnerTarget.setText("");
                        selectionControl = false;
                    }
                    else{
                        // select object
                    }
                }

                public void onNothingSelected(AdapterView<?> parent) {

                }
            });
于 2012-07-07T06:30:22.690 回答
5

就我而言,尽管微调器中显示了大小“2”,但在完成某些选择之前什么都不会发生!

我有一个列出所有微调器值的 xml 文件 (data_sizes.xml)。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="chunks">
        <item>2</item>
        <item>4</item>
        <item>8</item>
        <item>16</item>
        <item>32</item>
    </string-array>
</resources>    

在 main.xml 文件中:微调器元素

<Spinner android:id="@+id/spinnerSize"  
android:layout_marginLeft="50px"
android:layout_width="fill_parent"                  
android:drawSelectorOnTop="true"
android:layout_marginTop="5dip"
android:prompt="@string/SelectSize"
android:layout_marginRight="30px"
android:layout_height="35px" /> 

然后在我的java代码中,我添加了:

在我的活动中:宣言

Spinner spinnerSize;
ArrayAdapter adapter;

在公共 void 函数中 - initControls():定义

spinnerSize = (Spinner)findViewById(R.id.spinnerSize);
adapter = ArrayAdapter.createFromResource(this, R.array.chunks, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerSize.setAdapter(adapter);
spinnerSize.setOnItemSelectedListener(new MyOnItemSelectedListener());

我的微调器监听器:

/* 微调监听器 */

class MyOnItemSelectedListener implements OnItemSelectedListener {

    public void onItemSelected(AdapterView<?> parent,
        View view, int pos, long id) {
        chunkSize = new Integer(parent.getItemAtPosition(pos).toString()).intValue();
    }
    public void onNothingSelected(AdapterView<?> parent) {
      // Dummy
    }
}
于 2011-01-18T17:49:56.260 回答
3

合并这个:

private long previousItemId = 0;

@Override
public long getItemId(int position) {
    long nextItemId = random.nextInt(Integer.MAX_VALUE);
    while(previousItemId == nextItemId) {
        nextItemId = random.nextInt(Integer.MAX_VALUE);
    }
    previousItemId = nextItemId;
    return nextItemId;
}

有了这个答案

public class SpinnerInteractionListener
        implements AdapterView.OnItemSelectedListener, View.OnTouchListener {

    private AdapterView.OnItemSelectedListener onItemSelectedListener;

    public SpinnerInteractionListener(AdapterView.OnItemSelectedListener selectedListener) {
        this.onItemSelectedListener = selectedListener;
    }

    boolean userSelect = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        userSelect = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        if(userSelect) {
            onItemSelectedListener.onItemSelected(parent, view, pos, id);
            userSelect = false;
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        if(userSelect) {
            onItemSelectedListener.onNothingSelected(parent);
            userSelect = false;
        }
    }
}
于 2016-06-06T16:34:52.570 回答
2

您可以将数组中的第一个单元格设为空({"","some","some",...}),如果位置为 0,则不执行任何操作;

public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    if(position>0) {
        label.setText(MainActivity.questions[position - 1]);
    }
}
  • 如果你用 xml 文件填充数组,你可以让第一项为空
于 2015-11-08T16:13:07.667 回答
2

我的解决方案,如果你有一个 TextView 作为微调器的每一行:

    // TODO: add a fake item as the last one of "items"
    final ArrayAdapter<String> adapter=new ArrayAdapter<String>(..,..,items) 
      {
      @Override
      public View getDropDownView(final int position,final View convertView,final ViewGroup parent)
        {
        final View dropDownView=super.getDropDownView(position,convertView,parent);
        ((TextView)dropDownView.findViewById(android.R.id.text1)).setHeight(position==getCount()-1?0:getDimensionFromAttribute(..,R.attr.dropdownListPreferredItemHeight));
        dropDownView.getLayoutParams().height=position==getCount()-1?0:LayoutParams.MATCH_PARENT;
        return dropDownView;
        }
      }

    ...
    spinner.setAdapter(adapter);
    _actionModeSpinnerView.setSelection(dataAdapter.getCount()-1,false);



  public static int getDimensionFromAttribute(final Context context,final int attr)
    {
    final TypedValue typedValue=new TypedValue();
    if(context.getTheme().resolveAttribute(attr,typedValue,true))
      return TypedValue.complexToDimensionPixelSize(typedValue.data,context.getResources().getDisplayMetrics());
    return 0;
    }
于 2015-12-04T23:45:16.570 回答
1

我假设您想要一个Spinner带有第一个空的不可见项目(这是一个奇怪的功能,Spinner如果不选择一个项目就无法显示列表)。您应该添加一个包含数据的类:

data class YourData(val id: Int, val name: String?)

这是适配器。

class YourAdapter(
    context: Context,
    private val textViewResourceId: Int,
    private var items: ArrayList<YourData>
) : ArrayAdapter<YourData>(context, textViewResourceId, items) {

    private var inflater: LayoutInflater = context.getSystemService(
        Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

    override fun getCount(): Int = items.size + 1

    override fun getItem(position: Int): YourData? =
        if (position == 0) YourData(0, "") else items[position - 1]

    override fun getItemId(position: Int): Long = position.toLong()

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View =
        if (position == 0) {
            getFirstTextView(convertView)
        } else {
            getTextView(convertView, parent, position - 1)
        }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View =
        getView(position, convertView, parent)

    private fun getFirstTextView(convertView: View?): View {
        // Just simple TextView as initial selection.
        var textView: TextView? = convertView as? TextView
        val holder: FirstViewHolder
        if (textView?.tag !is FirstViewHolder) {
            textView = TextView(context) // inflater.inflate(R.layout.your_text, parent, false) as TextView
            textView.height = 0 // Hide first item.
            holder = FirstViewHolder()
            holder.textView = textView
            textView.tag = holder
        }
        return textView
    }

    private fun getTextView(
        convertView: View?,
        parent: ViewGroup,
        position: Int
    ): TextView {
        var textView: TextView? = convertView as? TextView
        val holder: ViewHolder
        if (textView?.tag is ViewHolder) {
            holder = textView.tag as ViewHolder
        } else {
            textView = inflater.inflate(textViewResourceId, parent, false) as TextView
            holder = ViewHolder()
            holder.textView = textView
            textView.tag = holder
        }
        holder.textView.text = items[position].name

        return textView
    }

    private class FirstViewHolder {
        lateinit var textView: TextView
    }

    private class ViewHolder {
        lateinit var textView: TextView
    }
}

去创造:

YourAdapter(context!!, R.layout.text_item, ArrayList())

要添加项目:

private fun fill(items: List<YourData>, adapter: YourAdapter) {
    adapter.run {
        clear()
        addAll(items)
        notifyDataSetChanged()
    }
}

当您Spinner使用该fill()命令将项目加载到您的时,您应该知道,索引也会增加。因此,如果您希望选择第 3 项,您现在应该选择第 4 项:spinner?.setSelection(index + 1)

于 2018-11-20T07:49:17.540 回答
0

说明 - 我们可以在我们的 arrayList 的第一个参数中发送一个虚拟值到微调适配器。然后将arrayList中我们的虚拟值的位置(0或arrayList之间的任何一个)发送到SpinnerAdapter构造函数。

public void addDataToList(LstQRTrackingSummary qrTrackingSummary) {
        SpinnerMenu mSpinnerMenuzero = new SpinnerMenu();
        mSpinnerMenuzero.setName("Select Action");
        spinner_list.add(mSpinnerMenuzero);

        if (qrTrackingSummary.getOQRTrackingButtons().getBtnToDispatch()) {
            SpinnerMenu mSpinnerMenu = new SpinnerMenu();
            mSpinnerMenu.setName("Dispatch");
            mSpinnerMenu.setmDrawable(ResourcesCompat.getDrawable(context.getResources(),R.drawable.ic_dispatch,null));
            spinner_list.add(mSpinnerMenu);
        }

        if (qrTrackingSummary.getOQRTrackingButtons().getBtnForwardToDispatch()) {
            SpinnerMenu mSpinnerMenu = new SpinnerMenu();
            mSpinnerMenu.setName("Forward To Dispatch");
            mSpinnerMenu.setmDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_forward_to_dispatch, null));
            spinner_list.add(mSpinnerMenu);
        }
  }

然后在微调适配器类中以与我相同的方式隐藏它并完成它。

public class SpinnerAdapter extends ArrayAdapter<SpinnerMenu> {

private Context context;
private ArrayList<SpinnerMenu> menuList;
private LayoutInflater inflater;
private SpinnerMenu spinnerMenu;
public Integer positionToHide ;

public SpinnerAdapter(Context context, Integer positionToHide,ArrayList<SpinnerMenu> menuList) {
    super(context, 0, menuList);
    this.context = context;
    this.positionToHide = positionToHide ;
    inflater = LayoutInflater.from(context);
    this.menuList = menuList;
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

@Override
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

private View getCustomView(int position, View convertView , ViewGroup parent) {
    View view = null ;
    if (view == null) {
        view = inflater.inflate(R.layout.spinner_baseview, parent, false);
    }

    ImageView spin_image = view.findViewById(R.id.spinner_image);
    TextView spin_text = view.findViewById(R.id.spinner_text);
    spinnerMenu = menuList.get(position);

    if (position == positionToHide) {
        TextView mytext = new TextView(context);
        mytext.setVisibility(View.GONE);
        mytext.setHeight(0);
        view = mytext;
        view.setVisibility(View.GONE);
    } else {
        spin_image.setImageDrawable(spinnerMenu.getmDrawable());
        spin_text.setText(spinnerMenu.getName());
    }
    return view;
}

}

于 2021-03-25T11:59:45.953 回答
0

基于@Jonik 的回答,我创建了 ArrayAdapter 的全功能扩展

class SpinnerArrayAdapter<T> : ArrayAdapter<T> {

    private val selectText : String
    private val resource: Int
    private val fieldId : Int
    private val inflater : LayoutInflater

    constructor(context: Context?, resource: Int, objects: Array<T>, selectText : String? = null) : super(context, resource, objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = 0
        this.inflater = LayoutInflater.from(context)
    }
    constructor(context: Context?, resource : Int, objects: List<T>, selectText : String? = null) : super(context, resource, objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = 0
        this.inflater = LayoutInflater.from(context)
    }
    constructor(context: Context?, resource: Int, textViewResourceId: Int, objects: Array<T>, selectText : String? = null) : super(context, resource, textViewResourceId, objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = textViewResourceId
        this.inflater = LayoutInflater.from(context)
    }
    constructor(context: Context?, resource : Int, textViewResourceId: Int, objects: List<T>, selectText : String? = null) : super(context, resource, textViewResourceId,  objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = textViewResourceId
        this.inflater = LayoutInflater.from(context)
    }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View {
        if(position == 0) {
            return initialSelection(true)
        }

        return createViewFromResource(inflater, position -1, convertView, parent, resource)
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        if(position == 0) {
            return initialSelection(false)
        }
        return createViewFromResource(inflater, position -1, convertView, parent, resource)
    }

    override fun getCount(): Int {
        return super.getCount() + 1 // adjust for initial selection
    }

    private fun initialSelection(inDropDown: Boolean) : View {
        // Just simple TextView as initial selection.
        val view = TextView(context)
        view.setText(selectText)
        view.setPadding(8, 0, 8, 0)

        if(inDropDown) {
            // Hide when dropdown is open
            view.height = 0
        }
        return view
    }

    private fun createViewFromResource(inflater: LayoutInflater, position: Int,
                                       @Nullable convertView: View?, parent: ViewGroup?, resource: Int): View {
        val view: View
        val text: TextView?

        if (convertView == null || (convertView is TextView)) {
            view = inflater.inflate(resource, parent, false)
        } else {
            view = convertView
        }

        try {
            if (fieldId === 0) {
                //  If no custom field is assigned, assume the whole resource is a TextView
                text = view as TextView
            } else {
                //  Otherwise, find the TextView field within the layout
                text = view.findViewById(fieldId)

                if (text == null) {
                    throw RuntimeException("Failed to find view with ID "
                            + context.getResources().getResourceName(fieldId)
                            + " in item layout")
                }
            }
        } catch (e: ClassCastException) {
            Log.e("ArrayAdapter", "You must supply a resource ID for a TextView")
            throw IllegalStateException(
                    "ArrayAdapter requires the resource ID to be a TextView", e)
        }

        val item = getItem(position)
        if (item is CharSequence) {
            text.text = item
        } else {
            text.text = item!!.toString()
        }

        return view
    }
于 2018-08-10T08:34:25.550 回答