EditText focalisable à l’intérieur de ListView

J’ai passé environ 6 heures à ce sujet jusqu’à présent, et je n’ai touché que des barrages routiers. Le principe général est qu’il existe une ligne dans ListView (qu’elle soit générée par l’adaptateur ou ajoutée en tant que vue d’en-tête) contenant un widget EditText et un Button . Tout ce que je veux faire, c’est être capable d’utiliser le jogball / les flèches, de 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 – 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:

  

Vue d’en-tête:

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

En supposant qu’il y ait d’autres éléments dans l’adaptateur, l’utilisation des touches fléchées déplace la sélection vers le haut / bas dans la liste, comme prévu; mais quand on arrive à la ligne d’en-tête, il est également affiché avec le sélecteur, et il n’y a aucun moyen de se concentrer dans le EditText utilisant le jogball. Remarque: en tapant sur EditText le focaliserez sur ce point, mais cela dépend d’un écran tactile, ce qui ne devrait pas être une exigence.

ListView apparemment deux modes à cet égard:
1. setItemsCanFocus(true) : le sélecteur n’est jamais affiché, mais le EditText peut être mis en EditText lors de l’utilisation des flèches. L’algorithme de recherche de focus est difficile à prédire, et aucun retour visuel (sur les lignes: avoir des enfants focalisables) sur l’élément 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 EditText ne peut jamais être mis au point – même si vous appuyez dessus.

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

Ce que je pense 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 veux définir la focalisation pour un seul élément de la liste, de sorte que le sélecteur ligne entière pour les éléments non focalisables et traversant l’arborescence pour les éléments contenant des enfants pouvant être focalisés.

N’importe quels preneurs?

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 étaient si différents et je suis tombé sur ceci de ListView.java:

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

Ainsi, lorsque vous appelez setItemsCanFocus(false) , cela définit également la possibilité de focalisation descendante de sorte qu’aucun enfant ne puisse se concentrer. Cela explique pourquoi je ne pouvais pas basculer mItemsCanFocus dans mItemsCanFocus de ListView – car ListView bloquait alors le focus sur tous les enfants.

Ce que j’ai maintenant:

  

J’utilise beforeDescendants car 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 d’abord le focus et dessine les sélecteurs.

Puis, dans OnItemSelectedListener, comme je connais la vue d’en-tête que je veux remplacer par le sélecteur (il faudrait plus de travail pour déterminer dynamicment si une position donnée contient une vue focalisable), je peux modifier la focusabilité descendante et définir FocusText. Et lorsque je quitte cet en-tête, changez-le de 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 appels setItemsCanFocus commentés. Avec ces appels, j’ai eu le bon comportement, mais setItemsCanFocus(false) provoqué le passage du focus à EditText, vers un autre widget en dehors de ListView, vers le ListView et l’affichage du sélecteur sur le prochain élément sélectionné distrayant. Supprimer le ItemsCanFocus change, et le simple fait de basculer la focabilité des descendants m’a permis d’obtenir le comportement souhaité. Tous les éléments dessinent le sélecteur normalement, mais à la ligne EditText, il se concentre sur le champ de texte. Ensuite, en sortant de ce EditText, il a recommencé à dessiner le sélecteur.

Cela m’a aidé
Dans votre manifeste:

  

Ma tâche était d’implémenter ListView qui se développe lorsque l’utilisateur clique dessus. L’espace supplémentaire affiche EditText où vous pouvez saisir du texte. L’application devrait être fonctionnelle sur 2.2+ (jusqu’à 4.2.2 au moment de la rédaction de ce document)

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

Je voulais partager ma solution finale:

  1. définir listview à android:descendantFocusability="afterDescendants"
  2. définir setItemsCanFocus(true); à setItemsCanFocus(true);
  3. définir votre activité sur android:windowSoftInputMode="adjustResize" Beaucoup de gens suggèrent adjustPan mais adjustResize donne beaucoup mieux ux imho, il suffit de tester cela dans votre cas. Avec adjustPan vous obtiendrez des adjustPan base obscures par exemple. Docs suggère que (“Ceci est généralement moins souhaitable que le redimensionnement”). Également sur 4.0.4 après que l’utilisateur commence à taper sur le clavier logiciel l’écran passe au sumt.
  4. sur 4.2.2 avec adjustResize il y a quelques problèmes avec le focus EditText. La solution consiste à appliquer la solution rjrjr à partir de ce thread. Il a l’air effrayant mais ce n’est pas le cas. Et il fonctionne. Essayez-le

Supplémentaire 5. En raison de l’actualisation de l’adaptateur (en raison du redimensionnement de la vue) lorsque EditText concentre sur les versions antérieures à HoneyComb, j’ai trouvé un problème avec les vues inversées: fonctionne sur 4.0.3

Si vous faites des animations, vous pouvez changer le comportement en adjustPan pour les versions pré-nid d’abeilles afin que redimensionner ne déclenche pas et que l’adaptateur ne rafraîchit pas les vues. Vous avez juste besoin d’append 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 un UX acceptable sur les appareils 2.2 - 4.2.2. J'espère que cela permettra aux gens de gagner du temps car il m'a fallu au moins plusieurs heures pour arriver à cette conclusion.

Cela m’a sauvé la vie —>

  1. mettre cette ligne

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Ensuite, dans votre manifeste dans le tag d’activité, saisissez ceci ->

Votre intention habituelle

Nous essayons ceci sur une courte liste qui ne fait aucune vue de recyclage. Jusqu’ici tout va bien.

XML:

    

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, AtsortingbuteSet 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(); } } }); } } 

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

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

Beaucoup d’heures perdues et ce n’est pas une vraie solution. J’espère que ça aide quelqu’un de dur.

Si la liste est dynamic et contient des widgets pouvant être focalisés, la bonne option consiste à utiliser RecyclerView au lieu de ListView IMO.

Les solutions de contournement qui définissent adjustPan , FOCUS_AFTER_DESCENDANTS ou mémorisent manuellement la position ciblée ne sont que des solutions de contournement. Ils ont des casiers de coin (défilement + problèmes de 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 des insertions, des mises à jour et des suppressions individuelles. La vue ciblée n’est pas recréée, donc aucun problème lié aux contrôles de formulaire n’est perdu. En prime, RecyclerView anime les insertions et les retraits d’éléments de liste.

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

Parfois, lorsque vous utilisez android:windowSoftInputMode="stateAlwaysHidden" dans l’activité manifeste ou xml, cette fois, il perdra le focus du clavier. Donc, vérifiez d’abord cette propriété dans votre fichier XML et affichez-la, si c’est le cas, supprimez-la. Après avoir ajouté ces options au fichier manifeste dans side activity android:windowSoftInputMode="adjustPan" et append cette propriété à listview dans xml android:descendantFocusability="beforeDescendants"

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 🙂

La partie la plus importante est de mettre l’ accent sur 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 liste de cellules xml commence comme suit:

   

nextFocusLeft / Right est également important pour la navigation D-Pad.

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

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

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

Essayez ceci

 android:windowSoftInputMode="adjustNothing" 

dans le

activité

section de votre manifeste. Oui, il ajuste les rien, ce qui signifie que le editText restra où il est quand l’IME s’ouvre. Mais ce n’est qu’un petit inconvénient qui résout complètement le problème de la perte de concentration.