选项为空的默认选择项的下拉列表控件

10

我正在尝试创建一个带有默认空选项的下拉列表,但它显示了下拉列表中的第一项。如果我在下拉列表的选项源字符串中添加null值,那么打开下拉列表后就会显示空行。我该怎么做?这是我正在使用的代码:

  String[] ch = {"Session1", "Session2", "Session3"};
  Spinner sp = (Spinner)findViewById(R.id.spinner1);
  TextView sess_name = findViewById(R.id.sessname);
  ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,ch);
  sp.setAdapter(adapter);

  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

  sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener({
      @Override
      public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
          int index = arg0.getSelectedItemPosition();
          sess_name.setText(ch[index]);

          Toast.makeText(getBaseContext(), "You have selected item : " + ch[index], Toast.LENGTH_SHORT).show();
      }

你是否担心空白行被允许作为选择(很容易修复),还是它根本就显示出来了(不确定如何修复)? - Barak
5个回答

15

Barak的解决方案存在问题。当您选择第一个项目时,Spinner不会调用OnItemSelectedListener的onItemSelected()方法并刷新空内容,因为前一个位置和选择位置都为0。

首先在您的字符串数组开头放置一个空字符串:

String[] test = {" ", "one", "two", "three"};

第二个构建适配器,不要修改getView(),修改getDropDownView()。将空白视图的高度设置为1px。

public class MyArrayAdapter extends ArrayAdapter<String> {

    private static final int ITEM_HEIGHT = ViewGroup.LayoutParams.WRAP_CONTENT;

    private int textViewResourceId;


    public MyArrayAdapter(Context context,
                          int textViewResourceId,
                          String[] objects) {
        super(context, textViewResourceId, objects);
        this.textViewResourceId = textViewResourceId;
    }

    @Override
    public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
        TextView textView;

        if (convertView == null) {
            textView = (TextView) LayoutInflater.from(getContext())
                   .inflate(textViewResourceId, parent, false);
        } else {
            textView = (TextView) convertView;
        }

        textView.setText(getItem(position));
        if (position == 0) {
            ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
            layoutParams.height = 1;
            textView.setLayoutParams(layoutParams);
        } else {
            ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
            layoutParams.height = ITEM_HEIGHT;
            textView.setLayoutParams(layoutParams);
        }

        return textView;
    }
}

+1,谢谢,但我仍然看到一些东西或某些行(可能是“1像素效果”)。 - fikr4n
运行良好,这是我最终使用的。但我不会硬编码96,而是使用LayoutParams.WRAP_CONTENT。 - Tony BenBrahim

6

我有点晚了,但是这就是我解决这个问题的方法。
如果用户取消选择初始项目,则微调器将保留初始的空状态。一旦选择了初始项目,它就像“正常”一样工作。
适用于2.3.3+,我没有在2.2及以下版本上测试

首先,创建一个适配器类...

public class EmptyFirstItemAdapter extends ArrayAdapter<String>{
    //Track the removal of the empty item
    private boolean emptyRemoved = false;

    /** Adjust the constructor(s) to fit your purposes. */
    public EmptyFirstitemAdapter(Context context, List<String> objects) {
        super(context, android.R.layout.simple_spinner_item, objects);
        setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    }

    @Override
    public int getCount() {
        //Adjust the count based on the removal of the empty item
        if(emptyRemoved){
            return super.getCount();            
        }
        return super.getCount()-1;            
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        if(!emptyRemoved){
            // Remove the empty item the first time the dropdown is displayed.
            emptyRemoved = true;
            // Set to false to prevent auto-selecting the first item after removal.
            setNotifyOnChange(false);
            remove(getItem(0));
            // Set it back to true for future changes.
            setNotifyOnChange(true);
        }
        return super.getDropDownView(position, convertView, parent);
    }

    @Override
    public long getItemId(int position) {
        // Adjust the id after removal to keep the id's the same as pre-removal.
        if(emptyRemoved){
            return position +1;
        }
        return position;
    }

}

这是我在strings.xml中使用的字符串数组

<string-array name="my_items">
    <item></item>
    <item>Item 1</item>
    <item>Item 2</item>
</string-array>

接下来,将一个 OnItemSelectedListener 添加到你的 Spinner 中...
mSpinner = (Spinner) mRootView.findViewById(R.id.spinner);
String[] opts = getResources().getStringArray(R.array.my_items);
//DO NOT set the entries in XML OR use an array directly, the adapter will get an immutable List.
List<String> vals = new ArrayList<String>(Arrays.asList(opts));
final EmptyFirstitemAdapter adapter = new EmptyFirstitemAdapter(getActivity(), vals);
mSpinner.setAdapter(adapter);
mSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    //Track that we have updated after removing the empty item
    private boolean mInitialized = false;
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if(!mInitialized && position == 0 && id == 1){
            // User selected the 1st item after the 'empty' item was initially removed,
            // update the data set to compensate for the removed item.
            mInitialized = true;
            adapter.notifyDataSetChanged();
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        // Nothing to do
    }
});

这可能不是一个“完美”的解决方案,但我希望它能帮助到某些人。


当我选择第一个项目时,它不会更新。只有在我选择另一个项目然后再选择第一个项目时才会更新。 - androidu
你是否按照我的示例在适配器上实现了getItemId()方法? - Tom Bollwitt
是的,我复制了你的所有代码示例,这肯定与+1偏移有关,因为只有在我第一次单击1项时才会表现出这种方式。 - androidu
你正在运行哪个版本的安卓系统?是在模拟器上还是实体设备上运行? 我刚刚构建了一个只有下拉菜单的活动示例应用程序。我在2.3.3、4.0.3和4.1.2模拟器以及Nexus 4(4.2.2)上运行它,没有任何问题。 - Tom Bollwitt
那么我一定做错了什么,但什么呢?我在 Galaxy Nexus 设备上进行了测试。 - androidu

2
经过一番思考,我相信我已经想到了一种方法来实现你的目标。它涉及创建一个自定义适配器并设置/维护一个标志来确定是否选择了下拉列表中的项目。使用这种方法,您不需要创建/使用虚假数据(空字符串)。
基本上,适配器的getView方法设置了关闭的下拉列表的文本。因此,如果您覆盖它并在其中设置条件,则可以在启动时具有空白字段,并在进行选择后将其显示在关闭的下拉列表框中。唯一的问题是您需要记住在需要在关闭的下拉列表中查看值时设置标志。
我创建了一个小的示例程序(下面是代码)。
请注意,我仅添加了我需要的单个构造函数以用于我的示例。您可以实现所有标准ArrayAdapter构造函数或仅实现您需要的构造函数。
SpinnerTest.java
public class SpinnerTestActivity extends Activity {
    private String[] planets = { "Mercury", "Venus", "Earth", "Mars",
            "Jupiter", "Saturn", "Uranus", "Neptune" };
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Spinner spinner = (Spinner) findViewById(R.id.spinner);
        CustomAdapter adapter = new CustomAdapter(this,              // Use our custom adapter
                android.R.layout.simple_spinner_item, planets);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
            @Override
            public void onItemSelected(AdapterView<?> parent, View view,
                    int pos, long id) {
                CustomAdapter.flag = true;                       // Set adapter flag that something
                has been chosen
            }
        });
    }
}

CustomAdapter.java

public class CustomAdapter extends ArrayAdapter {
    private Context context;
    private int textViewResourceId;
    private String[] objects;
    public static boolean flag = false;
    public CustomAdapter(Context context, int textViewResourceId,
            String[] objects) {
        super(context, textViewResourceId, objects);
        this.context = context;
        this.textViewResourceId = textViewResourceId;
        this.objects = objects;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null)
            convertView = View.inflate(context, textViewResourceId, null);
        if (flag != false) {
            TextView tv = (TextView) convertView;
            tv.setText(objects[position]);
        }
        return convertView;
    }
}

是的,我想从下拉列表中删除空白行。我该怎么做? - user1484667
已更新我的答案并提供了解决方案。 - Barak

0
这是我使用的代码。它以一种通用的方式正确处理空(空)选择。只要类T正确实现了toString()来显示在Spinner中显示的文本,以及equals(),使得可以通过引用而不是位置索引选择项目,它就可以与任何模型类T一起使用。
package com.10xdev.android.components;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * A spinner where no selection is possible, and other enhancements.
 * requires model class to properly implement Object.equals, with semantic comparaison (such as id comparaison)
 * and a proper toString(), whose result will be displayed in the spinner
 *
 * @author tony.benbrahim
 */

public class EnhancedSpinner<T> extends Spinner {
    private final EnhanceArraySpinnerAdapter<T> spinnerAdapter;
    private final List<T> items = new ArrayList<>();
    private T selected = null;

    public EnhancedSpinner(final Context context, final AttributeSet attributeSet) {
        super(context, attributeSet);
        spinnerAdapter = new EnhanceArraySpinnerAdapter<>(context, items);
        setAdapter(spinnerAdapter);
    }

    /**
     * sets the items to be displayed
     *
     * @param items
     */
    public void setItems(final List<T> items) {
        this.items.clear();
        //very iffy, but works because of type erasure
        this.items.add((T) "");
        this.items.addAll(items);
        spinnerAdapter.notifyDataSetChanged();
        updateSelected();
    }

    /**
     * set the selected item. this may be called before or after setting items
     *
     * @param item the item to select, or null to clear the selection
     */
    public void setSelected(final T item) {
        this.selected = item;
        updateSelected();
    }

    /**
     * gets the selected item, or null if no item is selected
     *
     * @return
     */
    @Override
    public T getSelectedItem() {
        return getSelectedItemPosition() != 0 ? (T) super.getSelectedItem() : null;
    }

    /**
     * set the error message for the select
     *
     * @param errorMessage
     */
    public void setError(final String errorMessage) {
        final TextView errorText = (TextView) getSelectedView();
        errorText.setError("error");
        errorText.setTextColor(Color.RED);
        errorText.setText(errorMessage);
    }

    private void updateSelected() {
        if (selected == null) {
            setSelection(0);
        } else {
            for (int i = 1; i < items.size(); ++i) {
                if (selected.equals(items.get(i))) {
                    setSelection(i);
                    break;
                }
            }
        }
    }

    private class EnhanceArraySpinnerAdapter<T> extends ArrayAdapter<T> {
        private final LayoutInflater inflater;

        public EnhanceArraySpinnerAdapter(final Context context, final List<T> objects) {
            super(context, android.R.layout.simple_spinner_item, objects);
            inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        @Override
        public View getDropDownView(final int position, final View convertView, final ViewGroup parent) {
            final TextView textView = convertView != null ? (TextView) convertView
                    : (TextView) inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
            final Object item = getItem(position);
            textView.setText(item.toString());
            final ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
            layoutParams.height = position == 0 ? 1 : LayoutParams.WRAP_CONTENT;
            textView.setLayoutParams(layoutParams);
            return textView;
        }
    }
}

-1

你必须将下拉框的第一个元素设置为空,或者用一个表示未选择任何内容的string来进行设置,例如:

String[] ch= {"","Session1", "Session2", "Session3"};

或者

String[] ch= {"Nothing selected", "Session1", "Session2", "Session3"};

希望能够帮到你


1
他已经拥有那个功能,但不希望在下拉列表中显示出来。 - Barak
我确实知道这个问题,但我想从下拉菜单中删除空行,有什么解决办法吗? - user1484667

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接