我try 在我的应用程序中创建一个通知覆盖,它可以显示有关特定应用程序重要事件的通知.

由于这是我第一次直接使用WindowManager,我很困惑.如果可能的话,有人能帮我弄清楚发生了什么事,我怎样才能实现我想要的吗.

class NotificationOverlay(private val context: Context) {

    private val windowManager: WindowManager =
        context.getSystemService(Context.WINDOW_SERVICE) as WindowManager

    private val layoutParams = WindowManager.LayoutParams().apply {
        gravity = android.view.Gravity.TOP or android.view.Gravity.CENTER_HORIZONTAL
        width = WindowManager.LayoutParams.MATCH_PARENT
        height = WindowManager.LayoutParams.WRAP_CONTENT
        format = PixelFormat.TRANSLUCENT
        dimAmount = 0.5f
        flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND
        type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
    }

    private val notifications = mutableListOf<NotificationItem>().also {
        it.addAll(listOf(
            NotificationItem(title = "not 1", message = "first notification"),
            NotificationItem(title = "not 2", message = "second notification")
        ))
    }
    private val notificationsAdapter = NotificationAdapter(::removeNotification)
    private val recyclerView = RecyclerView(context).apply {
        layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
        adapter = notificationsAdapter
    }


    private fun removeNotification(notification: NotificationItem){
        notifications.remove(notification)
        notificationsAdapter.submitList(notifications)
        if(notificationsAdapter.currentList.isEmpty()){
            windowManager.removeView(recyclerView)
        }
    }

    fun show(){
        windowManager.addView(recyclerView, layoutParams)

        notificationsAdapter.submitList(notifications)
    }
}

Edited

嗯,我发现问题不在WindowManager中,而在DiffUtils中,但无法理解它的错误,因为它非常简单,我多次实现了这种DiffUtils,无论如何,如果您知道为什么这样做不起作用,我会在这里发布代码:

    class NotificationAdapter(private val onCloseClicked: (NotificationItem) -> Unit):
        ListAdapter<NotificationItem, NotificationAdapter.NotificationViewHolder>(DiffCallback()) {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationViewHolder {
            val binding = ItemNotificationOverlayBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return NotificationViewHolder(binding, onCloseClicked)
        }
    
    
        override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) {
            holder.bind(currentList[position])
        }
    
        class NotificationViewHolder(private val itemBinding: ItemNotificationOverlayBinding, private val onCloseClicked: (NotificationItem) -> Unit): RecyclerView.ViewHolder(itemBinding.root) {
            fun bind(item: NotificationItem) {
                itemBinding.title.text = item.title
                itemBinding.message.text = item.message
                itemBinding.close.setOnClickListener {
                    onCloseClicked.invoke(item)
                }
            }
        }
    
        class DiffCallback : DiffUtil.ItemCallback<NotificationItem>() {
    
            override fun areItemsTheSame(oldItem: NotificationItem, newItem: NotificationItem) =
                oldItem.id == newItem.id
    
            override fun areContentsTheSame(oldItem: NotificationItem, newItem: NotificationItem) =
                oldItem == newItem
        }
    }

这么简单的构造有什么错?我已经快发疯了,我想扔掉这个难题,实施老派notifyItemRemoved

Edited 2

所以@IamFr0ssT提供的答案解决了这个问题.我更深入地了解了为什么会发生这种情况,原因是在main submitList函数的androidx.recyclerview.widget.AsyncListDiffer类中.它正在执行以下判断:

if (newList == mList) {
            // nothing to do (Note - still had to inc generation, since may have ongoing work)
            if (commitCallback != null) {
                commitCallback.run();
            }
            return;
        }

所以我的差异从来没有被计算过,因为我提交了相同的列表参考.

@IamFr0ssT建议的额外toList()函数所做的是创建一个列表的新实例,从而使different计算我的diff.如果深入toList()函数,它最终会根据提供的列表...return ArrayList(this)创建一个ArrayList的新实例

这个问题与WindowManager无关,只是DiffUtil

推荐答案

您正在将可变列表notifications传递给适配器,而适配器并没有复制列表,它只是使用相同的列表.

removeNotification回调中编辑列表时,您正在编辑适配器正在使用的列表.

因此,当计算差异时,它会将其认为当前显示但未显示的列表与自身进行比较.因此,没有差异和notifyItemRemoved或其他事件.

我认为,要解决这个问题,只需在调用submitList()时调用可变列表中的.toList():

class NotificationOverlay(private val context: Context) {
    ...

    private fun removeNotification(notification: NotificationItem){
        notifications.remove(notification)
        notificationsAdapter.submitList(notifications.toList())
        if(notificationsAdapter.currentList.isEmpty()){
            windowManager.removeView(recyclerView)
        }
    }

    fun show(){
        windowManager.addView(recyclerView, layoutParams)

        notificationsAdapter.submitList(notifications.toList())
    }
}

此外,如何获取NotificationItem.身份证件?每个条目都应该不同.

Android相关问答推荐

无法理解Kotlin Coroutines and Flows中的J.C.编程行为

懒惰列的滚动到项目不按预期工作'

使用Kotlin的SD卡

KMM项目生成错误-';在项目';中找不到测试类:共享';

Android事件:APP_SCOUT_HANG警告SQLite

请求标头为空/无法通过拦截器获取

BroadCastReceiver的onReceive方法中的Intent上的Extras为空

如何判断堆肥是否为空?

Kotlin 协程、 retrofit 、android

如何从包装在泛型中的 retrofit 调用中检索密钥?

如何用jetpack compose实现垂直李克特量表

Jetpack Compose 动画的行为是什么?

在事件中使用 Context/Toast 时不需要的重组 - Jetpack Compose

在 Jetpack Compose 中单击时更改表面项目的背景 colored颜色

如何在jetpack compose中通过lamda返回columnScope/RowScope

获取模板向导配方类 Intellij 中的应用程序包名称

为什么我不能直接记住 mutableStateOf 可组合函数?

如何对齐文本和图标可组合,以便即使在文本溢出后它们也能保持在一起?

Android Jetpack Compose - 每次文本字段值更改时,可组合函数都会重新组合

如何在jetpack compose中创建水印文字效果