EditText focalisable dans ListView

121

J'ai passé environ 6 heures là-dessus jusqu'à présent et je n'ai rencontré que des barrages routiers. Le principe général est qu'il y a une ligne dans un ListView(qu'elle soit générée par l'adaptateur ou ajoutée en tant que vue d'en-tête) qui contient un EditTextwidget et un fichier Button. Tout ce que je veux faire, c'est pouvoir utiliser le jogball / les flèches, pour naviguer dans le sélecteur vers des éléments individuels comme d'habitude, mais quand j'arrive à une ligne particulière - même si je dois identifier explicitement la ligne - qui a un focusable enfant, je veux que cet enfant se concentre au lieu d'indiquer la position avec le sélecteur.

J'ai essayé de nombreuses possibilités et jusqu'à présent, je n'ai pas eu de chance.

disposition:

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

Vue d'en-tête:

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

En supposant qu'il existe d'autres éléments dans l'adaptateur, l'utilisation des touches fléchées déplacera la sélection vers le haut / bas dans la liste, comme prévu; mais en arrivant à la ligne d'en-tête, il est également affiché avec le sélecteur, et aucun moyen de se concentrer sur l' EditTextutilisation du jogball. Remarque: en tapant sur le EditText sera le concentrer à ce moment - là, mais qui repose sur un écran tactile, ce qui ne devrait pas être une exigence.

ListViewa apparemment deux modes à cet égard:
1 setItemsCanFocus(true).: le sélecteur n'est jamais affiché, mais le EditTextpeut obtenir le focus en utilisant les flèches. L'algorithme de recherche de focus est difficile à prédire et aucun retour visuel (sur les lignes: avoir des enfants focalisables ou non) sur quel élément est sélectionné, les deux pouvant donner à l'utilisateur une expérience inattendue.
2 setItemsCanFocus(false).: le sélecteur est toujours dessiné en mode non tactile et EditTextne peut jamais obtenir la mise au point, même si vous appuyez dessus.

Pour aggraver les choses, l'appel editTextView.requestFocus()renvoie true, mais en fait ne donne pas le focus EditText.

Ce que j'envisage est fondamentalement un hybride de 1 et 2, où plutôt que le paramètre de liste si tous les éléments sont focalisables ou non, je souhaite définir la focalisation pour un seul élément de la liste, de sorte que le sélecteur passe de manière transparente à la sélection du ligne entière pour les éléments non focalisables et traversant l'arborescence du focus pour les éléments contenant des enfants focalisables

N'importe quels preneurs?

Joe
la source

Réponses:

101

Désolé, a répondu à ma propre question. Ce n'est peut-être pas la solution la plus correcte ou la plus élégante, mais cela fonctionne pour moi et donne une expérience utilisateur assez solide. J'ai regardé dans le code de ListView pour voir pourquoi les deux comportements sont si différents, et je suis tombé sur cela de ListView.java:

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

Ainsi, lors de l'appel setItemsCanFocus(false), il définit également la focalisation descendante de telle sorte qu'aucun enfant ne puisse obtenir le focus. Cela explique pourquoi je ne pouvais pas simplement basculer mItemsCanFocusdans OnItemSelectedListener de ListView - parce que ListView bloquait alors le focus sur tous les enfants.

Ce que j'ai maintenant:

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

J'utilise beforeDescendantsparce que le sélecteur ne sera dessiné que lorsque le ListView lui-même (pas un enfant) a le focus, donc le comportement par défaut doit être que le ListView prend le focus en premier et dessine les sélecteurs.

Ensuite, dans OnItemSelectedListener, puisque je sais quelle vue d'en-tête je veux remplacer le sélecteur (il faudrait plus de travail pour déterminer dynamiquement si une position donnée contient une vue focalisable), je peux changer la focalisation descendante et mettre le focus sur le EditText. Et quand je quitte cet en-tête, changez-le à nouveau.

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

Notez les setItemsCanFocusappels commentés . Avec ces appels, j'ai eu le bon comportement, mais j'ai fait setItemsCanFocus(false)passer le focus de EditText, à un autre widget en dehors de ListView, de retour à ListView et j'ai affiché le sélecteur sur l'élément sélectionné suivant, et ce focus sautant était distrayant. La suppression du changement ItemsCanFocus et le simple basculement de la focalisation descendante m'ont donné le comportement souhaité. Tous les éléments dessinent le sélecteur normalement, mais lorsque vous accédez à la ligne avec EditText, il se concentre plutôt sur le champ de texte. Ensuite, en sortant de cet EditText, il a recommencé à dessiner le sélecteur.

Joe
la source
très cool, n'a pas encore testé. Avez-vous testé sur 1.5, 1.6 et 3.0?
Rafael Sanches
Rafael Sanches: Je n'ai pas touché au projet depuis la version 2.1, mais à ce moment-là, il a été confirmé qu'il fonctionnait en 1.5, 1.6 et 2.1. Je ne garantis pas qu'il fonctionne toujours dans la version 2.2 ou ultérieure.
Joe
13
seulement nécessaire android: descendantFocusability = "afterDescendants" - de toute façon +1
kellogs
5
@kellogs: oui, descendantFocusability="afterDescendants"permettra à votre EditText de prendre le focus à l'intérieur de ListView, mais vous n'obtiendrez alors aucun sélecteur d'élément de liste lorsque vous naviguez avec un dpad. Ma tâche était d'avoir le sélecteur d'élément de liste sur toutes les lignes sauf celle avec le EditText. Heureux que cela ait aidé. FWIW, nous avons fini par réévaluer cette implémentation et avons décidé qu'un focusable dans un ListView n'était tout simplement pas une conception d'interface utilisateur idiomatique pour Android, nous avons donc abandonné l'idée en faveur d'une approche plus conviviale pour Android.
Joe
5
Cela a-t-il été testé sur Ice Cream Sandwich? Je ne peux pas le faire fonctionner. Merci.
Rajat Anantharam
99

Cela m'a aidé.
Dans votre manifeste:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
Logan
la source
3
Je ne suis pas sûr de voir la pertinence. La configuration de windowSoftInputMode modifie simplement la façon dont l'IME ajuste le reste du contenu de la fenêtre lorsqu'elle est ouverte. Il ne vous permet pas de modifier sélectivement le type de focus de ListView. Pouvez-vous expliquer un peu plus, comment cela se rapporte au cas d'utilisation initial?
Joe
1
@Joe Lorsque l'IME est ouvert, le curseur - et probablement le focus aussi - saute simplement sur mon écran, ce qui rend impossible la saisie de texte. Votre OnItemSelectedListenerne change pas cela. Cependant, la solution simple d'Iogan fonctionne comme un charme, merci!
Gubbel
2
@Gubbel: En effet, cela ne changerait pas du tout cela, car la question d'origine portait sur quelque chose d'entièrement différent :) Le correctif de Glad logan fonctionne pour ce que vous cherchiez, mais il n'est tout simplement pas lié à la question à distance.
Joe
8
En utilisant ce plus la android:descendantFocusabilitypropriété de Joe, j'ai réussi EditTextà ListViewrésoudre correctement le clavier, à voter pour les deux. android:descendantFocusabilityen lui-même n'a pas fait l'affaire et je n'étais pas très enthousiasmé @Overriding onItemSelectedpar les 14 EditTextsecondes auxquelles je dois faire face. :) Merci!
Thomson Comer du
Cela a fonctionné pour moi, mais notez que si vous utilisez TabHost ou TabActivity, vous devez définir android: windowSoftInputMode = "AdjustPan" pour la définition TabActivity dans le manifeste.
kiduxa
18

Ma tâche était de mettre en œuvre ListViewce qui se développe lorsqu'on clique dessus. L'espace supplémentaire montre EditTextoù vous pouvez saisir du texte. L'application doit être fonctionnelle sur 2.2+ (jusqu'à 4.2.2 au moment de la rédaction de cet article)

J'ai essayé de nombreuses solutions à partir de ce post et d'autres que j'ai pu trouver; les a testés sur des appareils 2.2 à 4.2.2. Aucune des solutions n'était satisfaisante sur tous les appareils 2.2+, chaque solution présentant des problèmes différents.

Je voulais partager ma solution finale:

  1. définir listview sur android:descendantFocusability="afterDescendants"
  2. définir listview sur setItemsCanFocus(true);
  3. réglez votre activité sur android:windowSoftInputMode="adjustResize" Beaucoup de gens suggèrent adjustPanmais adjustResizedonne une bien meilleure ux à mon humble avis, testez simplement cela dans votre cas. Avec, adjustPanvous obtiendrez les éléments de la liste du bas obscurcis par exemple. Les documents suggèrent que ("Ceci est généralement moins souhaitable que le redimensionnement"). Aussi sur 4.0.4 après que l'utilisateur commence à taper sur le clavier logiciel, l'écran se déplace vers le haut.
  4. sur 4.2.2 avec adjustResizeil y a quelques problèmes avec le focus EditText. La solution consiste à appliquer la solution rjrjr à partir de ce thread. Cela a l'air effrayant mais ce n'est pas le cas. Et il fonctionne. Essayez-le.

Supplémentaire 5. En raison de la mise à jour de l'adaptateur (à cause du redimensionnement de la vue) lorsque les EditTextgains se concentrent sur les versions antérieures à HoneyComb, j'ai trouvé un problème avec les vues inversées: obtenir la vue pour l'élément ListView / ordre inverse sur 2.2; fonctionne sur 4.0.3

Si vous faites des animations, vous voudrez peut-être changer le comportement adjustPandes versions pré-nid d'abeille afin que le redimensionnement ne se déclenche pas et que l'adaptateur n'actualise pas les vues. Vous avez juste besoin d'ajouter quelque chose comme ça

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

Tout cela donne une ux acceptable sur les appareils 2.2 - 4.2.2. J'espère que cela fera gagner du temps aux gens car il m'a fallu au moins plusieurs heures pour arriver à cette conclusion.

AndroidGecko
la source
1
juste la première et la deuxième étape assez dans mon cas
Kalpesh Lakhani
Fonctionne parfaitement avec mon xElement.setOnClickListener (..) dans ArrayAdapter et un ListView.setOnItemClickListener (...) - enfin !! Big thx.
Javatar
10

Cela m'a sauvé la vie --->

  1. définir cette ligne

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Ensuite, dans votre manifeste dans la balise d'activité, tapez ceci ->

    <activity android:windowSoftInputMode="adjustPan">

Votre intention habituelle

ravi ranjan
la source
7

Nous essayons cela sur une courte liste qui ne fait aucun recyclage de vue. Jusqu'ici tout va bien.

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();
        }
      }
    });
  }
}
rjrjr
la source
Légèrement modifiée, cette solution fonctionne pour moi après 2 jours de @ # ( : if (sticky! = Null) {sticky.RequestFocus (); sticky.RequestFocusFromTouch (); sticky = null;}
Chris van de Steeg
4

ce message correspondait exactement à mes mots clés. J'ai un en-tête ListView avec un EditText de recherche et un bouton de recherche.

Afin de donner le focus à EditText après avoir perdu le focus initial, le seul HACK que j'ai trouvé est:

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

J'ai perdu beaucoup d'heures et ce n'est pas une vraie solution. J'espère que cela aide quelqu'un de dur.

Rafael Sanches
la source
2

Si la liste est dynamique et contient des widgets focalisables, la bonne option consiste à utiliser RecyclerView au lieu de ListView IMO.

Les solutions de contournement ensemble adjustPan, FOCUS_AFTER_DESCENDANTSou manuellement position mise au point se souviennent, sont en effet des solutions de contournement juste. Ils ont des cas d'angle (problèmes de défilement + clavier logiciel, changement de position du curseur dans EditText). Ils ne changent pas le fait que ListView crée / détruit des vues en masse pendant notifyDataSetChanged.

Avec RecyclerView, vous notifiez les insertions, les mises à jour et les suppressions individuelles. La vue focalisée n'est pas recréée donc aucun problème avec les contrôles de formulaire perdant le focus. En prime, RecyclerView anime les insertions et suppressions d'éléments de liste.

Voici un exemple tiré de la documentation officielle sur la façon de démarrer avec RecyclerView: Guide du développeur - Créer une liste avec RecyclerView

Pēteris Caune
la source
1

Parfois, lorsque vous utilisez android:windowSoftInputMode="stateAlwaysHidden"dans l'activité manifeste ou xml, cette fois, il perdra le focus clavier. Vérifiez donc d'abord cette propriété dans votre xml et votre manifeste, si elle existe, supprimez-la. Après avoir ajouté ces options au fichier manifeste dans l'activité secondaire android:windowSoftInputMode="adjustPan"et ajoutez cette propriété à la vue de liste en xmlandroid:descendantFocusability="beforeDescendants"


la source
0

Une autre solution simple consiste à définir votre onClickListener, dans la méthode getView (..), de votre 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;

De cette façon, votre ligne est cliquable, et votre vue intérieure aussi :)

Weber Antoine
la source
1
La question concerne la mise au point, pas la cliquabilité.
Joe
0

La partie la plus importante est de faire fonctionner le focus pour la cellule de liste. Surtout pour la liste sur Google TV, c'est essentiel:

La méthode setItemsCanFocus de la vue liste fait l'affaire:

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

Ma cellule de liste xml commence comme suit:

<?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 sont également importants pour la navigation D-Pad.

Pour plus de détails, consultez les excellentes autres réponses.

Michael Biermann
la source
0

Je viens de trouver une autre solution. Je crois que c'est plus un hack qu'une solution mais cela fonctionne sur Android 2.3.7 et Android 4.3 (j'ai même testé ce bon vieux D-pad)

lancez votre webview comme d'habitude et ajoutez ceci: (merci Michael Bierman)

listView.setItemsCanFocus(true);

Pendant l'appel getView:

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });
alaeri
la source
qu'est-ce que voir ici en vue.requestFocus?
Narendra Singh
c'est une vieille réponse mais elle faisait référence à la vue dans la portée donnée comme argument à OnFocusChangeListener je crois
alaeri
1
Cela provoque une boucle dans le changement de focus.
Morteza Rastgoo
0

Essayez ça

android:windowSoftInputMode="adjustNothing"

dans le

activité

section de votre manifeste. Oui, cela n'ajuste rien, ce qui signifie que le editText restera où il se trouve lors de l'ouverture de l'IME. Mais ce n'est qu'un petit inconvénient qui résout encore complètement le problème de la perte de concentration.

Tor_Gash
la source