/* * Copyright 2015, Randy Saborio & Tinbytes, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions * and limitations under the License. */ package com.tinbytes.samples.showhidetoolbar.util; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.TargetApi; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.view.animation.LinearInterpolator; /** * Helper class for RecyclerView/Toolbar scroll listener. */ public final class RecyclerViewUtils { /** * This class simplifies the hide/show Toolbar animation depicted in MainActivity.java. * Check MainActivity2.java to see how to use it. */ public static class ShowHideToolbarOnScrollingListener extends RecyclerView.OnScrollListener { public static final String SHOW_HIDE_TOOLBAR_LISTENER_STATE = "show-hide-toolbar-listener-state"; // The elevation of the toolbar when content is scrolled behind private static final float TOOLBAR_ELEVATION = 14f; private Toolbar toolbar; private State state; public ShowHideToolbarOnScrollingListener(Toolbar toolbar) { this.toolbar = toolbar; this.state = new State(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void toolbarSetElevation(float elevation) { if (AndroidUtils.isLollipop()) { toolbar.setElevation(elevation == 0 ? 0 : TOOLBAR_ELEVATION); } } private void toolbarAnimateShow(final int verticalOffset) { toolbar.animate() .translationY(0) .setInterpolator(new LinearInterpolator()) .setDuration(180) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { toolbarSetElevation(verticalOffset == 0 ? 0 : TOOLBAR_ELEVATION); } }); } private void toolbarAnimateHide() { toolbar.animate() .translationY(-toolbar.getHeight()) .setInterpolator(new LinearInterpolator()) .setDuration(180) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { toolbarSetElevation(0); } }); } @Override public final void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (state.scrollingOffset > 0) { if (state.verticalOffset > toolbar.getHeight()) { toolbarAnimateHide(); } else { toolbarAnimateShow(state.verticalOffset); } } else if (state.scrollingOffset < 0) { if (toolbar.getTranslationY() < toolbar.getHeight() * -0.6 && state.verticalOffset > toolbar.getHeight()) { toolbarAnimateHide(); } else { toolbarAnimateShow(state.verticalOffset); } } } } @Override public final void onScrolled(RecyclerView recyclerView, int dx, int dy) { state.verticalOffset = recyclerView.computeVerticalScrollOffset(); state.scrollingOffset = dy; int toolbarYOffset = (int) (dy - toolbar.getTranslationY()); toolbar.animate().cancel(); if (state.scrollingOffset > 0) { if (toolbarYOffset < toolbar.getHeight()) { if (state.verticalOffset > toolbar.getHeight()) { toolbarSetElevation(TOOLBAR_ELEVATION); } toolbar.setTranslationY(state.translationY = -toolbarYOffset); } else { toolbarSetElevation(0); toolbar.setTranslationY(state.translationY = -toolbar.getHeight()); } } else if (state.scrollingOffset < 0) { if (toolbarYOffset < 0) { if (state.verticalOffset <= 0) { toolbarSetElevation(0); } toolbar.setTranslationY(state.translationY = 0); } else { if (state.verticalOffset > toolbar.getHeight()) { toolbarSetElevation(TOOLBAR_ELEVATION); } toolbar.setTranslationY(state.translationY = -toolbarYOffset); } } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public void onRestoreInstanceState(State state) { this.state.verticalOffset = state.verticalOffset; this.state.scrollingOffset = state.scrollingOffset; if (AndroidUtils.isLollipop()) { toolbar.setElevation(state.elevation); toolbar.setTranslationY(state.translationY); } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public State onSaveInstanceState() { state.translationY = toolbar.getTranslationY(); if (AndroidUtils.isLollipop()) { state.elevation = toolbar.getElevation(); } return state; } /** * Parcelable RecyclerView/Toolbar state for simpler saving/restoring its current state. */ public static final class State implements Parcelable { public static Creator<State> CREATOR = new Creator<State>() { public State createFromParcel(Parcel parcel) { return new State(parcel); } public State[] newArray(int size) { return new State[size]; } }; // Keeps track of the overall vertical offset in the list private int verticalOffset; // Determines the scroll UP/DOWN offset private int scrollingOffset; // Toolbar values private float translationY; private float elevation; State() { } State(Parcel parcel) { this.verticalOffset = parcel.readInt(); this.scrollingOffset = parcel.readInt(); this.translationY = parcel.readFloat(); this.elevation = parcel.readFloat(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(verticalOffset); parcel.writeInt(scrollingOffset); parcel.writeFloat(translationY); parcel.writeFloat(elevation); } } } private RecyclerViewUtils() { } }