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

时间:2011-07-26 14:31:54

标签: android view android-edittext scrollview programmatically-created

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

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

25 个答案:

答案 0 :(得分:396)

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

答案 1 :(得分:50)

如果你想将视图滚动到滚动视图的中心,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,更改x输入的ysmoothScroll位置。并使用view.getTop()代替view.getLeft()view.getBottom()代替v.getRight()

:)

答案 2 :(得分:43)

这对我很有用:

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

public void RequestChildFocus(View child,View focused)

child - 此ViewParent的子项需要焦点。该视图将包含焦点视图。实际上并不一定是关注焦点。

专注 - 作为实际拥有焦点的儿童后代的视图

答案 3 :(得分:24)

在我看来,滚动到给定矩形的最佳方法是通过View.requestRectangleOnScreen(Rect, Boolean)。您应该在想要滚动到的View上调用它并传递您想要在屏幕上显示的本地矩形。第二个参数应为false以便平滑滚动,true用于立即滚动。

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

答案 4 :(得分:12)

我根据WarrenFaith的答案制作了一个小实用程序方法,如果该视图在滚动视图中已经可见,则该代码也会考虑,无需滚动。

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

答案 5 :(得分:10)

您应该关注TextView请求:

    mTextView.requestFocus();

答案 6 :(得分:7)

我的EditText嵌套在我的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();

答案 7 :(得分:4)

另一个变种是:

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

或者您可以使用post()方法。

答案 8 :(得分:4)

我知道要想获得更好的答案可能为时已晚,但是理想的完美解决方案必须是定位器之类的系统。我的意思是,当系统为“编辑器”字段定位时,它会将字段放置在键盘上方,因此,根据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结果。

Many thanks to all other answers inspiring me much.

答案 9 :(得分:2)

参考:https://stackoverflow.com/a/6438240/2624806

以下工作要好得多。

mObservableScrollView.post(new Runnable() {
            public void run() { 
                mObservableScrollView.fullScroll([View_FOCUS][1]); 
            }
        });

答案 10 :(得分:2)

O(N*N)

我的实际项目中的答案。

enter image description here

答案 11 :(得分:2)

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

getRolesSubject.next(mockResponse);

答案 12 :(得分:1)

我认为我使用

找到了更优雅,更不容易出错的解决方案
  

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

答案 13 :(得分:1)

yourScrollView.smoothScrollTo(0, yourEditText.getTop());

就去做吧;)

答案 14 :(得分: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);
}

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

答案 15 :(得分: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));
        }
    });
}

答案 16 :(得分:0)

我的解决方案是:

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

答案 17 :(得分:0)

如果scrlMain是您的NestedScrollView,则使用以下内容,

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

                    }
                });

答案 18 :(得分:0)

您可以像这样使用ObjectAnimator

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

答案 19 :(得分:0)

Que:有没有办法以编程方式将滚动视图滚动到特定的edittext?

Ans:Recyclerview中的嵌套滚动视图上一个位置添加了记录数据。

adapter.notifyDataSetChanged();
nested_scroll.setScrollY(more Detail Recycler.getBottom());

Is there a way to programmatically scroll a scroll view to a specific edit text?

答案 20 :(得分: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());
            }
        });
    }

答案 21 :(得分: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);
        }
    });
}

答案 22 :(得分:0)

如果你想在软键盘打开时滚动到一个视图,那么它可能会有点棘手。 到目前为止,我得到的最佳解决方案是结合使用 inset 回调和 requestRectangleOnScreen 方法。

首先,您需要设置插入回调:

fun View.doOnApplyWindowInsetsInRoot(block: (View, WindowInsetsCompat, Rect) -> Unit) {
    val initialPadding = recordInitialPaddingForView(this)
    val root = getRootForView(this)
    ViewCompat.setOnApplyWindowInsetsListener(root) { v, insets ->
        block(v, insets, initialPadding)
        insets
    }
    requestApplyInsetsWhenAttached()
}

fun View.requestApplyInsetsWhenAttached() {
    if (isAttachedToWindow) {
        requestApplyInsets()
    } else {
        addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
            override fun onViewAttachedToWindow(v: View) {
                v.removeOnAttachStateChangeListener(this)
                v.requestApplyInsets()
            }

            override fun onViewDetachedFromWindow(v: View) = Unit
        })
    }
}

我们正在根视图上设置回调以确保我们被调用。 Insets 可能会在我们所讨论的视图收到它们之前被消耗掉,所以我们必须在这里做额外的工作。

现在几乎很容易:

doOnApplyWindowInsetsInRoot { _, _, _ ->
    post {
        if (viewInQuestion.hasFocus()) {
            requestRectangleOnScreen(Rect(0, 0, width, height))
        }
    }
}

您可以摆脱焦点检查。它用于限制对 requestRectangleOnScreen 的调用次数。我使用 post 在可滚动父级计划滚动到焦点视图后运行操作。

答案 23 :(得分:0)

检查Android源代码,您会发现已经存在android:configChanges=... - ScrollView的成员函数 - 它完全符合要求。不幸的是,这个功能是出于标记scrollToChild(View)的一些不明原因。基于该函数,我编写了以下函数,该函数查找指定为参数的private上方的第一个ScrollView并滚动它以使其在View中可见:

ScrollView

答案 24 :(得分:0)

如果有人正在寻找 Kotlin 版本,您可以使用扩展功能来做到这一点

fun ScrollView.scrollToChild(view: View, onScrolled: (() -> Unit)? = null) {
    view.requestFocus()
    val scrollBounds = Rect()
    getHitRect(scrollBounds)
    if (!view.getLocalVisibleRect(scrollBounds)) {
        findViewTreeLifecycleOwner()?.lifecycleScope?.launch(Dispatchers.Main) {
            smoothScrollTo(0, view.bottom - 40)
            onScrolled?.invoke()
        }
    }
}

有一个小回调可以让你在滚动后做一些事情。