CollapsingToolbarLayout CollapsingToolbarLayout has been in view for a long time and has a very cool effect that most of you are familiar with. In the process of use, we may obtain the state of contraction or expansion because of the product requirements, here I show you a way to obtain this state.





CollapsingToolbarLayout this structure is typically CollapsingToolbarLayout, which is nested with AppBarLayout. Within AppBarLayout there is an interface that looks like this:

    /**
     * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
     * offset changes.
     */
    public interface OnOffsetChangedListener {
        /**
         * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
         * child views to implement custom behavior based on the offset (for instance pinning a
         * view at a certain y value).
         *
         * @param appBarLayout the {@link AppBarLayout} which offset has changed
         * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
         */
        void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);
    }Copy the code

The interface is called when the verticalOffset occurs. We can determine the current state of the interface by the exposed verticalOffset. Let’s write a Demo to test it:





The demo code

The following information is displayed:





It comes in with printed information that is expanded by default

It comes in expanded by default with an offset of 0; If we slide it up to shrink it will print the following message:





The information is displayed when the state becomes contracted

You can see that it goes from 0 to -300; We are looking at the log information from the contracted state to the expanded state:





The shrunk state changes to the expanded state log information

From the above we can see that the offset changed from -270 to 0. Thus we can get the corresponding state information according to this offset. When 0 we can say it’s expanded, when do we say it’s contracted? How do I get the value of -300? We can use appBarLayout. GetTotalScrollRange () this method:

/**
     * Returns the scroll range of all children.
     *
     * @return the scroll range in px
     */
    public final int getTotalScrollRange(a) {
        if(mTotalScrollRange ! = INVALID_SCROLL_RANGE) {return mTotalScrollRange;
        }

        int range = 0;
        for (int i = 0, z = getChildCount(); i < z; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final int childHeight = child.getMeasuredHeight();
            final int flags = lp.mScrollFlags;

            if((flags & LayoutParams.SCROLL_FLAG_SCROLL) ! =0) {
                // We're set to scroll so add the child's height
                range += childHeight + lp.topMargin + lp.bottomMargin;

                if((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) ! =0) {
                    // For a collapsing scroll, we to take the collapsed height into account.
                    // We also break straight away since later views can't scroll beneath
                    // us
                    range -= ViewCompat.getMinimumHeight(child);
                    break; }}else {
                // As soon as a view doesn't have the scroll flag, we end the range calculation.
                // This is because views below can not scroll under a fixed view.
                break; }}return mTotalScrollRange = Math.max(0, range - getTopInset());
    }Copy the code

As you can see from the code and comments, this method is to get how far the AppBarLayout subclass can slide. In the Demo onCreate method we call this method:

Log.e(TAG, "app_bar.getTotalScrollRange" + app_bar.getTotalScrollRange());Copy the code

Print it out as follows:





image.png

Why is it 0? In theory it should be 300! Then I put this method inside the OnOffsetChangedListener onOffsetChanged method and print:





The following information is displayed:





In this listener we get the correct value, so why is this happening? The next time analysis

From this, we can draw the following conclusions:

1. When verticalOffset==0, we can judge that the state is expanded; 2. When Math. Abs (verticalOffset) > = AppBarLayout. GetTotalScrollRange () we can judge for contracting, to note here appBarLayout. GetTotalScrollRange () in OnOffsetChangedListener. OnOffs The etChanged() method is called to get the correct value to prevent zero;

For a practical application, we have a requirement that we can pull refresh when we stretch, but we can’t pull refresh when we shrink. This requirement should be very common. I’m sure many people will think of event distribution when they hear this requirement, but we can do this. CollapsingToolbarLayout We use Android-ultra-pull-to-refresh To do the drop-down Refresh container, nested in CollapsingToolbarLayout, and then place behavior on PtrClassicFrameLayout, as shown in the collapse view: Main interface layout:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.zzw.T.ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="? attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="? attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_scrolling" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        app:layout_anchor="@id/app_bar"
        app:layout_anchorGravity="bottom|end"
        app:srcCompat="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>Copy the code

R.layout.content_scrolling.xml:

<?xml version="1.0" encoding="utf-8"? >
<in.srain.cube.views.ptr.PtrClassicFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ptr"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:ptr_resistance="1.7"
    app:ptr_ratio_of_header_height_to_refresh="1.2"
    app:ptr_duration_to_close="300"
    app:ptr_duration_to_close_header="2000"
    app:ptr_keep_header_when_refresh="true"
    app:ptr_pull_to_fresh="false"
    tools:context="com.zzw.T.ScrollingActivity">


    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:showIn="@layout/activity_scrolling">


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

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="EFGJERJOREJROEJGOERJ" />

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/colorPrimary" />
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</in.srain.cube.views.ptr.PtrClassicFrameLayout>Copy the code

Content_scrolling (PtrClassicFrameLayout) for the behavior that triggered the contraction. Inside is a NestedScrollView. Maybe RecyclrView should be used more often. The JAVA code is as follows:

  package com.zzw.T;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import in.srain.cube.views.ptr.PtrClassicFrameLayout;
import in.srain.cube.views.ptr.PtrDefaultHandler;
import in.srain.cube.views.ptr.PtrFrameLayout;
import in.srain.cube.views.ptr.PtrHandler;

public class ScrollingActivity extends AppCompatActivity {

    private static final String TAG = "ScrollingActivity";

    private AppBarLayout app_bar;
    private int verticalOffset;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scrolling);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        app_bar = (AppBarLayout) findViewById(R.id.app_bar);

        final PtrClassicFrameLayout ptrFrameLayout = (PtrClassicFrameLayout) findViewById(R.id.ptr);
        ptrFrameLayout.setPtrHandler(new PtrHandler() {
            @Override
            public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
                return verticalOffset >= 0 && PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header);
            }

            @Override
            public void onRefreshBegin(PtrFrameLayout frame) {
                frame.postDelayed(new Runnable() {
                    @Override
                    public void run(a) { ptrFrameLayout.refreshComplete(); }},2000); }}); app_bar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                Log.e(TAG, "verticalOffset:" + verticalOffset);
                Log.e(TAG, "getTotalScrollRange():" + app_bar.getTotalScrollRange());
                ScrollingActivity.this.verticalOffset = verticalOffset; }}); Log.e(TAG,"getTotalScrollRange():"+ app_bar.getTotalScrollRange()); }}Copy the code

We uncomplicate our handling of the issue of event distribution by deciding whether we can perform a pull-down refresh:

verticalOffset >= 0 && PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header);Copy the code

It can only be refreshed when it is in the stretched state. The effect picture is as follows:





You can do this too, just in a different way:





What kind of effect can be achieved depends on the demand. This article is over, the level is limited, what opinions can be put forward below, we discuss together. Thank you for downloading the Demo