自定义View练习——自定义StickyHeadLayout

NestedScrollingParent接口提供了八个接口,按照顺序大致可以这么来记:
start-->accepted-->preScroll-->scroll-->preFling-->fling-->stop

    boolean onStartNestedScroll( View child,  View target,  int axes);

    void onNestedScrollAccepted( View child, View target,  int axes);

    void onNestedPreScroll(View target, int dx, int dy,  int[] consumed);

    void onNestedScroll( View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);

    boolean onNestedPreFling(View target, float velocityX, float velocityY);

    boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);

    void onStopNestedScroll( View target);

    int getNestedScrollAxes();
StickyLayout.gif

如果不使用系统提供的CoordinatorLayout以及AppbarLayout,那么该怎么实现头部悬停View呢?
NestedScrollingParent与NestedScrollingChild接口配合使用就能达到想要的效果。由于系统提供的诸如NestedScrollView,RecyclerView等都实现了NestedScrollingChild接口,因此我们只需要实现一个NestedScrollingParent接口,来配合NestedScrollView来使用就可以了。

先看一下布局

<com.goodsnow.view.custom.nestedscroll.MySimpleStickyLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:ignore="MissingDefaultResource">

    <TextView
        android:textColor="#ffffff"
        android:textSize="50dp"
        android:gravity="center"
        android:id="@+id/tvTop"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="@color/colorAccent"
        android:text="Top" />

    <TextView
        android:id="@+id/tvMiddle"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="stickyHeady"
        android:textColor="@android:color/white"
        android:textSize="20dp" />

    <androidx.core.widget.NestedScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

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

            <TextView
                android:gravity="center"
                android:text="scrollView"
                android:textColor="@android:color/white"
                android:textSize="40dp"
                android:layout_width="match_parent"
                android:layout_height="800dp"
                android:background="@color/design_default_color_error" />

            <TextView
                android:gravity="center"
                android:text="scrollView"
                android:textColor="@android:color/white"
                android:textSize="40dp"
                android:layout_width="match_parent"
                android:layout_height="800dp"
                android:background="@color/design_default_color_primary" />


        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

</com.goodsnow.view.custom.nestedscroll.MySimpleStickyLayout>

对于MySimpleStickyLayout的代码来说,主要分为三部分。第一部分是测量,第二部分是重写接口,第三部分检测边界。

一:测量
  @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTop = findViewById(R.id.tvTop);
        mMiddle = findViewById(R.id.tvMiddle);
        mBottom = findViewById(R.id.scrollView);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup.LayoutParams layoutParams = mBottom.getLayoutParams();
        layoutParams.height = getMeasuredHeight() - mMiddle.getMeasuredHeight();
        setMeasuredDimension(getMeasuredWidth(), mTop.getMeasuredHeight() + mMiddle.getMeasuredHeight() + mBottom.getMeasuredHeight());
    }

因为MySimpleStickyLayout如果不重新测量的话,那么他的高度就只有屏幕的高度,而当我们向上滑动时,底部就会出现空白没有展示View的区域。为了将该区域进行绘制,因此进行了重新测量。

二:重写接口
    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return true;
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        super.onNestedPreScroll(target, dx, dy, consumed);

      boolean hide = dy >= 0 && getScrollY() < mTop.getHeight();
       boolean show = dy <= 0 && getScrollY() >= 0&& !ViewCompat.canScrollVertically(target, -1);
        if (hide || show) {
            scrollBy(0, dy);
            consumed[1] = dy;
        }
    }

onStartNestedScroll()返回true是因为需要当前View进行拦截。在onNestedPreScroll()中进行了是否消费的计算。其中mScrollY如果为正值,则表示向上移动,mScrollX为正值,则表示向左移动。dy为正值,也表示向上移动。

三:边界检查
    @Override
    public void scrollTo(int x, int y) {
        if(y<0){
            y=0;
        }
        if(y>mTop.getMeasuredHeight()){
            y=mTop.getMeasuredHeight();
        }
        if(y!=getScrollY()){
            super.scrollTo(x, y);

        }
    }

内容参考:鸿洋的博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容