kotlin 无法管理flutter应用程序主屏幕小部件中存在的列表视图中的单个项目上的单击事件

zqdjd7g9  于 2023-04-12  发布在  Kotlin
关注(0)|答案(1)|浏览(172)

我在我的Android应用的主屏幕小部件中有一个列表视图。我使用一个名为home widget的flutter插件来实现Kotlin和dart代码之间的通信。列表视图的每个项目中都有两个元素:复选框和文本。当复选框被单击时,我想执行HomeWidgetBackgroundIntent.getBroadcast方法来触发dart代码中的后台回调(这会在数据库中进行一些更改),当单击文本时,我想使用HomeWidgetLaunchIntent.getActivity方法来打开具有特定URI的应用程序。这两种方法都是挂起的意图。问题是android没有'我不允许你为一个列表项目添加不同的挂起意图。他们希望开发人员为单个项目使用fillInIntent,然后设置公共的挂起意图模板。我应该如何检测哪个元素被点击(复选框或文本)并为这两种情况设置不同的未决意图模板?我也想过让未决意图模板触发小部件的接收者,然后调用home_这里的widget插件方法(基于fillInIntent中的数据集)。但是由于这些方法是挂起的intent,我如何在这里触发挂起的intent(通常挂起的intent在点击事件时触发,但不可能在onReceive方法中触发它们)?
关于我的小部件的代码:

override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, widgetData: SharedPreferences) {
        Log.d("debugging", "widget loaded")

        val sharedPref: SharedPreferences = context.getSharedPreferences(
                "ApplicationListener", Context.MODE_PRIVATE)
        appWidgetIds.forEach { widgetId ->
            val views = RemoteViews(context.packageName, R.layout.widget_layout).apply {

                val todosStr  = widgetData.getString("todos", "null")
                val todos = ArrayList<HashMap<String, Any>>()
                val todosRemoteView = RemoteViews.RemoteCollectionItems.Builder()
                if(todosStr != "null"){
                    val jObj = JSONObject(todosStr)
                    val jsonArry = jObj.getJSONArray("todos")
                    for (i in 0 until jsonArry.length()) {
                        val todo = HashMap<String, Any>()
                        val obj = jsonArry.getJSONObject(i)
                        todo["id"] = obj.getInt("id")
                        todo["taskName"] = obj.getString("taskName")
                        todos.add(todo)

                        val view = RemoteViews(context.packageName, R.layout.each_todo).apply {
                            setTextViewText(R.id.each_todo_container_text, todo["taskName"].toString())
                            setCompoundButtonChecked(R.id.each_todo_container_checkbox, todo["finished"].toString().toBoolean())
                            //i want to have a separate pending intent for the text view and the check box

                            val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(context,
                                    Uri.parse("myAppWidget://todo_checked/${todo["id"].toString()}"))
                            setOnCheckedChangeResponse(
                                    R.id.each_todo_container_checkbox,
                                    RemoteViews.RemoteResponse.fromPendingIntent(backgroundIntent)
                            )
                            //this pending intent doesn't get triggered

                            val fillInIntent = Intent().apply {
                                Bundle().also { extras ->
                                    extras.putInt(EXTRA_ITEM, todo["id"].toString().toInt())
                                    putExtras(extras)
                                }
                            }
                            setOnClickFillInIntent(R.id.each_todo_container_text, fillInIntent)
                        }

                        todosRemoteView.addItem(todo["id"].toString().toInt().toLong(), view)
                    }
                }

                setRemoteAdapter(
                        R.id.todos_list,
                        todosRemoteView
                    .build()
                )

               val editTodoIntent =  HomeWidgetLaunchIntent.getActivity(
                       context,
                       MainActivity::class.java,
                       Uri.parse("http://edit_todo/$timeType"))
                setPendingIntentTemplate(R.id.todos_list, editTodoIntent)
                //how to set two different pending intent templates here for the text view and checkbox?
            }

            appWidgetManager.updateAppWidget(widgetId, views)
    }

    }

    override fun onReceive(context: Context?, intent: Intent?) {
        val viewIndex: Int = intent!!.getIntExtra(EXTRA_ITEM, 0)
        Log.d("debugging", "an item is clicked $viewIndex")
        //i can make the pending intent template of the list view trigger this onReceive function and I can use the data inside intent(set by fillInIntent) to find out which element of the two was clicked 
        //but how can I trigger the pending intent here?

        super.onReceive(context, intent)
    }
}
mbjcgjjk

mbjcgjjk1#

请随意阅读这个android示例,以便更好地理解我在这里写的内容。我将解释你在阅读网站后可能会知道的东西(也许你已经阅读过了)。
首先,您不需要通过HomeWidgetLaunchIntent.getActivity创建setPendingIntentTemplate的Intent,您需要通过以下方式创建它的PendingIntent

...
val pendingIntentTemplate = Intent(context, MyWidgetProvider::class.java).run {
    action = "Some action name"  // This is a constant in the android example
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
    data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
    // This creates the PendingIntent you need. Also I had to add the PendingIntent.FLAG_MUTABLE flag to avoid errors, maybe is the OS version of my android emulator, feel free to test.
    PendingIntent.getBroadcast(context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
}
setPendingIntentTemplate(R.id.listView_id, pendingIntentTemplate)
...

IMO你应该使用RemoteViewsService.RemoteViewsFactory作为适配器,并使用setOnClickFillInIntent来设置你必须覆盖的getViewAt函数中的行中的每个元素,这是我在android示例中发现的方法,更容易理解。你可以在android示例中找到更多信息,仔细阅读所有文档。forgetVewAt应该有这样的内容:

class CustomAdapter(val context: Context, val intent: Intent) :
   RemoteViewsService.RemoteViewsFactory {
...
   override fun getViewAt(position: Int): RemoteViews {
        val remoteViews = RemoteViews(context.packageName, R.layout.widget_layout).apply { 
            setTextViewText(R.id.textView_id, "Item $position")
            val checkboxfillInIntent = Intent().apply {
                Bundle().also { extras ->
                    extras.putInt("id", position)
                    extras.putBoolean("isCheckBox", true)
                    putExtras(extras)
                }
            }
            val textfillInIntent = Intent().apply {
                Bundle().also { extras ->
                    extras.putInt("id", position)
                    extras.putBoolean("isCheckBox", false)
                    putExtras(extras)
                }
            }
            setOnClickFillInIntent(R.id.checkBox_id, checkboxfillInIntent)
            setOnClickFillInIntent(R.id.textView_id, textfillInIntent)
        }
        return remoteViews
   }
...
}

也许你可以在你的RemoteViews.RemoteCollectionItems.Builder中调用两个setOnClickFillInIntent,我认为你应该可以,但我更喜欢使用RemoteViewsService.RemoteViewsFactory来遵循android示例。
在android示例中,它们不运行应用,但您可以在onReceive函数中运行您的应用(现在这里您使用HomeWidgetLaunchIntent.getActivity并使用send()运行它):

override fun onReceive(context: Context, intent: Intent) {
    ...
    if (intent.action == "Some action name") {  // This is the action constant of the android example
        val id = intent.getIntExtra("id")
        val isCheckBox = intent.getBooleanExtra("isCheckBox")
        ...
        if (isCheckBox) {
            ...
        } else {
            val pendingIntent = HomeWidgetLaunchIntent.getActivity(context, MainActivity::class.java)
            pendingIntent.send()
        }
    }
    ...
}

如果我遗漏了什么,请随时询问。我试图调整我必须匹配您所需要的代码。

相关问题