Focusable EditText inside ListView

Do tej pory spędziłem nad tym około 6 godzin i uderzałem tylko w blokady. Ogólne założenie jest takie, że w ListView (niezależnie od tego, czy jest generowany przez adapter, czy dodawany jako widok nagłówka) znajduje się wiersz zawierający widżet EditText i Button. Wszystko, co chcę zrobić, to być w stanie użyć jogball / strzałki, aby nawigować selektor do poszczególnych elementów, takich jak normalne, ale kiedy dojdę do określonego wiersza-nawet jeśli muszę wyraźnie zidentyfikować wiersz - który ma focusable dziecko, chcę, aby dziecko się skupiło, zamiast wskazywać pozycję za pomocą selektora.

Próbowałem wielu możliwości i do tej pory nie miałem szczęścia.

Układ:

<ListView
    android:id="@android:id/list" 
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent"
    />

Widok nagłówka:

EditText view = new EditText(this);
listView.addHeaderView(view, null, true);

Zakładając, że w adapterze są inne elementy, użycie klawiszy strzałek przesunie zaznaczenie w górę/w dół listy, zgodnie z oczekiwaniami; ale po dotarciu do wiersza nagłówka jest on również wyświetlany za pomocą selektora i nie ma możliwości skupienia się na EditText za pomocą jogball. Uwaga: stukanie w EditText will skupić go w tym momencie, jednak to opiera się na ekranie dotykowym, który nie powinien być wymogiem.

ListView najwyraźniej ma dwa tryby pod tym względem:
1. setItemsCanFocus(true): selektor nigdy nie jest wyświetlany, ale {[3] } może uzyskać ostrość podczas używania strzałek. Algorytm wyszukiwania ostrości jest trudny do przewidzenia, a brak wizualnej informacji zwrotnej (na dowolnych wierszach: o dzieciach z możliwością ustawiania ostrości lub nie), na którym elemencie jest wybrany, z których oba mogą dać użytkownikowi nieoczekiwane doświadczenie.
2. setItemsCanFocus(false): Selektor jest zawsze rysowany w trybie bezdotykowym i EditText nigdy nie może uzyskać ostrości - nawet jeśli go dotkniesz.

Co gorsza, wywołanie editTextView.requestFocus() zwraca true, ale w rzeczywistości nie daje fokusu EditText.

To, co wyobrażam sobie jest w zasadzie hybrydą 1 i 2, gdzie zamiast ustawiania listy, czy wszystkie elementy są fokusowalne, chcę ustawić fokusowalność dla pojedynczego elementu na liście, aby selektor płynnie przechodzenie od zaznaczania całego wiersza dla elementów, które nie mogą być ustawione ostrości, i przechodzenia przez drzewo ostrości dla elementów, które zawierają elementy potomne, które mogą być ustawione ostrości.

Jacyś chętni?
Author: Catalina, 2010-04-21

13 answers

Przepraszam, odpowiedziałem na własne pytanie. Może nie jest to najbardziej poprawne lub najbardziej eleganckie rozwiązanie, ale działa dla mnie i daje całkiem solidne wrażenia użytkownika. Przyjrzałem się kodowi ListView, aby zobaczyć, dlaczego te dwa zachowania są tak różne, i natknąłem się na to z ListView.java:

    public void setItemsCanFocus(boolean itemsCanFocus) {
        mItemsCanFocus = itemsCanFocus;
        if (!itemsCanFocus) {
            setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        }
    }

Tak więc, podczas wywoływania setItemsCanFocus(false), ustawia się również fokusowość potomka tak, że żadne dziecko nie może uzyskać fokusu. To wyjaśnia, dlaczego nie mogłem po prostu przełączyć mItemsCanFocus w Onitemselectedlistener ListView -- ponieważ ListView był wtedy blokujący fokus dla wszystkich dzieci.

Co mam teraz:

<ListView
    android:id="@android:id/list" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent"
    android:descendantFocusability="beforeDescendants"
    />

Używam beforeDescendants ponieważ selektor będzie rysowany tylko wtedy, gdy ListView (nie jest potomkiem) ma fokus, więc domyślnym zachowaniem musi być to, że ListView najpierw pobiera fokus i rysuje selektory.

Następnie w OnItemSelectedListener, ponieważ wiem, który Widok nagłówka chcę nadpisać selektor (wymagałoby więcej pracy, aby dynamicznie określić, czy dana pozycja zawiera focusable view), mogę zmienić focusability i ustawić focus na EditText. A kiedy będę nawigował z tego nagłówka, zmień go z powrotem.

public void onItemSelected(AdapterView<?> listView, View view, int position, long id)
{
    if (position == 1)
    {
        // listView.setItemsCanFocus(true);

        // Use afterDescendants, because I don't want the ListView to steal focus
        listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        myEditText.requestFocus();
    }
    else
    {
        if (!listView.isFocused())
        {
            // listView.setItemsCanFocus(false);

            // Use beforeDescendants so that the EditText doesn't re-take focus
            listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
            listView.requestFocus();
        }
    }
}

public void onNothingSelected(AdapterView<?> listView)
{
    // This happens when you start scrolling, so we need to prevent it from staying
    // in the afterDescendants mode if the EditText was focused 
    listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}

Zwróć uwagę na komentowane połączenia setItemsCanFocus. Z tych połączeń, mam poprawne zachowanie, ale setItemsCanFocus(false) spowodował, że focus przeskoczyć z EditText, do innego widżetu poza ListView, z powrotem do ListView i wyświetlane selektor na następnym wybranym elemencie, a to skoki ostrości było rozpraszające. Usuwanie zmian ItemsCanFocus, i samo przełączanie ostrości potomka dało mi pożądane zachowanie. Wszystkie elementy rysują selektor jak zwykle, ale po dotarciu do wiersza z Edittekst, koncentruje się na polu tekstowym. Następnie kontynuując ten EditText, zaczął ponownie rysować selektor.

 101
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-26 17:18:35

To mi pomogło.
W manifeście:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
 100
Author: logan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-16 08:50:44

Moim zadaniem było zaimplementowanie ListView, które rozszerza się po kliknięciu. Dodatkowa spacja pokazuje EditText, gdzie można wprowadzić tekst. Aplikacja powinna działać na 2.2+ (do 4.2.2 w momencie pisania tego)

Wypróbowałem wiele rozwiązań z tego postu i innych, które mogłem znaleźć; przetestowałem je na urządzeniach 2.2 do 4.2.2. Żadne z rozwiązań nie było zadowalające dla wszystkich urządzeń 2.2+, każde rozwiązanie przedstawiało inne problemy.

Chciałem podzielić się moim ostatecznym rozwiązaniem :

  1. Ustaw listview na android:descendantFocusability="afterDescendants"
  2. Ustaw listview na setItemsCanFocus(true);
  3. Ustaw aktywność na android:windowSoftInputMode="adjustResize" Wiele osób sugeruje adjustPan ale adjustResize daje znacznie lepszy ux imho, po prostu przetestuj to w swoim przypadku. Za pomocą adjustPan otrzymasz na przykład zasłonięte dolne listy. Dokumenty sugerują, że ("jest to na ogół mniej pożądane niż zmiana rozmiaru"). Również w 4.0.4 po rozpoczęciu pisania na miękkiej klawiaturze ekran przesuwa się do góry.
  4. na 4.2.2 z {[7] } są pewne problemy z EditText skup się. Rozwiązaniem jest zastosowanie roztworu rjrjr z tego wątku. Wygląda przerażająco, ale tak nie jest. I to działa. Spróbuj.

Dodatkowe 5. Z powodu odświeżania adaptera (z powodu zmiany rozmiaru widoku), gdy EditText skupi się na wersjach przedpremierowych znalazłem problem z odwróconymi widokami: pobieranie widoku dla pozycji ListView / odwrotnej kolejności na 2.2; działa na 4.0.3

Jeśli robisz jakieś animacje możesz chcieć zmienić zachowanie na adjustPan dla wersje pre-honeycomb, dzięki czemu resize nie odpala się, a adapter nie odświeża widoków. Wystarczy dodać coś takiego

if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

Wszystko to daje akceptowalny ux na urządzeniach 2.2 - 4.2.2. Mam nadzieję, że zaoszczędzi to ludziom trochę czasu, ponieważ dojście do tego wniosku Zajęło mi co najmniej kilka godzin.

 18
Author: AndroidGecko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:59

To uratowało mi życie- - - >

  1. Ustaw tę linię

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Następnie w swoim manifeście w znaczniku aktywności wpisz ten-->

    <activity android:windowSoftInputMode="adjustPan">

Twoje zwykłe intencje

 10
Author: ravi ranjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-18 17:26:06

Próbujemy tego na krótkiej liście, która nie robi żadnego widoku recyklingu. Jak na razie dobrze.

XML:

<RitalinLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
  <ListView
      android:id="@+id/cart_list"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:scrollbarStyle="outsideOverlay"
      />
</RitalinLayout>

Java:

/**
 * It helps you keep focused.
 *
 * For use as a parent of {@link android.widget.ListView}s that need to use EditText
 * children for inline editing.
 */
public class RitalinLayout extends FrameLayout {
  View sticky;

  public RitalinLayout(Context context, AttributeSet attrs) {
    super(context, attrs);

    ViewTreeObserver vto = getViewTreeObserver();

    vto.addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
      @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) {
        if (newFocus == null) return;

        View baby = getChildAt(0);

        if (newFocus != baby) {
          ViewParent parent = newFocus.getParent();
          while (parent != null && parent != parent.getParent()) {
            if (parent == baby) {
              sticky = newFocus;
              break;
            }
            parent = parent.getParent();
          }
        }
      }
    });

    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override public void onGlobalLayout() {
        if (sticky != null) {
          sticky.requestFocus();
        }
      }
    });
  }
}
 7
Author: rjrjr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-06 04:37:56

Ten post pasował dokładnie do moich słów kluczowych. Mam nagłówek ListView z edytorem wyszukiwania i przyciskiem wyszukiwania.

W celu nadania fokusu Edittextowi po utracie początkowego Fokusa jedyny HACK jaki znalazłem to:

    searchText.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {
            // LOTS OF HACKS TO MAKE THIS WORK.. UFF...
            searchButton.requestFocusFromTouch();
            searchText.requestFocus();
        }
    });

Stracił wiele godzin i to nie jest prawdziwy fix. Mam nadzieję, że to pomoże komuś twardemu.

 4
Author: Rafael Sanches,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 09:13:09

Jeśli lista jest dynamiczna i zawiera widżety fokusowalne, to właściwą opcją jest użycie RecyclerView zamiast ListView IMO.

Obejścia, które ustawiają adjustPan, FOCUS_AFTER_DESCENDANTS, lub ręcznie zapamiętać pozycję skupioną, są rzeczywiście tylko obejścia. Mają narożne obudowy (przewijanie + problemy z miękką klawiaturą, zmiana pozycji w EditText). Nie zmienia to faktu, że ListView tworzy / niszczy widoki masowo podczas notifyDataSetChanged.

Z RecyclerView, powiadamiasz o poszczególnych wstawianiu, aktualizacjach i usuwaniu. Widok ostrości nie jest odtwarzany, więc nie ma problemów z utratą ostrości przez kontrolki formularza. Dodatkowo RecyclerView animuje wstawianie i usuwanie elementów listy.

Oto przykład z oficjalnych dokumentów, jak zacząć RecyclerView: Przewodnik dla programistów-Utwórz listę za pomocą RecyclerView

 2
Author: Pēteris Caune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-16 13:47:24

Czasami, gdy używasz android:windowSoftInputMode="stateAlwaysHidden" w manifest activity lub xml, tym razem stracisz ostrość klawiatury. Więc najpierw sprawdź tę właściwość w XML i manifest, jeśli jest tam po prostu usuń go. Po dodaniu tych opcji do pliku manifestu w Side activity android:windowSoftInputMode="adjustPan" i dodaniu tej właściwości do listview w xml android:descendantFocusability="beforeDescendants"

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-25 05:57:40

Innym prostym rozwiązaniem jest zdefiniowanie onClickListener, w getView(..) method, of your ListAdapter.

public View getView(final int position, View convertView, ViewGroup parent){
    //initialise your view
    ...
    View row = context.getLayoutInflater().inflate(R.layout.list_item, null);
    ...

    //define your listener on inner items

    //define your global listener
    row.setOnClickListener(new OnClickListener(){
        public void onClick(View v) {
            doSomethingWithViewAndPosition(v,position);
        }
    });

    return row;

W ten sposób twój wiersz jest klikalny, a twój wewnętrzny widok też:)

 0
Author: Weber Antoine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-04 09:15:29

Najważniejszą częścią jest uzyskanie Fokusa pracującego dla komórki listy. Szczególnie dla listy w Google TV jest to niezbędne:

SetItemsCanFocus metoda widoku listy działa w ten sposób:

...
mPuzzleList = (ListView) mGameprogressView.findViewById(R.id.gameprogress_puzzlelist);
mPuzzleList.setItemsCanFocus(true);
mPuzzleList.setAdapter(new PuzzleListAdapter(ctx,PuzzleGenerator.getPuzzles(ctx, getResources(), version_lite)));
...

Moja lista komórek xml zaczyna się następująco:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/puzzleDetailFrame"
             android:focusable="true"
             android:nextFocusLeft="@+id/gameprogress_lessDetails"
             android:nextFocusRight="@+id/gameprogress_reset"
...

NextFocusLeft / Right są również ważne dla nawigacji D-Pad.

Aby uzyskać więcej informacji, sprawdź inne świetne odpowiedzi.

 0
Author: Michael Biermann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-04 20:11:06

Właśnie znalazłem inne rozwiązanie. Uważam, że to bardziej hack niż rozwiązanie, ale działa na Androidzie 2.3.7 i Androidzie 4.3 (nawet przetestowałem ten stary dobry D-pad)

Init your webview as usual and add this: (thanks Michael Bierman)

listView.setItemsCanFocus(true);

Podczas wywołania getView:

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });
 0
Author: alaeri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-11 12:44:13

Just try this

android:windowSoftInputMode="adjustNothing"

W

Aktywność

Część manifestu. Tak, dostosowuje nicość, co oznacza, że editText pozostanie tam, gdzie jest, gdy IME się otwiera. Ale to tylko mała niedogodność, która nadal całkowicie rozwiązuje problem utraty ostrości.

 0
Author: Tor_Gash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-20 14:46:22

W moim przypadku w widoku listy znajduje się 14 wejść do edycji tekstu. Problem, z którym miałem do czynienia, gdy klawiatura Otwarta, edytować fokus tekst utracone, przewijanie układu, a tak szybko, jak focused widok nie jest widoczny dla klawiatury użytkownika w dół. To nie było dobre dla doświadczenia użytkownika. Nie mogę użyć windowSoftInputMethod= "adjustPan". Tak więc po tak wielu poszukiwaniach, znalazłem link który napełnia niestandardowy układ i ustawia dane w widoku jako adapter za pomocą LinearLayout i scrollView i działa dobrze dla mojego przypadku.

 0
Author: Sumit Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-12 10:46:25