前两天创建新项目的activity的时候,不小心选择了ScrollingActivity,打开一看里面有一个SnackBar来显示出吐司。感觉跟Toast一样,于是研究了一番。先来简单的介绍一下SnackBar,Snackbar 是 Android 5.0 新特性——Material Design 中的一个控件,用来代替 Toast ,Snackbar与Toast的主要区别是:Snackbar可以滑动退出,也可以处理用户交互(点击)事件。总之Toast能做的它也可以做,而且部分功能做的更好。比如显示时长,Toast最多3s,SnackBar可以自定义时长。这一点比Toast强大很多。
因为SnackBar并不能完全支撑我们的开发需要,所以这里通过修改源码拓展了一些功能
这里的拓展功能是我根据我当前项目需要,就只修改了这两处的源码。大家可以根据项目需求自行拓展
1.MySnackBar.java(这个类没有修改,新增了一个方法,用来调用)
/**
* 自定义SnackBar显示位置
* @param type
*/
public void setGravity(int type) {
BaseTransientBottomBar.DISPLAY_LOCATION_TYPE = type;
}
2.BaseTransientBottomBar.java(这个类里面修改了两处,一个是显示位置,一个是显示动画)
这里是显示位置修改的代码
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 Behavior behavior = new 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(BaseCallback.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, pause the timeout
SnackBarManager.getInstance().pauseTimeout(mManagerCallback);
break;
case SwipeDismissBehavior.STATE_IDLE:
// If the view has been released and is idle, restore the timeout
SnackBarManager.getInstance()
.restoreTimeoutIfPaused(mManagerCallback);
break;
}
}
});
clp.setBehavior(behavior);
// Also set the inset edge so that views can dodge the bar correctly
clp.insetEdge = Gravity.BOTTOM;
}
//SnackBar源码为 mTargetParent.addView(mView),但是为了兼容动态显示的位置,这里使用了动态布局加载包裹,可以实现自由切换底部还是顶部
LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//此处相当于布局文件中的Android:layout_gravity属性
if(DISPLAY_LOCATION_TYPE == 1){
param.gravity = Gravity.TOP;
} else if(DISPLAY_LOCATION_TYPE == 2){
param.gravity = Gravity.BOTTOM;
} else if(DISPLAY_LOCATION_TYPE == 3){
param.gravity = Gravity.CENTER;
}
mView.setLayoutParams(param);
LinearLayout linear = new LinearLayout(mContext);
//注意,对于LinearLayout布局来说,设置横向还是纵向是必须的!否则就看不到效果了。
linear.setOrientation(LinearLayout.HORIZONTAL);
linear.addView(mView);
mTargetParent.addView(linear);
}
mView.setOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
}
@Override
public void onViewDetachedFromWindow(View view) {
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(BaseCallback.DISMISS_EVENT_MANUAL);
}
});
}
}
});
if (ViewCompat.isLaidOut(mView)) {
if (shouldAnimate()) {
// If animations are enabled, animate it in
animateViewIn();
} 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 View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
mView.setOnLayoutChangeListener(null);
if (shouldAnimate()) {
// If animations are enabled, animate it in
animateViewIn();
} else {
// Else if anims are disabled just call back now
onViewShown();
}
}
});
}
}
这里是显示动画修改的代码
/**
* 动画开始方法
*/
void animateViewIn() {
if (Build.VERSION.SDK_INT >= 12) {
final int viewHeight;
if(DISPLAY_LOCATION_TYPE == 1){
//顶部显示
viewHeight = -mView.getHeight();
}else{
//底部显示
viewHeight = mView.getHeight();
}
if (USE_OFFSET_API) {
ViewCompat.offsetTopAndBottom(mView, viewHeight);
} else {
mView.setTranslationY(viewHeight);
}
final ValueAnimator animator = new ValueAnimator();
animator.setIntValues(viewHeight, 0);
animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
animator.setDuration(ANIMATION_DURATION);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
mContentViewCallback.animateContentIn(
ANIMATION_DURATION - ANIMATION_FADE_DURATION,
ANIMATION_FADE_DURATION);
}
@Override
public void onAnimationEnd(Animator animator) {
onViewShown();
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private int mPreviousAnimatedIntValue = viewHeight;
@Override
public void onAnimationUpdate(ValueAnimator animator) {
int currentAnimatedIntValue = (int) animator.getAnimatedValue();
if (USE_OFFSET_API) {
ViewCompat.offsetTopAndBottom(mView,
currentAnimatedIntValue - mPreviousAnimatedIntValue);
} else {
mView.setTranslationY(currentAnimatedIntValue);
}
mPreviousAnimatedIntValue = currentAnimatedIntValue;
}
});
animator.start();
} else {
final Animation anim = android.view.animation.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);
}
}
/**
* 动画结束方法
* @param event
*/
private void animateViewOut(final int event) {
if (Build.VERSION.SDK_INT >= 12) {
final ValueAnimator animator = new ValueAnimator();
if(DISPLAY_LOCATION_TYPE == 1){
//改成0,顶部显示才会生效(design_layout_snackbar.xml里的layout_gravity由bottom改为top)
//如果要底部显示(注释这一行即可)
mView.setTranslationY(0);
//顶部显示
animator.setIntValues(0, -mView.getHeight());
}else{
//底部显示
animator.setIntValues(0, mView.getHeight());
}
animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
animator.setDuration(ANIMATION_DURATION);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
mContentViewCallback.animateContentOut(0, ANIMATION_FADE_DURATION);
}
@Override
public void onAnimationEnd(Animator animator) {
onViewHidden(event);
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private int mPreviousAnimatedIntValue = 0;
@Override
public void onAnimationUpdate(ValueAnimator animator) {
int currentAnimatedIntValue = (int) animator.getAnimatedValue();
if (USE_OFFSET_API) {
ViewCompat.offsetTopAndBottom(mView,
currentAnimatedIntValue - mPreviousAnimatedIntValue);
} else {
mView.setTranslationY(currentAnimatedIntValue);
}
mPreviousAnimatedIntValue = currentAnimatedIntValue;
}
});
animator.start();
} else {
final Animation anim = android.view.animation.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);
}
}
Q:486789970(QQ现在很少用)
V:18588400509(如果着急,可以直接加微信)
email:[email protected]
---财财亲笔