EditText attivabile all'interno di ListView


121

Finora ho impiegato circa 6 ore su questo argomento e non ho incontrato altro che blocchi stradali. La premessa generale è che c'è una riga in a ListView(sia che sia generata dall'adattatore o aggiunta come vista di intestazione) che contiene un EditTextwidget e un file Button. Tutto quello che voglio fare è essere in grado di utilizzare il jogball / frecce, per navigare il selettore su singoli elementi come al solito, ma quando arrivo a una riga particolare - anche se devo identificare esplicitamente la riga - che ha un focusable bambino, voglio che quel bambino si concentri invece di indicare la posizione con il selettore.

Ho provato molte possibilità e finora non ho avuto fortuna.

disposizione:

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

Visualizzazione intestazione:

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

Supponendo che ci siano altri elementi nell'adattatore, l'utilizzo dei tasti freccia sposterà la selezione su / giù nell'elenco, come previsto; ma quando si arriva alla riga di intestazione, viene visualizzata anche con il selettore e non è possibile concentrarsi EditTextsull'uso del jogball. Nota: toccando il si EditText metterà a fuoco a quel punto, tuttavia ciò si basa su un touchscreen, che non dovrebbe essere un requisito.

ListViewapparentemente ha due modalità a questo proposito:
1 setItemsCanFocus(true).: il selettore non viene mai visualizzato, ma EditTextpuò essere messo a fuoco quando si usano le frecce. L'algoritmo di ricerca dello stato attivo è difficile da prevedere e nessun feedback visivo (su nessuna riga: avere figli attivabili o meno) sull'elemento selezionato, entrambi possono offrire all'utente un'esperienza inaspettata.
2 setItemsCanFocus(false).: il selettore viene sempre disegnato in modalità non touch e EditTextnon può mai essere messo a fuoco, anche se lo tocchi.

A peggiorare le cose, la chiamata editTextView.requestFocus()restituisce true, ma in realtà non fornisce lo stato attivo EditText.

Quello che sto immaginando è fondamentalmente un ibrido di 1 e 2, dove invece dell'impostazione dell'elenco se tutti gli elementi sono attivabili o meno, voglio impostare la focalizzazione per un singolo elemento nell'elenco, in modo che il selettore passi senza problemi dalla selezione del intera riga per gli elementi non attivabili e attraversando l'albero del focus per gli elementi che contengono elementi secondari attivabili.

Eventuali acquirenti?

Risposte:


101

Scusa, ho risposto alla mia domanda. Potrebbe non essere la soluzione più corretta o più elegante, ma per me funziona e offre un'esperienza utente piuttosto solida. Ho esaminato il codice di ListView per vedere perché i due comportamenti sono così diversi e l'ho trovato da ListView.java:

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

Quindi, quando si chiama setItemsCanFocus(false), imposta anche la focalizzazione discendente in modo tale che nessun bambino possa ottenere la messa a fuoco. Questo spiega perché non potevo semplicemente attivare mItemsCanFocusOnItemSelectedListener di ListView, perché ListView bloccava lo stato attivo su tutti i bambini.

Quello che ho adesso:

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

Uso beforeDescendantsperché il selettore verrà disegnato solo quando lo stesso ListView (non un bambino) è attivo, quindi il comportamento predefinito deve essere che ListView si attivi per primo e disegna i selettori.

Quindi in OnItemSelectedListener, poiché so quale vista dell'intestazione voglio sovrascrivere il selettore (richiederebbe più lavoro per determinare dinamicamente se una data posizione contiene una vista attivabile), posso cambiare la focalizzazione discendente e impostare lo stato attivo su EditText. E quando esco da quell'intestazione, cambiarla di nuovo.

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

Nota le setItemsCanFocuschiamate commentate . Con quelle chiamate, ho ottenuto il comportamento corretto, ma ho setItemsCanFocus(false)causato il passaggio dello stato attivo da EditText, a un altro widget al di fuori di ListView, di nuovo a ListView e ho visualizzato il selettore sull'elemento selezionato successivo e quel focus saltante era fonte di distrazione. La rimozione della modifica ItemsCanFocus e la semplice attivazione della focalizzazione discendente mi hanno consentito di ottenere il comportamento desiderato. Tutti gli elementi disegnano il selettore normalmente, ma quando si arriva alla riga con EditText, si è concentrato invece sul campo di testo. Quindi, uscendo da quel EditText, ha iniziato a disegnare di nuovo il selettore.


molto bello, non ancora testato. Hai provato con 1.5, 1.6 e 3.0?
Rafael Sanches

Rafael Sanches: Non ho toccato il progetto dalla 2.1, ma a quel tempo è stato confermato che funzionava in 1.5, 1.6 e 2.1. Non garantisco che funzioni ancora nella versione 2.2 o successiva.
Joe

13
necessario solo android: descendantFocusability = "afterDescendants" - comunque +1
kellogs

5
@kellogs: sì, descendantFocusability="afterDescendants"consentirà al tuo EditText di concentrarsi all'interno di ListView, ma poi non ottieni alcun selettore di voci dell'elenco durante la navigazione con un dpad. Il mio compito era di avere il selettore di voci di elenco su tutte le righe tranne quella con EditText. Sono contento che abbia aiutato. FWIW, abbiamo finito per rivalutare questa implementazione e abbiamo deciso che un focusable all'interno di un ListView non è semplicemente un design dell'interfaccia utente Android idiomatico, quindi abbiamo scartato l'idea a favore di un approccio più compatibile con Android.
Joe

5
È stato testato su Ice Cream Sandwich? Non riesco a farlo funzionare. Grazie.
Rajat Anantharam,

99

Questo mi ha aiutato.
Nel tuo manifest:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>

3
Non sono sicuro di vedere la rilevanza. L'impostazione di windowSoftInputMode cambia semplicemente il modo in cui l'IME regola il resto del contenuto della finestra quando è aperto. Non consente di modificare selettivamente il tipo di focus di ListView. Puoi spiegare un po 'di più, come questo si collega al caso d'uso iniziale?
Joe

1
@ Joe Quando l'IME è aperto, il cursore - e probabilmente anche il focus - salta semplicemente sullo schermo, rendendo impossibile l'inserimento del testo. Il tuo OnItemSelectedListenernon cambia questo. Tuttavia la semplice soluzione di Iogan funziona a meraviglia, grazie!
Gubbel

2
@ Gubbel: In effetti, non cambierebbe affatto, perché la domanda originale riguardava qualcosa di completamente diverso :) Sono contento che la correzione di Logan funzioni per quello che stavi cercando, ma semplicemente non è nemmeno lontanamente correlata alla domanda.
Joe

8
Usando questo plus la android:descendantFocusabilityproprietà di Joe ha ottenuto il mio EditTextall'interno di una ListViewrisoluzione della tastiera correttamente, votato entrambi. android:descendantFocusabilitydi per sé non ha funzionato e non ero neanche lontanamente entusiasta @Overriding onItemSelectedper tutti i 14 EditTexts con cui ho a che fare. :) Grazie!
Thomson Comer

Questo ha funzionato per me, ma nota se stai usando TabHost o TabActivity, devi impostare android: windowSoftInputMode = "AdjustPan" per la definizione TabActivity nel manifest.
kiduxa

18

Il mio compito era implementare ListViewche si espande quando cliccato. Lo spazio aggiuntivo mostra EditTextdove è possibile inserire del testo. L'app dovrebbe essere funzionale su 2.2+ (fino a 4.2.2 al momento della stesura di questo)

Ho provato numerose soluzioni da questo post e altre che ho trovato; li ha testati su dispositivi da 2.2 a 4.2.2. Nessuna delle soluzioni era soddisfacente su tutti i dispositivi 2.2+, ogni soluzione presentava problemi diversi.

Volevo condividere la mia soluzione finale:

  1. imposta listview su android:descendantFocusability="afterDescendants"
  2. imposta listview su setItemsCanFocus(true);
  3. imposta la tua attività su android:windowSoftInputMode="adjustResize" Molte persone suggeriscono adjustPanma adjustResizedà molto meglio ux imho, prova questo nel tuo caso. Con adjustPansi ottengono gli elementi dell'elenco inferiore oscurati, ad esempio. Documenti suggeriscono che ("Questo è generalmente meno desiderabile del ridimensionamento"). Anche in 4.0.4, dopo che l'utente inizia a digitare sulla tastiera virtuale, lo schermo si sposta in alto.
  4. sulla 4.2.2 con adjustResizeci sono alcuni problemi con il focus di EditText. La soluzione è applicare la soluzione rjrjr da questo thread. Sembra spaventoso ma non lo è. E funziona. Provalo e basta.

Ulteriori 5. A causa dell'aggiornamento dell'adattatore (a causa del ridimensionamento della vista) quando i EditTextguadagni si concentrano sulle versioni precedenti di HoneyComb, ho riscontrato un problema con le viste invertite: ottenere Visualizza per elemento ListView / ordine inverso su 2.2; funziona su 4.0.3

Se stai eseguendo alcune animazioni potresti voler modificare il comportamento adjustPanper le versioni pre-nido d'ape in modo che il ridimensionamento non si attivi e l'adattatore non aggiorni le viste. Hai solo bisogno di aggiungere qualcosa di simile

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

Tutto ciò fornisce ux accettabili sui dispositivi 2.2 - 4.2.2. Spero che possa far risparmiare tempo alle persone poiché mi ci sono volute almeno diverse ore per arrivare a questa conclusione.


1
solo il primo e il secondo passo sono sufficienti nel mio caso
Kalpesh Lakhani

Funziona perfettamente con il mio xElement.setOnClickListener (..) in ArrayAdapter e un ListView.setOnItemClickListener (...) - finalmente !! Grazie grande.
Javatar

10

Questo mi ha salvato la vita --->

  1. imposta questa linea

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Quindi nel tuo manifest nel tag attività digita questo ->

    <activity android:windowSoftInputMode="adjustPan">

Il tuo solito intento


7

Lo stiamo provando su un breve elenco che non considera il riciclaggio. Fin qui tutto bene.

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>

Giava:

/**
 * 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();
        }
      }
    });
  }
}

Leggermente modificata, questa soluzione funziona per me dopo 2 giorni di @ # ( : if (sticky! = Null) {sticky.RequestFocus (); sticky.RequestFocusFromTouch (); sticky = null;}
Chris van de Steeg

4

questo post corrispondeva esattamente alle mie parole chiave. Ho un'intestazione ListView con un EditText di ricerca e un pulsante di ricerca.

Per dare il focus all'EditText dopo aver perso il focus iniziale l'unico HACK che ho trovato è:

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

Ho perso molte ore e non è una vera soluzione. Spero che aiuti qualcuno duro.


2

Se l'elenco è dinamico e contiene widget attivabili, l'opzione giusta è usare RecyclerView invece di ListView IMO.

Le soluzioni alternative che impostano adjustPan, FOCUS_AFTER_DESCENDANTSo ricordano manualmente la posizione focalizzata, sono in effetti solo soluzioni alternative. Hanno casi d'angolo (scorrimento + problemi di tastiera virtuale, posizione del cursore che cambia in EditText). Non cambiano il fatto che ListView crea / distrugge le visualizzazioni in massa durante notifyDataSetChanged.

Con RecyclerView, si notificano singoli inserimenti, aggiornamenti ed eliminazioni. La visualizzazione focalizzata non viene ricreata, quindi nessun problema con i controlli del modulo che perdono lo stato attivo. Come bonus aggiuntivo, RecyclerView anima gli inserimenti e le rimozioni degli elementi dell'elenco.

Ecco un esempio tratto dalla documentazione ufficiale su come iniziare con RecyclerView: Guida per sviluppatori : creare un elenco con RecyclerView


1

alcune volte quando si utilizza android:windowSoftInputMode="stateAlwaysHidden"in attività manifest o xml, quella volta perderà il focus della tastiera. Quindi prima controlla quella proprietà nel tuo xml e manifest, se è lì, rimuovilo. Dopo aver aggiunto queste opzioni al file manifest nell'attività secondaria android:windowSoftInputMode="adjustPan"e aggiungere questa proprietà a listview in xmlandroid:descendantFocusability="beforeDescendants"


0

Un'altra semplice soluzione è definire il tuo onClickListener, nel metodo getView (..), del tuo 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;

In questo modo la tua riga è cliccabile e anche la tua vista interna :)


1
La domanda riguarda la messa a fuoco, non la cliccabilità.
Joe

0

La parte più importante è far funzionare il focus per la cella dell'elenco. Soprattutto per l'elenco su Google TV, questo è essenziale:

Il metodo setItemsCanFocus della visualizzazione elenco fa il trucco:

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

Il mio elenco di celle xml inizia come segue:

<?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 sono importanti anche per la navigazione con il D-Pad.

Per maggiori dettagli controlla le altre fantastiche risposte.


0

Ho appena trovato un'altra soluzione. Credo che sia più un trucco che una soluzione, ma funziona su Android 2.3.7 e Android 4.3 (ho anche testato quel buon vecchio D-pad)

avvia la visualizzazione web come al solito e aggiungi questo: (grazie Michael Bierman)

listView.setItemsCanFocus(true);

Durante la chiamata getView:

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });

cosa si vede qui in view.requestFocus?
Narendra Singh

questa è una vecchia risposta ma si riferiva alla vista nell'ambito di applicazione data come argomento a OnFocusChangeListener credo
alaeri

1
Provoca un loop nel cambiare focus.
Morteza Rastgoo

0

Prova questo

android:windowSoftInputMode="adjustNothing"

nel

attività

sezione del tuo manifest. Sì, non regola nulla, il che significa che editText rimarrà dov'è quando si apre IME. Ma questo è solo un piccolo inconveniente che risolve completamente il problema di perdere la concentrazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.