Как скрыть один элемент в Android Spinner

101

Я ищу способ скрыть один элемент в виджете счетчика Android. Это позволит имитировать счетчик без выбранных элементов и гарантирует, что обратный вызов onItemSelected () всегда вызывается для каждого выбранного элемента (если скрытый элемент является «текущим»). Обычно в счетчике всегда есть один элемент, который не генерирует обратный вызов, а именно текущий.

В stackoverflow есть код для отключения (серого) элементов, но не для того, чтобы полностью скрыть элементы, как будто их не существует.

После долгих экспериментов я придумал несколько хакерское решение, которое работает на различных старых и новых платформах Android. У него есть незначительные косметические недостатки, которые трудно заметить. Я все еще хотел бы услышать более официальное решение, кроме «не делайте этого со счетчиком».

Это всегда скрывает первый элемент в счетчике, но его можно довольно легко расширить, чтобы скрыть произвольный элемент или более одного элемента. Добавьте фиктивный элемент, содержащий пустую строку, в начало вашего списка элементов счетчика. Вы можете установить текущий выбор счетчика на элемент 0 до открытия диалогового окна счетчика, это будет имитировать невыбранный счетчик.

Пример настройки счетчика с переопределением метода ArrayAdapter:

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);
Джорджи
источник
что вы нашли в других сетях? что ты уже испробовал?
dldnh
Извини, я не знаю, как это сделать.
Луист,
Хорошее решение! Но я думаю, что tv.setVisibility(View.GONE);линия не нужна. Комментирование этого не имеет никакого (визуального) значения, по крайней мере, для Android 4.4.2 / KitKit (на LG / Google Nexus 4).
Матиас
Ответ на этот вопрос работает хорошо ..
Ratatouille
Возможно, это не улучшение, но я использовал setTag(1)textView в позиции 0, а затем использовал, convertView.getTag() != nullчтобы определить, было ли повторно используемое представление представлением высоты 0, созданным для позиции 0, или обычным представлением, используемым для других элементов счетчика. Это было сделано для того, чтобы я мог super.getDropDownView(position, convertView, parent)иногда использовать вместо того, чтобы всегда создавать новое представление.
Даниэль Хандоджо

Ответы:

50

Чтобы скрыть произвольный элемент или более одного элемента, я думаю, вы можете реализовать свой собственный адаптер и установить индекс (или список индексов), который вы хотите скрыть.

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

(Я не тестировал код) надеюсь, что это поможет.

Aebsubis
источник
1
Это не выход. Обновление вопроса содержит правильный код.
dldnh 04
13
Без tv.setHeight (0) TextView все еще виден.
v4r
привет, я использовал этот код, чтобы скрыть первый элемент в моем счетчике, он работает нормально, мой счетчик покажет мне второй элемент, но когда я щелкну этот второй элемент, текст этого элемента будет установлен на моем счетчике, я не хотите отображать текст на моем счетчике, когда я нажимаю на этот элемент, пожалуйста, помогите мне?
Achin
1
Потрясающе :) Простое решение :)
Чайтанья
1
Работает как шарм ..!
Krupa
20

Легче скрыть элемент в конце списка, усекая список.

Но сначала вы должны выбрать его, чтобы он появился в счетчике, а затем проверить, был ли выбран один из отображаемых элементов.

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
Ромич
источник
3
Я искал полчаса, пытаясь найти чистый подход, и это, безусловно, лучший подход. Просто обрежьте список, но элемент действительно существует. Превосходно.
KSdev
1
Похоже, это не работает в Lollipop, тест [Select one] изначально не отображается в Spinner. Один и тот же код в более старых версиях Android, похоже, делает то, что мы все хотим.
Джонатан Кэрил
1
Текст счетчика изменяется на «String3» при изменении ориентации, даже если счетчик остается нетронутым. @Romich
Yksh
кто-нибудь может изучить мой вопрос ?
Moeez
5

Чтобы скрыть любой элемент в раскрывающемся списке счетчика, вам необходимо передать позицию элемента, который необходимо скрыть на основе требуемых критериев. Я добился этого в случае использования скрытия элемента, который выбран из раскрывающегося списка

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) {

        }
    });
Джиджу Индукхудан
источник
1

Ради интереса я решил использовать "Подсказка" в качестве подсказки. Этот код создан для Xamarin.Android, но его можно идеально перенести на Java за 10 минут. Используйте его как простой, 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;
        }
    }
}
KD
источник
кто-нибудь может изучить мой вопрос ?
Moeez
1

Я нашел это решение, которое решило мою проблему.

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;

       }
    });
Абдул Ваджид
источник
0

Я думаю, что будет лучше поставить проверку в Array List, а не в Spinner, потому что после фильтрации элемента его можно будет добавить в Spinner

Ахилеш
источник
0

Другой подход, который лучше всего сработал для меня, - это вернуть новый пустой объект представления. Это в значительной степени чистый подход, поскольку вы не играете с элементами массива.

Создайте свой класс адаптера, расширяющий 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;
}
jeet.chanchawat
источник
0

Это очень старый вопрос, но я нашел хороший (и, вероятно) чистый способ не показывать первые элементы. Вдохновленный ответом @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)
    }
}
Мухамед Авдич
источник