我的滚动视图活动很长。它是具有用户必须填写的各个字段的表单。我在表单的一半下方有一个复选框,当用户选中它时,我想滚动到视图的特定部分。是否可以通过编程方式滚动到EditText对象(或任何其他视图对象)?
另外,我知道使用X和Y坐标可以做到这一点,但是我想避免这样做,因为表单可能因用户而异。
我的滚动视图活动很长。它是具有用户必须填写的各个字段的表单。我在表单的一半下方有一个复选框,当用户选中它时,我想滚动到视图的特定部分。是否可以通过编程方式滚动到EditText对象(或任何其他视图对象)?
另外,我知道使用X和Y坐标可以做到这一点,但是我想避免这样做,因为表单可能因用户而异。
Answers:
private final void focusOnView(){
your_scrollview.post(new Runnable() {
@Override
public void run() {
your_scrollview.scrollTo(0, your_EditBox.getBottom());
}
});
}
your_scrollview.post(...
如果要将视图滚动到滚动视图的中心,可以大大提高Sherif elKhatib的答案。此可重用的方法将视图平滑滚动到HorizontalScrollView的可见中心。
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
使用
...
int vTop = view.getTop();
int vBottom = view.getBottom();
int sHeight = scroll.getBottom();
scroll.smoothScrollTo(((vTop + vBottom - sHeight) / 2), 0);
...
这对我来说很好:
targetView.getParent().requestChildFocus(targetView,targetView);
公共无效RequestChildFocus(查看子级,查看焦点)
孩子 -这ViewParent想要对焦的孩子。该视图将包含焦点视图。实际上不一定是焦点。
聚焦 -视图是实际具有焦点的孩子的后代
我认为滚动到给定矩形的最佳方法是通过View.requestRectangleOnScreen(Rect, Boolean)
。您应该在View
要滚动到并传递要在屏幕上看到的局部矩形的位置上调用它。第二个参数应该是false
平滑滚动和true
立即滚动。
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, false);
scrollview.smoothScrollTo
如果所需的视图不在屏幕上,则将无法正常工作((在Android模拟器中使用sdk版本26进行了尝试)
new Handler().post()
吗?
我基于WarrenFaith的Answer做了一个小的实用程序方法,该代码还考虑了该视图是否已经在滚动视图中可见,而无需滚动。
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());
}
});
}
}
scrollBounds
应该是该scrollView.getHitRect
方法的参数。
您应该将TextView
请求重点放在:
mTextView.requestFocus();
我的EditText嵌套在ScrollView的几层中,而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();
另一个变化是:
scrollView.postDelayed(new Runnable()
{
@Override
public void run()
{
scrollView.smoothScrollTo(0, img_transparent.getTop());
}
}, 2000);
或者您可以使用该post()
方法。
我知道要想得到更好的答案可能为时已晚,但是理想的完美解决方案必须是定位器之类的系统。我的意思是,当系统为“编辑器”字段定位时,它将字段放置在键盘上方,因此,根据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结果。
非常感谢其他所有启发我的答案。
如果ScrollView是ChildView的直接父级,则以上答案将正常工作。如果您的ChildView被包装在ScrollView中的另一个ViewGroup中,则将导致意外行为,因为View.getTop()获得相对于其父项的位置。在这种情况下,您需要执行以下操作:
public static void scrollToInvalidInputView(ScrollView scrollView, View view) {
int vTop = view.getTop();
while (!(view.getParent() instanceof ScrollView)) {
view = (View) view.getParent();
vTop += view.getTop();
}
final int scrollPosition = vTop;
new Handler().post(() -> scrollView.smoothScrollTo(0, scrollPosition));
}
参考:https : //stackoverflow.com/a/6438240/2624806
跟随效果更好。
mObservableScrollView.post(new Runnable() {
public void run() {
mObservableScrollView.fullScroll([View_FOCUS][1]);
}
});
我想我发现使用以下方法更优雅,更不易出错
它不涉及数学运算,并且与其他提出的解决方案相反,它将正确处理上下滚动。
/**
* 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);
}
查看Android源代码,您会发现ScrollView
– 的成员函数已经scrollToChild(View)
完全符合要求。不幸的是,由于某种晦涩的原因,此功能被标记为private
。基于该函数,我编写了以下函数,该函数ScrollView
在View
指定的参数之上找到第一个参数并对其进行滚动,以使其在以下位置可见ScrollView
:
private void make_visible(View view)
{
int vt = view.getTop();
int vb = view.getBottom();
View v = view;
for(;;)
{
ViewParent vp = v.getParent();
if(vp == null || !(vp instanceof ViewGroup))
break;
ViewGroup parent = (ViewGroup)vp;
if(parent instanceof ScrollView)
{
ScrollView sv = (ScrollView)parent;
// Code based on ScrollView.computeScrollDeltaToGetChildRectOnScreen(Rect rect) (Android v5.1.1):
int height = sv.getHeight();
int screenTop = sv.getScrollY();
int screenBottom = screenTop + height;
int fadingEdge = sv.getVerticalFadingEdgeLength();
// leave room for top fading edge as long as rect isn't at very top
if(vt > 0)
screenTop += fadingEdge;
// leave room for bottom fading edge as long as rect isn't at very bottom
if(vb < sv.getChildAt(0).getHeight())
screenBottom -= fadingEdge;
int scrollYDelta = 0;
if(vb > screenBottom && vt > screenTop)
{
// need to move down to get it in view: move down just enough so
// that the entire rectangle is in view (or at least the first
// screen size chunk).
if(vb-vt > height) // just enough to get screen size chunk on
scrollYDelta += (vt - screenTop);
else // get entire rect at bottom of screen
scrollYDelta += (vb - screenBottom);
// make sure we aren't scrolling beyond the end of our content
int bottom = sv.getChildAt(0).getBottom();
int distanceToBottom = bottom - screenBottom;
scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
}
else if(vt < screenTop && vb < screenBottom)
{
// need to move up to get it in view: move up just enough so that
// entire rectangle is in view (or at least the first screen
// size chunk of it).
if(vb-vt > height) // screen size chunk
scrollYDelta -= (screenBottom - vb);
else // entire rect at top
scrollYDelta -= (screenTop - vt);
// make sure we aren't scrolling any further than the top our content
scrollYDelta = Math.max(scrollYDelta, -sv.getScrollY());
}
sv.smoothScrollBy(0, scrollYDelta);
break;
}
// Transform coordinates to parent:
int dy = parent.getTop()-parent.getScrollY();
vt += dy;
vb += dy;
v = parent;
}
}
scrollView.post(new Runnable() {
@Override
public void run() {
scrollView.smoothScrollTo(0, myTextView.getTop());
}
});
从我的实际项目中回答。
就我而言,这不是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);
}
});
}
问:是否可以通过编程方式将滚动视图滚动到特定的edittext?
Ans:嵌套滚动视图在recyclerview的最后一个位置添加了记录数据。
adapter.notifyDataSetChanged();
nested_scroll.setScrollY(more Detail Recycler.getBottom());
以下是我正在使用的:
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);
}
这将获得显示视图底部所需的滚动量,包括该视图底部的任何边距。
垂直滚动,适用于表格。答案基于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));
}
});
}
HorizontalScrollView
在此方法中使用a 吗?
您可以这样使用ObjectAnimator
:
ObjectAnimator.ofInt(yourScrollView, "scrollY", yourView.getTop()).setDuration(1500).start();
根据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());
}
});
}
如果scrlMain是您的NestedScrollView,则使用以下代码,
scrlMain.post(new Runnable() {
@Override
public void run() {
scrlMain.fullScroll(View.FOCUS_UP);
}
});
the form may changed from user to user
但您可以使用mEditView.getTop();