多个按钮的Android小部件点击监听器

40

我想为我的应用程序创建一个小部件。从我阅读的一篇安卓开发者网站上得知,所有的onclick监听器都需要有Intent。但是如果我只想让我的按钮在小部件本身中更新数据,而不想启动一个新活动呢?

以下是一些安卓演示代码:

Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

// Get the layout for the App Widget and attach an on-click listener
// to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);

我想要一个按钮,当我点击它时会进行一次http网络调用,然后将结果显示在小部件中。如果我必须使用意图,我该如何做?而且我需要能够区分哪个按钮被点击了。

为什么小部件使用意图而不是正常的onclick监听器,在那里调用类似于活动的函数?

编辑

我的小部件提供程序:

public class MyWidgetProvider extends AppWidgetProvider {

private static final String MyOnClick1 = "myOnClickTag1";
private static final String MyOnClick2 = "myOnClickTag2";
private static final String MyOnClick3 = "myOnClickTag3";

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

    // Get all ids
    ComponentName thisWidget = new ComponentName(context, MyWidgetProvider.class);
    int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

    for (int widgetId : allWidgetIds) {

        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);

        remoteViews.setOnClickPendingIntent(R.id.widget_button_stayarm, getPendingSelfIntent(context, MyOnClick1));
        remoteViews.setOnClickPendingIntent(R.id.widget_button_awayarm, getPendingSelfIntent(context, MyOnClick2));
        remoteViews.setOnClickPendingIntent(R.id.widget_button_dissarm, getPendingSelfIntent(context, MyOnClick3));

        remoteViews.setTextViewText(R.id.widget_textview_gpscoords, "gps cords");

        appWidgetManager.updateAppWidget(widgetId, remoteViews);
    }
}

protected PendingIntent getPendingSelfIntent(Context context, String action) {
    Intent intent = new Intent(context, getClass());
    intent.setAction(action);
    return PendingIntent.getBroadcast(context, 0, intent, 0);
}

@Override
public void onReceive(Context context, Intent intent) {

    if (MyOnClick1.equals(intent.getAction())) {
        // your onClick action is here
        Toast.makeText(context, "Button1", Toast.LENGTH_SHORT).show();
        Log.w("Widget", "Clicked button1");
    } else if (MyOnClick2.equals(intent.getAction())) {
        Toast.makeText(context, "Button2", Toast.LENGTH_SHORT).show();
        Log.w("Widget", "Clicked button2");
    } else if (MyOnClick3.equals(intent.getAction())) {
        Toast.makeText(context, "Button3", Toast.LENGTH_SHORT).show();
        Log.w("Widget", "Clicked button3");
    }
};
}

我的Android清单文件:

<receiver
    android:name="widget.MyWidgetProvider"
    android:icon="@drawable/fsk"
    android:label="FSK Widget" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>

    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/example_appwidget_info" />
</receiver>
4个回答

60

可以为小部件中的视图创建一个 onClick 事件。您可以创建任意数量的 onClick 事件。

在您的小部件类的顶部,创建一个静态变量,它将是您的 onClick 名称标记:

private static final String MyOnClick = "myOnClickTag";

定义一个辅助方法来自动创建每个PendingIntent

protected PendingIntent getPendingSelfIntent(Context context, String action) {
    Intent intent = new Intent(context, getClass());
    intent.setAction(action);
    return PendingIntent.getBroadcast(context, 0, intent, 0);
}

将以下代码添加到您的视图中:

onClick = "此处填写代码"

    remoteViews.setOnClickPendingIntent(R.id.button, 
                      getPendingSelfIntent(context, MyOnClick));

在您的Widget类中创建一个onReceive方法,并在其中设置这个onClick事件:

public void onReceive(Context context, Intent intent) {

    if (MyOnClick.equals(intent.getAction())){
        //your onClick action is here
    }
};

当您设置标签时,每当视图被按下,onReceive都会捕获该事件并执行与我们日常使用的标准onClick事件完全相同的操作。

编辑:根据您的答案,您能否将您的更新内容替换为以下行,并再次尝试:

    RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_det);
    thisWidget = new ComponentName(context, MyWidgetProvider.class);    
    remoteViews.setOnClickPendingIntent(R.id.widget_button_stayarm, getPendingSelfIntent(context, MyOnClick1));
    remoteViews.setOnClickPendingIntent(R.id.widget_button_awayarm, getPendingSelfIntent(context, MyOnClick2));
    remoteViews.setOnClickPendingIntent(R.id.widget_button_dissarm, getPendingSelfIntent(context, MyOnClick3));
    remoteViews.setTextViewText(R.id.widget_textview_gpscoords, "gps cords");
    appWidgetManager.updateAppWidget(thisWidget, remoteViews);

1
是的,你可以使用action标签来区分按钮点击。你可以为两个不同的按钮设置相同的标签,或者你可以创建许多onclick事件并将它们分别设置。不,这不是一个服务。经典的Widget类扩展了AppWidgetProvider。 - canova
getPendingSelfIntent(context, MyOnClick)为什么在我的代码中被标记成红色下划线了? - Zapnologica
1
抱歉,这是我的错 :) 在我的回答中添加。 - canova
1
很抱歉这么麻烦,但我现在已经实现了,但是当我点击小部件中的按钮时,它不会调用onrecive函数吗?我已经设置了断点,但它没有被触发?我已经将我的代码粘贴到原始答案中。 - Zapnologica
+1 如果我早些时候就找到了这个答案,那将会节省我很多时间!易于理解和清晰明了 - 谢谢! - Dog Lover
显示剩余5条评论

21

只需在您的 onReceive 方法中调用 super。

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);//add this line
    if (MyOnClick1.equals(intent.getAction())) {
        // your onClick action is here
        Toast.makeText(context, "Button1", Toast.LENGTH_SHORT).show();
        Log.w("Widget", "Clicked button1");
    } else if (MyOnClick2.equals(intent.getAction())) {
        Toast.makeText(context, "Button2", Toast.LENGTH_SHORT).show();
        Log.w("Widget", "Clicked button2");
    } else if (MyOnClick3.equals(intent.getAction())) {
        Toast.makeText(context, "Button3", Toast.LENGTH_SHORT).show();
        Log.w("Widget", "Clicked button3");
    }
}

6
super.onReceive(context, intent);解决了我的问题。谢谢! - Dog Lover

3

这里是一个例子:

public class SimpleWidgetProvider extends AppWidgetProvider {

    private static final String MY_BUTTTON_START = "myButtonStart";
    RemoteViews remoteViews;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int count = appWidgetIds.length;

        for (int i = 0; i < count; i++) {
            int widgetId = appWidgetIds[i];

            remoteViews = new RemoteViews(context.getPackageName(),
                R.layout.widget_layout);
            remoteViews.setTextViewText(R.id.textView, number);

            Intent intent = new Intent(context, SimpleWidgetProvider.class);
            intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
                0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

            remoteViews.setOnClickPendingIntent(R.id.actionButton,
                getPendingSelfIntent(context, MY_BUTTTON_START));

            appWidgetManager.updateAppWidget(widgetId, remoteViews);
        }
    }

    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);

        if (MY_BUTTTON_START.equals(intent.getAction())){
            Toast.makeText(context, "Click", Toast.LENGTH_SHORT).show();
        }
    };

    protected PendingIntent getPendingSelfIntent(Context context, String action) {
        Intent intent = new Intent(context, getClass());
        intent.setAction(action);
        return PendingIntent.getBroadcast(context, 0, intent, 0);
    }
}


你能帮我吗?我正在使用相同的代码,但 onReceive 从未被调用。 - Muhammad Mehdi Raza
非常感谢Faxriddin的快速回复,问题已经解决了。我在下面这行代码中漏掉了(context,getClass()): Intent intent = new Intent(context, getClass()); - Muhammad Mehdi Raza

0

对于任何试图通过点击App小部件(或主屏幕小部件)Flutter代码进行通信的人,请记住,接受的答案并不适用于所有情况,因为广播由操作系统管理,并且可能会延迟处理(请阅读我在Canova答案中的评论以获取更多信息)。

我使用的解决方案是这样的(它立即打开应用程序,并根据是否通过点击App小部件打开应用程序来处理导航):

  1. 按照这里所述,将您需要的数据保存在PendingIntent中以便以后恢复:在Android中传递值给Pending Intents,然后将待处理意图设置为视图(请注意,我的待处理意图是一个getActivity,而不是getBroadcast,这意味着活动将立即打开):

    val intent = Intent(context, MainActivity::class.java)
                     intent.action = "es.antonborri.home_widget.action.LAUNCH"
    var flags = PendingIntent.FLAG_UPDATE_CURRENT
    if (Build.VERSION.SDK_INT >= 23) {
     flags = flags or PendingIntent.FLAG_IMMUTABLE
    }
    intent.putExtra( "keyHomeScreenWidgetOpen", true)
    val openAppIntent = PendingIntent.getActivity(context, 0, intent, flags)
    setOnClickPendingIntent(R.id.homeScreenWidgetImage, openAppIntent)
    
  2. 根据此处所述,在MainActivity.kt上创建一个Method Channel处理程序:https://docs.flutter.dev/platform-integration/platform-channels?tab=type-mappings-kotlin-tab

  3. 恢复我们在步骤1中保存的值,并将其保存在首选项中:

    private fun getInitialIntent() : Boolean {
     try {
         val extras: Bundle? = intent.extras
         if (extras != null) {
             val widgetData: SharedPreferences =
                 applicationContext.getSharedPreferences("HomeWidgetPreferences", Context.MODE_PRIVATE)
             val openedByHomeScreenWidget = extras.getBoolean("keyHomeScreenWidgetOpen")
             if (openedByHomeScreenWidget) {
                 var preferences = widgetData.edit()
                 preferences.putBoolean("keyHomeScreenWidgetOpen", true)
                 preferences.commit()
                 return true
             }
         }
         return false
     }
     catch (error:Exception){
         return false
     }
    

    }

  4. 通过method channel在main.dart上调用该方法,以接收操作的确认:

     MethodChannel channel =
     const MethodChannel(Constants.CHANNEL_HOME_SCREEN_WIDGET);
     bool openedByHomeScreenWidget =
     await channel.invokeMethod("getInitialIntent");
     if (kDebugMode) {
      print(
       "openedByWidget: $openedByHomeScreenWidget");
     }
    
  5. 现在,您可以通过首选项在任何地方访问数据。请记住,我们在步骤1中保存的值已在步骤3中保存在首选项中。步骤4仅用于在代码的dart端获得来自method channel的反馈。

PD:我在我的Flutter项目中使用了HomeWidget插件...我建议你也实现它来创建你的App小部件。
希望这对其他人有所帮助...大家都开心编码!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接