Android:ScrollView强制到底部

发布于 2024-09-06 01:08:59 字数 35 浏览 4 评论 0原文

我希望 ScrollView 从底部开始。有什么方法吗?

I would like a ScrollView to start all the way at the bottom. Any methods?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

ぶ宁プ宁ぶ 2024-09-13 01:08:59

你应该像这样运行scroll.post中的代码:

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

you should run the code inside the scroll.post like this:

scroll.post(new Runnable() {            
    @Override
    public void run() {
           scroll.fullScroll(View.FOCUS_DOWN);              
    }
});
不气馁 2024-09-13 01:08:59

scroll.fullScroll(View.FOCUS_DOWN) 也应该可以工作。

将其放入 scroll.Post(Runnable run)

Kotlin 代码中

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

scroll.fullScroll(View.FOCUS_DOWN) also should work.

Put this in a scroll.Post(Runnable run)

Kotlin Code

scrollView.post {
   scrollView.fullScroll(View.FOCUS_DOWN)
}
最偏执的依靠 2024-09-13 01:08:59

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

scroll.fullScroll(View.FOCUS_DOWN) will lead to the change of focus. That will bring some strange behavior when there are more than one focusable views, e.g two EditText. There is another way for this question.

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

This works well.

Kotlin Extension

fun ScrollView.scrollToBottom() {
    val lastChild = getChildAt(childCount - 1)
    val bottom = lastChild.bottom + paddingBottom
    val delta = bottom - (scrollY+ height)        
    smoothScrollBy(0, delta)
}

Or if you want to further enhance to check whether it is already at the bottom before scrolling, you can do something like this:

    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
    }
}
っ左 2024-09-13 01:08:59

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

Sometimes scrollView.post doesn't work

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

BUT if you use scrollView.postDelayed, it will definitely work

 scrollView.postDelayed(new Runnable() {
        @Override
        public void run() {
            scrollView.fullScroll(ScrollView.FOCUS_DOWN);
        }
    },1000);
压抑⊿情绪 2024-09-13 01:08:59

对我来说最有效的是

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

What worked best for me is

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());
     }
});
情未る 2024-09-13 01:08:59

我增加工作完美。

    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 不再有这个错误,你应该使用它。

I increment to work perfectly.

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

Note

This answer is a workaround for really old versions of android. Today the postDelayed has no more that bug and you should use it.

香草可樂 2024-09-13 01:08:59

我试过了,成功了。

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

i tried that successful.

scrollView.postDelayed(new Runnable() {
    @Override
    public void run() {
        scrollView.smoothScrollTo(0, scrollView.getHeight());
    }
}, 1000);
逆流 2024-09-13 01:08:59

以下是滚动到底部的一些其他方法

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

Here is some other ways to scroll to bottom

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

Using

If you scrollview already laid out

my_scroll_view.scrollToBottom()

If your scrollview is not finish laid out (eg: you scroll to bottom in Activity onCreate method ...)

my_scroll_view.post { 
   my_scroll_view.scrollToBottom()          
}
盗梦空间 2024-09-13 01:08:59

当视图尚未加载时,您无法滚动。您可以“稍后”通过上面的 post 或 sleep 调用来完成此操作,但这不是很优雅。

最好规划滚动并在下一个 onLayout() 上执行。示例代码如下:

https://stackoverflow.com/a/10209457/1310343

When the view is not loaded yet, you cannot scroll. You can do it 'later' with a post or sleep call as above, but this is not very elegant.

It is better to plan the scroll and do it on the next onLayout(). Example code here:

https://stackoverflow.com/a/10209457/1310343

动听の歌 2024-09-13 01:08:59

需要考虑的一件事是不应该设置的内容。确保您的子控件(尤其是 EditText 控件)没有设置 RequestFocus 属性。这可能是布局上最后解释的属性之一,它将覆盖其父级(布局或 ScrollView)上的重力设置。

One thing to consider is what NOT to set. Make certain your child controls, especially EditText controls, do not have the RequestFocus property set. This may be one of the last interpreted properties on the layout and it will override gravity settings on its parents (the layout or ScrollView).

玩世 2024-09-13 01:08:59

不完全是问题的答案,但我需要在 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();
        }
    }
});

Not exactly the answer to the question, but I needed to scroll down as soon as an EditText got the focus. However the accepted answer would make the ET also lose focus right away (to the ScrollView I assume).

My workaround was the following:

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();
        }
    }
});
浸婚纱 2024-09-13 01:08:59

我实际上发现调用 fullScroll 两次可以解决问题:

myScrollView.fullScroll(View.FOCUS_DOWN);

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

它可能与执行第一次(不成功)滚动后立即激活 post() 方法有关。我认为这种行为发生在 myScrollView 上的任何先前方法调用之后,因此您可以尝试用任何可能与您相关的其他方法替换第一个 fullScroll() 方法。

I actually found that calling fullScroll twice does the trick:

myScrollView.fullScroll(View.FOCUS_DOWN);

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

It may have something to do with the activation of the post() method right after performing the first (unsuccessful) scroll. I think this behavior occurs after any previous method call on myScrollView, so you can try replacing the first fullScroll() method by anything else that may be relevant to you.

梦途 2024-09-13 01:08:59

使用 Kotlin 协程还有另一种很酷的方法。与具有可运行(post/postDelayed)的处理程序相比,使用协程的优点是它不会启动昂贵的线程来执行延迟操作。

launch(UI){
    delay(300)
    scrollView.fullScroll(View.FOCUS_DOWN)
}

将协程的 HandlerContext 指定为 UI 非常重要,否则可能无法从 UI 线程调用延迟操作。

Using there is another cool way to do this with Kotlin coroutines. The advantage of using a coroutine opposed to a Handler with a runnable (post/postDelayed) is that it does not fire up an expensive thread to execute a delayed action.

launch(UI){
    delay(300)
    scrollView.fullScroll(View.FOCUS_DOWN)
}

It is important to specify the coroutine's HandlerContext as UI otherwise the delayed action might not be called from the UI thread.

酸甜透明夹心 2024-09-13 01:08:59

在这种情况下,仅使用 scroll.scrollTo(0, sc.getBottom()) 不起作用,请使用 scroll.post

示例:

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

In those case were using just scroll.scrollTo(0, sc.getBottom()) don't work, use it using scroll.post

Example:

scroll.post(new Runnable() {
  @Override
  public void run() {
  scroll.fullScroll(View.FOCUS_DOWN);
  } 
});
柏拉图鍀咏恒 2024-09-13 01:08:59

即使封装在 .post() 中,scroll.fullScroll(View.FOCUS_DOWN) 也可能无法工作,一个可能的原因是视图未布局。在这种情况下 View.doOnLayout()可能是更好的选择:

scroll.doOnLayout(){
    scroll.fullScroll(View.FOCUS_DOWN)
}

或者,为勇敢的灵魂提供更详细的选择: https ://chris.banes.dev/2019/12/03/suspending-views/

One possible reason of why scroll.fullScroll(View.FOCUS_DOWN) might not work even wrapped in .post() is that the view is not laid out. In this case View.doOnLayout() could be a better option:

scroll.doOnLayout(){
    scroll.fullScroll(View.FOCUS_DOWN)
}

Or, something more elaborated for the brave souls: https://chris.banes.dev/2019/12/03/suspending-views/

落叶缤纷 2024-09-13 01:08:59

所有答案的组合对我来说很有效:

扩展函数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毫秒,否则滚动将出现错误(不会滚动到整个底部)。当前高度与底部之间的增量越大,延迟时间就应该越大。

A combination of all answers did the trick for me:

Extension Function PostDelayed

private fun ScrollView.postDelayed(
    time: Long = 325, // ms
    block: ScrollView.() -> Unit
) {
    postDelayed({block()}, time)
}

Extension Function measureScrollHeight

fun ScrollView.measureScrollHeight(): Int {
    val lastChild = getChildAt(childCount - 1)
    val bottom = lastChild.bottom + paddingBottom
    val delta = bottom - (scrollY+ height)
    return delta
}

Extension Function ScrolltoBottom

fun ScrollView.scrollToBottom() {
    postDelayed {
        smoothScrollBy(0, measureScrollHeight())
    }
}

Be aware that the minimum delay should be at least 325ms or the scrolling will be buggy (not scrolling to the entire bottom). The larger your delta between the current height and the bottom is, the larger should be the delayed time.

绝不放开 2024-09-13 01:08:59

这里有人说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);

Some people here said that scrollView.post didn't work.

If you don't want to use scrollView.postDelayed, another option is to use a listener. Here is what I did in another use case :

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

You can add it to your view this way :

my_view.getViewTreeObserver().addOnPreDrawListener(viewVisibilityChanged);
自由如风 2024-09-13 01:08:59

如果您的最低 SDK 为 29 或更高,您可以使用以下命令:

View childView = findViewById(R.id.your_view_id_in_the_scroll_view)
if(childView != null){
  scrollview.post(() -> scrollview.scrollToDescendant(childView));
}

If your minimum SDK is 29 or upper you could use this:

View childView = findViewById(R.id.your_view_id_in_the_scroll_view)
if(childView != null){
  scrollview.post(() -> scrollview.scrollToDescendant(childView));
}
蘸点软妹酱 2024-09-13 01:08:59

立即生效。毫不拖延。

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

This works instantly. Without delay.

// 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());
      }
    }
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文