Kaydırma görünümünü belirli bir düzenleme metnine programlı olarak kaydırmanın bir yolu var mı?


206

Bir scrollview ile çok uzun bir faaliyetim var. Kullanıcının doldurması gereken çeşitli alanları olan bir formdur. Formumun yarısında bir onay kutusu var ve kullanıcı kontrol ettiğinde görünümün belirli bir bölümüne gitmek istiyorum. Bir EditText nesnesine (veya başka bir görünüm nesnesine) programlı olarak kaydırmanın herhangi bir yolu var mı?

Ayrıca, bunun X ve Y koordinatları kullanarak mümkün olduğunu biliyorum, ancak form kullanıcıdan kullanıcıya değişebileceğinden bunu yapmaktan kaçınmak istiyorum.


3
the form may changed from user to userama sadece kullanabilirsinizmEditView.getTop();
nebkat

1
Birisi CoordinatorLayout içinde NestedScrollView kullanıyorsa, stackoverflow.com/questions/52083678/…
user1506104

Yanıtlar:


451
private final void focusOnView(){
        your_scrollview.post(new Runnable() {
            @Override
            public void run() {
                your_scrollview.scrollTo(0, your_EditBox.getBottom());
            }
        });
    }

129
Ben smoothScrollTo (0, your_EditBox.getTop ()) kullanarak daha iyi bir sonuç (istenen görünüme kaydırma boğmak) ama bunun dışında - büyük bir cevap alır bulundu.
Muzikant

18
xmenW.K @. Kısa Yanıt: Kullanıcı arayüzü, yapılacak işleri sıraya göre yapıyor ve temelde kullanıcı arayüzü iş parçacığını söylüyorsunuz, ne zaman, daha önce yapmanız gereken her şeyi yaptıktan sonra bunu yapın (kaydırma) . Temel olarak kaydırma işlemini kuyruğa koyuyorsunuz ve iş parçacığının mümkün olduğunda yapmasına izin veriyorsunuz, siz istemeden önce yaptığı işlerin sırasına uyuyorsunuz.
Martin Marconcini

37
Runnable'ı yeni bir Handler başlatmak yerine ScrollView'e de göndermek mümkündür:your_scrollview.post(...
Şerif elKhatib

Görüş merkezi almak için getBottom () yerine herhangi bir yöntem var mı? çünkü benim durumum EditText yerine googleMap.
Zin Win Htet

5
getBottom () ve getTop (), üst
öğeye göredir

57

Görünümü kaydırma görünümünün merkezine kaydırmak istiyorsanız, Şerif elKhatib'in yanıtı büyük ölçüde geliştirilebilir . Bu yeniden kullanılabilir yöntem pürüzsüz görünümü HorizontalScrollView öğesinin görünür merkezine kaydırır.

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

Dikey ScrollViewkullanım için

...
int vTop = view.getTop();
int vBottom = view.getBottom();
int sHeight = scroll.getBottom();
scroll.smoothScrollTo(((vTop + vBottom - sHeight) / 2), 0);
...

3
Kodunuzu ayarlamanız gerekiyor. Kaydırma görünümünün genişliğini almanız ve formülü sv.smoothScrollTo (((vLeft + vRight) / 2) - (svWidth / 2), 0) olarak değiştirmeniz gerekir;
İlköğretim

2
Aksi takdirde scrollView içindeki görünüm ortalanmaz. Düzenlemeyi zaten senin için yaptım.
İlköğretim

Formülü tam olarak kontrol ettiniz mi, çünkü
eskisi

1
@ Temel Sevgiler ve ahmads formülleri aynı cebirsel ifadenin basitleştirilmiş ve genişletilmiş versiyonlarıdır, ayarlamaya gerek yoktur.
k29

1
@ k29 Burada görünen iki formüle atıfta bulunuyor musunuz? İkisi de benden, yorumumun formülünü daha da basitleştirdim ve cevabı buna göre düzenledim. Ama geri kalan her şey orijinal cevaptan hala aynı ve benim için de çok yardımcı oldu.
İlköğretim

51

Bu benim için iyi çalışıyor:

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

herkese açık geçersiz RequestChildFocus (Çocuğu görüntüle, Odaklanmış görüntüleme)

child - Bu ViewParent ürününün odaklanmak isteyen çocuğu. Bu görünüm odaklanmış görünümü içerir. Aslında odaklanılan görüş her zaman gerekli değildir.

odaklanmış - Gerçekten odaklanmış bir çocuğun soyundan gelen görüş


Herhangi bir yumuşak kaydırmada atlar türünü görüntüle
silentsudo

32

Bence belirli bir dikdörtgene ilerlemenin en iyi yolu View.requestRectangleOnScreen(Rect, Boolean). ViewEkranda görünmesini istediğiniz yerel bir dikdörtgeni kaydırmak ve geçmek istediğiniz bir yere çağırmalısınız . İkinci parametre falsedüzgün kaydırma ve truehemen kaydırma için olmalıdır.

final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, false);

1
Bir görünüm çağrı cihazı içinde benim için çalıştı kaydırma görünümü ve şimdi burada belirtilen kodu kullanarak çalışan bir görünüm kaydırmak gerekir.
Pankaj

2
Kesinlikle bu kabul edilen cevap olmalı scrollview.smoothScrollTo, gerekli görünüm ekran dışındaysa çalışmaz, (sdk 26 sürümü ile android emülatörde denendi)
Sony

@Michael İçeri girmeli miyim new Handler().post()?
Johnny Five

@JohnnyFive Bu kodu kullandığınız içeriğe bağlıdır.
Michael

12

WarrenFaith'in Yanıtına dayalı küçük bir yardımcı program yaptım, bu görünüm kaydırma görünümünde zaten görünürse, kaydırma gerekmiyorsa bu kodu da dikkate alır.

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

Sanırım yöntem scrollBoundsiçin parametre olmalıdır scrollView.getHitRect.
Vijay C


7

EditText'im, kendisinin mizanpajın kök görünümü olmayan ScrollView'imin içine birkaç katman yerleştirildi. GetTop () ve getBottom (), içerdiği görünüm içindeki koordinatları bildiriyor gibi göründüğü için, EditText üst öğelerini yineleyerek ScrollView üstünden EditText üstündeki mesafeyi hesaplamak vardı.

// 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();

5

Başka bir varyasyon:

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

veya post()yöntemi kullanabilirsiniz .


5

Bunun daha iyi bir cevap için çok geç olabileceğini biliyorum, ancak istenen mükemmel bir çözüm konumlandırıcı gibi bir sistem olmalı. Yani, sistem bir Editör alanı için bir konumlandırma yaptığında, alanı klavyeye kadar yerleştirir, böylece UI / UX kuralları mükemmeldir.

Kodun altında ne yapar Android yolu düzgün konumlandırma olduğunu. Her şeyden önce geçerli kaydırma noktasını referans noktası olarak tutarız. İkinci şey, bir düzenleyici için en iyi konumlandırma kaydırma noktasını bulmak, bunu yapmak için yukarı kaydırıyoruz ve daha sonra düzenleyici alanlarının ScrollView bileşenini en iyi konumlandırmayı yapmasını istemek. Gatcha! En iyi pozisyonu öğrendik. Şimdi yapacağımız şey, bir önceki noktadan yeni bulduğumuz noktaya sorunsuz bir şekilde ilerlemek. İsterseniz , yalnızca smoothScrollTo yerine scrollTo öğesini kullanarak düzgün kaydırmayı atlayabilirsiniz .

NOT: Ana kapsayıcı ScrollView, scrollViewSignup adlı bir üye alanıdır, çünkü örneğim çok fazla anlayabileceğiniz gibi bir kayıt ekranıdır.

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

Bu bloğu tüm EditText örnekleri için kullanmak ve ekran kodunuzla hızlı bir şekilde entegre etmek istiyorsanız. Aşağıdaki gibi bir travers yapabilirsiniz. Bunu yapmak için, ana OnFocusChangeListener'ı focusChangeListenerToScrollEditor adlı bir üye alanı haline getirdim ve onCreate sırasında aşağıdaki gibi çağırın.

traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);

Ve yöntem uygulaması aşağıdaki gibidir.

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

Burada yaptığımız şey, tüm EditText örneği çocuklarını dinleyiciyi odakta aramalarıdır.

Bu çözüme ulaşmak için, burada tüm çözümleri kontrol ettim ve daha iyi UI / UX sonucu için yeni bir çözüm ürettim.

Bana ilham veren diğer tüm cevaplar için çok teşekkürler.

3

ScrollView, ChildView öğesinin doğrudan üst öğesiyse yukarıdaki yanıtlar iyi çalışır. ChildView öğeniz ScrollView içindeki başka bir ViewGroup'a sarılıyorsa, View.getTop () üst öğeye göre konumu aldığından beklenmedik davranışlara neden olur. Bu durumda, bunu uygulamanız gerekir:

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

Ebeveynlerin pozisyonlarını da hesaba katarak bu en kapsamlı çözümdür. Gönderdiğiniz için teşekkürler!
Gustavo Baiocchi Costa


2

Bence daha zarif ve daha az hataya meyilli bir çözüm buldum

ScrollView.requestChildRectangleOnScreen

Herhangi bir matematik yoktur ve önerilen diğer çözümlerin aksine, yukarı ve aşağı doğru kaydırmayı idare edecektir.

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

Şu anda değiştirilmesi postDelayeddurumunda, daha güvenilir hale getirmek için içine sarmak iyi bir fikirdirScrollView

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

1

Android kaynak kodunu inceleyerek, ScrollView- scrollToChild(View)- bir üye işlevinin zaten talep edildiğini tam olarak yapan olduğunu görebilirsiniz. Ne yazık ki, bu işlev bazı belirsiz nedenlerle işaretlenmiştir private. İlk bulan fonksiyonu aşağıdaki yazdım bu işlevi göre ScrollViewyukarıda Viewbir parametre ve içinde görünür hale gelir kayar böylece olarak belirtilen 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;
     }
 }

1

Benim çözümüm:

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

1
 scrollView.post(new Runnable() {
                    @Override
                    public void run() {
                        scrollView.smoothScrollTo(0, myTextView.getTop());

                    }
                });

Pratik projemden cevap veriyorum.

resim açıklamasını buraya girin


0

Benim durumumda, bu değil EditText, bu googleMap. Ve bu şekilde başarılı bir şekilde çalışıyor.

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


0

Ne kullanıyorum:

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

Bu, o görünümün altındaki herhangi bir kenar boşluğu da dahil olmak üzere görünümün altını göstermek için gerekli kaydırma miktarını alır.


0

Dikey kaydırma, formlar için iyi. Cevap Ahmadalibaloch yatay kaydırma dayanmaktadır.

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

u HorizontalScrollViewbu yöntemde bir kullanmanız gerektiğinden emin misiniz?
Shahood ul Hassan

0

Bunun gibi kullanabilirsiniz ObjectAnimator:

ObjectAnimator.ofInt(yourScrollView, "scrollY", yourView.getTop()).setDuration(1500).start();

0

Şerif'in cevabına dayanarak, aşağıdakiler kullanım durumum için en iyi sonucu verdi. getTop()Bunun yerine getBottom()ve smoothScrollTo()yerine kayda değer değişiklikler yapılıyor 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());
            }
        });
    }

-1

ScrlMain NestedScrollView'ınızsa, aşağıdakileri kullanın,

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

                    }
                });
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.