滚动到滚动视图中的特定视图

时间:2014-01-31 15:04:06

标签: android scrollview

我在scrollview中添加了一个scrollview和子项。在某些时候,我需要滚动到一个特定的视图。

<scrollview>

1. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

2. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

3. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

4. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

5. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

6. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

7. <linearlayout>

    <textview></textview>
    <textview></textview>

   </linearlayout>

   <button>
   </button>

 </scrollview>

以上布局是动态创建的。所以我不能在这里发布xml文件。布局创建完全是动态的。甚至线性布局内的子视图的数量也可能不同。

所以,当我点击按钮时,我需要滚动到特定的视图说这里当我点击按钮时我需要滚动到线性布局4。 我尝试使用scrollTo方法,但它滚动到scrollview的顶部。

请提供一些建议。

16 个答案:

答案 0 :(得分:86)

这应该可以解决问题:

View targetView = findViewById(R.id.DESIRED_VIEW_ID);  
targetView.getParent().requestChildFocus(targetView,targetView);
  

public void RequestChildFocus(View child,View focused)

child - 此ViewParent的子项需要焦点。该视图将包含焦点视图。实际上并不一定是关注焦点。

专注 - 作为实际拥有焦点的儿童后代的视图

答案 1 :(得分:85)

如果我们需要滚动到的孩子不是直接孩子,那么以上解决方案不起作用

我在我的项目中使用了以下解决方案,分享它可能对其他人有帮助

/**
 * Used to scroll to the given view.
 *
 * @param scrollViewParent Parent ScrollView
 * @param view View to which we need to scroll.
 */
private void scrollToView(final ScrollView scrollViewParent, final View view) {
    // Get deepChild Offset
    Point childOffset = new Point();
    getDeepChildOffset(scrollViewParent, view.getParent(), view, childOffset);
    // Scroll to child.
    scrollViewParent.smoothScrollTo(0, childOffset.y);
}

/**
 * Used to get deep child offset.
 * <p/>
 * 1. We need to scroll to child in scrollview, but the child may not the direct child to scrollview.
 * 2. So to get correct child position to scroll, we need to iterate through all of its parent views till the main parent.
 *
 * @param mainParent        Main Top parent.
 * @param parent            Parent.
 * @param child             Child.
 * @param accumulatedOffset Accumulated Offset.
 */
private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset) {
    ViewGroup parentGroup = (ViewGroup) parent;
    accumulatedOffset.x += child.getLeft();
    accumulatedOffset.y += child.getTop();
    if (parentGroup.equals(mainParent)) {
        return;
    }
    getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}

答案 2 :(得分:23)

试试这个:

ScrollView sv = // your scrollview
View insideView = // find the View that you need to scroll to which is inside this ScrollView
sv.scrollTo(0, (int)insideView.getY());

insideView.getY()无法在API等级11下工作,对于低于11的API级别,您可以将insideView.getY()替换为insideView.getTop()

答案 3 :(得分:9)

我认为我已经找到了更优雅且容易出错的解决方案

  

ScrollView.requestChildRectangleOnScreen

不涉及数学,与其他提议的解决方案相反,它将正确处理向上和向下滚动。

void scrollToRow(ScrollView scrollView, LinearLayout linearLayout, TextView textViewToShow) {
    Rect textRect = new Rect(); //coordinates to scroll to
    textViewToShow.getHitRect(textRect); //fills textRect with coordinates of TextView relative to its parent (LinearLayout) 
    scrollView.requestChildRectangleOnScreen(linearLayout, textRect, false); //ScrollView will make sure, the given textRect is visible
}

最好将其包装到postDelayed中,以使其更可靠,以防ScrollView暂时更改

private void scrollToRow(final ScrollView scrollView, final LinearLayout linearLayout, final TextView textViewToShow) {
    long delay = 100; //delay to let finish with possible modifications to ScrollView
    scrollView.postDelayed(new Runnable() {
        public void run() {
            Rect textRect = new Rect(); //coordinates to scroll to
            textViewToShow.getHitRect(textRect); //fills textRect with coordinates of TextView relative to its parent (LinearLayout) 
            scrollView.requestChildRectangleOnScreen(linearLayout, textRect, false); //ScrollView will make sure, the given textRect is visible
        }
    }, delay);
}

好不好吗?

答案 4 :(得分:6)

由于你可以知道你需要滚动到的孩子LinearLayout,尝试这个怎么样。

ScrollView sv = // your scrollview
View highlightedItem = // find the LinearLayout or View that you need to scroll to which is inside this ScrollView
sv.scrollTo(0, highlightedItem.getY());

有关scrollTo以及smoothScrollTo

的更多信息

答案 5 :(得分:2)

科特琳:试试看,对我来说就像魅力一样!

parentScrollView.post(object : Runnable {
        override fun run() {
            parentScrollView.smoothScrollTo(0, targetView.bottom)
        }

    })

答案 6 :(得分:1)

如果目标视图不是ScrollView的直接子项,那么这是一个有效的解决方案:

public int findYPositionInView (View rootView, View targetView)
{
  return findYPositionInView (rootView, targetView, 0);
}


// returns -1 if targetView not found
private int findYPositionInView (View rootView, View targetView, int yCumulative)
{
  if (rootView == targetView)
    return yCumulative;

  if (rootView instanceof ViewGroup)
  {
    ViewGroup parentView = (ViewGroup)rootView;
    for (int i = 0;  i < parentView.getChildCount ();  i++)
    {
      View child = parentView.getChildAt (i);
      int yChild = yCumulative + (int)child.getY ();

      int yNested = findYPositionInView (child, targetView, yChild);
      if (yNested != -1)
        return yNested;
    }
  }

  return -1; // not found
}

像这样使用:

int yScroll = findYPositionInView (scrollView, targetView);
scrollView.scrollTo (0, yScroll);

此外,如果您想设置焦点,请执行以下操作:

targetView.requestFocus ();

并且,如果您想要键盘显示,请执行以下操作:

if (targetView instanceof EditText)
{
  targetView.post (new Runnable ()
  {
    @Override public void run ()
    {
      InputMethodManager imm = (InputMethodManager)context.getSystemService (Context.INPUT_METHOD_SERVICE);
      imm.showSoftInput (targetView, InputMethodManager.SHOW_FORCED);
    }
  });
}

答案 7 :(得分:1)

在进行了很多研究并尝试了许多方法之后,以下内容为我工作了WONDERS。

考虑到您的主要父级是ScrollView,子级视图是LinearLayout或RelativeLayout,它们中可能包含更多视图,因此可以使用两种方法-

情况1:您想关注childView的顶部。使用以下代码-

public void scrollToViewTop(ScrollView scrollView, View childView) {
    long delay = 100; //delay to let finish with possible modifications to ScrollView
    scrollView.postDelayed(new Runnable() {
        public void run() {
            scrollView.smoothScrollTo(0, childView.getTop());
        }
    }, delay);
}

情况2:将注意力集中在childView的底部。使用以下代码-

public void scrollToViewBottom(ScrollView scrollView, View childView) {
    long delay = 100; //delay to let finish with possible modifications to ScrollView
    scrollView.postDelayed(new Runnable() {
        public void run() {
            scrollView.smoothScrollTo(0, childView.getBottom());
        }
    }, delay);
}

要使用这些功能,只需像这样调用它们-

scrollToViewTop(scrollView, childView);
scrollToViewBottom(scrollView, childView);

希望它可以节省您的时间。编码愉快!

答案 8 :(得分:0)

使用:

final  ScrollView scrollView= (ScrollView) 
int speed=1000;
findViewById(R.id.main_scrollView);
final View view = findViewById(R.id.your_view); 
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this);   
                ObjectAnimator.ofInt(scrollView, "scrollY",  (int) view.getY()).setDuration(speed).start();
            }
        }); 

答案 9 :(得分:0)

这里rootView是您的父视图,childView是您要滚动视图的视图

public static int getRelativeTop(View rootView, View childView) {
    if(childView.getParent() == rootView) return childView.getTop();
    else return childView.getTop() + getRelativeTop(rootView, (View) childView.getParent());
}

答案 10 :(得分:0)

您可以使用smoothScrollTo方法滚动到滚动视图中的特定视图。 -示例:

my_scroll_View.smoothScrollTo(0, editText.getBottom());

祝你好运!

答案 11 :(得分:0)

我像扩展程序一样使用

fun ScrollView.focusOnView(toView: View){

    Handler().post(Runnable {
        this.smoothScrollTo(0, toView.top)
    })

}

用法:

myScrollView.focusOnView(myView)

答案 12 :(得分:0)

对于Kotlin用户。 如果上述代码不起作用。 试试吧。

val your_scrollview = findView...
val your_view_inside_SV = findView...
your_scrollview.post( { your_scrollview.scrollTo(0, your_view_inside_SV.getY().toInt()) })

答案 13 :(得分:0)

对我来说,实现此目标的最佳方法是使用requestRectangleOnScreen()函数。 您可以通过View进行滚动。 您不需要访问ScrollView,也不需要计算childOffset。 它不会改变焦点,似乎也不是代码中的黑客。 (我永远猜不到requestChildFocus()用于滚动)

还可以在kotlin中添加一些小的扩展功能,以使其使用起来更简单:

  1. 为View添加两个扩展功能:
fun View.requestOnScreen() = post {
    requestRectangleOnScreen(getDrawingRect())
}
    
fun View.getDrawingRect() = Rect().also(::getDrawingRect)
  1. 在需要时使用该功能

view.requestOnScreen()

仅此而已。

答案 14 :(得分:0)

从 API 29 开始,ScrollView 上有一个可用的方法 scrollToDescendant(View child) 可以替代其他自定义解决方案。

安卓文档: https://developer.android.com/reference/android/widget/ScrollView#scrollToDescendant(android.view.View)

答案 15 :(得分:-4)

尝试在要滚动到的视图上调用view.requestFocus()