Jak ukryć jeden element w Android Spinner


101

Szukam sposobu, aby ukryć jeden element w widżecie przędzarki Androida. Pozwoliłoby to na zasymulowanie pokrętła bez wybranych elementów i zapewniłoby, że wywołanie zwrotne onItemSelected () jest zawsze wywoływane dla każdego wybranego elementu (jeśli ukryty element jest elementem „bieżącym”). Zwykle w pokrętle zawsze znajduje się jeden element, który nie generuje wywołania zwrotnego, a mianowicie bieżący.

W przepływie stosu jest kod dotyczący wyłączania (wyszarzania) elementów, ale nie dotyczy tego, jak całkowicie ukryć elementy tak, jakby nie istniały.

Po wielu eksperymentach wymyśliłem nieco hakerskie rozwiązanie, które działa na różnych starych i nowych platformach Androida. Ma kilka drobnych wad kosmetycznych, które trudno zauważyć. Wciąż chciałbym usłyszeć o bardziej oficjalnym rozwiązaniu, innym niż „nie rób tego za pomocą błystki”.

To zawsze ukrywa pierwszy element w pokrętle, ale można go dość łatwo rozszerzyć, aby ukryć dowolny element lub więcej niż jeden element. Dodaj fikcyjny element zawierający pusty ciąg na początku listy elementów przewijanych. Możesz chcieć ustawić bieżący wybór pokrętła na pozycję 0, zanim otworzy się okno dialogowe pokrętła, co zasymuluje niezaznaczoną pokrętło.

Przykład konfiguracji Spinner z przesłonięciem metody 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);

co znalazłeś w innych interwebach? Czego spróbowałeś do tej pory?
dldnh

Przepraszam, nie wiem jak to zrobić.
Louisth

Niezłe rozwiązanie! Ale myślę, że tv.setVisibility(View.GONE);linia jest niepotrzebna. Komentowanie tego nie wydaje się robić żadnej (wizualnej) różnicy, przynajmniej w systemie Android 4.4.2 / KitKit (na LG / Google Nexus 4).
Matthias

Odpowiedź na to pytanie działa dobrze ...
Rat-a-tat-a-tat Ratatouille

Może to nie być poprawą, ale użyłem setTag(1)w textView pozycji 0, a następnie użyłem go convertView.getTag() != nulldo określenia, czy ponownie użyty widok był widokiem wysokości 0 utworzonym dla pozycji 0, czy normalnym widokiem używanym dla innych elementów pokrętła. To było tak, że mogłem super.getDropDownView(position, convertView, parent)czasami używać zamiast zawsze tworzyć nowy widok.
Daniel Handojo,

Odpowiedzi:


49

Aby ukryć dowolny element lub więcej niż jeden element, myślę, że możesz zaimplementować własny adapter i ustawić indeks (lub tablicę indeksów), który chcesz ukryć.

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

Podczas tworzenia listy elementów użyj własnego adaptera.

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

(Nie testowałem kodu) mam nadzieję, że to pomoże.


1
To nie jest rozwiązanie. Aktualizacja pytania zawiera poprawny kod.
dldnh

13
Bez tv.setHeight (0) TextView jest nadal widoczny.
v4r

Cześć, użyłem tego kodu, aby ukryć pierwszą pozycję w mojej tarczy, działa dobrze, moja tarcza pokaże mi drugą pozycję, ale kiedy kliknąłem drugą pozycję, tekst na tej pozycji zostanie ustawiony na mój spinner, ja nie chcę wyświetlać żadnego tekstu na mojej tarczy po kliknięciu na ten element, poprowadź mnie?
Achin

1
Niesamowite :) Proste rozwiązanie :)
Chaitanya

1
Działa jak marzenie..!
Krupa Kakkad

20

Łatwiej jest ukryć element na końcu listy, skracając listę.

Ale musisz go najpierw zaznaczyć, aby pojawił się w pokrętle, a następnie sprawdzić, czy wybór został zmieniony na jeden z wyświetlonych elementów.

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
Szukałem przez pół godziny, próbując znaleźć czyste podejście, a to jest zdecydowanie najlepsze podejście. Po prostu skróć listę, ale element faktycznie istnieje. Doskonały.
KSdev

1
Wydaje się, że to nie działa w Lollipop, test [Wybierz jeden] nie pojawia się początkowo w Spinner. Wydaje się, że ten sam kod w starszych wersjach Androida robi to, czego wszyscy chcemy.
Jonathan Caryl

1
Tekst przędzarki zmienia się na „String3” przy zmianie orientacji, nawet gdy pokrętło pozostaje nietknięte. @Romich
Yksh

czy ktoś może spojrzeć na moje pytanie ?
Moeez

5

Aby ukryć dowolny element w rozwijanym menu spinner, musisz podać pozycję elementu, który ma być ukryty na podstawie wymaganych kryteriów. Osiągnąłem to w przypadku użycia ukrywania elementu, który jest wybrany z listy rozwijanej

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

A ustawienie adaptera wygląda jakoś tak

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

Po wybraniu niektórych pozycji z listy rozwijanej również pozycja musi ulec zmianie

 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

Dla zainteresowania stworzyłem rozwiązanie polegające na użyciu „Prompt” jako podpowiedzi. Ten kod jest przeznaczony Xamarin.Android, ale można go doskonale przenieść na Javę w 10 minut. Używaj go jak prostego, ArrayAdapterbez dodawania elementów indeksowanych 0 lub zliczanych do tablicy źródłowej. Ustawia się również SpinnerGeolocation.SelectedItemIdna -1, gdy nic nie jest wybrane ( hintjest to bieżąca pozycja).

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

czy ktoś może spojrzeć na moje pytanie ?
Moeez

1

Znalazłem to rozwiązanie, które rozwiązało mój problem.

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

Myślę, że lepiej będzie umieścić walidację na liście Array zamiast na Spinner, ponieważ po przefiltrowaniu elementu będzie można bezpiecznie dodać w Spinner


0

Innym podejściem, które działało najlepiej, jest zwrócenie nowego pustego obiektu widoku. Jest to dość czyste podejście, ponieważ nie bawisz się elementami tablicy.

Utwórz rozszerzającą klasę adaptera ArrayAdapter

wewnątrz twojej metody

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

To bardzo stare pytanie, ale znalazłem ładny (i prawdopodobnie) czysty sposób, aby nie pokazywać również pierwszych pozycji. Zainspirowany odpowiedzią @ Romicha, dodałem podobną logikę, aby pominąć pierwszą pozycję (y).

To skutecznie ukrywa dowolną liczbę elementów (domyślnie 1). Kod zgłasza tylko rozmiar obiektów do renderowania jako krótszy niż jest w rzeczywistości, a także zmienia indeks elementów do renderowania, więc pomijamy dowolną liczbę elementów.

Aby uprościć sprawę, wykluczyłem rozwiązanie, którego obecnie używam, które obsługuje ukrywanie listy losowych elementów, ale można nim łatwo zarządzać za pomocą kilku poprawek w kodzie.

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)
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.