首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在安卓上实现NestedScrolling?

如何在安卓上实现NestedScrolling?
EN

Stack Overflow用户
提问于 2015-04-27 07:53:54
回答 1查看 16K关注 0票数 55

借助support-v4库22.1.0,android支持嵌套滚动(早于android 5.0)。不幸的是,这个特性并没有真正的文档化。有两个接口(NestedScrollingParentNestedScrollingChild)以及两个助手委托类(NestedScrollingChildHelperNestedScrollingParentHelper)。

有没有人在安卓系统上使用过NestedScrolling?

我试着设置了一个小示例,其中我使用了同时实现NestedScrollingParentNestedScrollingChildNestedScrollView

我的布局如下所示:

代码语言:javascript
复制
<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="vertical">

    <View
        android:id="@+id/header"
        android:layout_width="match_parent" android:layout_height="100dp"
        android:background="#AF1233"/>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/child"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >

      <FrameLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#12AF33"
            android:text="@string/long_text"/>

      </FrameLayout>
    </android.support.v4.widget.NestedScrollView>

  </LinearLayout>

</android.support.v4.widget.NestedScrollView>

我想在父级(id = NestedScrollView )中显示一个子级和另一个子级(id = NestedScrollView )。

其想法是,在运行时通过使用OnPredrawListener调整子滚动视图的高度

代码语言:javascript
复制
public class MainActivity extends Activity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final NestedScrollView parentScroll = (NestedScrollView) findViewById(R.id.parent);
    final NestedScrollView nestedScroll = (NestedScrollView) findViewById(R.id.child);
    parentScroll.setNestedScrollingEnabled(false);
    final View header = findViewById(R.id.header);

    parentScroll.getViewTreeObserver()
        .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
          @Override public boolean onPreDraw() {
            if (parentScroll.getHeight() > 0) {
              parentScroll.getViewTreeObserver().removeOnPreDrawListener(this);
              nestedScroll.getLayoutParams().height = parentScroll.getHeight() - 40;
              nestedScroll.setLayoutParams(nestedScroll.getLayoutParams());
              nestedScroll.invalidate();
              return false;
            }
            return true;
          }
        });

  }
}

因此标题视图将部分滚动离开,40个像素将保持可见,因为我将嵌套子滚动视图的高度设置为parentScroll.getHeight() - 40。好了,在运行时设置高度并滚动父滚动视图的工作方式与预期的一样(标题向外滚动,40个像素保持可见,然后子滚动视图填充标题下的其余屏幕)。

我期望"NestedScrolling“意味着我可以在屏幕上的任何地方做出滚动手势(由父滚动视图捕获的触摸事件),如果父滚动视图已经到达末尾,嵌套的子滚动视图将开始滚动。然而,情况似乎并非如此(既不适用于简单的滚动手势,也不适用于投掷手势)。

如果触摸事件在其边界内开始,则触摸事件始终由嵌套子滚动视图处理,否则由父滚动视图处理。

这是“嵌套滚动”的预期行为吗?还是有一个选项可以改变这种行为?

我还尝试用NestedRecyclerView替换嵌套子滚动视图。我继承了RecyclerView并实现了NestedScrollingChild,其中我将所有方法委托给NestedScrollingChildHelper

代码语言:javascript
复制
public class NestedRecyclerView extends RecyclerView implements NestedScrollingChild {

  private final NestedScrollingChildHelper scrollingChildHelper =
      new NestedScrollingChildHelper(this);


  public void setNestedScrollingEnabled(boolean enabled) {
    scrollingChildHelper.setNestedScrollingEnabled(enabled);
  }

  public boolean isNestedScrollingEnabled() {
    return scrollingChildHelper.isNestedScrollingEnabled();
  }

  public boolean startNestedScroll(int axes) {
    return scrollingChildHelper.startNestedScroll(axes);
  }

  public void stopNestedScroll() {
    scrollingChildHelper.stopNestedScroll();
  }

  public boolean hasNestedScrollingParent() {
    return scrollingChildHelper.hasNestedScrollingParent();
  }

  public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
      int dyUnconsumed, int[] offsetInWindow) {

    return scrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed,
        dyUnconsumed, offsetInWindow);
  }

  public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
    return scrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
  }

  public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
    return scrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
  }

  public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
    return scrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
  }
}

NestedRecyclerView根本不会滚动。所有的触摸事件都会被父滚动视图捕获。

EN

回答 1

Stack Overflow用户

发布于 2015-07-15 05:18:16

我在这上面花了相当多的时间,只是检查了安卓代码,试图弄清楚NestedScrollView中发生了什么。下面的方法应该是可行的。

代码语言:javascript
复制
public abstract class ParentOfNestedScrollView extends NestedScrollView{

    public ParentOfNestedScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /* 
    Have this return the range you want to scroll to until the 
    footer starts scrolling I have it as headerCard.getHeight() 
    on most implementations
    */
    protected abstract int getScrollRange();

    /*
    This has the parent do all the scrolling that happens until 
    you are ready for the child to scroll.
    */
    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed){
        if (dy > 0 && getScrollY() < getScrollRange()) {
            int oldScrollY = getScrollY();
            scrollBy(0, dy);
            consumed[1] = getScrollY() - oldScrollY;
        }
    }

    /*
    Sometimes the parent scroll intercepts the event when you don't
    want it to.  This prevents this from ever happening.
    */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }
}

我的一些代码是从这个question借用的。在此基础上,我只需根据需要扩展该类。我的xml将子元素作为NestedScrollView作为子元素,父元素作为下面的元素。这不能很好地处理我想要的,这是一项正在进行中的工作。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29885067

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档