/* * Copyright 2016 Hippo Seven * * 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.hippo.ehviewer.widget; import android.animation.ValueAnimator; import android.content.Context; import android.os.Build; import android.support.annotation.Nullable; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.Snackbar; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; import com.hippo.drawerlayout.DrawerLayout; import com.hippo.ehviewer.R; import com.hippo.yorozuya.AnimationUtils; import java.util.ArrayList; import java.util.List; @CoordinatorLayout.DefaultBehavior(EhDrawerLayout.Behavior.class) public class EhDrawerLayout extends DrawerLayout { private List<View> mAboveSnackViewList; public EhDrawerLayout(Context context, AttributeSet attrs) { super(context, attrs); } public EhDrawerLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void addAboveSnackView(View view) { if (null == mAboveSnackViewList) { mAboveSnackViewList = new ArrayList<>(); } mAboveSnackViewList.add(view); } public void removeAboveSnackView(View view) { if (null == mAboveSnackViewList) { return; } mAboveSnackViewList.remove(view); } public int getAboveSnackViewCount() { return null == mAboveSnackViewList ? 0 : mAboveSnackViewList.size(); } @Nullable public View getAboveSnackViewAt(int index) { if (null == mAboveSnackViewList || index < 0 || index >= mAboveSnackViewList.size()) { return null; } else { return mAboveSnackViewList.get(index); } } public static class Behavior extends CoordinatorLayout.Behavior<EhDrawerLayout> { // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is // because we can use view translation properties which greatly simplifies the code. private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11; @Override public boolean layoutDependsOn(CoordinatorLayout parent, EhDrawerLayout child, View dependency) { // We're dependent on all SnackbarLayouts (if enabled) return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, EhDrawerLayout child, View dependency) { if (dependency instanceof Snackbar.SnackbarLayout) { for (int i = 0, n = child.getAboveSnackViewCount(); i < n; i++) { View view = child.getAboveSnackViewAt(i); updateChildTranslationForSnackbar(parent, child, view); } } return false; } private void updateChildTranslationForSnackbar(CoordinatorLayout parent, EhDrawerLayout view, final View child) { final float targetTransY = getChildTranslationYForSnackbar(parent, view); float childTranslationY = 0.0f; Object obj = child.getTag(R.id.fab_translation_y); if (obj instanceof Float) { childTranslationY = (Float) obj; } if (childTranslationY == targetTransY) { // We're already at (or currently animating to) the target value, return... return; } ValueAnimator fabTranslationYAnimator = null; obj = child.getTag(R.id.fab_translation_y_animator); if (obj instanceof ValueAnimator) { fabTranslationYAnimator = (ValueAnimator) obj; } // Make sure that any current animation is cancelled if (fabTranslationYAnimator != null && fabTranslationYAnimator.isRunning()) { fabTranslationYAnimator.cancel(); } final float currentTransY = ViewCompat.getTranslationY(child); if (child.isShown() && Math.abs(currentTransY - targetTransY) > (child.getHeight() * 0.667f)) { // If the FAB will be travelling by more than 2/3 of it's height, let's animate // it instead if (fabTranslationYAnimator == null) { fabTranslationYAnimator = ValueAnimator.ofFloat(); fabTranslationYAnimator.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR); fabTranslationYAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { ViewCompat.setTranslationY(child, (Float) animation.getAnimatedValue()); } }); child.setTag(R.id.fab_translation_y_animator, fabTranslationYAnimator); } fabTranslationYAnimator.setFloatValues(currentTransY, targetTransY); fabTranslationYAnimator.start(); } else { // Now update the translation Y ViewCompat.setTranslationY(child, targetTransY); } child.setTag(R.id.fab_translation_y, targetTransY); } private float getChildTranslationYForSnackbar(CoordinatorLayout parent, EhDrawerLayout child) { float minOffset = 0; final List<View> dependencies = parent.getDependencies(child); for (int i = 0, z = dependencies.size(); i < z; i++) { final View view = dependencies.get(i); if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(child, view)) { minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - view.getHeight()); } } return minOffset; } } }