有没有办法以编程方式将滚动视图滚动到特定的编辑文本?


206

我的滚动视图活动很长。它是具有用户必须填写的各个字段的表单。我在表单的一半下方有一个复选框,当用户选中它时,我想滚动到视图的特定部分。是否可以通过编程方式滚动到EditText对象(或任何其他视图对象)?

另外,我知道使用X和Y坐标可以做到这一点,但是我想避免这样做,因为表单可能因用户而异。


3
the form may changed from user to user但您可以使用mEditView.getTop();
nebkat 2011年

1
如果有人在CoordinatorLayout中使用NestedScrollView,则可以通过stackoverflow.com/questions/52083678/…
user1506104

Answers:


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

129
我发现使用smoothScrollTo(0,your_EditBox.getTop())可以获得更好的结果(将妈妈滚动到所需的视图),但除此之外-很好的答案。
Muzikant 2012年

18
@ xmenW.K。简短的答案:因为UI会根据要做的事情进行工作,并且您基本上是在告诉UI线程,在您可以做完之后,现在可以做的事情(滚动) 。您基本上是将滚动条放入队列中,并让线程在可能的情况下进行滚动,并在请求之前遵守其执行顺序。
Martin Marconcini 2014年

37
也可以将Runnable张贴到ScrollView本身,而不是实例化新的Handler:your_scrollview.post(...
Sherif elKhatib 2014年

有什么方法可以获取视图中心,而不是getBottom()?因为我的情况是googleMap而不是EditText。
Zin Win Htet

5
getBottom()和getTop()相对于父级
令人生畏的2016年

56

如果要将视图滚动滚动视图的中心,可以大大提高Sherif elKhatib的答案。此可重用的方法将视图平滑滚动到Horizo​​ntalScrollView的可见中心。

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

垂直ScrollView使用

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

3
您必须调整代码。您必须获取滚动视图的宽度,并且必须将公式更改为sv.smoothScrollTo((((vLeft + vRight)/ 2)-(svWidth / 2),0);
小学

2
否则,scrollView中的视图将不会居中。我已经为你做了编辑。
2015年

您是否检查过该公式是否工作正常,因为较旧的公式对我而言
效果

1
@Elementary Yours和ahmads公式是同一代数表达式的简化版本和扩展版本,无需进行任何调整。
k29

1
@ k29您在这里引用两个可见的公式吗?它们都是我的,我进一步简化了我的评论公式,并据此编辑了答案。但是其余所有内容与原始答案仍然相同,对我也非常有帮助。
小学

51

这对我来说很好:

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

公共无效RequestChildFocus(查看子级,查看焦点)

孩子 -这ViewParent想要对焦的孩子。该视图将包含焦点视图。实际上不一定是焦点。

聚焦 -视图是实际具有焦点的孩子的后代


在任何平滑滚动中查看跳动的种类
–silentsudo

32

我认为滚动到给定矩形的最佳方法是通过View.requestRectangleOnScreen(Rect, Boolean)。您应该在View要滚动到并传递要在屏幕上看到的局部矩形的位置上调用它。第二个参数应该是false平滑滚动和true立即滚动。

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

1
在视图分页器中为我工作的地方有scrollview,需要滚动到一个正在使用此处提到的代码工作的视图。
Pankaj

2
绝对可以,这是一个可以接受的答案,scrollview.smoothScrollTo如果所需的视图不在屏幕上,则将无法正常工作((在Android模拟器中使用sdk版本26​​进行了尝试)
索尼

@Michael我应该包扎new Handler().post()吗?
约翰尼五

@JohnnyFive取决于您使用此代码的上下文。
迈克尔

12

我基于WarrenFaith的Answer做了一个小的实用程序方法,该代码还考虑了该视图是否已经在滚动视图中可见,而无需滚动。

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

我猜scrollBounds应该是该scrollView.getHitRect方法的参数。
Vijay C


7

我的EditText嵌套在ScrollView的几层中,而ScrollView本身不是布局的根视图。因为getTop()和getBottom()似乎在报告其包含视图中的坐标,所以我让它通过遍历EditText的父级来计算从ScrollView顶部到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();


5

我知道要想得到更好的答案可能为时已晚,但是理想的完美解决方案必须是定位器之类的系统。我的意思是,当系统为“编辑器”字段定位时,它将字段放置在键盘上方,因此,根据UI / UX规则,它是完美的。

下面的代码使Android定位更加流畅。首先,我们将当前滚动点作为参考点。第二件事是找到编辑器的最佳定位滚动点,为此,我们滚动到顶部,然后请求编辑器字段使ScrollView组件执行最佳定位。加塔!我们已经学会了最好的位置。现在,我们要做的是从上一个点平滑滚动到新发现的点。如果需要,可以使用scrollTo而不是smoothScrollTo来省略平滑滚动。

注意:主容器ScrollView是一个名为scrollViewSignup的成员字段,因为您的示例是一个注册屏幕,您可能会发现很多。

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

如果要将此块用于所有EditText实例,并快速将其与屏幕代码集成。您可以像下面这样简单地创建一个遍历器。为此,我已经将主要的OnFocusChangeListener设为一个名为focusChangeListenerToScrollEditor的成员字段,并在onCreate期间按如下方式对其进行调用。

traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);

并且该方法的实现如下。

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

因此,我们在这里所做的是使所有EditText实例子级成为焦点的侦听器。

为了获得该解决方案,我已经在这里签出了所有解决方案,并生成了一个新的解决方案以获得更好的UI / UX结果。

非常感谢其他所有启发我的答案。

3

如果ScrollView是ChildView的直接父级,则以上答案将正常工作。如果您的ChildView被包装在ScrollView中的另一个ViewGroup中,则将导致意外行为,因为View.getTop()获得相对于其父项的位置。在这种情况下,您需要执行以下操作:

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

这是最全面的解决方案,因为它也考虑了父母的职位。感谢您的发布!
古斯塔沃·贝亚基奥·科斯塔


2

我想我发现使用以下方法更优雅,更不易出错

ScrollView.requestChildRectangleOnScreen

它不涉及数学运算,并且与其他提出的解决方案相反,它将正确处理上下滚动。

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

最好将其包装起来postDelayed以使其更可靠,以防万一当前ScrollView被更改

/**
 * 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源代码,您会发现ScrollView– 的成员函数已经scrollToChild(View)完全符合要求。不幸的是,由于某种晦涩的原因,此功能被标记为private。基于该函数,我编写了以下函数,该函数ScrollViewView指定的参数之上找到第一个参数并对其进行滚动,以使其在以下位置可见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

我的解决方案是:

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

                    }
                });

从我的实际项目中回答。

在此处输入图片说明


0

就我而言,这不是EditText,那是googleMap。它可以像这样成功地工作。

    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

以下是我正在使用的:

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

这将获得显示视图底部所需的滚动量,包括该视图底部的任何边距。


0

垂直滚动,适用于表格。答案基于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));
        }
    });
}

您确定您应该HorizontalScrollView在此方法中使用a 吗?
Shahood ul Hassan


0

根据Sherif的回答,以下内容最适合我的用例。值得注意的变化是getTop()代替getBottom()smoothScrollTo()代替的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());
            }
        });
    }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.