Як сховати один елемент в Android Spinner


101

Я шукаю спосіб приховати один елемент у віджеті спінера Android. Це дозволить вам імітувати спінер без вибраних елементів та гарантує, що зворотний виклик onItemSelected () завжди викликається для кожного обраного елемента (якщо прихований елемент є "поточним"). Зазвичай у спінері завжди є один елемент, який не генерує зворотний дзвінок, а саме поточний.

У stackoverflow є якийсь код, як відключити (витіснити) елементи, але не як повністю приховати елементи так, ніби їх не існує.

Після довгих експериментів я придумав дещо зламане рішення, яке працює на різних старих і нових платформах Android. У нього є незначні косметичні недоліки, які важко помітити. Я все ще хотів би почути більш офіційне рішення, окрім "не робіть цього зі спінером".

Це завжди ховає перший елемент у прядильній машині, але його можна досить легко розширити, щоб приховати довільний предмет або більше одного елемента. Додайте фіктивний елемент, що містить порожній рядок, на початку списку елементів спінеру. Ви можете встановити поточний вибір спінера на пункт 0 до того, як відкриється діалогове вікно спінера, це буде імітувати невибраний спінер.

Приклад налаштування Spinner з методом 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).
Маттіас

Відповідь на це питання працює добре.
Rat-a-tat-a-tat Ratatouille

Це може не бути вдосконаленням, але я використовував setTag(1)textView у позиції 0, а потім convertView.getTag() != nullвизначав, чи повторно використаний вид був переглядом висоти 0, створеним для позиції 0, або звичайним видом, який використовується для інших елементів спінера. Це було так, що я міг super.getDropDownView(position, convertView, parent)іноді використовувати, а не завжди створювати новий погляд.
Daniel Handojo

Відповіді:


49

Щоб приховати довільний елемент або кілька елементів, я думаю, що ви можете реалізувати власний адаптер і встановити індекс (або список масивів індексів), який ви хочете приховати.

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

(Я не перевіряв код) сподівання, що це допомагає.


1
Це не рішення. Оновлення запитання надає правильний код.
dldnh

13
Без tv.setHeight (0) TextView все ще видно.
v4r

привіт, я використовував цей код, щоб приховати перший елемент у своєму спінері, він працює нормально, мій спінер покаже мені другий елемент, але коли я натиснув цей другий елемент, текст цього пункту буде встановлений моєму спінеру, я не хочу показувати текст на моєму спінінгу, коли я натиснув на цей предмет, будь ласка, направляйте мене?
Ахін

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

чи може хтось заглянути в моє запитання ?
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

Я думаю, що буде краще поставити валідацію у списку масивів, а не на 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;
}

0

Це дуже давнє запитання, але я знайшов приємний (і, мабуть), чистий спосіб також не показувати перші предмети. Натхненний відповіддю @ Роміча, я додав аналогічну логіку, щоб пропустити перший предмет.

Це фактично приховує довільну кількість елементів (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)
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.