Apakah ada cara untuk menggulir tampilan gulir secara terprogram ke teks edit tertentu?

206

Saya memiliki aktivitas yang sangat panjang dengan scrollview. Ini adalah formulir dengan berbagai bidang yang harus diisi oleh pengguna. Saya memiliki kotak centang setengah jalan ke formulir saya, dan ketika pengguna memeriksanya saya ingin menggulir ke bagian tertentu dari tampilan. Apakah ada cara untuk menggulir ke objek EditText (atau objek tampilan lainnya) secara terprogram?

Juga, saya tahu ini mungkin menggunakan koordinat X dan Y tetapi saya ingin menghindari melakukan ini karena formulir dapat berubah dari pengguna ke pengguna.

NotACleverMan
sumber
3
the form may changed from user to usertetapi Anda bisa menggunakanmEditView.getTop();
nebkat
1
jika seseorang menggunakan NestedScrollView di dalam CoordinatorLayout, Anda dapat menggulir ke tampilan tertentu melalui stackoverflow.com/questions/52083678/…
user1506104

Jawaban:

451
private final void focusOnView(){
        your_scrollview.post(new Runnable() {
            @Override
            public void run() {
                your_scrollview.scrollTo(0, your_EditBox.getBottom());
            }
        });
    }
Sherif elKhatib
sumber
129
Saya menemukan bahwa menggunakan smoothScrollTo (0, your_EditBox.getTop ()) mendapatkan hasil yang lebih baik (lebih baik menggulir ke tampilan yang diinginkan) tetapi selain itu - jawaban yang bagus.
Muzikant
18
@ xmenW.K. Jawaban Singkat: karena UI melakukan tugasnya berdasarkan antrian untuk melakukan sesuatu, dan Anda pada dasarnya memberi tahu utas UI, ketika Anda bisa, setelah Anda melakukan semua yang harus Anda lakukan sebelumnya sekarang, lakukan ini (gulir) . Anda pada dasarnya menempatkan gulungan itu dalam antrian dan membiarkan utas melakukannya kapan pun bisa, dengan menghormati urutan hal-hal yang dilakukannya sebelum Anda memintanya.
Martin Marconcini
37
Dimungkinkan juga untuk memposting Runnable ke ScrollView itu sendiri alih-alih instantiating Handler baru:your_scrollview.post(...
Sherif elKhatib
Apakah ada metode untuk mendapatkan pusat tampilan daripada getBottom ()? karena case saya adalah googleMap, bukan EditText.
Zin Win Htet
5
getBottom () dan getTop () relatif terhadap orang tua
cambunctious
57

Jawaban Sherif elKhatib dapat sangat ditingkatkan, jika Anda ingin menggulir tampilan ke tengah tampilan gulir . Metode yang dapat digunakan kembali ini menggulung tampilan ke pusat HorizontalScrollView yang terlihat.

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

Untuk ScrollViewpenggunaan vertikal

...
int vTop = view.getTop();
int vBottom = view.getBottom();
int sHeight = scroll.getBottom();
scroll.smoothScrollTo(((vTop + vBottom - sHeight) / 2), 0);
...
ahmadalibaloch
sumber
3
Anda harus menyesuaikan kode Anda. Anda harus mendapatkan lebar tampilan gulir dan Anda harus mengubah rumus menjadi sv.smoothScrollTo (((vLeft + vRight) / 2) - (svWidth / 2), 0);
Elementary
2
Kalau tidak, tampilan di scrollView tidak akan terpusat. Saya sudah melakukan pengeditan untuk Anda.
Elementary
Apakah Anda memeriksa rumus yang bekerja dengan tepat, karena yang lebih tua bekerja dengan baik untuk saya
ahmadalibaloch
1
@Elementary Rumus Anda dan ahmads adalah versi yang disederhanakan dan diperluas dari ekspresi aljabar yang sama, tidak perlu untuk penyesuaian.
k29
1
@ k29 Anda mengacu pada dua rumus yang terlihat di sini? Mereka berdua dari saya, saya selanjutnya menyederhanakan rumus komentar saya dan mengedit jawabannya. Tetapi semua sisanya masih sama dari jawaban yang asli dan juga sangat membantu bagi saya.
Elementary
51

Ini bekerja dengan baik untuk saya:

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

public void RequestChildFocus (Lihat anak, Lihat fokus)

child - Anak dari ViewParent ini yang ingin fokus. Tampilan ini akan berisi tampilan fokus. Belum tentu pandangan yang benar-benar memiliki fokus.

terfokus - Pandangan yang merupakan keturunan anak yang sebenarnya memiliki fokus

Swas_99
sumber
Lihat jenis lompatan dalam gulungan halus mana pun
silentsudo
32

Menurut pendapat saya cara terbaik untuk menggulir ke persegi panjang yang diberikan adalah melalui View.requestRectangleOnScreen(Rect, Boolean). Anda harus menyebutnya di tempat Viewyang ingin Anda gulir dan melewati kotak lokal yang Anda ingin terlihat di layar. Parameter kedua harus falseuntuk pengguliran halus dan trueuntuk pengguliran segera.

final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, false);
Michael
sumber
1
Bekerja untuk saya di dalam pager tampilan ada scrollview dan perlu gulir ke tampilan yang berfungsi sekarang menggunakan kode yang disebutkan di sini.
Pankaj
2
Jelas jawaban ini harus diterima, scrollview.smoothScrollTotidak akan berfungsi jika tampilan yang diminta tidak aktif, (dicoba di emulator android dengan versi SD 26)
Sony
@Michael Haruskah saya membungkus new Handler().post()?
Johnny Five
@JohnnyFive Tergantung pada konteks di mana Anda menggunakan kode ini.
Michael
12

Saya membuat metode utilitas kecil berdasarkan Answer dari WarrenFaith, kode ini juga memperhitungkan jika tampilan itu sudah terlihat di scrollview, tidak perlu untuk scroll.

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
sumber
Saya kira scrollBoundsharus menjadi parameter untuk scrollView.getHitRectmetode ini.
Vijay C
10

Anda harus membuat TextViewfokus permintaan Anda :

    mTextView.requestFocus();
Ovidiu Latcu
sumber
7

EditText saya bersarang beberapa lapisan di dalam ScrollView saya, yang itu sendiri bukan tampilan root tata letak. Karena getTop () dan getBottom () tampaknya melaporkan koordinat di dalamnya yang berisi tampilan, saya sudah menghitung jarak dari bagian atas ScrollView ke bagian atas EditText dengan beralih melalui orang tua dari EditText.

// 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();
Platypus Terluka
sumber
5

Varition lain adalah:

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

atau Anda dapat menggunakan post()metode ini.

Goran Horia Mihail
sumber
5

Saya tahu ini mungkin sudah terlambat untuk jawaban yang lebih baik tetapi solusi sempurna yang diinginkan harus berupa sistem seperti positioner. Maksudku, ketika sistem membuat posisi untuk bidang Editor itu menempatkan bidang hanya ke keyboard, sehingga UI / UX aturan itu sempurna.

Apa yang membuat kode di bawah ini adalah cara penempatan Android dengan lancar. Pertama-tama kita menjaga titik gulir saat ini sebagai titik referensi. Hal kedua adalah menemukan titik gulir penentuan posisi terbaik untuk seorang editor, untuk melakukan ini kita gulir ke atas, dan kemudian meminta bidang editor untuk membuat komponen ScrollView untuk melakukan penentuan posisi terbaik. Gatcha! Kami telah mempelajari posisi terbaik. Sekarang, apa yang akan kita lakukan adalah menggulir dengan lancar dari titik sebelumnya ke titik yang baru kita temukan. Jika mau, Anda dapat menghilangkan pengguliran yang lancar dengan menggunakan scrollTo alih-alih smoothScrollTo saja.

CATATAN: Wadah utama ScrollView adalah bidang anggota bernama scrollViewSignup, karena contoh saya adalah layar pendaftaran, karena Anda mungkin sering tahu.

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

Jika Anda ingin menggunakan blok ini untuk semua instance EditText , dan cepat mengintegrasikannya dengan kode layar Anda. Anda cukup membuat traverser seperti di bawah ini. Untuk melakukan ini, saya telah membuat OnFocusChangeListener utama bidang anggota bernama focusChangeListenerToScrollEditor , dan menyebutnya selama onCreate seperti di bawah ini.

traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);

Dan implementasinya adalah sebagai berikut.

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

Jadi, apa yang kami lakukan di sini adalah menjadikan semua anak contoh EditText untuk memanggil pendengar sebagai fokus.

Untuk mencapai solusi ini, saya telah memeriksanya semua solusi di sini, dan menghasilkan solusi baru untuk hasil UI / UX yang lebih baik.

Terima kasih banyak untuk semua jawaban lain yang menginspirasi saya.
Suphi ÇEVİKER
sumber
3

Jawaban di atas akan berfungsi dengan baik jika ScrollView adalah induk langsung dari ChildView. Jika ChildView Anda sedang dibungkus dalam ViewGroup lain di ScrollView, itu akan menyebabkan perilaku yang tidak terduga karena View.getTop () mendapatkan posisi relatif terhadap induknya. Dalam hal demikian, Anda perlu menerapkan ini:

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
sumber
Ini adalah solusi yang paling komprehensif karena juga memperhitungkan posisi orang tua. Terima kasih untuk posting!
Gustavo Baiocchi Costa
2

Saya pikir saya telah menemukan solusi yang lebih elegan dan lebih sedikit kesalahan menggunakan

ScrollView.requestChildRectangleOnScreen

Tidak ada matematika yang terlibat, dan bertentangan dengan solusi yang diusulkan lainnya, itu akan menangani dengan benar menggulir ke atas dan ke bawah.

/**
 * 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
}

Adalah ide yang baik untuk membungkusnya postDelayedagar lebih andal, jika ScrollViewsedang diubah saat ini

/**
 * 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
sumber
1

Meneliti kode sumber Android, Anda dapat menemukan bahwa sudah ada fungsi anggota ScrollView- scrollToChild(View)- yang melakukan persis apa yang diminta. Sayangnya, fungsi ini karena beberapa alasan tidak jelas ditandai private. Berdasarkan fungsi itu saya telah menulis fungsi berikut ini yang menemukan yang pertama di ScrollViewatas Viewditentukan sebagai parameter dan menggulirnya sehingga menjadi terlihat di dalam 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
sumber
1

Solusi saya adalah:

            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
sumber
1
 scrollView.post(new Runnable() {
                    @Override
                    public void run() {
                        scrollView.smoothScrollTo(0, myTextView.getTop());

                    }
                });

Menjawab dari proyek praktis saya.

masukkan deskripsi gambar di sini

XpressGeek
sumber
0

Dalam kasus saya, itu bukan EditText, itu googleMap. Dan itu berhasil seperti ini.

    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
sumber
0

Berikut ini adalah apa yang saya gunakan:

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

Ini mendapatkan jumlah gulir yang diperlukan untuk menunjukkan bagian bawah tampilan, termasuk margin di bagian bawah tampilan itu.

LukeWaggoner
sumber
0

Gulir vertikal, bagus untuk formulir. Jawaban didasarkan pada gulir horizontal 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
sumber
apakah Anda yakin harus menggunakan HorizontalScrollViewmetode ini?
Shahood ul Hassan
0

Anda bisa menggunakan ObjectAnimatorseperti ini:

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

Berdasarkan jawaban Sherif, berikut ini paling sesuai untuk kasus penggunaan saya. Perubahan penting getTop()bukannya getBottom()dan smoothScrollTo()bukan 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
sumber
-1

Jika scrlMain adalah NestedScrollView Anda, gunakan yang berikut ini,

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

                    }
                });
Ashwin Balani
sumber