在NestedScrollView中平滑滚动到子视图

16

我在NestedScrollView中有一个可展开的视图,它的父项是CardView。当展开动画结束后,我想要创建平滑的滚动到子项。但我只找到了一种解决方案:

 scrollView.requestChildFocus(someView, someView);

这段代码运行良好,但是当调用requestChildFocus时它会立即滚动,这让我有点烦。是否有可能平滑地滚动到子元素?


请点击以下链接查看有关NestedScrollView类的详细信息:https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html#smoothScrollTo(int, int) - Budius
@Budius,如果我没记错的话,没有这样的方法。 - Near1999
我已经传递了官方文档的链接,我非常确定它存在,因为文档是基于代码注释自动生成的。也许你需要更新到最新的支持库? - Budius
你的意思是说,原始的Android文档中没有描述过这样的方法? - LilaQ
8个回答

31

我想滚动到的childView有一个父级CardView,因此childView.getTop()返回相对于CardView而不是ScrollView的值。 因此,要获取相对于ScrollView的顶部,我应该获取childView.getParent().getParent()并将其强制转换为View,然后调用getTop()

滚动位置的计算方式如下:

int scrollTo = ((View) childView.getParent().getParent()).getTop() + childView.getTop();
nestedScrollView.smoothScrollTo(0, scrollTo);

1
nestedScrollView.smoothScrollTo 对我来说根本不起作用(Support lib 27.1.1) - Konstantin Konopko

6

接下来是 @jerry-sha 的更多回答

fun NestedScrollView.smoothScrollTo(view: View) {
  var distance = view.top
  var viewParent = view.parent
  //traverses 10 times
  for (i in 0..9) {
    if ((viewParent as View) === this) break
    distance += (viewParent as View).top
    viewParent = viewParent.getParent()
  }
  smoothScrollTo(0, distance)
}

4

我在scrollview的不同级别中有子视图,因此基于已接受的答案编写了这个函数来计算距离和滚动。

  private int findDistanceToScroll(View view){
    int distance = view.getTop();
    ViewParent viewParent = view.getParent();
    //traverses 10 times
    for(int i = 0; i < 10; i++) {
        if (((View) viewParent).getId() == R.id.journal_scrollview) {
            return distance;
        }
        distance += ((View) viewParent).getTop();
        viewParent = viewParent.getParent();
    }
    Timber.w("view not found");
    return 0;
}

然后使用滚动方式:
journal_scrollview.smoothScrollTo(0, distance);

1
尝试阅读源代码。
svMain.setSmoothScrollingEnabled(true);
Rect rect = new Rect();
rect.top = 0;
rect.left = 0;
rect.right = tv4.getWidth();
rect.bottom =tv4.getHeight();
svMain.requestChildRectangleOnScreen(tv4,rect,false);

rect 是您希望在屏幕上显示视图的位置。


获取CardView的位置并尝试滚动到该位置。 - tiny sunlight
不起作用,它现在滚动到视图大小的前四分之一左右。 - Near1999

1

你可以使用我的库ViewPropertyObjectAnimator来完成这个功能。

假设mNestedScrollView是你的NestedScrollViewmChildView是你想要滚动到的子View,你可以按照以下步骤操作:

ViewPropertyObjectAnimator.animate(mNestedScrollView).scrollY(mChildView.getTop()).start();

请确保在调用 .animate(...) 时,mChildView.getTop() 不为 0

编辑:

如我所说:在调用 .animate(...) 时,请确保您的 View 顶部不为零。换句话说,只有当您的子 View 已经具有尺寸时才调用 .animate(...)。您怎样才能确定这一点?例如像这样:

mChildView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
       int width = mChildView.getWidth();
       int height = mChildView.getHeight();
        if (width > 0 && height > 0) {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
              mChildView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            } else {
              mChildView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }

            ViewPropertyObjectAnimator.animate(mNestedScrollView)
                    .scrollY(mChildView.getTop())
                    .start();
        }
    }
});

我的子视图在动画结束时,其顶部始终为0。 - Near1999
现在我无法检查这个答案(我会明天做)。你能告诉我 '.scrollY()' 是试图将我的子视图放置到屏幕顶部还是第一个可见的像素吗? - Near1999
mChildView.getTop() 表示从您的 View 顶部到 NestedScrollView 内容顶部的像素数。这个命令将精确滚动您的 NestedScrollView 到这个位置。 - Bartek Lipinski

1
主要问题是计算相对于嵌套滚动视图的正确x/y位置。然而,这里大部分答案都试图通过导航层次结构并硬编码所需视图的层次级别来提出解决方案。在我看来,如果您更改了视图组层次结构,则这种方法非常容易出错。
因此,我建议使用更加清晰的方法,它基于Rect计算相对位置。然后,您可以使用嵌套滚动视图的smoothScrollTo(..)方法滚动到所需位置。

0

我发现已被接受的答案可行,但它仅适用于当前他们的视图结构,并且不能用作对所有具有不同视图结构的类似滚动的静态方法。我在一个实用程序类中制作了它的多个版本,直到它找到它的ScrollView或NestedScrollView父项为止。我使它可以使用scrollToView(View,View)方法来处理ScrollView和NestedScrollView,以防以后更新我使用的内容或其他情况。当然,您也可以直接调用正确的“子方法”。

public static void scrollToView(View scrollView, View scrollToView) {
    if (scrollToView == null) {
        Log.d(TAG, "scrollToView() failed due to scrollToView == NULL!");
        return;
    }
    if (scrollView instanceof NestedScrollView) {
        scrollToInNestedView((NestedScrollView) scrollView, scrollToView);
    } else if (scrollView instanceof ScrollView) {
        scrollToInScrollView((ScrollView) scrollView, scrollToView);
    } else {
        Log.d(TAG, "scrollToView() failed due to scrollView not appearing to be any kind of scroll view!");
    }
}

public static void scrollToInNestedView(NestedScrollView scrollView, View scrollToView) {
    if (scrollView == null || scrollToView == null) {
        return;
    }
    scrollView.post(() -> {
        int startY = scrollView.getScrollY();
        int requiredY = scrollToView.getTop();
        View parent = (View) scrollToView.getParent();
        while (parent != null && !(parent instanceof NestedScrollView)) {
            requiredY += parent.getTop();
            parent = (View) parent.getParent();
        }
        if (requiredY != startY) {
            scrollView.smoothScrollTo(0, requiredY);
        }
    });
}

public static void scrollToInScrollView(ScrollView scrollView, View scrollToView) {
    if (scrollView == null || scrollToView == null) {
        return;
    }
    scrollView.post(() -> {
        int startY = scrollView.getScrollY();
        int requiredY = scrollToView.getTop();
        View parent = (View) scrollToView.getParent();
        while (parent != null && !(parent instanceof ScrollView)) {
            requiredY += parent.getTop();
            parent = (View) parent.getParent();
        }
        if (requiredY != startY) {
            scrollView.smoothScrollTo(0, requiredY);
        }
    });
}

0

这应该可以完成工作,其中childView是您想要滚动到的视图

public static void scrollToView(final NestedScrollView nestedScrollView, final View viewToScrollTo) {
        final int[] xYPos = new int[2];
        viewToScrollTo.getLocationOnScreen(xYPos);
        final int[] scrollxYPos = new int[2];
        nestedScrollView.getLocationOnScreen(scrollxYPos);
        int yPosition = xYPos[1];
        if (yPosition < 0) {
            yPosition = 0;
        }
        nestedScrollView.scrollTo(0, scrollxYPos[1] - yPosition);
    }

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