web-dev-qa-db-fra.com

Faites défiler jusqu'à une vue spécifique dans la vue de défilement

J'ai ajouté un scrollview et les sous-enfants à l'intérieur du scrollview. À un moment donné, j'ai besoin de faire défiler jusqu'à une vue spécifique.

<scrollview>

1. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

2. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

3. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

4. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

5. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

6. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

7. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

   <button>
   </button>

 </scrollview>

La mise en page ci-dessus a été créée dynamiquement. donc je ne peux pas avoir le fichier XML posté ici. La mise en page est complètement dynamique. Même le nombre de vues d'enfants dans la représentation linéaire peut également varier.

Ainsi, lorsque je clique sur le bouton, je dois faire défiler l'écran vers une vue particulière, dire ici lorsque je clique sur le bouton, il me faut faire défiler jusqu'à la disposition linéaire 4.. scrollview. 

S'il vous plaît fournir quelques suggestions.

38
Rajagopal

Cela devrait faire l'affaire: 

View targetView = findViewById(R.id.DESIRED_VIEW_ID);  
targetView.getParent().requestChildFocus(targetView,targetView);

public void RequestChildFocus (Afficher un enfant, Voir ciblé)

enfant - Enfant de ce ViewParent qui veut se concentrer. Cette vue contiendra la vue focalisée. Ce n'est pas nécessairement la vue qui a réellement le focus.

focalisé - La vue qui est un descendant d'enfant qui a réellement le focus

81
Swas_99

Si un enfant vers lequel nous devons faire défiler n'est pas un enfant direct, les solutions ci-dessus ne fonctionnent pas

J'ai utilisé la solution ci-dessous dans mon projet, le partager peut être utile aux autres

/**
 * Used to scroll to the given view.
 *
 * @param scrollViewParent Parent ScrollView
 * @param view View to which we need to scroll.
 */
private void scrollToView(final ScrollView scrollViewParent, final View view) {
    // Get deepChild Offset
    Point childOffset = new Point();
    getDeepChildOffset(scrollViewParent, view.getParent(), view, childOffset);
    // Scroll to child.
    scrollViewParent.smoothScrollTo(0, childOffset.y);
}

/**
 * Used to get deep child offset.
 * <p/>
 * 1. We need to scroll to child in scrollview, but the child may not the direct child to scrollview.
 * 2. So to get correct child position to scroll, we need to iterate through all of its parent views till the main parent.
 *
 * @param mainParent        Main Top parent.
 * @param parent            Parent.
 * @param child             Child.
 * @param accumulatedOffset Accumulated Offset.
 */
private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset) {
    ViewGroup parentGroup = (ViewGroup) parent;
    accumulatedOffset.x += child.getLeft();
    accumulatedOffset.y += child.getTop();
    if (parentGroup.equals(mainParent)) {
        return;
    }
    getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}
78
Vasanth

essaye ça:

ScrollView sv = // your scrollview
View insideView = // find the View that you need to scroll to which is inside this ScrollView
sv.scrollTo(0, (int)insideView.getY());

insideView.getY() ne fonctionnera pas en dessous du niveau 11 de l'API. Pour un niveau inférieur à 11, vous pouvez remplacer insideView.getY() par insideView.getTop().

20
Dev

Je pense avoir trouvé une solution plus élégante et sujette aux erreurs en utilisant 

ScrollView.requestChildRectangleOnScreen

Pas de math, et contrairement aux autres solutions proposées, il gérera correctement le défilement dans les deux sens.

void scrollToRow(ScrollView scrollView, LinearLayout linearLayout, TextView textViewToShow) {
    Rect textRect = new Rect(); //coordinates to scroll to
    textViewToShow.getHitRect(textRect); //fills textRect with coordinates of TextView relative to its parent (LinearLayout) 
    scrollView.requestChildRectangleOnScreen(linearLayout, textRect, false); //ScrollView will make sure, the given textRect is visible
}

C'est une bonne idée de l'envelopper dans postDelayed pour le rendre plus fiable, au cas où la ScrollView serait modifiée pour le moment.

private void scrollToRow(final ScrollView scrollView, final LinearLayout linearLayout, final TextView textViewToShow) {
    long delay = 100; //delay to let finish with possible modifications to ScrollView
    scrollView.postDelayed(new Runnable() {
        public void run() {
            Rect textRect = new Rect(); //coordinates to scroll to
            textViewToShow.getHitRect(textRect); //fills textRect with coordinates of TextView relative to its parent (LinearLayout) 
            scrollView.requestChildRectangleOnScreen(linearLayout, textRect, false); //ScrollView will make sure, the given textRect is visible
        }
    }, delay);
}

Juste Nice n'est pas?

8
Štarke

Puisque vous pouvez connaître l'enfant LinearLayout que vous devez faire défiler, pourquoi ne pas essayer ceci.

ScrollView sv = // your scrollview
View highlightedItem = // find the LinearLayout or View that you need to scroll to which is inside this ScrollView
sv.scrollTo(0, highlightedItem.getY());

Plus d'informations sur scrollTo ainsi que smoothScrollTo

6
midhunhk

Ici, rootView est votre vue parent et childView est une vue où vous souhaitez faire défiler votre vue.

public static int getRelativeTop(View rootView, View childView) {
    if(childView.getParent() == rootView) return childView.getTop();
    else return childView.getTop() + getRelativeTop(rootView, (View) childView.getParent());
}
0
ad joy

Utilisation :

final  ScrollView scrollView= (ScrollView) 
int speed=1000;
findViewById(R.id.main_scrollView);
final View view = findViewById(R.id.your_view); 
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this);   
                ObjectAnimator.ofInt(scrollView, "scrollY",  (int) view.getY()).setDuration(speed).start();
            }
        }); 
0
Mohammad Javad

Voici une solution qui fonctionne si la vue cible n'est pas un enfant direct de votre ScrollView:

public int findYPositionInView (View rootView, View targetView)
{
  return findYPositionInView (rootView, targetView, 0);
}


// returns -1 if targetView not found
private int findYPositionInView (View rootView, View targetView, int yCumulative)
{
  if (rootView == targetView)
    return yCumulative;

  if (rootView instanceof ViewGroup)
  {
    ViewGroup parentView = (ViewGroup)rootView;
    for (int i = 0;  i < parentView.getChildCount ();  i++)
    {
      View child = parentView.getChildAt (i);
      int yChild = yCumulative + (int)child.getY ();

      int yNested = findYPositionInView (child, targetView, yChild);
      if (yNested != -1)
        return yNested;
    }
  }

  return -1; // not found
}

Utilisez-le comme ceci:

int yScroll = findYPositionInView (scrollView, targetView);
scrollView.scrollTo (0, yScroll);

De plus, si vous souhaitez définir le focus, procédez comme suit:

targetView.requestFocus ();

Et si vous voulez que le clavier apparaisse, faites ceci:

if (targetView instanceof EditText)
{
  targetView.post (new Runnable ()
  {
    @Override public void run ()
    {
      InputMethodManager imm = (InputMethodManager)context.getSystemService (Context.INPUT_METHOD_SERVICE);
      imm.showSoftInput (targetView, InputMethodManager.SHOW_FORCED);
    }
  });
}
0
Peri Hartman

J'ai obtenu le résultat souhaité en utilisant requestChildFocus () comme suggéré par la réponse de Swas_99 ci-dessus .

Cependant, comme le défilement n'est pas fluide, j'ai examiné le code source de requestChildFocus qui appelle scrollToChild qui appelle offsetDescendantRectToMyCoords . À partir de là, j'ai eu l'idée d'utiliser le code C #/Xamarin suivant, qui donne un effet similaire à celui de requestChildFocus (), mais avec un défilement régulier:

private static void ScrollToView(View ArgTarget) {
    var CurParent = ArgTarget.Parent;

    while (CurParent != null) {
      var ScrollArea = CurParent as ScrollView;

      if (ScrollArea != null) {
        var ViewRect = new Rect();
        ArgTarget.GetDrawingRect(ViewRect);

        ScrollArea.OffsetDescendantRectToMyCoords(ArgTarget, ViewRect);
        ScrollArea.SmoothScrollTo(ViewRect.Left, ViewRect.Top);

        break;
      }

      CurParent = CurParent.Parent;
   }
}

L'idée de base est de trouver la vue cible contenant ScrollView. Ensuite, récupérez le rectangle de dessin de la vue cible, ajustez ses coordonnées pour qu'il soit compris par ScrollView, puis demandez à ScrollView de faire défiler l'écran jusqu'à la position souhaitée. L'effet final est que la vue vers laquelle vous souhaitez faire défiler finit toujours par être entièrement visible près du haut de l'écran (vous pouvez jouer avec ViewRect.Top si vous souhaitez plutôt centrer la vue sur l'écran).

0
Eric Mutta