Android 自定义SnackBar和下滑取消
如何自定义SnackBar
首先我们得了解SnackBar的布局:
之前我看有一些方案是获取内部的contentLayout,然后做一些处理。但是现在已经行不通了:
@RestrictTo(LIBRARY_GROUP)
public static final class SnackbarLayout extends BaseTransientBottomBar.SnackbarBaseLayout@RestrictTo(LIBRARY_GROUP)
public class SnackbarContentLayout extends LinearLayout implements ContentViewCallback
现在这些都添加的有@RestrictTo(LIBRARY_GROUP)
注解,只能内部使用。
那所以有一个比较挫一点的方案,就是全部移除,并添加:
val customView = layoutInflater.inflate(R.layout.layout_snack_info_sticker_tips, null).apply {findViewById<TextView>(R.id.snack_info_sticker_tips).text = message
}// 替换默认视图
(view as ViewGroup).apply {removeAllViews()addView(customView)
}
需要注意的一个点:
Snackbar.view
这里拿到的是最外层的SnackbarLayout- SnackbarLayout左右有一个默认的padding,查看代码是12dp
下滑取消
我们知道Snackbar的下滑取消是通过Behavior
实现的,它是BaseTransientBottomBar
的内部类:
public static class Behavior extends SwipeDismissBehavior<View>
那么有一定就需要注意,Snackbar挂载的父view就必须是ConstraintLayout
,否则就不会生效。
另外默认的滑动取消是从左向右。
那么如何实现下滑取消,也很简单:
- 把默认的滑动取消屏蔽掉
- 重新拦截时间自己处理
var initialX = 0.0fvar initialY = 0.0fbehavior = object : BaseTransientBottomBar.Behavior() {// 禁用原有的向右滑动关闭override fun canSwipeDismissView(child: View): Boolean = false// 添加触摸事件处理override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: View, event: MotionEvent): Boolean {when (event.action) {MotionEvent.ACTION_DOWN -> {// 如果点击的是snackbar,再记录起始位置if (parent.isPointInChildBounds(child, event.x.toInt(), event.y.toInt())){initialX = event.xinitialY = event.y} else { // 否则直接不处理return false}}MotionEvent.ACTION_UP -> {// 计算滑动距离val dx = abs(event.x - initialX)val dy = abs(event.y - initialY)// 当纵向滑动距离大于横向时拦截事件,且超过阈值if (dy > dx && dy > 20) {dismiss()return true}}}return super.onInterceptTouchEvent(parent, child, event)}}