我希望 ScrollView 从底部开始。有什么方法吗?
19 回答
您应该像这样运行 scroll.post 中的代码:
scroll.post(new Runnable() {
@Override
public void run() {
scroll.fullScroll(View.FOCUS_DOWN);
}
});
scroll.fullScroll(View.FOCUS_DOWN)
也应该工作。
把这个放在一个scroll.Post(Runnable run)
Kotlin 代码
scrollView.post {
scrollView.fullScroll(View.FOCUS_DOWN)
}
scroll.fullScroll(View.FOCUS_DOWN)
会导致重心的变化。当有多个可聚焦视图(例如两个 EditText)时,这会带来一些奇怪的行为。这个问题还有另一种方法。
View lastChild = scrollLayout.getChildAt(scrollLayout.getChildCount() - 1);
int bottom = lastChild.getBottom() + scrollLayout.getPaddingBottom();
int sy = scrollLayout.getScrollY();
int sh = scrollLayout.getHeight();
int delta = bottom - (sy + sh);
scrollLayout.smoothScrollBy(0, delta);
这很好用。
Kotlin 扩展
fun ScrollView.scrollToBottom() {
val lastChild = getChildAt(childCount - 1)
val bottom = lastChild.bottom + paddingBottom
val delta = bottom - (scrollY+ height)
smoothScrollBy(0, delta)
}
有时 scrollView.post 不起作用
scrollView.post(new Runnable() {
@Override
public void run() {
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
但是如果你使用scrollView.postDelayed,它肯定会工作
scrollView.postDelayed(new Runnable() {
@Override
public void run() {
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
},1000);
最适合我的是
scroll_view.post(new Runnable() {
@Override
public void run() {
// This method works but animates the scrolling
// which looks weird on first load
// scroll_view.fullScroll(View.FOCUS_DOWN);
// This method works even better because there are no animations.
scroll_view.scrollTo(0, scroll_view.getBottom());
}
});
我增量工作完美。
private void sendScroll(){
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
try {Thread.sleep(100);} catch (InterruptedException e) {}
handler.post(new Runnable() {
@Override
public void run() {
scrollView.fullScroll(View.FOCUS_DOWN);
}
});
}
}).start();
}
笔记
这个答案是真正旧版本的android的解决方法。今天postDelayed
没有更多的错误,你应该使用它。
我试过成功。
scrollView.postDelayed(new Runnable() {
@Override
public void run() {
scrollView.smoothScrollTo(0, scrollView.getHeight());
}
}, 1000);
当视图尚未加载时,您无法滚动。您可以通过上面的帖子或睡眠电话“稍后”进行操作,但这不是很优雅。
最好计划滚动并在下一个 onLayout() 上执行。这里的示例代码:
要考虑的一件事是不要设置什么。确保您的子控件,尤其是 EditText 控件,没有设置 RequestFocus 属性。这可能是布局上最后解释的属性之一,它将覆盖其父级(布局或 ScrollView)上的重力设置。
这是滚动到底部的其他一些方法
fun ScrollView.scrollToBottom() {
// use this for scroll immediately
scrollTo(0, this.getChildAt(0).height)
// or this for smooth scroll
//smoothScrollBy(0, this.getChildAt(0).height)
// or this for **very** smooth scroll
//ObjectAnimator.ofInt(this, "scrollY", this.getChildAt(0).height).setDuration(2000).start()
}
使用
如果您的滚动视图已经布局
my_scroll_view.scrollToBottom()
如果您的滚动视图未完成布局(例如:您在 ActivityonCreate
方法中滚动到底部...)
my_scroll_view.post {
my_scroll_view.scrollToBottom()
}
不完全是问题的答案,但我需要在 EditText 获得焦点后立即向下滚动。然而,接受的答案会使 ET 也立即失去焦点(对于我假设的 ScrollView)。
我的解决方法如下:
emailEt.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
Toast.makeText(getActivity(), "got the focus", Toast.LENGTH_LONG).show();
scrollView.postDelayed(new Runnable() {
@Override
public void run() {
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
}, 200);
}else {
Toast.makeText(getActivity(), "lost the focus", Toast.LENGTH_LONG).show();
}
}
});
我实际上发现调用 fullScroll 两次可以解决问题:
myScrollView.fullScroll(View.FOCUS_DOWN);
myScrollView.post(new Runnable() {
@Override
public void run() {
myScrollView.fullScroll(View.FOCUS_DOWN);
}
});
它可能与执行第一次(不成功)滚动后立即激活 post() 方法有关。我认为这种行为发生在之前对 myScrollView 的任何方法调用之后,因此您可以尝试将第一个 fullScroll() 方法替换为可能与您相关的任何其他方法。
使用 Kotlin 协程还有另一种很酷的方法。与具有可运行(post/postDelayed)的 Handler 相比,使用协程的优点是它不会启动昂贵的线程来执行延迟操作。
launch(UI){
delay(300)
scrollView.fullScroll(View.FOCUS_DOWN)
}
将协程的 HandlerContext 指定为 UI 很重要,否则可能不会从 UI 线程调用延迟的操作。
在那些情况下,只使用scroll.scrollTo(0, sc.getBottom())不起作用,使用scroll.post
例子:
scroll.post(new Runnable() {
@Override
public void run() {
scroll.fullScroll(View.FOCUS_DOWN);
}
});
scroll.fullScroll(View.FOCUS_DOWN)
为什么即使包裹在其中也可能不起作用的一个可能原因.post()
是视图没有布局。在这种情况下, View.doOnLayout()可能是一个更好的选择:
scroll.doOnLayout(){
scroll.fullScroll(View.FOCUS_DOWN)
}
或者,为勇敢的灵魂提供更详细的内容:https ://chris.banes.dev/2019/12/03/suspending-views/
这立即起作用。不延误。
// wait for the scroll view to be laid out
scrollView.post(new Runnable() {
public void run() {
// then wait for the child of the scroll view (normally a LinearLayout) to be laid out
scrollView.getChildAt(0).post(new Runnable() {
public void run() {
// finally scroll without animation
scrollView.scrollTo(0, scrollView.getBottom());
}
}
}
}
如果您的最低 SDK 为 23 或更高版本,您可以使用:
View childView = findViewById(R.id.your_view_id_in_the_scroll_view)
if(childView != null){
scrollview.post(() -> scrollview.scrollToDescendant(childView));
}
所有答案的组合对我有用:
扩展功能 PostDelayed
private fun ScrollView.postDelayed(
time: Long = 325, // ms
block: ScrollView.() -> Unit
) {
postDelayed({block()}, time)
}
扩展函数 measureScrollHeight
fun ScrollView.measureScrollHeight(): Int {
val lastChild = getChildAt(childCount - 1)
val bottom = lastChild.bottom + paddingBottom
val delta = bottom - (scrollY+ height)
return delta
}
扩展功能 ScrolltoBottom
fun ScrollView.scrollToBottom() {
postDelayed {
smoothScrollBy(0, measureScrollHeight())
}
}
请注意,最小延迟应至少为 325 毫秒,否则滚动会出错(不会滚动到整个底部)。当前高度和底部之间的增量越大,延迟时间应该越大。
这里有人说 scrollView.post 不起作用。
如果您不想使用 scrollView.postDelayed,另一种选择是使用侦听器。这是我在另一个用例中所做的:
ViewTreeObserver.OnPreDrawListener viewVisibilityChanged = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (my_view.getVisibility() == View.VISIBLE) {
scroll_view.smoothScrollTo(0, scroll_view.getHeight());
}
return true;
}
};
您可以通过以下方式将其添加到您的视图中:
my_view.getViewTreeObserver().addOnPreDrawListener(viewVisibilityChanged);