如何在Android上实现NestedScrolling?

时间:2015-04-26 23:53:54

标签: android appcompat-v7-r22.1

使用support-v4库22.1.0 android支持嵌套滚动(pre android 5.0)。不幸的是,这个功能并没有真正记录。有两个接口(NestedScrollingParentNestedScrollingChild)以及两个辅助委托类(NestedScrollingChildHelperNestedScrollingParentHelper)。

有没有人在Android上使用NestedScrolling?

我尝试设置一个小例子,我使用NestedScrollView来实现NestedScrollingParentNestedScrollingChild

我的布局如下:

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

我想在header view(id = parent)中显示NestedScrollView和另一个NestedScrollView(id = child)。

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

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

  }
}

因此,标题视图将部分滚动,因为我将嵌套子滚动视图的高度设置为parentScroll.getHeight() - 40,所以仍然可以看到40个像素。 好的,在运行时设置高度并滚动父滚动视图就像预期的那样工作(标题滚出,40像素仍然可见,然后子滚动视图填充标题下方的屏幕其余部分)。

我希望&#34; NestedScrolling&#34;意味着我可以在屏幕上的任何位置制作滚动手势(触摸由父滚动视图捕获的事件),如果父滚动视图已到达结尾,则嵌套的子滚动视图开始滚动。 然而,似乎并非如此(对于简单的滚动手势和投掷手势都不是这样)。

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

这是&#34;嵌套滚动的预期行为&#34;或者是否有改变这种行为的选择?

我还尝试用NestedRecyclerView替换嵌套的子滚动视图。我将RecyclerView子类化并实施NestedScrollingChild,我将所有方法委托给NestedScrollingChildHelper

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根本没有滚动。所有触摸事件都由父滚动视图捕获。

1 个答案:

答案 0 :(得分:3)

我花了很多时间来解决这个问题,只是通过Android代码试图弄清楚NestedScrollView中发生了什么。以下应该有效。

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作为子节点,父节点作为子节点。这并不像我想的那样处理甩尾,这是一项正在进行中的工作。