[Android] This effect, I can't describe

Posted by garethj on Fri, 07 Jun 2019 18:21:17 +0200

Preface

Recently I received a request that embarrassed me.(Here's some crap)
The effect is that the top is partially suspended, followed by some layouts, a few switchable Tab pages below, and then scroll ~~Well, well, let's just look at the picture.


Effect

The main thing is the suspension of the top and Tab, and the effect of being topped off.

To achieve this effect, my drawer's special chopping knife has been foolishly moved.

thinking

Let's start with the idea of implementation. The above effects can be roughly divided into two parts:

  • 1. Suspend when Tab scrolls up to the top
    Tab is floating on top after scrolling ~~This effect can be easily achieved using CoordinatorLayout + AppBarLayout.(What?You don't know how to use these two controls yet?Well, you should be able to barely understand what follows)
  • 2. Top suspension and the effect of being "topped off"
    Simply wrap a FrameLayout around the CoordinatorLayout and change the top layout to it.Next listen for AppBarLayout scrolling and use topMargin to "top up" it

Now that the split is complete, the next step is to implement

Realization

  • Tab suspension effect
    Tab suspension using CoordinatorLayout, AppBarLayout, TabLayout, ViewPager
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFF">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFF">

        <LinearLayout
            android:id="@+id/ll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/header"
                android:layout_width="match_parent"
                android:layout_height="260dp"
                android:scaleType="centerCrop"
                android:src="@drawable/bg_header"/>
        </LinearLayout>

        <android.support.design.widget.TabLayout
            android:id="@+id/table_layout"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#FBDD9C"
            app:tabGravity="fill"
            app:tabIndicatorColor="#5f00"
            app:tabIndicatorHeight="4dp"
            app:tabMode="fixed"
            app:tabSelectedTextColor="#FFFFFF"
            app:tabTextColor="#FFFFFF"/>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
  • Set app:layout_scrollFlags="scroll|exitUntilCollapsed" in LinearLayout and TabLayout does not set app:layout_scrollFlags property
  • Use app:layout_behavior="@string/appbar_scrolling_view_behavior" in ViewPager

layout_scrollFlags: AppBarLayout's properties for Children View have five values: scroll, enterAlways, enterAlwaysCollapsed, snap, exitUntilCollapsed.Specific use can be referred to Android details five ScrollFlags for AppBarLayout
(I won't say much about the detailed use of CoordinatorLayout and AppBarLayout)

Then, just add a few list fragments to the ViewPager in Java code to see the following effect (Note: Lists can't be ListView s, you need RecyclerView)


Effect

So far, the effect has been mostly achieved.The final value needs to be pushed away.

  • Top "Topped off" effect
    At this point, the layout changes a little.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FFF">

        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#FFF">

            <LinearLayout
                android:id="@+id/ll"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

                <View
                    android:layout_width="match_parent"
                    android:layout_height="50dp"/>

                <ImageView
                    android:id="@+id/header"
                    android:layout_width="match_parent"
                    android:layout_height="260dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/bg_header"/>
            </LinearLayout>

            <android.support.design.widget.TabLayout
                android:id="@+id/table_layout"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:background="#FBDD9C"
                app:tabGravity="fill"
                app:tabIndicatorColor="#5f00"
                app:tabIndicatorHeight="4dp"
                app:tabMode="fixed"
                app:tabSelectedTextColor="#FFFFFF"
                app:tabTextColor="#FFFFFF"/>
        </android.support.design.widget.AppBarLayout>

        <android.support.v4.view.ViewPager
            android:id="@+id/vp"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    </android.support.design.widget.CoordinatorLayout>

    <TextView
        android:id="@+id/sticky_view"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:textColor="#FFFFFF"
        android:text="I will move except suspend"
        android:gravity="center"
        android:textSize="16sp"
        android:background="#39b9e9"/>
</FrameLayout>

On the original basis, a FrameLayout is set, and the suspended part at the top can be achieved through FrameLayout.This also results in the following layout being partially covered, so an empty View of the same height as the suspended portion is added to the LinearLayout.

The layout is complete. How can the effect of being pushed away be achieved?Simply listen for AppBarLayout scrolling in MainActivity.

    @BindView(R.id.app_bar)
    AppBarLayout mAppBar;
    @BindView(R.id.sticky_view)
    View mStickyView;
    @BindView(R.id.header)
    View mHeader;

    private void setAppBarListener() {
        mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                //Head Height (Remove Top Covered Part)
                int minScrollHeight = mHeader.getMeasuredHeight();
                int margin = minScrollHeight + verticalOffset;
                margin = margin > 0 ? 0 : margin;
                FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mStickyView.getLayoutParams();
                layoutParams.topMargin = margin;
                mStickyView.setLayoutParams(layoutParams);
            }
        });
}

Here the margin value is calculated by listening for AppBarLayout scrolling (when scrolling up, the verticalOffset value changes to 0, -1, -2... -n-1, -n).By changing topMargin, the effect of being "pushed away" is achieved.
Take a second look at the effect:


Effect

Knock here, I silently picked up the special kitchen knife for cutting products.

Tips

  • Problem: Smooth scrolling with CoordinatorLayout
    Solution: You can write a Behavior to add to AppBarLayout. Specific Solutions
  • Question: If you are trying to refresh a feature and you have a SwipeRefreshLayout on the outside of CoordinatorLayout, it triggers a refresh ~ (I've experienced it)
    Solution: This problem can be handled by monitoring AppBarLayout and setting swipeLayout's Enabled
mAblAppBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
   if (verticalOffset == 0) {
      swipeLayout.setEnabled(true);
   } else {
       if (!swipeLayout.isRefreshing()) {
          swipeLayout.setEnabled(false);
       }
   }
});

Source Address

Github

There are errors above, thank you for pointing out

Topics: Android xml encoding Java