/*
* Copyright (C) 2014 Jerzy Chalupski
* Copyright (C) 2016 Thomas Robert Altstidl
*
* 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.tr4android.support.extension.widget;
/**
* A floating action button menu build specifically for AppCompat Design Library FloatingActionButton
*/
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.Parcel;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.view.KeyEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.widget.AppCompatDrawableManager;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import com.tr4android.appcompat.extension.R;
import com.tr4android.support.extension.drawable.ColorTransitionDrawable;
import com.tr4android.support.extension.drawable.RotationTransitionDrawable;
import com.tr4android.support.extension.internal.PairedTouchListener;
import com.tr4android.support.extension.utils.ViewCompatUtils;
@SuppressLint("NewApi")
@CoordinatorLayout.DefaultBehavior(FloatingActionMenu.Behavior.class)
public class FloatingActionMenu extends ViewGroup {
public static final int EXPAND_UP = 0;
public static final int EXPAND_DOWN = 1;
public static final int EXPAND_LEFT = 2;
public static final int EXPAND_RIGHT = 3;
public static final int LABELS_ON_LEFT_SIDE = 0;
public static final int LABELS_ON_RIGHT_SIDE = 1;
// Animation stuff
private RotationTransitionDrawable mToggleDrawable;
private ColorTransitionDrawable mDimDrawable;
private Handler mAnimationHandler = new Handler();
private int mAnimationDuration = 300;
private int mAnimationDelay = 50;
// Preallocated Rect for retrieving child background padding
private Rect childBackgroundPadding = new Rect();
// Dimensions for layout
private int mButtonSpacing;
private int mLabelsMargin;
private int mLabelsVerticalOffset;
private int mExpandDirection;
private boolean mExpanded;
private FloatingActionButton mMainButton;
private int mButtonsCount;
private int mMaxButtonWidth;
private int mMaxButtonHeight;
// Label attributes
private int mLabelsStyle;
private int mLabelsPosition;
// Icon attributes
private Drawable mCloseDrawable;
private float mCloseAngle;
// View for dimming
private View mDimmingView;
private OnFloatingActionsMenuUpdateListener mListener;
public interface OnFloatingActionsMenuUpdateListener {
void onMenuExpanded();
void onMenuCollapsed();
}
public FloatingActionMenu(Context context) {
this(context, null);
}
public FloatingActionMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public FloatingActionMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
mButtonSpacing = getResources().getDimensionPixelSize(R.dimen.fam_spacing);
mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fam_label_spacing);
mLabelsVerticalOffset = 0;
TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionMenu, 0, 0);
mExpandDirection = attr.getInt(R.styleable.FloatingActionMenu_fabMenuExpandDirection, EXPAND_UP);
mLabelsPosition = attr.getInt(R.styleable.FloatingActionMenu_fabMenuLabelPosition, LABELS_ON_LEFT_SIDE);
mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionMenu_fabMenuLabelStyle, 0);
int mCloseDrawableResourceId = attr.getResourceId(R.styleable.FloatingActionMenu_fabMenuCloseIconSrc, 0);
mCloseDrawable = mCloseDrawableResourceId == 0 ? null : AppCompatDrawableManager.get().getDrawable(getContext(), mCloseDrawableResourceId);
mCloseAngle = attr.getFloat(R.styleable.FloatingActionMenu_fabMenuCloseIconAngle, 0);
mButtonSpacing = attr.getDimensionPixelSize(R.styleable.FloatingActionMenu_fabMenuSpacing, mButtonSpacing);
attr.recycle();
if (mLabelsStyle != 0 && expandsHorizontally()) {
throw new IllegalStateException("Action labels in horizontal expand orientation is not supported.");
}
// So we can catch the back button
setFocusableInTouchMode(true);
}
public void setOnFloatingActionsMenuUpdateListener(OnFloatingActionsMenuUpdateListener listener) {
mListener = listener;
}
private boolean expandsHorizontally() {
return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT;
}
private void setupMainButton() {
mMainButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
toggle();
}
});
// setup button drawable
mToggleDrawable = new RotationTransitionDrawable(mMainButton.getDrawable(), mCloseDrawable);
mToggleDrawable.setMaxRotation(mCloseAngle);
mMainButton.setImageDrawable(mToggleDrawable);
}
private int getColor(@ColorRes int id) {
return getResources().getColor(id);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
mMaxButtonWidth = 0;
mMaxButtonHeight = 0;
int maxLabelWidth = 0;
for (int i = 0; i < mButtonsCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
// Consider background padding in size measurement to account for compatibility shadow
child.getBackground().getPadding(childBackgroundPadding);
if (!expandsHorizontally()) {
mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth() - childBackgroundPadding.left - childBackgroundPadding.right);
height += child.getMeasuredHeight() - childBackgroundPadding.top - childBackgroundPadding.bottom;
LabelView label = (LabelView) child.getTag(R.id.fab_label);
if (label != null) {
maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth());
}
} else {
width += child.getMeasuredWidth() - childBackgroundPadding.left - childBackgroundPadding.right;
mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight() - childBackgroundPadding.top - childBackgroundPadding.bottom);
}
}
LayoutParams mainButtonParams = (LayoutParams) mMainButton.getLayoutParams();
if (!expandsHorizontally()) {
width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
width += mainButtonParams.leftMargin + mainButtonParams.rightMargin;
height += mButtonSpacing * (mButtonsCount - 1);
height = adjustForOvershoot(height);
height += (mExpandDirection == EXPAND_UP) ? mainButtonParams.bottomMargin + childBackgroundPadding.top
: mainButtonParams.topMargin + childBackgroundPadding.bottom;
} else {
height = mMaxButtonHeight;
height += mainButtonParams.topMargin + mainButtonParams.rightMargin;
width += mButtonSpacing * (mButtonsCount - 1);
width = adjustForOvershoot(width);
width += (mExpandDirection == EXPAND_LEFT) ? mainButtonParams.rightMargin + childBackgroundPadding.left
: mainButtonParams.leftMargin + childBackgroundPadding.right;
}
setMeasuredDimension(width, height);
}
private int adjustForOvershoot(int dimension) {
return dimension * 12 / 10;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
boolean expandUp = mExpandDirection == EXPAND_UP;
// Consider margin and background padding to account for compatibility shadow
mMainButton.getBackground().getPadding(childBackgroundPadding);
LayoutParams mainButtonParamsHorizontal = (LayoutParams) mMainButton.getLayoutParams();
int addButtonY = expandUp ? b - t - mMainButton.getMeasuredHeight() + childBackgroundPadding.top + childBackgroundPadding.bottom - mainButtonParamsHorizontal.bottomMargin : mainButtonParamsHorizontal.topMargin;
// Ensure mMainButton is centered on the line where the buttons should be
int buttonsHorizontalCenter = mLabelsPosition == LABELS_ON_LEFT_SIDE
? r - l - mMaxButtonWidth / 2 - mainButtonParamsHorizontal.rightMargin
: mMaxButtonWidth / 2 + mainButtonParamsHorizontal.leftMargin;
int addButtonLeft = buttonsHorizontalCenter - (mMainButton.getMeasuredWidth() - childBackgroundPadding.left - childBackgroundPadding.right) / 2;
mMainButton.layout(addButtonLeft - childBackgroundPadding.left, addButtonY - childBackgroundPadding.top, addButtonLeft - childBackgroundPadding.left + mMainButton.getMeasuredWidth(), addButtonY - childBackgroundPadding.top + mMainButton.getMeasuredHeight());
addButtonY -= childBackgroundPadding.top;
int labelsOffset = mMaxButtonWidth / 2 + mLabelsMargin;
int labelsXNearButton = mLabelsPosition == LABELS_ON_LEFT_SIDE
? buttonsHorizontalCenter - labelsOffset
: buttonsHorizontalCenter + labelsOffset;
int nextY = expandUp ?
addButtonY + childBackgroundPadding.top - mButtonSpacing :
addButtonY + mMainButton.getMeasuredHeight() - childBackgroundPadding.top - childBackgroundPadding.bottom + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mMainButton || child.getVisibility() == GONE) continue;
// Consider background padding to account for compatibility shadow
child.getBackground().getPadding(childBackgroundPadding);
int childX = buttonsHorizontalCenter - (child.getMeasuredWidth() - childBackgroundPadding.left - childBackgroundPadding.right) / 2;
int childY = expandUp ? nextY - child.getMeasuredHeight() + childBackgroundPadding.top + childBackgroundPadding.bottom : nextY;
child.layout(childX - childBackgroundPadding.left, childY - childBackgroundPadding.top, childX - childBackgroundPadding.left + child.getMeasuredWidth(), childY - childBackgroundPadding.top + child.getMeasuredHeight());
childY -= childBackgroundPadding.top;
// TODO: mAnimator.prepareView(child, expandedTranslation, collapsedTranslation, mExpanded, false);
if (mExpanded) {
((FloatingActionButton) child).show();
} else {
((FloatingActionButton) child).hide();
}
LayoutParams params = (LayoutParams) child.getLayoutParams();
if (!params.isAnimated()) {
// TODO: mAnimator.buildAnimationForView(child, visualYIndex, mExpandDirection, expandedTranslation, collapsedTranslation);
params.setAnimated(true);
}
LabelView label = (LabelView) child.getTag(R.id.fab_label);
if (label != null) {
int labelXAwayFromButton = mLabelsPosition == LABELS_ON_LEFT_SIDE
? labelsXNearButton - label.getMeasuredWidth()
: labelsXNearButton + label.getMeasuredWidth();
int labelLeft = mLabelsPosition == LABELS_ON_LEFT_SIDE
? labelXAwayFromButton
: labelsXNearButton;
int labelRight = mLabelsPosition == LABELS_ON_LEFT_SIDE
? labelsXNearButton
: labelXAwayFromButton;
int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight() - label.getMeasuredHeight()) / 2;
label.layout(labelLeft, labelTop, labelRight, labelTop + label.getMeasuredHeight());
label.setOnTouchListener(new PairedTouchListener(child));
child.setOnTouchListener(new PairedTouchListener(label));
// TODO: mAnimator.prepareView(label, expandedTranslation, collapsedTranslation, mExpanded, false);
if (mExpanded) {
label.setVisibility(VISIBLE);
} else {
label.setVisibility(GONE);
}
LayoutParams labelParams = (LayoutParams) label.getLayoutParams();
if (!labelParams.isAnimated()) {
// TODO: mAnimator.buildAnimationForView(label, visualYIndex, mExpandDirection, expandedTranslation, collapsedTranslation);
labelParams.setAnimated(true);
}
}
nextY = expandUp ?
childY + childBackgroundPadding.top - mButtonSpacing :
childY + child.getMeasuredHeight() - childBackgroundPadding.top - childBackgroundPadding.right + mButtonSpacing;
}
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
boolean expandLeft = mExpandDirection == EXPAND_LEFT;
// Consider margin and background padding to account for compatibility shadow
mMainButton.getBackground().getPadding(childBackgroundPadding);
LayoutParams mainButtonParamsVertical = (LayoutParams) mMainButton.getLayoutParams();
int addButtonX = expandLeft ? r - l - mMainButton.getMeasuredWidth() + childBackgroundPadding.right - mainButtonParamsVertical.rightMargin: mainButtonParamsVertical.leftMargin - childBackgroundPadding.left;
// Ensure mMainButton is centered on the line where the buttons should be
int addButtonTop = b - t - mMaxButtonHeight + (mMaxButtonHeight - mMainButton.getMeasuredHeight() - childBackgroundPadding.top - childBackgroundPadding.bottom) / 2 - mainButtonParamsVertical.bottomMargin + childBackgroundPadding.bottom;
mMainButton.layout(addButtonX, addButtonTop, addButtonX + mMainButton.getMeasuredWidth(), addButtonTop + mMainButton.getMeasuredHeight());
int nextX = expandLeft ?
addButtonX + childBackgroundPadding.left - mButtonSpacing :
addButtonX + mMainButton.getMeasuredWidth() - childBackgroundPadding.left - childBackgroundPadding.right + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mMainButton || child.getVisibility() == GONE) continue;
// Consider background padding to account for compatibility shadow
child.getBackground().getPadding(childBackgroundPadding);
int childX = expandLeft ? nextX - child.getMeasuredWidth() + childBackgroundPadding.right : nextX - childBackgroundPadding.left;
int childY = addButtonTop + (mMainButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2;
child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
// TODO: mAnimator.prepareView(child, expandedTranslation, collapsedTranslation, mExpanded, true);
if (mExpanded) {
((FloatingActionButton) child).show();
} else {
((FloatingActionButton) child).hide();
}
LayoutParams params = (LayoutParams) child.getLayoutParams();
if (!params.isAnimated()) {
// TODO: mAnimator.buildAnimationForView(child, visualXIndex, mExpandDirection, expandedTranslation, collapsedTranslation);
params.setAnimated(true);
}
nextX = expandLeft ?
childX + childBackgroundPadding.left - mButtonSpacing :
childX + child.getMeasuredWidth() - childBackgroundPadding.left - childBackgroundPadding.right + mButtonSpacing;
}
break;
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
private class LayoutParams extends ViewGroup.MarginLayoutParams {
// Tracker for efficient animation setting
private boolean mAnimated;
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public boolean isAnimated() {
return mAnimated;
}
public void setAnimated(boolean animated) {
mAnimated = animated;
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMainButton = (FloatingActionButton) getChildAt(0);
bringChildToFront(mMainButton);
setupMainButton();
mButtonsCount = getChildCount();
if (mLabelsStyle != 0) {
createLabels();
}
}
private void createLabels() {
Context context = new ContextThemeWrapper(getContext(), mLabelsStyle);
for (int i = 0; i < mButtonsCount; i++) {
FloatingActionButton button = (FloatingActionButton) getChildAt(i);
CharSequence title = button.getContentDescription();
if (button == mMainButton || title == null ||
button.getTag(R.id.fab_label) != null) continue;
LabelView label = new LabelView(context);
label.setAnimationOffset(mMaxButtonWidth / 2f + mLabelsMargin);
label.setTextAppearance(getContext(), mLabelsStyle);
label.setText(title);
addView(label);
button.setTag(R.id.fab_label, label);
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
mMainButton.setEnabled(enabled);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mExpanded) {
KeyEventCompat.startTracking(event);
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mExpanded) {
collapse();
return true;
}
return false;
}
private void startExpandAnimation(boolean animate) {
int delay = animate ? mAnimationDelay : 0;
mToggleDrawable.startTransition(animate ? mAnimationDuration : 0);
if (mDimDrawable != null)
mDimDrawable.startTransition(animate ? mAnimationDuration : 0);
int childIndex = 0;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
// Main button doesn't have any animation
if (child == mMainButton) continue;
mAnimationHandler.postDelayed(new Runnable() {
@Override
public void run() {
((FloatingActionButton) child).show();
LabelView label = (LabelView) child.getTag(R.id.fab_label);
if (label != null) {
label.show();
}
}
}, delay * childIndex);
childIndex++;
}
}
private void startCollapseAnimation(boolean animate) {
int delay = animate ? mAnimationDelay : 0;
mToggleDrawable.reverseTransition(animate ? mAnimationDuration : 0);
if (mDimDrawable != null)
mDimDrawable.reverseTransition(animate ? mAnimationDuration : 0);
int childIndex = 0;
for (int i = 0; i < mButtonsCount; i++) {
final View child = getChildAt(i);
// Main button doesn't have any animation
if (child == mMainButton) continue;
mAnimationHandler.postDelayed(new Runnable() {
@Override
public void run() {
((FloatingActionButton) child).hide();
LabelView label = (LabelView) child.getTag(R.id.fab_label);
if (label != null) {
label.hide();
}
}
}, delay * childIndex);
childIndex++;
}
}
/* Start Public API methods */
/**
* Method to easily setup a dimming for the specified view with the specified color
* @param dimmingView the view to use for dimming (the background color will be animated)
* @param dimmingColor the color to use for dimming (in expanded state)
*/
public void setupWithDimmingView(View dimmingView, @ColorInt int dimmingColor) {
mDimmingView = dimmingView;
mDimDrawable = new ColorTransitionDrawable(Color.TRANSPARENT, dimmingColor);
ViewCompatUtils.setBackground(mDimmingView, mDimDrawable);
// apply the appbar elevation so the dim gets rendered over it
ViewCompat.setElevation(this, getContext().getResources().getDimensionPixelSize(R.dimen.design_fab_elevation));
ViewCompat.setElevation(mDimmingView, getContext().getResources().getDimensionPixelSize(R.dimen.dim_elevation));
// set click listener and disable clicks
mDimmingView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
collapse();
}
});
mDimmingView.setClickable(false);
}
/**
* Collapse the FloatingActionMenu with an animation
*/
public void collapse() {
collapse(true);
}
/**
* Collapse the FloatingActionMenu immediately without an animation
*/
public void collapseImmediately() {
collapse(false);
}
/**
* Collapse the FloatingActionMenu
* @param animate whether it should be animated
*/
public void collapse(boolean animate) {
if (mExpanded) {
mExpanded = false;
startCollapseAnimation(animate);
if (mListener != null) {
mListener.onMenuCollapsed();
}
// So we don't catch the back button anymore
clearFocus();
if (mDimmingView != null) {
mDimmingView.setClickable(false);
}
}
}
/**
* Expand the FloatingActionMenu with an animation
*/
public void expand() {
expand(true);
}
/**
* Expand the FloatingActionMenu immediately without an animation
*/
public void expandImmediately() {
expand(false);
}
/**
* Expand the FloatingActionMenu
* @param animate whether it should be animated
*/
public void expand(boolean animate) {
if (!mExpanded) {
mExpanded = true;
startExpandAnimation(animate);
if (mListener != null) {
mListener.onMenuExpanded();
}
// So we can catch the back button
requestFocus();
if (mDimmingView != null) {
mDimmingView.setClickable(true);
}
}
}
/**
* Toggle the FloatingActionMenu
* This will collapse it when it is currently expanded and expand it when it is currently collapsed.
*/
public void toggle() {
if (mExpanded) {
collapse();
} else {
expand();
}
}
/**
* Check whether the FloatingActionMenu is expanded
* @return true if expanded, false if collapsed
*/
public boolean isExpanded() {
return mExpanded;
}
/**
* Add a new FloatingActionButton to the FloatingActionMenu
* @param button the FloatingActionButton to add
*/
public void addButton(FloatingActionButton button) {
addView(button, mButtonsCount - 1);
mButtonsCount++;
if (mLabelsStyle != 0) {
createLabels();
}
}
/**
* Remove an existing FloatingActionButton from the FloatingActionMenu
* @param button the FloatingActionButton to remove
*/
public void removeButton(FloatingActionButton button) {
removeView((View) button.getTag(R.id.fab_label));
removeView(button);
button.setTag(R.id.fab_label, null);
mButtonsCount--;
}
/* End Public API methods */
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.mExpanded = mExpanded;
return savedState;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
SavedState savedState = (SavedState) state;
mExpanded = savedState.mExpanded;
mToggleDrawable.setRotation(mExpanded ? mCloseAngle : 0f);
mDimDrawable.setColorRatio(mExpanded ? 1f : 0f);
super.onRestoreInstanceState(savedState.getSuperState());
} else {
super.onRestoreInstanceState(state);
}
}
public static class SavedState extends BaseSavedState {
public boolean mExpanded;
public SavedState(Parcelable parcel) {
super(parcel);
}
private SavedState(Parcel in) {
super(in);
mExpanded = in.readInt() == 1;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mExpanded ? 1 : 0);
}
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
/**
* Behavior designed for use with {@link FloatingActionMenu} instances. It's main function
* is to move all {@link FloatingActionButton}s views inside {@link FloatingActionMenu} so
* that any displayed {@link Snackbar}s do not cover them.
*/
public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionMenu> {
/**
* Default constructor for instantiating Behaviors.
*/
public Behavior() {
}
// 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;
public Behavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionMenu 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, FloatingActionMenu child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
}
}