不想写过多的代码,所以我很多都是直接在源码中复制的。代码有很多优化之处,根据自己需求来改吧,这里我只是做一个笔记,分享给大家。
既然要修改源码,所以你要清楚目的是什么。这里我的目标很明确,就是想把Snackbar既可以向上显示,也可以在底部显示,并且没有大的改动。我看源码发现布局文件design_layout_snackbar中有个layout_gravity 我猜这个是显示位置的参数了。我把它改为top,没想到成功了!但是问题来了,动画效果很丑!那么我就找到animateViewIn() animateViewOut(final int event) 这两个方法。看源码
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.design.widget.SwipeDismissBehavior;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
import android.support.v4.view.WindowInsetsCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.ut.teambrige.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import static com.ut.teambrige.widget.bar.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
/**
* Created by Charlie on 2016/10/9.
*/
public final class MySnackbar {
/**
* Callback class for {@link Snackbar} instances.
*
* @see Snackbar#setCallback(Snackbar.Callback)
*/
public static abstract class Callback {
/**
* Indicates that the Snackbar was dismissed via a swipe.
*/
public static final int DISMISS_EVENT_SWIPE = 0;
/**
* Indicates that the Snackbar was dismissed via an action click.
*/
public static final int DISMISS_EVENT_ACTION = 1;
/**
* Indicates that the Snackbar was dismissed via a timeout.
*/
public static final int DISMISS_EVENT_TIMEOUT = 2;
/**
* Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.
*/
public static final int DISMISS_EVENT_MANUAL = 3;
/**
* Indicates that the Snackbar was dismissed from a new Snackbar being shown.
*/
public static final int DISMISS_EVENT_CONSECUTIVE = 4;
/**
* @hide
*/
@IntDef({DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT,
DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE})
@Retention(RetentionPolicy.SOURCE)
public @interface DismissEvent {
}
/**
* Called when the given {@link Snackbar} has been dismissed, either through a time-out,
* having been manually dismissed, or an action being clicked.
*
* @param snackbar The snackbar which has been dismissed.
* @param event The event which caused the dismissal. One of either:
* {@link #DISMISS_EVENT_SWIPE}, {@link #DISMISS_EVENT_ACTION},
* {@link #DISMISS_EVENT_TIMEOUT}, {@link #DISMISS_EVENT_MANUAL} or
* {@link #DISMISS_EVENT_CONSECUTIVE}.
* @see Snackbar#dismiss()
*/
public void onDismissed(MySnackbar snackbar, @Snackbar.Callback.DismissEvent int event) {
// empty
}
/**
* Called when the given {@link Snackbar} is visible.
*
* @param snackbar The snackbar which is now visible.
* @see Snackbar#show()
*/
public void onShown(MySnackbar snackbar) {
// empty
}
}
/**
* @hide
*/
@IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
@IntRange(from = 1)
@Retention(RetentionPolicy.SOURCE)
public @interface Duration {
}
/**
* Show the Snackbar indefinitely. This means that the Snackbar will be displayed from the time
* that is {@link #show() shown} until either it is dismissed, or another Snackbar is shown.
*
* @see #setDuration
*/
public static final int LENGTH_INDEFINITE = -2;
/**
* Show the Snackbar for a short period of time.
*
* @see #setDuration
*/
public static final int LENGTH_SHORT = -1;
/**
* Show the Snackbar for a long period of time.
*
* @see #setDuration
*/
public static final int LENGTH_LONG = 0;
static final int ANIMATION_DURATION = 250;
static final int ANIMATION_FADE_DURATION = 180;
static final Handler sHandler;
static final int MSG_SHOW = 0;
static final int MSG_DISMISS = 1;
static {
sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_SHOW:
((MySnackbar) message.obj).showView();
return true;
case MSG_DISMISS:
((MySnackbar) message.obj).hideView(message.arg1);
return true;
}
return false;
}
});
}
private final ViewGroup mTargetParent;
private final Context mContext;
final MySnackbar.SnackbarLayout mView;
private int mDuration;
private MySnackbar.Callback mCallback;
private int mGravity = 0;
private final AccessibilityManager mAccessibilityManager;
private MySnackbar(ViewGroup parent, int gravity) {
mTargetParent = parent;
mContext = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(mContext);
if (gravity == 0) {
mView = (MySnackbar.SnackbarLayout) inflater.inflate(
R.layout.layout_snackbar_top, mTargetParent, false);
} else {
mView = (MySnackbar.SnackbarLayout) inflater.inflate(
R.layout.layout_snackbar_bottom, mTargetParent, false);
}
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
/**
* Make a Snackbar to display a message
*
*
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 a suitable parent,
* which is defined as a {@link CoordinatorLayout} or the window decor's content view,
* whichever comes first.
*
*
Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
* certain features, such as swipe-to-dismiss and automatically moving of widgets like
* {@link FloatingActionButton}.
*
* @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}
*/
@NonNull
public static MySnackbar make(@NonNull View view, @NonNull CharSequence text,
@Snackbar.Duration int duration, int gravity) {
MySnackbar snackbar = new MySnackbar(findSuitableParent(view), gravity);
snackbar.setText(text);
snackbar.setDuration(duration);
snackbar.setGravity(gravity);
return snackbar;
}
/**
* Make a Snackbar to display a message.
*
*
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 a suitable parent,
* which is defined as a {@link CoordinatorLayout} or the window decor's content view,
* whichever comes first.
*
*
Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
* certain features, such as swipe-to-dismiss and automatically moving of widgets like
* {@link FloatingActionButton}.
*
* @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}
*/
@NonNull
public static MySnackbar make(@NonNull View view, @StringRes int resId, @Snackbar.Duration int duration, int gravity) {
return make(view, view.getResources().getText(resId), duration, gravity);
}
private static ViewGroup findSuitableParent(View view) {
ViewGroup fallback = null;
do {
if (view instanceof CoordinatorLayout) {
// We've found a CoordinatorLayout, use it
return (ViewGroup) view;
} else if (view instanceof FrameLayout) {
if (view.getId() == android.R.id.content) {
// If we've hit the decor content view, then we didn't find a CoL in the
// hierarchy, so use it.
return (ViewGroup) view;
} else {
// It's not the content view but we'll use it as our fallback
fallback = (ViewGroup) view;
}
}
if (view != null) {
// Else, we will loop and crawl up the view hierarchy and try to find a parent
final ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
}
} while (view != null);
// If we reach here then we didn't find a CoL or a suitable content view so we'll fallback
return fallback;
}
/**
* Set the action to be displayed in this {@link Snackbar}.
*
* @param resId String resource to display
* @param listener callback to be invoked when the action is clicked
*/
@NonNull
public MySnackbar setAction(@StringRes int resId, View.OnClickListener listener) {
return setAction(mContext.getText(resId), listener);
}
/**
* Set the action to be displayed in this {@link Snackbar}.
*
* @param text Text to display
* @param listener callback to be invoked when the action is clicked
*/
@NonNull
public MySnackbar setAction(CharSequence text, final View.OnClickListener listener) {
final TextView tv = mView.getActionView();
if (TextUtils.isEmpty(text) || listener == null) {
tv.setVisibility(View.GONE);
tv.setOnClickListener(null);
} else {
tv.setVisibility(View.VISIBLE);
tv.setText(text);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onClick(view);
// Now dismiss the Snackbar
dispatchDismiss(Snackbar.Callback.DISMISS_EVENT_ACTION);
}
});
}
return this;
}
/**
* Sets the text color of the action specified in
* {@link #setAction(CharSequence, View.OnClickListener)}.
*/
@NonNull
public MySnackbar setActionTextColor(ColorStateList colors) {
final TextView tv = mView.getActionView();
tv.setTextColor(colors);
return this;
}
/**
* Sets the text color of the action specified in
* {@link #setAction(CharSequence, View.OnClickListener)}.
*/
@NonNull
public MySnackbar setActionTextColor(@ColorInt int color) {
final TextView tv = mView.getActionView();
tv.setTextColor(color);
return this;
}
/**
* Update the text in this {@link MySnackbar}.
*
* @param message The new text for the Toast.
*/
@NonNull
public MySnackbar setText(@NonNull CharSequence message) {
final TextView tv = mView.getMessageView();
tv.setText(message);
return this;
}
/**
* Update the text in this {@link MySnackbar}.
*
* @param resId The new text for the Toast.
*/
@NonNull
public MySnackbar setText(@StringRes int resId) {
return setText(mContext.getText(resId));
}
/**
* Set how long to show the view for.
*
* @param duration either be one of the predefined lengths:
* {@link #LENGTH_SHORT}, {@link #LENGTH_LONG}, or a custom duration
* in milliseconds.
*/
@NonNull
public MySnackbar setDuration(@MySnackbar.Duration int duration) {
mDuration = duration;
return this;
}
@NonNull
public MySnackbar setGravity(int gravity) {
mGravity = gravity;
return this;
}
@NonNull
/**
* Return the duration.
*
* @see #setDuration
*/
@MySnackbar.Duration
public int getDuration() {
return mDuration;
}
/**
* Returns the {@link MySnackbar}'s view.
*/
@NonNull
public View getView() {
return mView;
}
/**
* Show the {@link MySnackbar}.
*/
public void show() {
SnackbarManager.getInstance().show(mDuration, mManagerCallback);
}
/**
* Dismiss the {@link Snackbar}.
*/
public void dismiss() {
dispatchDismiss(Snackbar.Callback.DISMISS_EVENT_MANUAL);
}
void dispatchDismiss(@Snackbar.Callback.DismissEvent int event) {
SnackbarManager.getInstance().dismiss(mManagerCallback, event);
}
/**
* Set a callback to be called when this the visibility of this {@link Snackbar} changes.
*/
@NonNull
public MySnackbar setCallback(MySnackbar.Callback callback) {
mCallback = callback;
return this;
}
/**
* Return whether this {@link Snackbar} is currently being shown.
*/
public boolean isShown() {
return SnackbarManager.getInstance().isCurrent(mManagerCallback);
}
/**
* Returns whether this {@link Snackbar} is currently being shown, or is queued to be
* shown next.
*/
public boolean isShownOrQueued() {
return SnackbarManager.getInstance().isCurrentOrNext(mManagerCallback);
}
final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
@Override
public void show() {
sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, MySnackbar.this));
}
@Override
public void dismiss(int event) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, MySnackbar.this));
}
};
final void showView() {
if (mView.getParent() == null) {
final ViewGroup.LayoutParams lp = mView.getLayoutParams();
if (lp instanceof CoordinatorLayout.LayoutParams) {
// If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior
final CoordinatorLayout.LayoutParams clp = (CoordinatorLayout.LayoutParams) lp;
final MySnackbar.Behavior behavior = new MySnackbar.Behavior();
behavior.setStartAlphaSwipeDistance(0.1f);
behavior.setEndAlphaSwipeDistance(0.6f);
behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
@Override
public void onDismiss(View view) {
view.setVisibility(View.GONE);
dispatchDismiss(Snackbar.Callback.DISMISS_EVENT_SWIPE);
}
@Override
public void onDragStateChanged(int state) {
switch (state) {
case SwipeDismissBehavior.STATE_DRAGGING:
case SwipeDismissBehavior.STATE_SETTLING:
// If the view is being dragged or settling, cancel the timeout
SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
break;
case SwipeDismissBehavior.STATE_IDLE:
// If the view has been released and is idle, restore the timeout
SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
break;
}
}
});
clp.setBehavior(behavior);
// Also set the inset edge so that views can dodge the snackbar correctly
clp.insetEdge = Gravity.BOTTOM;
}
mTargetParent.addView(mView);
}
mView.setOnAttachStateChangeListener(new MySnackbar.SnackbarLayout.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
}
@Override
public void onViewDetachedFromWindow(View v) {
if (isShownOrQueued()) {
// If we haven't already been dismissed then this event is coming from a
// non-user initiated action. Hence we need to make sure that we callback
// and keep our state up to date. We need to post the call since removeView()
// will call through to onDetachedFromWindow and thus overflow.
sHandler.post(new Runnable() {
@Override
public void run() {
onViewHidden(Snackbar.Callback.DISMISS_EVENT_MANUAL);
}
});
}
}
});
if (ViewCompat.isLaidOut(mView)) {
if (shouldAnimate()) {
// If animations are enabled, animate it in
if (mGravity == 0) {
animateViewInTop();
} else {
animateViewInBottom();
}
} else {
// Else if anims are disabled just call back now
onViewShown();
}
} else {
// Otherwise, add one of our layout change listeners and show it in when laid out
mView.setOnLayoutChangeListener(new MySnackbar.SnackbarLayout.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View view, int left, int top, int right, int bottom) {
mView.setOnLayoutChangeListener(null);
if (shouldAnimate()) {
// If animations are enabled, animate it in
if (mGravity == 0) {
animateViewInTop();
} else {
animateViewInBottom();
}
} else {
// Else if anims are disabled just call back now
onViewShown();
}
}
});
}
}
void animateViewInBottom() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.setTranslationY(mView, mView.getHeight());
ViewCompat.animate(mView)
.translationY(0f)
.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
.setDuration(ANIMATION_DURATION)
.setListener(new ViewPropertyAnimatorListenerAdapter() {
@Override
public void onAnimationStart(View view) {
mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION,
ANIMATION_FADE_DURATION);
}
@Override
public void onAnimationEnd(View view) {
onViewShown();
}
}).start();
} else {
Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
R.anim.design_snackbar_in);
anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
anim.setDuration(ANIMATION_DURATION);
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
onViewShown();
}
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mView.startAnimation(anim);
}
}
private void animateViewOutBottom(final int event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.animate(mView)
.translationY(mView.getHeight())
.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
.setDuration(ANIMATION_DURATION)
.setListener(new ViewPropertyAnimatorListenerAdapter() {
@Override
public void onAnimationStart(View view) {
mView.animateChildrenOut(0, ANIMATION_FADE_DURATION);
}
@Override
public void onAnimationEnd(View view) {
onViewHidden(event);
}
}).start();
} else {
Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
R.anim.design_snackbar_out);
anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
anim.setDuration(ANIMATION_DURATION);
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
onViewHidden(event);
}
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mView.startAnimation(anim);
}
}
private void animateViewInTop() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.setTranslationY(mView, -mView.getHeight());
ViewCompat.animate(mView)
.translationY(0f)
.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
.setDuration(ANIMATION_DURATION)
.setListener(new ViewPropertyAnimatorListenerAdapter() {
@Override
public void onAnimationStart(View view) {
mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION,
ANIMATION_FADE_DURATION);
}
@Override
public void onAnimationEnd(View view) {
if (mCallback != null) {
mCallback.onShown(MySnackbar.this);
}
SnackbarManager.getInstance()
.onShown(mManagerCallback);
}
})
.start();
} else {
Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
R.anim.top_in);
anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
anim.setDuration(ANIMATION_DURATION);
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
if (mCallback != null) {
mCallback.onShown(MySnackbar.this);
}
SnackbarManager.getInstance()
.onShown(mManagerCallback);
}
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mView.startAnimation(anim);
}
}
private void animateViewOutTop(final int event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.animate(mView)
.translationY(-mView.getHeight())
.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
.setDuration(ANIMATION_DURATION)
.setListener(new ViewPropertyAnimatorListenerAdapter() {
@Override
public void onAnimationStart(View view) {
mView.animateChildrenOut(0, ANIMATION_FADE_DURATION);
}
@Override
public void onAnimationEnd(View view) {
onViewHidden(event);
}
})
.start();
} else {
Animation anim = AnimationUtils.loadAnimation(mView.getContext(), R.anim.top_out);
anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
anim.setDuration(ANIMATION_DURATION);
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
onViewHidden(event);
}
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mView.startAnimation(anim);
}
}
final void hideView(@Snackbar.Callback.DismissEvent final int event) {
if (shouldAnimate() && mView.getVisibility() == View.VISIBLE) {
if (mGravity == 0) {
animateViewOutTop(event);
} else {
animateViewOutBottom(event);
}
} else {
// If anims are disabled or the view isn't visible, just call back now
onViewHidden(event);
}
}
void onViewShown() {
SnackbarManager.getInstance().onShown(mManagerCallback);
if (mCallback != null) {
mCallback.onShown(this);
}
}
void onViewHidden(int event) {
// First tell the SnackbarManager that it has been dismissed
SnackbarManager.getInstance().onDismissed(mManagerCallback);
// Now call the dismiss listener (if available)
if (mCallback != null) {
mCallback.onDismissed(this, event);
}
if (Build.VERSION.SDK_INT < 11) {
// We need to hide the Snackbar on pre-v11 since it uses an old style Animation.
// ViewGroup has special handling in removeView() when getAnimation() != null in
// that it waits. This then means that the calculated insets are wrong and the
// any dodging views do not return. We workaround it by setting the view to gone while
// ViewGroup actually gets around to removing it.
mView.setVisibility(View.GONE);
}
// Lastly, hide and remove the view from the parent (if attached)
final ViewParent parent = mView.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(mView);
}
}
/**
* Returns true if we should animate the Snackbar view in/out.
*/
boolean shouldAnimate() {
return !mAccessibilityManager.isEnabled();
}
/**
* @hide
*/
public static class SnackbarLayout extends LinearLayout {
private TextView mMessageView;
private Button mActionView;
private int mMaxWidth;
private int mMaxInlineActionWidth;
interface OnLayoutChangeListener {
void onLayoutChange(View view, int left, int top, int right, int bottom);
}
interface OnAttachStateChangeListener {
void onViewAttachedToWindow(View v);
void onViewDetachedFromWindow(View v);
}
private MySnackbar.SnackbarLayout.OnLayoutChangeListener mOnLayoutChangeListener;
private MySnackbar.SnackbarLayout.OnAttachStateChangeListener mOnAttachStateChangeListener;
public SnackbarLayout(Context context) {
this(context, null);
}
public SnackbarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, android.support.design.R.styleable.SnackbarLayout);
mMaxWidth = a.getDimensionPixelSize(android.support.design.R.styleable.SnackbarLayout_android_maxWidth, -1);
mMaxInlineActionWidth = a.getDimensionPixelSize(
android.support.design.R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
if (a.hasValue(android.support.design.R.styleable.SnackbarLayout_elevation)) {
ViewCompat.setElevation(this, a.getDimensionPixelSize(
android.support.design.R.styleable.SnackbarLayout_elevation, 0));
}
a.recycle();
setClickable(true);
// Now inflate our content. We need to do this manually rather than using an
// in the layout since older versions of the Android do not inflate includes with
// the correct Context.
LayoutInflater.from(context).inflate(R.layout.layout_snackbar_include, this);
ViewCompat.setAccessibilityLiveRegion(this,
ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
ViewCompat.setImportantForAccessibility(this,
ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
// Make sure that we fit system windows and have a listener to apply any insets
ViewCompat.setFitsSystemWindows(this, true);
ViewCompat.setOnApplyWindowInsetsListener(this,
new android.support.v4.view.OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
// Copy over the bottom inset as padding so that we're displayed above the
// navigation bar
v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
v.getPaddingRight(), insets.getSystemWindowInsetBottom());
return insets;
}
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMessageView = (TextView) findViewById(R.id.snackbar_text);
mActionView = (Button) findViewById(R.id.snackbar_action);
}
TextView getMessageView() {
return mMessageView;
}
Button getActionView() {
return mActionView;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
final int multiLineVPadding = getResources().getDimensionPixelSize(
R.dimen.design_snackbar_padding_vertical_2lines);
final int singleLineVPadding = getResources().getDimensionPixelSize(
R.dimen.design_snackbar_padding_vertical);
final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
boolean remeasure = false;
if (isMultiLine && mMaxInlineActionWidth > 0
&& mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
multiLineVPadding - singleLineVPadding)) {
remeasure = true;
}
} else {
final int messagePadding = isMultiLine ? multiLineVPadding : singleLineVPadding;
if (updateViewsWithinLayout(HORIZONTAL, messagePadding, messagePadding)) {
remeasure = true;
}
}
if (remeasure) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
void animateChildrenIn(int delay, int duration) {
ViewCompat.setAlpha(mMessageView, 0f);
ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
.setStartDelay(delay).start();
if (mActionView.getVisibility() == VISIBLE) {
ViewCompat.setAlpha(mActionView, 0f);
ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
.setStartDelay(delay).start();
}
}
void animateChildrenOut(int delay, int duration) {
ViewCompat.setAlpha(mMessageView, 1f);
ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
.setStartDelay(delay).start();
if (mActionView.getVisibility() == VISIBLE) {
ViewCompat.setAlpha(mActionView, 1f);
ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
.setStartDelay(delay).start();
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mOnLayoutChangeListener != null) {
mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mOnAttachStateChangeListener != null) {
mOnAttachStateChangeListener.onViewAttachedToWindow(this);
}
ViewCompat.requestApplyInsets(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mOnAttachStateChangeListener != null) {
mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
}
}
void setOnLayoutChangeListener(MySnackbar.SnackbarLayout.OnLayoutChangeListener onLayoutChangeListener) {
mOnLayoutChangeListener = onLayoutChangeListener;
}
void setOnAttachStateChangeListener(MySnackbar.SnackbarLayout.OnAttachStateChangeListener listener) {
mOnAttachStateChangeListener = listener;
}
private boolean updateViewsWithinLayout(final int orientation,
final int messagePadTop, final int messagePadBottom) {
boolean changed = false;
if (orientation != getOrientation()) {
setOrientation(orientation);
changed = true;
}
if (mMessageView.getPaddingTop() != messagePadTop
|| mMessageView.getPaddingBottom() != messagePadBottom) {
updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
changed = true;
}
return changed;
}
private static void updateTopBottomPadding(View view, int topPadding, int bottomPadding) {
if (ViewCompat.isPaddingRelative(view)) {
ViewCompat.setPaddingRelative(view,
ViewCompat.getPaddingStart(view), topPadding,
ViewCompat.getPaddingEnd(view), bottomPadding);
} else {
view.setPadding(view.getPaddingLeft(), topPadding,
view.getPaddingRight(), bottomPadding);
}
}
}
final class Behavior extends SwipeDismissBehavior {
@Override
public boolean canSwipeDismissView(View child) {
return child instanceof Snackbar.SnackbarLayout;
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, Snackbar.SnackbarLayout child,
MotionEvent event) {
// We want to make sure that we disable any Snackbar timeouts if the user is
// currently touching the Snackbar. We restore the timeout when complete
if (parent.isPointInChildBounds(child, (int) event.getX(), (int) event.getY())) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
break;
}
}
return super.onInterceptTouchEvent(parent, child, event);
}
}
}
使用方法
MySnackbar.make(view, "TEST", Snackbar.LENGTH_SHORT, 0).show();
最后贴上源码地址:http://download.csdn.net/detail/dameinmax/9648770