Android ScrollView with sticky header

Foreword, when I ordered takeout one day, I noticed that the scrolling effect of the Ele.me list page was good, but I felt that the gesture sliding in it was quite complicated. What's the list page try to write this effect

1. First paste an effect diagram of the realization

The logic is that when the external ScrollView does not slide to the bottom, when it slides up, it slides the external ScrollView. When the external ScrollView reaches the bottom, we slide online again, which is to slide the internal list, and slide left and right. When the left and right sliding distance is greater than minPageSlop, then the left and right sliding is performed. The following is the rendering of the list page imitating Ele.me:

2. Introduce

在项目根目录的build.gradle文件下增加jitpack的repo地址
allprojects {
 repositories {
    jcenter()
    maven { url "https://jitpack.io" }
 }
}

在需要引入的module中引入library
dependencies {
    implementation 'com.github.WelliJohn:StickScrollView:0.0.3'
}

3. Layout description of the interface

    <wellijohn.org.stickscrollview.ScrollViewWithStickHeader
        android:id="@+id/stick_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1">

        <LinearLayout
            android:id="@+id/ll"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:descendantFocusability="blocksDescendants"
            android:focusableInTouchMode="true"
            android:orientation="vertical">
            //这里是header部分,可以随便自定义
            </LinearLayout>

            <LinearLayout
                android:id="@+id/ll_stick_list"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <android.support.design.widget.TabLayout
                    android:id="@+id/order_manager_tabs"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:background="#FFFFFF"
                    tools:tabGravity="fill"
                    tools:tabMode="fixed" />

                <android.support.v4.view.ViewPager
                    android:id="@+id/vp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />
            </LinearLayout>
        </LinearLayout>
    </wellijohn.org.stickscrollview.ScrollViewWithStickHeader>

For example, we see the list page interface that resembles Ele.me. We need to set Fragment in ViewPager. There are two lists on the left and right in the fragment. Look at the xml settings of the fragment:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <wellijohn.org.stickscrollview.ChildRecyclerView
        android:id="@+id/child_recyclerview"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:background="#EEEEEE" />

    <wellijohn.org.stickscrollview.ChildRecyclerView
        android:id="@+id/child_recyclerview_right"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="#FFFFFF"
        android:layout_weight="3" />
</LinearLayout>

4. Precautions

  • ScrollViewWithStickHeader currently supports placing ViewPager, ScrollView, RecyclerView, WebView
  • ScrollView, RecyclerView, WebView need to use ChildScrollView, ChildRecyclerView, ChildWebView correspondingly
  • When we are using it, we need to call mStickScrollView.setContentView(mContentView); mLLStickList is the part where we need StickHeader+list. If you don't have StickHeader, you can directly set the list to come in. In short, where you want to slide to the next slide is Simply slide the lower part, then you set the whole view below to mContentView. The ContentView just now is the View whose id is ll_stick_list.
  • In addition, the autoscroll property is added to ScrollViewWithStickHeader here, which is turned off by default. If autoscroll: true, when our finger is released, the contentView will judge whether it automatically slides to the top or is hidden.Auto scrolling renderings

Version 5.0.0.3 fixes the problem that the scrolling of the interface is disordered when there is an operation bar at the bottom.

When we have a view at the bottom that needs to be fixed, we need to pass mStickScrollView.setBottomView(mViewBottom);, as shown below:

6. We better know how to use any control, so here is a brief introduction to the design ideas of this control (ChildScrollView, ChildRecyclerView, and ChildWebView are all called sub-ScrollView)?

  • 6.1. When should we let the external ScrollView perform the sliding event, and when should we let the child ScrollView perform the sliding. In Android, we have a method getParent().requestDisallowInterceptTouchEvent(true); which is to let the view get the corresponding event.
  • 6.2. Now that we know how to make the view's touch event, then we need to understand under what circumstances we should let the parent view execute the scroll event, and when to let the child view execute the scroll event. As follows, I have the table:
parent ScrollVIew child ScrollView Gesture swipe direction Which view controls the sliding event
not at the bottom top Improvement parent ScrollView
not at the bottom top down parent ScrollView
bottom not at the top Improvement child ScrollView
bottom not at the top down child ScrollView
bottom top down parent ScrollView
bottom top Improvement child ScrollView

Here, when the parent ScrollView is not at the bottom, there will be no situation where the child ScrollView is not at the top, so it will not be analyzed here.

  • 6.3. After analyzing, under what circumstances should we let the child ScrollVIew or the parent ScrollView capture the sliding event, we can write the corresponding code in our child ScrollView to handle it? For example, the following is a rewrite of the onTouchEvent method of ChildScrollView, and the processing of other ChildRecyclerView and ChildWebView is the same:
@Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mScrollViewWithStickHeader == null) return super.onTouchEvent(event);
        int action = event.getAction();

        if (action == MotionEvent.ACTION_DOWN) {
            mLastX = event.getX();
            mLastY = event.getY();
            //首先判断外层ScrollView是否滑动到底部
            if (mScrollViewWithStickHeader.isBottom()) {
                getParent().requestDisallowInterceptTouchEvent(true);
                return super.onTouchEvent(event);
            } else {
                //拦截事件 本身不处理
                getParent().requestDisallowInterceptTouchEvent(false);
                return false;
            }
        }
        if (action == MotionEvent.ACTION_MOVE) {
            float nowY = event.getY();
            if (!mScrollViewWithStickHeader.isBottom() && !isScrolledToTop && nowY - mLastY > 0) {
                if (Math.abs(event.getX() - mLastX) < minPageSlop) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    return super.onTouchEvent(event);
                } else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    return false;
                }
            } else if (mScrollViewWithStickHeader.isBottom() && !isScrolledToBottom && nowY - mLastY < 0) {
                if (Math.abs(event.getX() - mLastX) < minPageSlop) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    return super.onTouchEvent(event);
                } else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    return false;
                }
            } else if (mScrollViewWithStickHeader.isBottom() && !isScrolledToTop && nowY - mLastY > 0) {
                if (Math.abs(event.getX() - mLastX) < minPageSlop) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    return super.onTouchEvent(event);
                } else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    return false;
                }
            } else {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
        }

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            getParent().requestDisallowInterceptTouchEvent(false);
        }

        return super.onTouchEvent(event);
    }

In this way, we can implement a ScrollView with a fixed head.

7. github address , your like or star is the biggest motivation for me to continue to open source.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325458958&siteId=291194637