Arrêtez ScrollView de défilement automatique à un EditText

Semble être un problème commun sans une excellente solution que j’ai trouvée. L’objective est d’empêcher un ScrollView de défiler automatiquement vers un EditText (ou tout autre affichage) qui a le focus.

Vous avez un tas de vues ( Button s, TextView s, etc.) dans un ScrollView , dont l’un est un EditText . En cliquant sur un bouton dans ScrollView , ScrollView défile vers le EditText (son hors-écran). Cela n’est pas souhaitable, car il y a d’autres éléments que vous ne souhaitez pas faire défiler hors de l’écran.

Maintenant, je peux empêcher que cela ne se produise lorsque l’écran affiche pour la première fois d’autres éléments focalisables dans ScrollView . Cependant, le problème général existe toujours. L’utilisateur fait défiler manuellement vers le EditText , entre des nombres, puis fait défiler vers le haut ( EditText off screen now), ils cliquent sur un bouton dans ScrollView , et devinez quoi? ScrollView défiler vers le bas EditText .

Je pense à l’extension de ScrollView et au ScrollView certaines méthodes comme findFocusableViewInBounds , mais j’ai l’impression que je vais me retrouver avec plus d’ennuis.

S’il vous plait aidez si vous le pouvez.

J’ai joué avec des choses comme avoir un EditText 0 hauteur en haut de mon ScrollView , en ajoutant des propriétés d’élément Next Focusable aux autres éléments du ScrollView , etc. Je suppose qu’un “hack” pourrait avoir pour EditText de perdre le focus sur EditText lorsque le clavier virtuel ou manuel est caché ou quelque chose.

Après avoir lutté contre ce problème pendant un certain temps, j’ai trouvé une solution qui semble fonctionner sans être trop laide. Tout d’abord, assurez-vous que tout ViewGroup (directement) contenant votre EditText a un descendantFocusability défini sur “Before Descendants”, focusable défini sur “true” et focusableInTouchMode défini sur “true”. Ce ne sera pas le ScrollView lui-même, mais la disposition à l’intérieur où vous avez vos différentes vues. Ensuite, ajoutez un onTouchListener à votre ScrollView qui supprime le focus du EditText chaque fois qu’il est touché, comme ceci:

 ScrollView scroll = (ScrollView)findViewById(R.id.scrollView1); scroll.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (myEditText.hasFocus()) { myEditText.clearFocus(); } return false; } }); 

Dis-moi si ça ne règle pas le problème. Ce qui devrait arriver, c’est que la mise en page soit mise au point au lieu du EditText , de sorte qu’aucun défilement ne devrait avoir lieu.

Il suffit de créer une vue vide en haut de linearlayout

  

Une seule ligne résout le problème

J’ai eu le même problème. Il y a un truc que j’utilise pour résoudre ce problème:

 public void onClick(View v) { button.requestFocusFromTouch(); //prevents from loosing focus and scrolling view down .... } 

Le problème ne réside pas dans le code Java, mais dans le code du manifeste.

Dans votre AndroidManifest.xml, ajoutez un atsortingbut à l’activité:

    

En ajoutant 2 parameters dans:

 android:focusable="true" android:focusableInTouchMode="true" 

Dans quelle disposition principale est là.

  

Par ce EditText ne sera pas mis au point automatiquement.

Voici ce que j’ai fait

          

La raison en est que, dans de tels cas, vous devez rendre toutes les autres vues focalisables dans la vue de défilement par un android:focusable="true" explicite android:focusable="true" et ensuite . Cela devrait fonctionner à chaque fois IMO

thomas88wp répondre, https://stackoverflow.com/a/6486348/528746 travaillé pour moi. Mais j’ai eu deux problèmes: 1. Lors du défilement, je voulais cacher le clavier
2. J’avais beaucoup de vues EditText et je ne voulais pas l’écrire pour chacune d’entre elles
(Je reçois getActivity () car j’écris ceci dans un fragment et non une activité)

  ScrollView scroll = (ScrollView)view.findViewById(R.id.layout_scroll); scroll.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // Check if the view with focus is EditText if (getActivity().getCurrentFocus() instanceof EditText) { EditText ed = (EditText)getActivity().getCurrentFocus(); if (ed.hasFocus()) { // Hide the keyboard InputMethodManager inputManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); // Clear the focus ed.clearFocus(); } } return false; } }); 

Mon correctif à ce bug le plus horrible, (notant que ceci est pré API11 seulement où ils ont modifié la méthode de fling pour ne pas être stupide).

L’ancienne méthode de fling trouve le prochain focus auquel elle va accéder, ce qui n’est pas vraiment utile. Les autres versions de cette classe ne fonctionnent pas vraiment car elles arrêtent de se concentrer lorsque l’utilisateur parcourt réellement le formulaire à partir du clavier.

 public class NonFocusingScrollView extends ScrollView { private boolean mBlockRequestFocusOnFling = false; public NonFocusingScrollView(Context context) { super(context); } public NonFocusingScrollView(Context context, AtsortingbuteSet attrs) { super(context, attrs); } public NonFocusingScrollView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public ArrayList getFocusables(int direction) { if(mBlockRequestFocusOnFling) return new ArrayList(); return super.getFocusables(direction); } @Override public void requestChildFocus(View child, View focused) { if(!mBlockRequestFocusOnFling) super.requestChildFocus(child, focused); } @Override public void fling(int velocityY) { mBlockRequestFocusOnFling = true; super.fling(velocityY); mBlockRequestFocusOnFling = false; } } 

Nous pouvons écrire un ScrollView personnalisé, remplacer la méthode onScrollChanged et effacer le focus de la vue ciblée et masquer éventuellement le clavier.

 @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { View v = getFocusedChild(); if (v != null) { InputMethodManager imm = (InputMethodManager) getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); v.clearFocus(); } super.onScrollChanged(l, t, oldl, oldt); } 

Une autre version du code de thomas88wp:

 ScrollView scroll = (ScrollView)getActivity().findViewById(R.id.scrollView_addNewBill); scroll.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View arg0, MotionEvent arg1) {    View focussedView = getCurrentFocus(); if( focussedView != null ) focussedView.clearFocus();       return false; } }); 

J’ai fait un projet de test pour expérimenter les différentes solutions si quelqu’un voulait jouer avec. https://github.com/marchold/EditText-ErrorPopup-Scroll-View

Créez un ScrollView personnalisé (créez une classe et faites-le étendre HorizontalScrollView) et créez un setter de lecture pour le défilement. Puis remplacez computeScrollDeltaToGetChildRectOnScreen.

Comment cela fonctionne: Chaque fois qu’Android a un texte d’édition ou quelque chose en focus qui est hors écran, il appelle la méthode computeScrollDeltaToGetChildRectOnScreen pour le voir. Si vous le remplacez et retournez 0 lorsqu’il est désactivé, il ne défilera pas …

Vous aurez donc une vue de défilement personnalisée comme celle-ci:

  public class TrackableHorizontalScrollView extends HorizontalScrollView { // true if we can scroll (not locked) // false if we cannot scroll (locked) private boolean mScrollable = true; public TrackableHorizontalScrollView(Context context) { super(context); } public TrackableHorizontalScrollView(Context context, AtsortingbuteSet attrs) { super(context, attrs); } public TrackableHorizontalScrollView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setScrollingEnabled(boolean enabled) { mScrollable = enabled; } public boolean isScrollable() { return mScrollable; } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: // if we can scroll pass the event to the superclass if (mScrollable) return super.onTouchEvent(ev); // only continue to handle the touch event if scrolling enabled return mScrollable; // mScrollable is always false at this sharepointfault: return super.onTouchEvent(ev); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // Don't do anything with intercepted touch events if // we are not scrollable if (!mScrollable) return false; else return super.onInterceptTouchEvent(ev); } @Override public void scrollTo(int x, int y){ if (!mScrollable) return; super.scrollTo(x, y); } //Custom smooth scroll method since norm is final and cannot be overridden public final void smooothScrollToIfEnabled(int x, int y){ if (!mScrollable) return; smoothScrollTo(x, y); } @Override protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect rect){ if (!mScrollable) return 0; return super.computeScrollDeltaToGetChildRectOnScreen(rect); } } 

Vous pouvez utiliser ceci dans votre XML comme ceci:

    

J’avais un problème similaire et finalement je l’ai fait fonctionner. Ma vue de défilement contient une série de boutons personnalisés, suivis d’un EditText (qui a normalement le focus, mais je ne veux pas perdre le focus). A chaque fois que les boutons étaient cliqués, la vue de défilement EditText automatiquement vers le EditText ciblé. Le remplacement de public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate) et le retour toujours false (comportement par défaut d’un ViewGroup ) ont fait l’affaire. J’espère que cela vous aidera aussi dans votre situation.

Seul ce code fonctionne pour moi:

 public static void preventScrollViewFromScrollingToEdiText(ScrollView view) { view.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); view.setFocusable(true); view.setFocusableInTouchMode(true); view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { v.requestFocusFromTouch(); return false; } }); } 

Tous les crédits vont à cette réponse originale .

J’ai souvent ce problème lorsque mes applications gèrent le changement d’orientation.

Dans ce cas, j’utilise le type de code suivant:

 @Override protected void onCreate(Bundle savedInstanceState) { ... // to avoid the scrollview to scroll to this element automatically mEditTextSearch.setFocusable(false); // Get the saved scroll position final int scrolly = savedInstanceState.getInt("scrolly"); mScrollView.post(new Runnable() { @Override public void run() { mScrollView.scrollTo(0, scrolly); // Restore the initial state of the EditText mEditTextSearch.setFocusable(true); mEditTextSearch.setFocusableInTouchMode(true); mEditTextSearch.setClickable(true); } }); ... } 

Pour moi, cela n’a pas fonctionné pour remplacer ScrollView onTouch. Aussi ne fonctionnait pas Android: descendantFocusability = “beforeDescendants” android: focusableInTouchMode = “true” Ceci et une autre des solutions mentionnées ne fonctionnaient que pour la première fois – seulement lorsque EditText n’est pas sélectionné, mais une fois que vous le sélectionnez, défilez automatiquement.

Étant donné que j’ai déjà écrit un code pour masquer un clavier lorsque je touche d’autres vues, je viens d’append deux lignes de code et cela fonctionne comme un champion:

 public static void setupUI(final Activity activity, final View view) { //view is the parent view in your layout OnTouchListener mTouchListener = new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { try { View vFocused = null; vFocused = activity.getCurrentFocus(); if (vFocused != null) { hideSoftKeyboard(activity, v); if (vFocused instanceof EditText) { vFocused.clearFocus();//this is the sortingck to avoid ScrollView autoscroll } } } catch (Exception e) { } return false; } }; // Set up touch listener for non-text box views to hide keyboard. if (!(view instanceof EditText) && !(view instanceof ViewGroup)) { view.setOnTouchListener(mTouchListener); } // If a layout container, iterate over children and seed recursion. if (view instanceof ViewGroup) { view.setOnTouchListener(mTouchListener); for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { View innerView = ((ViewGroup) view).getChildAt(i); setupUI(activity, innerView); } } } public static void hideSoftKeyboard(Context context, View v) { InputMethodManager inputMethodManager = (InputMethodManager) context .getSystemService(Activity.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0); } 

l'a également ajouté dans la vue racine:

 android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true" 

Peut-être que ce n'est pas vraiment une bonne solution, mais son fonctionnement.

La meilleure solution consiste à append des options de focus pour l’enfant de votre scrollview:

 android:descendantFocusability="beforeDescendants" android:focusable="true" android:focusableInTouchMode="true" 

Ensuite, votre fichier xml ressemblera à:

         

Ma solution est la suivante, pour tracer le code source et remplacer certaines fonctions pour arrêter le défilement automatique par élément ciblé.

Vous pouvez vérifier si la vue focusedView est TextView ou que son enfant est TextView, en utilisant focusedView.findViewById(R.id.textview_id_you_defined) != null ou focusedView instanceof TextView == true .

 public class StopAutoFocusScrollView extends ScrollView { private View focusedView; private ScrollMonitorListener listener; public interface ScrollMonitorListener { public boolean enableScroll(View view); } public StopAutoFocusScrollView(Context context) { super(context); } public StopAutoFocusScrollView(Context context, AtsortingbuteSet attrs) { super(context, attrs); } public StopAutoFocusScrollView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setScrollMonitorListener(ScrollMonitorListener listener) { this.listener = listener; } @Override public void requestChildFocus(View child, View focused) { focusedView = focused super.requestChildFocus(child, focused); } //flow : requestChildFocus -> scrollToChild -> scrollBy //Therefore, you can give listener to determine you want scroll to or not @Override public void scrollBy(int x, int y) { if (listener == null || listener.enableScroll(focusedView)) { super.scrollBy(x, y); } } } 

J’ai eu une objection légèrement différente à cette déficience exaspérante. Chaque fois que je tapais sur l’un des boutons RadioButt en dessous des EditTexts, la position de défilement sautait pour s’adapter à ce qu’Android déterminait comme étant l’EditText visible et concentré.

Toutes les tentatives pour conserver la position de défilement souhaitée via un Runnable qui a généré ScrollView.scrollTo(x,y) ont été scrupuleusement ignorées par Android!

Je partage ma solution dans l’espoir que cela sauvera quelqu’un d’autre 8 (huit) heures perdues.

 /* This interesting little 'hack' prevents unwanted scroll 'jump' occurring when user touches a RadioButton for example [ Causes focus to change - but maybe this is a lesser evil! ] */ mScrollView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() != MotionEvent.ACTION_UP) return false; mScrollView.clearFocus(); return false; } }); 

Essaye celui-là 🙂

 public class CustomHorizontalScrollView extends HorizontalScrollView { public CustomHorizontalScrollView(Context context) { super(context); } public CustomHorizontalScrollView(Context context, AtsortingbuteSet attrs) { super(context, attrs); } public CustomHorizontalScrollView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onTouchEvent(MotionEvent ev) { return super.onTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return super.onInterceptTouchEvent(ev); } @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); } //Custom smooth scroll method since norm is final and cannot be overridden public final void smooothScrollToIfEnabled(int x, int y) { smoothScrollTo(x, y); } @Override protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect rect) { /* if (getContext() != null && getContext() instanceof Activity) { Activity activity = (Activity) getContext(); if (!activity.isFinishing()) { View view = activity.getCurrentFocus(); if (view != null) { if (view instanceof EditText) { return 0; } } } } return super.computeScrollDeltaToGetChildRectOnScreen(rect); */ return 0; } }