diff options
author | Matt Garnes <matt@cyngn.com> | 2016-05-18 08:58:36 -0700 |
---|---|---|
committer | Matt Garnes <matt@cyngn.com> | 2016-05-18 16:44:25 -0700 |
commit | 37501475021dd830d371780aa1118ed2f2fc024a (patch) | |
tree | 3cf778785899682a0aa9d178d0903cf5d34523bd | |
parent | cb830c247c03edd62cab3c868d1c33d00204e50e (diff) | |
download | android_external_cyanogen_UICommon-stable/cm-13.0-ZNH2K.tar.gz android_external_cyanogen_UICommon-stable/cm-13.0-ZNH2K.tar.bz2 android_external_cyanogen_UICommon-stable/cm-13.0-ZNH2K.zip |
Add support to Snackbar for WindowInsetsstable/cm-13.0-ZNH2KBstable/cm-13.0-ZNH2K
- Add overloaded make method to accept WindowInsets as a parameter.
- If WindowInsets are set, apply a margin on all sides equal to the
system window size for the insets.
- If WindowInsets are set, expand and collapse the snackbar instead of
animating it up from a translation offscreen, since this does not
account for the blank space of the margin.
- Add a class to hold the SnackbarManager.Callback instance so it will
not be garbage collected while the View is on screen.
Change-Id: Ibd7f8a0d974510da3a49896e5952cb2096a10d5b
Issue-Id: CP-227
(cherry picked from commit d5b6fc53ad2f530a993bce8ccd789991e6bac6cf)
-rw-r--r-- | src/com/cyngn/uicommon/view/Snackbar.java | 178 |
1 files changed, 175 insertions, 3 deletions
diff --git a/src/com/cyngn/uicommon/view/Snackbar.java b/src/com/cyngn/uicommon/view/Snackbar.java index 57579ac..7bc7caf 100644 --- a/src/com/cyngn/uicommon/view/Snackbar.java +++ b/src/com/cyngn/uicommon/view/Snackbar.java @@ -17,6 +17,8 @@ package com.cyngn.uicommon.view; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; @@ -30,6 +32,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.WindowInsets; import android.view.animation.Interpolator; import android.widget.Button; import android.widget.FrameLayout; @@ -166,6 +169,7 @@ public final class Snackbar { private final SnackbarLayout mView; private int mDuration; private Callback mCallback; + private WindowInsets mWindowInsets; private Snackbar(ViewGroup parent, int maxLines) { mParent = parent; @@ -176,6 +180,50 @@ public final class Snackbar { mView.setMaxLines(maxLines); } + private Snackbar(ViewGroup parent, int maxLines, WindowInsets windowInsets) { + mParent = parent; + mContext = parent.getContext(); + + LayoutInflater inflater = LayoutInflater.from(mContext); + mView = (SnackbarLayout) inflater.inflate(R.layout.layout_snackbar, mParent, false); + + // Apply margins equal to the system insets given, preserving the original LayoutParams + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mView.getLayoutParams(); + layoutParams.setMargins(windowInsets.getSystemWindowInsetLeft(), + windowInsets.getSystemWindowInsetTop(), windowInsets.getSystemWindowInsetRight(), + windowInsets.getSystemWindowInsetBottom()); + mWindowInsets = windowInsets; + // Hide the Snackbar until the expand has started + mView.setVisibility(View.INVISIBLE); + mView.setLayoutParams(layoutParams); + mView.setMaxLines(maxLines); + } + + /** + * Make a Snackbar to display a message + * + * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given + * to {@code view}. Snackbar will walk up the view tree trying to find the window decor's + * content view. + * + * + * @param view The view to find a parent from. + * @param text The text to show. Can be formatted text. + * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or {@link + * #LENGTH_LONG} + * @param windowInsets WindowInsets that have been applied to the top level parent in + * the current view hierarchy. + */ + public static Snackbar make(View view, CharSequence text, + @Duration int duration, WindowInsets windowInsets) { + Snackbar snackbar = new Snackbar(findSuitableParent(view), + view.getResources().getInteger(R.integer.config_snackbar_text_max_lines), + windowInsets); + snackbar.setText(text); + snackbar.setDuration(duration); + return snackbar; + } + /** * Make a Snackbar to display a message * @@ -235,6 +283,25 @@ public final class Snackbar { return make(view, view.getResources().getText(resId), duration); } + /** + * Make a Snackbar to display a message. + * + * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given + * to {@code view}. Snackbar will walk up the view tree trying to find the window decor's + * content view. + * + * @param view The view to find a parent from. + * @param resId The resource id of the string resource to use. Can be formatted text. + * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or {@link + * #LENGTH_LONG} + * @param windowInsets WindowInsets that have been applied to the top level parent in + * the current view hierarchy. + */ + public static Snackbar make(View view, int resId, + @Duration int duration, WindowInsets windowInsets) { + return make(view, view.getResources().getText(resId), duration, windowInsets); + } + public static Snackbar make(View view, int resId, @Duration int duration, int maxlines) { return make(view, view.getResources().getText(resId), duration, maxlines); } @@ -403,6 +470,17 @@ public final class Snackbar { return mView.isShown(); } + /** + * Convenience object to contain an instance of the SnackbarManager.Callback + * interface, so that it can be passed to a View for holding. + */ + private static class ManagerCallbackHolder { + SnackbarManager.Callback managerCallback; + public ManagerCallbackHolder(SnackbarManager.Callback callback) { + managerCallback = callback; + } + } + private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() { @Override public void show() { @@ -422,13 +500,21 @@ public final class Snackbar { if (mView.isLaidOut()) { // If the view is already laid out, animate it now - animateViewIn(); + if (mWindowInsets != null) { + animateViewExpand(); + } else { + animateViewIn(); + } } else { // Otherwise, add one of our layout change listeners and animate it in when laid out mView.setOnLayoutChangeListener(new SnackbarLayout.OnLayoutChangeListener() { @Override public void onLayoutChange(View view, int left, int top, int right, int bottom) { - animateViewIn(); + if (mWindowInsets != null) { + animateViewExpand(); + } else { + animateViewIn(); + } mView.setOnLayoutChangeListener(null); } }); @@ -464,6 +550,55 @@ public final class Snackbar { }).start(); } + private void animateViewExpand() { + final int height = mView.getHeight(); + mView.getLayoutParams().height = 0; + mView.requestLayout(); + // Hide the Snackbar until the expand has started + mView.setVisibility(View.INVISIBLE); + final ValueAnimator valueAnimator = ValueAnimator.ofInt(0, height); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + // Don't show the Snackbar until the height has been laid out + // to something lower than the full height + if (mView.getMeasuredHeight() < height) { + mView.setVisibility(View.VISIBLE); + } + mView.getLayoutParams().height = (Integer) animation.getAnimatedValue(); + mView.requestLayout(); + } + }); + + valueAnimator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR); + valueAnimator.setDuration(ANIMATION_DURATION); + valueAnimator.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION, + ANIMATION_FADE_DURATION); + } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (mCallback != null) { + mCallback.onShown(Snackbar.this); + } + SnackbarManager.getInstance().onShown(mManagerCallback); + } + }); + valueAnimator.start(); + /* The other types of animations (aminateViewOut and animateViewIn) use + a ViewPropertyAnimator, which the view holds a reference to. The above + ValueAnimator can be garbage collected, along with the callback, as soon as it finishes. + Set the tag to store the ManagerCallback so our manager will remain in memory as long + as the view does, since that callback will handle additional animation. */ + mView.setTag(new ManagerCallbackHolder(mManagerCallback)); + + } + private void animateViewOut(final int event) { mView.animate().translationY(mView.getHeight()) .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR) @@ -489,11 +624,48 @@ public final class Snackbar { } + private void animateViewCollapse(final int event) { + int height = mView.getHeight(); + final ValueAnimator valueAnimator = ValueAnimator.ofInt(height, 0); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mView.getLayoutParams().height = (Integer) valueAnimator.getAnimatedValue(); + mView.requestLayout(); + } + }); + + valueAnimator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR); + valueAnimator.setDuration(ANIMATION_DURATION); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mView.animateChildrenOut(0, ANIMATION_FADE_DURATION); + } + + @Override + public void onAnimationEnd(Animator animation) { + onViewHidden(event); + } + }); + valueAnimator.start(); + /* The other types of animations (aminateViewOut and animateViewIn) use + a ViewPropertyAnimator, which the view holds a reference to. The above + ValueAnimator can be garbage collected, along with the callback, as soon as it finishes. + Set the tag to store the ManagerCallback so our manager will remain in memory as long + as the view does, since that callback will handle additional animation. */ + mView.setTag(new ManagerCallbackHolder(mManagerCallback)); + } + final void hideView(int event) { if (mView.getVisibility() != View.VISIBLE) { onViewHidden(event); } else { - animateViewOut(event); + if (mWindowInsets != null) { + animateViewCollapse(event); + } else { + animateViewOut(event); + } } } |