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

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


当前回答

如果有人正在寻找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()
        }
    }
}

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

其他回答

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

首先,你需要设置inset回调:

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

我们在根视图上设置了一个回调来确保我们被调用。插图可能在我们的视图接收到它们之前就被消耗掉了,所以我们必须在这里做额外的工作。

现在很简单了:

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

你可以摆脱焦点检查。它的存在是为了限制requestRectangleOnScreen的调用数量。我使用post在可滚动的父滚动到聚焦视图后运行一个操作。

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

如果有人正在寻找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()
        }
    }
}

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

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

尽管去做吧;)

以下是我正在使用的:

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

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