Gibt es eine Möglichkeit, eine Bildlaufansicht programmgesteuert zu einem bestimmten Bearbeitungstext zu scrollen?

206

Ich habe eine sehr lange Aktivität mit einer Bildlaufansicht. Es ist ein Formular mit verschiedenen Feldern, die der Benutzer ausfüllen muss. Ich habe ein Kontrollkästchen in der Mitte meines Formulars, und wenn der Benutzer es aktiviert, möchte ich zu einem bestimmten Teil der Ansicht scrollen. Gibt es eine Möglichkeit, programmgesteuert zu einem EditText-Objekt (oder einem anderen Ansichtsobjekt) zu scrollen?

Ich weiß auch, dass dies mit X- und Y-Koordinaten möglich ist, aber ich möchte dies vermeiden, da sich das Formular von Benutzer zu Benutzer ändern kann.

NotACleverMan
quelle
3
the form may changed from user to useraber Sie können nur verwendenmEditView.getTop();
nebkat
1
Wenn jemand NestedScrollView in CoordinatorLayout verwendet, können Sie über stackoverflow.com/questions/52083678/…
user1506104

Antworten:

451
private final void focusOnView(){
        your_scrollview.post(new Runnable() {
            @Override
            public void run() {
                your_scrollview.scrollTo(0, your_EditBox.getBottom());
            }
        });
    }
Sherif elKhatib
quelle
129
Ich habe festgestellt, dass die Verwendung von glattScrollTo (0, your_EditBox.getTop ()) ein besseres Ergebnis liefert (ersticken Sie das Scrollen zur gewünschten Ansicht), aber ansonsten - eine großartige Antwort.
Muzikant
18
@ xmenW.K. Kurze Antwort: Da die Benutzeroberfläche ihre Arbeit auf der Grundlage einer Reihe von zu erledigenden Aufgaben erledigt und Sie dem Benutzeroberflächenthread im Grunde sagen, wann Sie dies tun können, nachdem Sie alles getan haben, was Sie zuvor tun mussten (scrollen). . Sie stellen die Schriftrolle im Grunde genommen in die Warteschlange und lassen den Thread dies tun, wenn es möglich ist, wobei Sie die Reihenfolge der Aktionen einhalten, die er ausgeführt hat, bevor Sie ihn angefordert haben.
Martin Marconcini
37
Es ist auch möglich, das Runnable in der ScrollView selbst zu veröffentlichen, anstatt einen neuen Handler zu instanziieren:your_scrollview.post(...
Sherif elKhatib
Gibt es eine Methode zum Abrufen des Blickwinkels anstelle von getBottom ()? Denn mein Fall ist googleMap anstelle von EditText.
Zin Win Htet
5
getBottom () und getTop () sind relativ zum Elternteil
cambunctious
55

Die Antwort von Sherif elKhatib kann erheblich verbessert werden, wenn Sie die Ansicht in die Mitte der Bildlaufansicht scrollen möchten . Mit dieser wiederverwendbaren Methode wird die Ansicht sanft in die sichtbare Mitte einer HorizontalScrollView verschoben.

private final void focusOnView(final HorizontalScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int vLeft = view.getLeft();
            int vRight = view.getRight();
            int sWidth = scroll.getWidth();
            scroll.smoothScrollTo(((vLeft + vRight - sWidth) / 2), 0);
        }
    });
}

Für eine vertikale ScrollViewÄnderung der xund yPosition der Eingänge von smoothScroll. Und verwenden Sie view.getTop()anstelle von view.getLeft()und view.getBottom()anstelle von v.getRight().

:) :)

ahmadalibaloch
quelle
3
Sie müssen Ihren Code anpassen. Sie müssen die Breite der Bildlaufansicht ermitteln und die Formel in sv.smoothScrollTo (((vLeft + vRight) / 2) - (svWidth / 2), 0) ändern.
Grundschule
2
Andernfalls wird die Ansicht in der scrollView nicht zentriert. Ich habe die Bearbeitung bereits für Sie durchgeführt.
Grundschule
Haben Sie überprüft,
ob
1
@Elementary Ihre und ahmads Formeln sind vereinfachte und erweiterte Versionen desselben algebraischen Ausdrucks. Anpassungen sind nicht erforderlich.
29.
1
@ k29 beziehen Sie sich hier auf die beiden sichtbaren Formeln? Sie sind beide von mir, ich habe die Formel meines Kommentars weiter vereinfacht und die Antwort entsprechend bearbeitet. Aber der Rest ist immer noch der gleiche wie bei der ursprünglichen Antwort und war auch für mich sehr hilfreich.
Grundschule
51

Das funktioniert gut für mich:

  targetView.getParent().requestChildFocus(targetView,targetView);

public void RequestChildFocus (Kind anzeigen, Fokussiert anzeigen)

Kind - Das Kind dieses ViewParent, das den Fokus haben möchte. Diese Ansicht enthält die fokussierte Ansicht. Es ist nicht unbedingt die Ansicht, die tatsächlich den Fokus hat.

fokussiert - Die Ansicht, die ein Nachkomme eines Kindes ist, das tatsächlich fokussiert ist

Swas_99
quelle
Sehen Sie Art von Sprüngen in jeder glatten Schriftrolle
silentsudo
32

Meiner Meinung nach ist der beste Weg, um zu einem bestimmten Rechteck zu scrollen, via View.requestRectangleOnScreen(Rect, Boolean). Sie sollten es auf einem ViewBild aufrufen, zu dem Sie einen Bildlauf durchführen möchten, und ein lokales Rechteck übergeben, das auf dem Bildschirm sichtbar sein soll. Der zweite Parameter sollte falsefür das reibungslose Scrollen und truefür das sofortige Scrollen sein.

final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, false);
Michael
quelle
1
Arbeitete für mich in einem Ansichtspager. Es gibt eine Bildlaufansicht und Sie müssen zu einer Ansicht scrollen, die jetzt mit dem hier genannten Code funktioniert.
Pankaj
2
Auf jeden Fall sollte dies die akzeptierte Antwort sein, scrollview.smoothScrollTowird nicht funktionieren, wenn die erforderliche Ansicht außerhalb des Bildschirms ist (versucht im Android-Emulator mit SDK-Version 26)
Sony
@ Michael Soll ich einpacken new Handler().post()?
Johnny Five
@JohnnyFive Es hängt vom Kontext ab, in dem Sie diesen Code verwenden.
Michael
12

Ich habe eine kleine Dienstprogrammmethode basierend auf Answer from WarrenFaith erstellt. Dieser Code berücksichtigt auch, ob diese Ansicht bereits in der Bildlaufansicht sichtbar ist und kein Bildlauf erforderlich ist.

public static void scrollToView(final ScrollView scrollView, final View view) {

    // View needs a focus
    view.requestFocus();

    // Determine if scroll needs to happen
    final Rect scrollBounds = new Rect();
    scrollView.getHitRect(scrollBounds);
    if (!view.getLocalVisibleRect(scrollBounds)) {
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                scrollView.smoothScrollTo(0, view.getBottom());
            }
        });
    } 
}
Tobrun
quelle
Ich denke, scrollBoundssollte der Parameter für die scrollView.getHitRectMethode sein.
Vijay C
10

Sie sollten Ihre TextViewAnfrage fokussieren:

    mTextView.requestFocus();
Ovidiu Latcu
quelle
7

Mein EditText war mehrere Ebenen in meiner ScrollView verschachtelt, die selbst nicht die Stammansicht des Layouts ist. Da getTop () und getBottom () die Koordinaten in der enthaltenen Ansicht zu melden schienen, ließ ich den Abstand vom oberen Rand der ScrollView zum oberen Rand des EditText berechnen, indem ich die übergeordneten Elemente des EditText durchlief.

// Scroll the view so that the touched editText is near the top of the scroll view
new Thread(new Runnable()
{
    @Override
    public
    void run ()
    {
        // Make it feel like a two step process
        Utils.sleep(333);

        // Determine where to set the scroll-to to by measuring the distance from the top of the scroll view
        // to the control to focus on by summing the "top" position of each view in the hierarchy.
        int yDistanceToControlsView = 0;
        View parentView = (View) m_editTextControl.getParent();
        while (true)
        {
            if (parentView.equals(scrollView))
            {
                break;
            }
            yDistanceToControlsView += parentView.getTop();
            parentView = (View) parentView.getParent();
        }

        // Compute the final position value for the top and bottom of the control in the scroll view.
        final int topInScrollView = yDistanceToControlsView + m_editTextControl.getTop();
        final int bottomInScrollView = yDistanceToControlsView + m_editTextControl.getBottom();

        // Post the scroll action to happen on the scrollView with the UI thread.
        scrollView.post(new Runnable()
        {
            @Override
            public void run()
            {
                int height =m_editTextControl.getHeight();
                scrollView.smoothScrollTo(0, ((topInScrollView + bottomInScrollView) / 2) - height);
                m_editTextControl.requestFocus();
            }
        });
    }
}).start();
Verletzter Schnabeltier
quelle
5

Eine andere Variante wäre:

scrollView.postDelayed(new Runnable()
{
    @Override
    public void run()
    {
        scrollView.smoothScrollTo(0, img_transparent.getTop());
    }
}, 2000);

oder Sie können die post()Methode verwenden.

Goran Horia Mihail
quelle
5

Ich weiß, dass dies für eine bessere Antwort zu spät sein kann, aber eine gewünschte perfekte Lösung muss ein System wie ein Positionierer sein. Ich meine, wenn das System eine Position für ein Editor-Feld vornimmt, platziert es das Feld nur bis zur Tastatur, so dass es nach UI / UX-Regeln perfekt ist.

Was der folgende Code macht, ist die reibungslose Positionierung von Android. Zunächst behalten wir den aktuellen Bildlaufpunkt als Referenzpunkt bei. Zweitens müssen Sie den besten Positionierungs-Bildlaufpunkt für einen Editor finden. Dazu scrollen wir nach oben und fordern die Editorfelder auf, die ScrollView-Komponente so zu gestalten, dass sie die beste Positionierung ausführt. Gatcha! Wir haben die beste Position gelernt. Jetzt scrollen wir reibungslos vom vorherigen Punkt zu dem Punkt, den wir neu gefunden haben. Wenn Sie möchten, können Sie das reibungslose Scrollen unterlassen, indem Sie scrollTo anstelle vonoothScrollTo verwenden .

HINWEIS: Der Hauptcontainer ScrollView ist ein Mitgliedsfeld mit dem Namen scrollViewSignup, da mein Beispiel ein Anmeldebildschirm war, wie Sie vielleicht viel herausfinden.

view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(final View view, boolean b) {
            if (b) {
                scrollViewSignup.post(new Runnable() {
                    @Override
                    public void run() {
                        int scrollY = scrollViewSignup.getScrollY();
                        scrollViewSignup.scrollTo(0, 0);
                        final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
                        view.requestRectangleOnScreen(rect, true);

                        int new_scrollY = scrollViewSignup.getScrollY();
                        scrollViewSignup.scrollTo(0, scrollY);
                        scrollViewSignup.smoothScrollTo(0, new_scrollY);
                    }
                });
            }
        }
    });

Wenn Sie diesen Block für alle EditText- Instanzen verwenden und ihn schnell in Ihren Bildschirmcode integrieren möchten . Sie können einfach einen Traverser wie unten erstellen. Zu diesem Zweck habe ich den OnFocusChangeListener-Hauptbereich zu einem Mitgliedsfeld mit dem Namen focusChangeListenerToScrollEditor gemacht und ihn während onCreate wie folgt aufgerufen.

traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);

Und die Methodenimplementierung ist wie folgt.

private void traverseEditTextChildren(ViewGroup viewGroup, View.OnFocusChangeListener focusChangeListenerToScrollEditor) {
    int childCount = viewGroup.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View view = viewGroup.getChildAt(i);
        if (view instanceof EditText)
        {
            ((EditText) view).setOnFocusChangeListener(focusChangeListenerToScrollEditor);
        }
        else if (view instanceof ViewGroup)
        {
            traverseEditTextChildren((ViewGroup) view, focusChangeListenerToScrollEditor);
        }
    }
}

Wir haben hier also alle EditText-Instanzkinder dazu gebracht, den Listener im Fokus aufzurufen.

Um diese Lösung zu erreichen, habe ich alle Lösungen hier überprüft und eine neue Lösung für ein besseres UI / UX-Ergebnis generiert.

Vielen Dank an alle anderen Antworten, die mich sehr inspiriert haben.
Suphi ÇEVİKER
quelle
3

Die obigen Antworten funktionieren einwandfrei, wenn ScrollView das direkte übergeordnete Element von ChildView ist. Wenn Ihre ChildView in eine andere ViewGroup in der ScrollView eingeschlossen wird, führt dies zu unerwartetem Verhalten, da View.getTop () die Position relativ zu der übergeordneten Version erhält. In diesem Fall müssen Sie Folgendes implementieren:

public static void scrollToInvalidInputView(ScrollView scrollView, View view) {
    int vTop = view.getTop();

    while (!(view.getParent() instanceof ScrollView)) {
        view = (View) view.getParent();
        vTop += view.getTop();
    }

    final int scrollPosition = vTop;

    new Handler().post(() -> scrollView.smoothScrollTo(0, scrollPosition));
}
Sam Fisher
quelle
Dies ist die umfassendste Lösung, da sie auch die Positionen der Eltern berücksichtigt. Danke fürs Schreiben!
Gustavo Baiocchi Costa
2

Ich denke, ich habe eine elegantere und weniger fehleranfällige Lösung gefunden

ScrollView.requestChildRectangleOnScreen

Es ist keine Mathematik erforderlich, und im Gegensatz zu anderen vorgeschlagenen Lösungen wird das korrekte Scrollen sowohl nach oben als auch nach unten behandelt.

/**
 * Will scroll the {@code scrollView} to make {@code viewToScroll} visible
 * 
 * @param scrollView parent of {@code scrollableContent}
 * @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
 * @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
 */
void scrollToView(ScrollView scrollView, ViewGroup scrollableContent, View viewToScroll) {
    Rect viewToScrollRect = new Rect(); //coordinates to scroll to
    viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout) 
    scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
}

Es ist eine gute Idee, es einzuwickeln postDelayed, um es zuverlässiger zu machen, falls das ScrollViewgerade geändert wird

/**
 * Will scroll the {@code scrollView} to make {@code viewToScroll} visible
 * 
 * @param scrollView parent of {@code scrollableContent}
 * @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
 * @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
 */
private void scrollToView(final ScrollView scrollView, final ViewGroup scrollableContent, final View viewToScroll) {
    long delay = 100; //delay to let finish with possible modifications to ScrollView
    scrollView.postDelayed(new Runnable() {
        public void run() {
            Rect viewToScrollRect = new Rect(); //coordinates to scroll to
            viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout) 
            scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
        }
    }, delay);
}
Štarke
quelle
1

Wenn Sie den Android-Quellcode untersuchen, können Sie feststellen, dass es bereits eine Mitgliedsfunktion von ScrollView- scrollToChild(View)- gibt, die genau das tut, was angefordert wird. Leider ist diese Funktion aus einem unbekannten Grund markiert private. Basierend auf dieser Funktion habe ich folgende Funktion geschrieben, die die erste ScrollViewüber dem Viewals Parameter angegebenen Funktion findet und sie so scrollt, dass sie im folgenden Bereich sichtbar wird ScrollView:

 private void make_visible(View view)
 {
  int vt = view.getTop();
  int vb = view.getBottom();

  View v = view;

  for(;;)
     {
      ViewParent vp = v.getParent();

      if(vp == null || !(vp instanceof ViewGroup))
         break;

      ViewGroup parent = (ViewGroup)vp;

      if(parent instanceof ScrollView)
        {
         ScrollView sv = (ScrollView)parent;

         // Code based on ScrollView.computeScrollDeltaToGetChildRectOnScreen(Rect rect) (Android v5.1.1):

         int height = sv.getHeight();
         int screenTop = sv.getScrollY();
         int screenBottom = screenTop + height;

         int fadingEdge = sv.getVerticalFadingEdgeLength();

         // leave room for top fading edge as long as rect isn't at very top
         if(vt > 0)
            screenTop += fadingEdge;

         // leave room for bottom fading edge as long as rect isn't at very bottom
         if(vb < sv.getChildAt(0).getHeight())
            screenBottom -= fadingEdge;

         int scrollYDelta = 0;

         if(vb > screenBottom && vt > screenTop) 
           {
            // need to move down to get it in view: move down just enough so
            // that the entire rectangle is in view (or at least the first
            // screen size chunk).

            if(vb-vt > height) // just enough to get screen size chunk on
               scrollYDelta += (vt - screenTop);
            else              // get entire rect at bottom of screen
               scrollYDelta += (vb - screenBottom);

             // make sure we aren't scrolling beyond the end of our content
            int bottom = sv.getChildAt(0).getBottom();
            int distanceToBottom = bottom - screenBottom;
            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
           }
         else if(vt < screenTop && vb < screenBottom) 
           {
            // need to move up to get it in view: move up just enough so that
            // entire rectangle is in view (or at least the first screen
            // size chunk of it).

            if(vb-vt > height)    // screen size chunk
               scrollYDelta -= (screenBottom - vb);
            else                  // entire rect at top
               scrollYDelta -= (screenTop - vt);

            // make sure we aren't scrolling any further than the top our content
            scrollYDelta = Math.max(scrollYDelta, -sv.getScrollY());
           }

         sv.smoothScrollBy(0, scrollYDelta);
         break;
        }

      // Transform coordinates to parent:
      int dy = parent.getTop()-parent.getScrollY();
      vt += dy;
      vb += dy;

      v = parent;
     }
 }
Jaromír Adamec
quelle
1

Meine Lösung ist:

            int[] spinnerLocation = {0,0};
            spinner.getLocationOnScreen(spinnerLocation);

            int[] scrollLocation = {0, 0};
            scrollView.getLocationInWindow(scrollLocation);

            int y = scrollView.getScrollY();

            scrollView.smoothScrollTo(0, y + spinnerLocation[1] - scrollLocation[1]);
w4kskl
quelle
1
 scrollView.post(new Runnable() {
                    @Override
                    public void run() {
                        scrollView.smoothScrollTo(0, myTextView.getTop());

                    }
                });

Antwort aus meinem praktischen Projekt.

Geben Sie hier die Bildbeschreibung ein

XpressGeek
quelle
0

In meinem Fall ist das nicht EditText, das ist googleMap. Und so funktioniert es erfolgreich.

    private final void focusCenterOnView(final ScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int centreX=(int) (view.getX() + view.getWidth()  / 2);
            int centreY= (int) (view.getY() + view.getHeight() / 2);
            scrollView.smoothScrollBy(centreX, centreY);
        }
    });
}
Zin Win Htet
quelle
0

Folgendes verwende ich:

int amountToScroll = viewToShow.getBottom() - scrollView.getHeight() + ((LinearLayout.LayoutParams) viewToShow.getLayoutParams()).bottomMargin;
// Check to see if scrolling is necessary to show the view
if (amountToScroll > 0){
    scrollView.smoothScrollTo(0, amountToScroll);
}

Dadurch wird der Bildlaufbetrag abgerufen, der erforderlich ist, um den unteren Rand der Ansicht anzuzeigen, einschließlich aller Ränder am unteren Rand dieser Ansicht.

LukeWaggoner
quelle
0

Vertikale Schriftrolle, gut für Formulare. Die Antwort basiert auf der horizontalen Schriftrolle von Ahmadalibaloch.

private final void focusOnView(final HorizontalScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int top = view.getTop();
            int bottom = view.getBottom();
            int sHeight = scroll.getHeight();
            scroll.smoothScrollTo(0, ((top + bottom - sHeight) / 2));
        }
    });
}
Qamar
quelle
Sind Sie sicher, dass Sie HorizontalScrollViewbei dieser Methode ein verwenden sollten?
Shahood ul Hassan
0

Sie können ObjectAnimatorwie folgt verwenden:

ObjectAnimator.ofInt(yourScrollView, "scrollY", yourView.getTop()).setDuration(1500).start();
Tazeem Siddiqui
quelle
0

Basierend auf Sherifs Antwort funktionierte das Folgende am besten für meinen Anwendungsfall. Bemerkenswerte Änderungen sind getTop()anstelle von getBottom()und smoothScrollTo()anstelle von scrollTo().

    private void scrollToView(final View view){
        final ScrollView scrollView = findViewById(R.id.bookmarksScrollView);
        if(scrollView == null) return;

        scrollView.post(new Runnable() {
            @Override
            public void run() {
                scrollView.smoothScrollTo(0, view.getTop());
            }
        });
    }
Datchung
quelle
-1

Wenn scrlMain Ihre NestedScrollView ist, verwenden Sie Folgendes:

scrlMain.post(new Runnable() {
                    @Override
                    public void run() {
                        scrlMain.fullScroll(View.FOCUS_UP);

                    }
                });
Ashwin Balani
quelle