Android:强制ScrollView滚动到底部

239

我想让ScrollView从底部开始,有什么方法吗?


1
我认为只需要使用scrollTo(),是的,我刚刚查看了ScrollView的开发文档。非常简单。 - Noah Seidman
19个回答

360

你应该像这样在scroll.post内运行代码:

scroll.post(new Runnable() {            
    @Override
    public void run() {
           scroll.fullScroll(View.FOCUS_DOWN);              
    }
});

4
有没有某种方法可以在不失去当前元素焦点的情况下进行操作?当我这样做时,会丢失我当前元素的焦点(与ScrollView不同)。 - rkmax
2
只有在布局尚未测量和布置的情况下才需要这样做(例如,如果您在onCreate中运行它)。在按下按钮等情况下,不需要使用.post()。 - Patrick Boos
21
如果您不想专注于 ScrollView,您可以使用scroll.scrollTo(0, scroll.getHeight());跳转到底部,或者使用scroll.smoothScrollTo(0, scroll.getHeight());进行更平滑的滚动。 - yuval
这段代码可能会导致内存泄漏吗?它创建了一个匿名的Runnable,该Runnable持有对一个活动(滚动)视图的引用。如果在Runnable执行其任务时活动被销毁,它将泄漏对滚动视图的引用,不是吗? - Jenever
3
在 Kotlin 中:scrollView.post { scrollView.fullScroll(View.FOCUS_DOWN) } 的意思是滚动视图到底部。 - Albert Vila Calvo
@AlbertVilaCalvo,你评论中的代码对我很有用,可以将我的滚动条滚动到最底部。这里大多数的解决方案都会在接近末尾时停止滚动。谢谢!! - Nafeez Quraishi

339

scroll.fullScroll(View.FOCUS_DOWN) 也应该可行。

将此放入 scroll.Post(Runnable run) 中。

Kotlin 代码

scrollView.post {
   scrollView.fullScroll(View.FOCUS_DOWN)
}

6
看起来那似乎没有任何作用,有什么建议吗? 我尝试了在onCreate和后来的按钮onClick中使用它。 - RichardJohnn
1
如果布局大小在调用之前发生了变化,这种方法就不起作用了。需要使用post方法。 - MLProgrammer-CiM
5
除非您给视图足够的时间完全展开,否则以下内容将无法正常工作,这只是简化了ademar的答案。scrollView.postDelayed(new Runnable() { @Override public void run() { scrollView.fullScroll(View.FOCUS_UP // 或者 Down); } }, 1000); - EngineSense
1
scroll.fullScroll(View.FOCUS_DOWN) 会导致焦点的改变。当存在多个可聚焦视图时,例如两个 EditText 时,这将带来一些奇怪的行为。请查看我在底部提供的另一个解决方案。 - peacepassion
scroll.fullScroll(View.FOCUS_DOWN)会导致焦点的改变。当有多个可聚焦视图时,例如两个EditText,这将带来一些奇怪的行为。请查看我下面的答案。 - peacepassion
显示剩余6条评论

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

或者,如果你想进一步增强功能,以便在滚动之前检查是否已经到达底部,你可以这样做:
    fun ScrollView.scrollToBottom() {
    val lastChild = children.lastOrNull() ?: return
    val bottom = lastChild.bottom + paddingBottom
    val currentY = height + scrollY
    val alreadyAtBottom = bottom <= currentY
    if (!alreadyAtBottom) {
        val delta = bottom - currentY
        smoothScrollBy(0, delta)
    } else {
        // already at bottom, do nothing
    }
}

太高兴了,我不必再浪费时间在这上面了。正是我所需要的。 - Alexandre G
8
这个回答应该得到更多的赞。迄今为止,它是最好的答案。 - Eric Lange
1
这是目前这里最好的答案。 - void pointer
1
尝试了这页上的所有其他方法,甚至是在这些方法之下。这是唯一有效的方法,而且不会导致我的EditText失去焦点。谢谢。 - Joel
我不知道为什么,但是它对我来说无法滚动到底部! - Kishan Solanki
显示剩余5条评论

58
有时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);

感谢您的识别。postDelayed是更好的解决方案。 - Prashant
@Prashant :) :) :) - Ajay Shrestha
1
请记住,在活动完成时需要处理您的Runnable。 - FabioR
如何处理可运行的@FabioR? - NightStorm

39

对我最有效的是

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

我尝试过这个,但是这里的算法是错误的。请在底部检查我的答案。 - peacepassion

28

我希望我的递增函数能够完美工作。

    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 已经没有那个问题了,您应该使用它。


3
不超过两个,但在我的手机(LG JellyBean 4.1.2 Optimus G)上运行良好。 - Youngjae
10
为什么不使用 scrollView.postDelayed(runnable, 100)? - Matt Wolfe
1
@MattWolfe在过去的一些旧版本的安卓系统中可能无法正常工作。但是今天这个答案已经过时了。 - ademar111190
5
请使用 scrollView.postDelayed(runnable, 100)!为了上帝的爱,请这样做! :) - Alex Lockwood
1
我在@AlexLockwood处添加了一条注释,希望人们使用postDelayed :) - ademar111190

9

我尝试过这个并且成功了。

scrollView.postDelayed(new Runnable() {
    @Override
    public void run() {
        scrollView.smoothScrollTo(0, scrollView.getHeight());
    }
}, 1000);

4

这里有几种滚动到底部的方法:

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

如果您的滚动视图没有完成布局(例如:在Activity的onCreate方法中滚动到底部...)

my_scroll_view.post { 
   my_scroll_view.scrollToBottom()          
}

3

当视图尚未加载时,您无法滚动。您可以像上面那样使用post或sleep调用来稍后执行,但这并不是非常优雅的做法。

更好的方法是计划滚动并在下一个onLayout()中执行。以下是示例代码:

https://dev59.com/mWLVa4cB1Zd3GeqPukb_#10209457


3

有一件事需要考虑,那就是不要设置什么。请确保你的子控件,尤其是 EditText 控件,没有设置 RequestFocus 属性。这可能是布局中最后一个被解释的属性,它会覆盖其父级(布局或 ScrollView)上的重力设置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接