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

此外,我知道这是可能的使用X和Y坐标,但我想避免这样做,因为形式可能会从用户到用户的变化。


当前回答

我认为我已经找到了更优雅、更不容易出错的解决方案

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
}

如果ScrollView正在被更改,那么将它封装到postDelayed中以使其更加可靠是个好主意

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

其他回答

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

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

垂直滚动,适合窗体。答案是基于艾哈迈迪巴洛赫水平卷轴。

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

你应该使你的TextView请求焦点:

    mTextView.requestFocus();

以下是我正在使用的:

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

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