如何在 Android Spinner 中隐藏一项

Posted

技术标签:

【中文标题】如何在 Android Spinner 中隐藏一项【英文标题】:How to hide one item in an Android Spinner 【发布时间】:2012-04-09 10:33:56 【问题描述】:

我正在寻找一种在 android 微调器小部件中隐藏一个项目的方法。这将允许您模拟没有选择任何项目的微调器,并确保始终为每个选择的项目调用 onItemSelected() 回调(如果隐藏的项目是“当前”项目)。通常,微调器中始终有一项不会生成回调,即当前项。

*** 上有一些关于如何禁用(灰显)项目的代码,而不是如何完全隐藏项目,就好像它们不存在一样。

经过大量实验后,我想出了一个可以在各种新旧 Android 平台上运行的有点骇人听闻的解决方案。它有一些难以察觉的轻微外观缺陷。除了“不要用微调器这样做”之外,我仍然希望听到更官方的解决方案。

这总是隐藏微调器中的第一个项目,但可以很容易地扩展为隐藏任意项目或多个项目。在微调器项目列表的开头添加一个包含空字符串的虚拟项目。您可能希望在微调器对话框打开之前将当前微调器选择设置为项目 0,这将模拟未选择的微调器。

带有 ArrayAdapter 方法覆盖的 Spinner 设置示例:

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");

// Populate the spinner using a customized ArrayAdapter that hides the first (dummy) entry
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list) 
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent)
    
        View v = null;

        // If this is the initial dummy entry, make it hidden
        if (position == 0) 
            TextView tv = new TextView(getContext());
            tv.setHeight(0);
            tv.setVisibility(View.GONE);
            v = tv;
        
        else 
            // Pass convertView as null to prevent reuse of special case views
            v = super.getDropDownView(position, null, parent);
        

        // Hide scroll bar because it appears sometimes unnecessarily, this does not prevent scrolling 
        parent.setVerticalScrollBarEnabled(false);
        return v;
    
;

dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

【问题讨论】:

您在其他互联网上发现了什么?到目前为止你尝试过什么? 对不起啦,我不知道该怎么办。 不错的解决方案!但我认为tv.setVisibility(View.GONE); 行是不必要的。至少在 Android 4.4.2/KitKit 上(在 LG/Google Nexus 4 上)将其注释掉似乎没有任何(视觉)差异。 这个问题的答案效果很好.. 这可能不是一个改进,但我在位置 0 的 textView 上使用了setTag(1),然后使用convertView.getTag() != null 来确定重用的视图是为位置 0 创建的高度为 0 的视图还是用于其他微调器项目的普通视图。这样我有时可以使用 super.getDropDownView(position, convertView, parent) 而不是总是创建一个新视图。 【参考方案1】:

要隐藏任意项目或多个项目,我认为您可以实现自己的适配器并设置要隐藏的索引(或索引数组列表)。

public class CustomAdapter extends ArrayAdapter<String> 

     private int hidingItemIndex;

     public CustomAdapter(Context context, int textViewResourceId, String[] objects, int hidingItemIndex) 
         super(context, textViewResourceId, objects);
         this.hidingItemIndex = hidingItemIndex;
     

     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) 
         View v = null;
         if (position == hidingItemIndex) 
             TextView tv = new TextView(getContext());
             tv.setVisibility(View.GONE);
             v = tv;
          else 
             v = super.getDropDownView(position, null, parent);
         
         return v;
     
 

并在创建项目列表时使用您的自定义适配器。

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");

int hidingItemIndex = 0;

CustomAdapter dataAdapter = new CustomAdapter(this, android.R.layout.simple_spinner_item, list, hidingItemIndex);

dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

(我没有测试过代码)希望有所帮助。

【讨论】:

这不是解决方案。问题的更新提供了正确的代码。 没有 tv.setHeight(0),TextView 仍然可见。 太棒了 :) 简单的解决方案 :) 非常感谢。我的数组中只有一项。我添加了它并将第一个的高度设置为 0。完美运行 像魅力一样工作..!【参考方案2】:

通过截断列表更容易隐藏列表末尾的项目。

但您必须先选择它,它才会出现在微调器中,然后检查选择是否已更改为显示的项目之一。

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

【讨论】:

我找了半个小时试图找到一个干净的方法,这是迄今为止最好的方法。只是截断列表,但该项目实际上存在。很棒。 这似乎在 Lollipop 中不起作用,[Select one] 测试最初没有出现在 Spinner 中。旧 Android 版本上的相同代码似乎确实可以满足我们的需求。 即使微调器未触及,在方向更改时微调器文本也会更改为“String3”。 @Romich 有人可以查看我的question 吗?【参考方案3】:

要隐藏微调器下拉列表中的任何项目,您需要根据所需的条件传递需要隐藏的项目的位置。 我在隐藏从下拉列表中选择的项目的用例中实现了这一点

public class CustomAdapter extends ArrayAdapter<String> 

private List<String> dates;
private int hideItemPostion;

public CustomAdapter (Context context, int resource, List<String> dates) 
    super(context, resource,dates);
    this.dates=dates;

public void setItemToHide(int itemToHide)

    this.hideItemPostion =itemToHide;

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) 
    View v = null;
    if (position == hideItemPostion) 
        TextView tv = new TextView(getContext());
        tv.setVisibility(View.GONE);
        tv.setHeight(0);
        v = tv;
        v.setVisibility(View.GONE);
    
    else
        v = super.getDropDownView(position, null, parent);
    return v;

设置适配器是这样的

final CustomAdapter dataAdapter = new CustomAdapter(this,R.layout.spinner_item,dates);
    dataAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
    spinner.setAdapter(dataAdapter);
    dataAdapter.setItemToHide(0);

在从下拉列表中选择一些项目时,位置也需要更改

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() 
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, final int i, long l) 
        dataAdapter.notifyDataSetChanged();
            mEPGDateSelector.setSelection(i);
            dataAdapter.setItemToHide(i);

             @Override
        public void onNothingSelected(AdapterView<?> adapterView) 

        
    );

【讨论】:

这个算法有效...创建新的视图对象( TextView )并将其作为返回传递给适配器,就像魅力一样。此外,您不需要编写 setItemToHide 函数,如果条件重要,您可以使用你的目的【参考方案4】:

出于兴趣,我做了一个使用“提示”作为提示的解决方案。此代码是为Xamarin.Android 编写的,但它可以在 10 分钟内完美地移植到 Java。像使用简单的ArrayAdapter 一样使用它,而无需将 0 索引或计数索引项添加到源数组。它还会在未选择任何内容时将SpinnerGeolocation.SelectedItemId 设置为-1(hint 是当前项)。

public class ArrayAdapterWithHint<T>: ArrayAdapter<T>

    protected bool HintIsSet = false;
    protected int HintResource = 0;

    public ArrayAdapterWithHint(Context context, int textViewResourceId,
                   T[] objects)
        : base(context, textViewResourceId, objects)
    
    
    public ArrayAdapterWithHint(Context context, int hintResource,
                   int textViewResourceId, T[] objects)
        : base(context, textViewResourceId, objects)
    
        HintResource = hintResource;
    
    public ArrayAdapterWithHint(Context context, int textViewResourceId,
             IList<T> objects)
        : base(context, textViewResourceId, objects)
    
    
    public ArrayAdapterWithHint(Context context, int hintResource,
                    int textViewResourceId, IList<T> objects)
        : base(context, textViewResourceId, objects)
    
        HintResource = hintResource;
    

    public override View GetDropDownView(int position, View convertView,
                ViewGroup parent)
    
        if (HintIsSet)
            return base.GetDropDownView(position + 1,
                               convertView, parent);
        return base.GetDropDownView(position, convertView, parent);
    

    public override View GetView(int position, View convertView,
                      ViewGroup parent)
    
        if (!HintIsSet && parent is Spinner && 
                    !string.IsNullOrWhiteSpace((parent as Spinner).Prompt))
        
            Insert((parent as Spinner).Prompt, 0);
            HintIsSet = true;
            (parent as Spinner).SetSelection(base.Count - 1);
        
        if (HintIsSet && position >= base.Count - 1)
        
            View hintView = base.GetView(0, convertView, parent);
            if (hintView is TextView)
                (hintView as TextView).SetTextAppearance(
                                                     Context, HintResource);
            return hintView;
        
        if (HintIsSet && position < base.Count - 1)
            return base.GetView(position + 1, convertView, parent);
        return base.GetView(position, convertView, parent);
    

    public override long GetItemId(int position)
    
        if (HintIsSet)
        
            if (position >= base.Count - 1)
                return -1;
            return position;
        
        return base.GetItemId(position);
    

    public override int Count
    
        get
        
            return base.Count > 0 && HintIsSet ? base.Count - 1 : base.Count;
        
    

【讨论】:

有人可以查看我的question 吗?【参考方案5】:

我认为将验证放在 Array List 而不是 Spinner 上会更好,因为一旦过滤了项目,在 Spinner 中添加将是安全的

【讨论】:

【参考方案6】:

我发现这个解决方案解决了我的问题。

final Spinner mySpinner = (Spinner)findViewById(R.id.spinner_triptype);

   final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.spinner_item, R.id.weekofday, triptype_initial);

   final ArrayAdapter<String> adapter_temp = new ArrayAdapter<String>
(this,R.layout.spinner_item, R.id.weekofday, triptype_array);


   mySpinner.setAdapter(adapter);
    mySpinner.setOnTouchListener(new View.OnTouchListener() 
       @Override
       public boolean onTouch(View v, MotionEvent event) 
       // display your error popup here
        if(flag_spinner_isFirst)
           mySpinner.setAdapter(adapter_temp);
           flag_spinner_isFirst = false;
          
           v.onTouchEvent(event);
           return true;

       
    );

【讨论】:

【参考方案7】:

另一种最适合我的方法是返回一个新的空白视图对象。这是一种相当干净的方法,因为您不使用数组元素。

创建扩展 ArrayAdapter 的适配器类

在你的方法中

public View getView(int position, View convertView, ViewGroup parent) 
    View row = getCustomView();
    if(position==0) // put the desired check here.
         
            row  = new View(context);
         
    
    return row;

【讨论】:

【参考方案8】:

这是一个非常古老的问题,但我找到了一种很好(并且可能)干净的方式来不显示第一个项目。 受@Romich 回答的启发,我添加了类似的逻辑来跳过第一项。

这有效地隐藏了任意数量的项目(默认为 1)。该代码仅报告要渲染的对象的大小比实际要短,并且还会更改要渲染的项目的索引,因此我们跳过任意数量的项目。

为了简单起见,我排除了我目前使用的支持隐藏随机项目列表的解决方案,但只需对代码进行一些调整即可轻松管理。

class ArrayAdapterCustom(context: Context, textViewResourceId: Int, vararg objects: String)
    : ArrayAdapter<String>(context, textViewResourceId, objects) 

    //Can skip first n items (skip 1 as default)
    var hideFirstItemsCount = 1

    override fun getCount(): Int 
        return super.getCount() - hideFirstItemsCount
    

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View 
        return super.getDropDownView(position + hideFirstItemsCount, convertView, parent)
    

【讨论】:

【参考方案9】:

无需调整模型的更好方法。

public class HidableSpinnerArrayAdapter<T> extends ArrayAdapter<T> 

    ...

    @Override
    public boolean isEnabled(int position) 
        // override this check what items are enabled/disabled
    

    // Change color item
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) 
        // when hiding items, cannot reuse views
        View view = super.getDropDownView(position, 
            null /* convertView usually */, parent);
        if (!isEnabled(position)) 
            TextView tv = (TextView) view;
            tv.setEnabled(false);
            tv.setMaxHeight(0);
            tv.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
            return tv;
        
        else 
            return view;
        
    

【讨论】:

以上是关于如何在 Android Spinner 中隐藏一项的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 中使用 Spinner Dependable 创建动态视图

Android-----spinner组件使用(实现下单)

Android控件之Spinner简单使用

android spinner默认啥都不选择

Android中怎样设置Spinner显示的字体颜色

Android中怎样设置Spinner显示的字体颜色