消息推送弹窗的简单实现

2,059 阅读1分钟

本文的目的是实现一个类似Notification的通知弹窗,效果是有信息来的时候自动弹出,可以上滑取消,也可以规定时间内自动取消,不会在通知栏里面生成通知。如有不足之处或者更好的实现方法,请指出。 先上效果图。

1.找到DecorView

view-layer.jpg

如上图(图侵删)所示,DecorView是最顶层的View,是整个视图的根节点,继承自FrameLayout。而这一步就是要找到DecorView

fun findDecorView(activity: Activity): ViewGroup? {
    var view: View? = activity.findViewById(android.R.id.content)
    var frameLayout: FrameLayout? = null
    while (view != null) {
        val parent = view.parent
        view = if (parent is View) parent else null
        if (parent is FrameLayout) {
            frameLayout = parent
        }
    }
    return frameLayout
}

// 我发现可以直接从当前的activity取到DecorView
fun findDecorView2(activity: Activity): ViewGroup = activity.window.decorView as ViewGroup

2.将弹窗添加到DecorView中

fun show() {
    val notificationView = currentActivity.layoutInflater.inflate(R.layout.layout_notification, null)
    val layoutNotification: RelativeLayout = notificationView.findViewById(R.id.layout_notification)
    
    val decorView = findDecorView(currentActivity) ?: return
    decorView.postDelayed(kotlinx.coroutines.Runnable { decorView.addView(notificationView) }, 100)
    
    // 添加弹出动画
    notificationView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.notification_enter))

    // 设置3秒内自动关闭
    notificationView.postDelayed({ 
        val parent = notificationView.parent
        if (parent is ViewGroup) {
            parent.removeView(notificationView)
        }
    }, 3000)
    
    // 设置监听
    setOnTouchListener(notificationView)
}

3.监听用户操作

fun setOnTouchListener(view: View) {
    var x = 0f
    var y = 0f
    // 滑动最小距离
    val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop 
    view.setOnTouchListener { view, event ->
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                x = event.x
                y = event.y
            }
            MotionEvent.ACTION_UP -> {
                // 计算水平和竖直方向的移动距离
                val offsetX = event.x - x
                val offsetY = event.y - y
       
                // 如果滑动距离小于最小滑动距离,则认做是单击事件
                if (abs(offsetX) < touchSlop && abs(offsetY) < touchSlop) {
                    // 点击事件
                    context.startActivity(Intent(context, ResultActivity::class.java))
                }
                // 判断是否有上滑操作
                else if (offsetY < -touchSlop) {
                    removeView(view)
                } 
            }
        }
        true
    }
}
    
fun removeView(view: View) {
    // 添加上滑取消动画效果
    view.startAnimation(AnimationUtils.loadAnimation(this, R.anim.notification_exit))
    
    notificationView.postDelayed({ 
        val parent = notificationView.parent
        if (parent is ViewGroup) {
            parent.removeView(notificationView)
        }
    }, 100)
}