A graphic.

 


(Alipay) (Effect achieved)


2. Explanation of ideas

TabLayout+ScrollView implementation. Each TAB corresponds to a layer layout wrapped in the ScrollView, and there are four tabs above that, which requires four layout files that represent the styling content of each layer.

1. After the page is loaded, remember the Distance that the parent layout of each layer slides to the top of the Screen;

2. Operation:

A. When clicking TAB: ScrollView Distance of this layer;

B. When sliding the ScrollView to the corresponding layer: Select the corresponding TAB.


Code 3.

1. Layout code

<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.ganshenml.tabscrollviewdemo.MainActivity">

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

        <android.support.design.widget.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></android.support.design.widget.TabLayout>

        <FrameLayout
            android:id="@+id/wrapperFl"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></FrameLayout>
    </FrameLayout>

    <com.ganshenml.tabscrollviewdemo.ObservableScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

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

        </LinearLayout>

    </com.ganshenml.tabscrollviewdemo.ObservableScrollView>
</LinearLayout> Copy the code

The layout code is very simple, and some questions you might have are:

A. What is wrapperFl used for?

B. ObservableScrollView and what is it?

(Explained later)


2. Logical code

public class MainActivity extends AppCompatActivity implements ObservableScrollView.ScrollViewListener {
    private static final String TAG = "MainActivity";
    private FrameLayout wrapperFl;
    private TabLayout tabLayout;
    private ObservableScrollView scrollView;
    private LinearLayout containerLl;
    private boolean firstAlreadyInflated = true; private ViewGroup firstFloorVg; private ViewGroup secondFloorVg; private ViewGroup thirdFloorVg; private ViewGroup fourthFloorVg; private int secondFloorVgPositionDistance; / / the second sliding distance to the top of the private int thirdFloorVgPositionDistance; private int fourthFloorVgPositionDistance; private int currentPosition = 0; private boolean tabInterceptTouchEventTag =true; // flag bit, Override protected void onCreate(Bundle savedInstanceState) {scrollView@override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
        initViews();
        initListeners();
    }

    private void initViews() {
        wrapperFl = (FrameLayout) findViewById(R.id.wrapperFl);
        tabLayout = (TabLayout) findViewById(R.id.tabLayout);
        scrollView = (ObservableScrollView) findViewById(R.id.scrollView);
        containerLl = (LinearLayout) findViewById(R.id.containerLl);
        for (int i = 0; i < 4; i++) {
            tabLayout.addTab(tabLayout.newTab().setText("tab" + (i + 1)));
        }

        firstFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_first, null);
        secondFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_second, null);
        thirdFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_third, null);
        fourthFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_fourth, null);

        containerLl.addView(firstFloorVg);
        containerLl.addView(secondFloorVg);
        containerLl.addView(thirdFloorVg);
        containerLl.addView(fourthFloorVg);

    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        ifFirstalreadyvehicle {// Determine the location of each layer from the top of your screen and the distance required to slide to the top of your screenfalse;
            int[] firstFloorVgPosition = new int[2];
            int[] secondFloorVgPosition = new int[2];
            int[] thirdFloorVgPosition = new int[2];
            int[] fourthFloorVgPosition = new int[2];
            firstFloorVg.getLocationOnScreen(firstFloorVgPosition);
            secondFloorVg.getLocationOnScreen(secondFloorVgPosition);
            thirdFloorVg.getLocationOnScreen(thirdFloorVgPosition);
            fourthFloorVg.getLocationOnScreen(fourthFloorVgPosition);
            int firstFloorVgPositionAnchor = firstFloorVgPosition[1];
            int secondFloorVgPositionAnchor = secondFloorVgPosition[1];
            int thirdFloorVgPositionAnchor = thirdFloorVgPosition[1];
            int fourthFloorVgPositionAnchor = fourthFloorVgPosition[1];

            Log.d(TAG, "The first layer is:" + firstFloorVgPosition[1]);
            Log.d(TAG, "The second layer is:" + secondFloorVgPosition[1]);
            Log.d(TAG, "The third layer is:" + thirdFloorVgPosition[1]);
            Log.d(TAG, "The fourth layer is:" + fourthFloorVgPosition[1]);

            secondFloorVgPositionDistance = secondFloorVgPositionAnchor - firstFloorVgPositionAnchor;
            thirdFloorVgPositionDistance = thirdFloorVgPositionAnchor - firstFloorVgPositionAnchor;
            fourthFloorVgPositionDistance = fourthFloorVgPositionAnchor - firstFloorVgPositionAnchor;
        }
    }

    private void initListeners() {
        wrapperFl.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d(TAG,"wrapperFl onTouch");
                tabInterceptTouchEventTag = true; // Let TAB handle the slidingreturn false; }}); tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                currentPosition = tab.getPosition();
                if(! TabInterceptTouchEventTag) {/ / manual sliding when the page is not again the processingreturn;
                }
                scrollView.computeScroll();
                switch (currentPosition) {
                    case 0:
                        scrollView.smoothScrollTo(0, 0);
                        break;
                    case 1:
                        scrollView.smoothScrollTo(0, secondFloorVgPositionDistance);
                        break;
                    case 2:
                        scrollView.smoothScrollTo(0, thirdFloorVgPositionDistance);
                        break;
                    case 3:
                        scrollView.smoothScrollTo(0, fourthFloorVgPositionDistance);
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
        scrollView.setScrollViewListener(this);
        scrollView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d(TAG, "scrollView onTouch");
                tabInterceptTouchEventTag = false; // Let scrollView handle slidingreturn false; }}); } @Override public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {if(tabInterceptTouchEventTag) {/ / let the TAB to deal with the slidereturn;
        }
        Log.d(TAG, "Current position of scrollView -- >" + y);
        if (y < secondFloorVgPositionDistance) {
            if (currentPosition != 0) {
                scrollView.computeScroll();
                tabLayout.getTabAt(0).select();
            }
        } else if (y < thirdFloorVgPositionDistance) {
            if (currentPosition != 1) {
                scrollView.computeScroll();
                tabLayout.getTabAt(1).select();
            }
        } else if (y < fourthFloorVgPositionDistance) {
            if (currentPosition != 2) {
                scrollView.computeScroll();
                tabLayout.getTabAt(2).select();
            }
        } else {
            if(currentPosition ! = 3) { scrollView.computeScroll(); tabLayout.getTabAt(3).select(); }}}}Copy the code

A. tabInterceptTouchEventTag as sign is to prevent because scrollview triggered TAB Selected so as to result in a scrollview sliding again sliding is not smooth.

B. wrapperFl there is going to give tabInterceptTouchEventTag assignment, because TabLayout Touch, Click, events such as Focus has been digested, not listening to the corresponding values in these events, So wrapperFl is used to listen for Touch events.

C. ObservableScrollView is an extension of Scrollview, adding and changing the following methods:

  public void setScrollViewListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if(scrollViewListener ! = null) { scrollViewListener.onScrollChanged(this, x, y, oldx, oldy); } } public interface ScrollViewListener { void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy); }Copy the code


In this way, you can listen to the scrollView sliding event in the main interface of the activity, so as to obtain the current position of the entire ScrollView, and then determine whether to select the corresponding TAB.


Ps: If you want to make alipay like TabLayout also has a TopView content style, you can refer to the blog before written practice: View sliding fixed effect implementation >>


Finally, the full code can be viewed at GitHub>>