【问题标题】:Button click event for android widgetandroid小部件的按钮单击事件
【发布时间】:2013-01-25 17:23:00
【问题描述】:

我有一个 android 小部件,它每 10 分钟从服务器获取数据并将其显示在屏幕上。
我想为该小部件添加一个“刷新”按钮。
当用户单击该按钮时,我想运行从服务器获取信息的方法。
向应用程序中的按钮添加事件处理程序非常简单,但是我找不到小部件的示例。
我想获得一些关于向小部件中的按钮单击添加功能的帮助。

【问题讨论】:

标签: android android-widget


【解决方案1】:

这里还有一个应该有帮助的例子:

package com.automatic.widget;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;

public class Widget extends AppWidgetProvider {

    private static final String SYNC_CLICKED    = "automaticWidgetSyncButtonClick";

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        RemoteViews remoteViews;
        ComponentName watchWidget;

        remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        watchWidget = new ComponentName(context, Widget.class);

        remoteViews.setOnClickPendingIntent(R.id.sync_button, getPendingSelfIntent(context, SYNC_CLICKED));
        appWidgetManager.updateAppWidget(watchWidget, remoteViews);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        super.onReceive(context, intent);

        if (SYNC_CLICKED.equals(intent.getAction())) {

            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

            RemoteViews remoteViews;
            ComponentName watchWidget;

            remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
            watchWidget = new ComponentName(context, Widget.class);

            remoteViews.setTextViewText(R.id.sync_button, "TESTING");

            appWidgetManager.updateAppWidget(watchWidget, remoteViews);

        }
    }

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

【讨论】:

  • 感谢这么好的例子我已经使用了这个代码和我的应用程序工作正常......非常感谢
  • 非常感谢,这个答案帮助了我。 =)
  • 这段代码在没有与其他示例混淆的情况下工作 - 这是开发指南信息的一个极好的、简洁的示例 - 谢谢!
  • 关于PendingIntent.getBroadcast(context, 0, intent, 0) 的注释:如果您有多个小部件实例,这将导致错误。问题:当您单击任何小部件实例时,只有最后一个小部件会更新。解决方案:将 widgetId 传递给 getPendingSelfIntent 并试试这个:PendingIntent.getBroadcast(context, widgetId, intent, 0) Detailed description
【解决方案2】:

我发现了如何做到这一点。
在><receiver><intent-filter>标签中的AndroidManifest.xml文件中添加一个动作:

<action android:name="MY_PACKAGE_NAME.WIDGET_BUTTON" />

在提供者中添加一个与动作名称匹配的常量:

public static String WIDGET_BUTTON = "MY_PACKAGE_NAME.WIDGET_BUTTON";

onUpdate() 方法中添加与操作匹配的待处理意图:

Intent intent = new Intent(WIDGET_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.MY_BUTTON_ID, pendingIntent );

最后,在 onRecieve() 方法中,检查动作名称:

 if (WIDGET_BUTTON.equals(intent.getAction())) {
//your code here

    }

【讨论】:

  • 感谢@Sharon Haim Pour!
  • 我认为有趣的是,您可以(并且可能应该)使用显式意图而不是隐式意图。这意味着您不必在清单中定义操作,您应该像这样创建意图:Intent intent = new Intent(context, MyClass.class);
  • 谢谢!这个小部件的按钮事件监听器对我有很大帮助。现在,是时候进行实验了,我必须探索更多关于 Android 小部件制作的知识。
  • 此代码比上面的代码更好,因为它使用显式意图,这使得从配置活动中调用意图也变得更容易
  • 这是什么? views
【解决方案3】:

这是另一个具有以下好处的答案:

  • 它处理所有 App Widget 实例(用户可能在您的屏幕上有多个不同配置/尺寸的 Widget 实例)。所有实例的编码都是官方文档规定的。请参阅Guide > App Widgets > Using the AppWidgetProvider Class ,向下滚动到“ExampleAppWidgetProvider”的代码示例。
  • onReceive 中的主力代码实际上调用了onUpdate(这样可以减少代码重复)。
  • onUpdate(Context context) 中的代码是通用的,因此可以放入任何 AppWidgetProvider 子类中。

代码:

public class MyWidget extends AppWidgetProvider {

    private static final String ACTION_UPDATE_CLICK = 
              "com.example.myapp.action.UPDATE_CLICK";

    private static int mCount = 0;

    private static String getMessage() {
        return String.valueOf(mCount++);
    }

    private PendingIntent getPendingSelfIntent(Context context, String action) {
        // An explicit intent directed at the current class (the "self").
        Intent intent = new Intent(context, getClass());
        intent.setAction(action);
        return PendingIntent.getBroadcast(context, 0, intent, 0);
    }

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

        String message = getMessage();

        // Loop for every App Widget instance that belongs to this provider.
        // Noting, that is, a user might have multiple instances of the same
        // widget on
        // their home screen.
        for (int appWidgetID : appWidgetIds) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
                                                      R.layout.my_widget);

            remoteViews.setTextViewText(R.id.textView_output, message);
            remoteViews.setOnClickPendingIntent(R.id.button_update,
                                                getPendingSelfIntent(context,
                                                           ACTION_UPDATE_CLICK)
            );

            appWidgetManager.updateAppWidget(appWidgetID, remoteViews);

        }
    }

    /**
     * A general technique for calling the onUpdate method,
     * requiring only the context parameter.
     *
     * @author John Bentley, based on Android-er code.
     * @see <a href="http://android-er.blogspot.com
     * .au/2010/10/update-widget-in-onreceive-method.html">
     * Android-er > 2010-10-19 > Update Widget in onReceive() method</a>
     */
    private void onUpdate(Context context) {
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance
                (context);

        // Uses getClass().getName() rather than MyWidget.class.getName() for
        // portability into any App Widget Provider Class
        ComponentName thisAppWidgetComponentName =
                new ComponentName(context.getPackageName(),getClass().getName()
        );
        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
                thisAppWidgetComponentName);
        onUpdate(context, appWidgetManager, appWidgetIds);
    }

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

        if (ACTION_UPDATE_CLICK.equals(intent.getAction())) {
            onUpdate(context);
        }
    }

}

小部件看起来像这样

这建立在@Kels、@SharonHaimPour 和@Erti-ChrisEelmaa 的getPendingSelfIntent 工作之上。

它还建立在Android-er > 2010-10-19 > Update Widget in onReceive() method(不是我)的基础上,其中演示了如何在 App Widget 实例的基础上从 onReceive 调用 onUpdate。我将该代码通用化并将其包装在 callOnUpdate 中。

【讨论】:

    【解决方案4】:
    protected PendingIntent getPendingSelfIntent(Context context, String action) {
        Intent intent = new Intent(context, getClass());
        intent.setAction(action);
        return PendingIntent.getBroadcast(context, 0, intent, 0);
    }
    
    views.setOnClickPendingIntent(R.id.Timm, getPendingSelfIntent(context,
                                  "ham"));
    

    也喜欢网址:

    How to correctly handle click events on Widget

    如果您以其他方式解决了问题,请提供此作为答案

    【讨论】:

    • 我不明白把这段代码放在哪里。你在哪里调用函数的点击?
    【解决方案5】:

    pendingIntent中,我们还可以添加额外的属性appWidgetId以便稍后在onReceive中重用它来更新小部件点击的小部件实例

    class ExampleAppWidgetProvider : AppWidgetProvider() {
    
        override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray {
    
            appWidgetIds.forEach { appWidgetId ->
                Log.e("TAG", "onUpdate $appWidgetId")
                val pendingRefreshClickIntent: PendingIntent = Intent(context, javaClass).let {
                    it.action = ACTION_REFRESH_CLICK
                    it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
                    return@let PendingIntent.getBroadcast(
                        context,
                        appWidgetId, // click in all instances widget will work well (base on Alireza Mirian comment in the top answer)
                        it,
                        PendingIntent.FLAG_UPDATE_CURRENT
                    )
                }
    
                val views = RemoteViews(
                    context.packageName,
                    R.layout.example_appwidget
                )
                views.setOnClickPendingIntent(R.id.button_refresh, pendingRefreshClickIntent)
    
                appWidgetManager.updateAppWidget(appWidgetId, views)
            }
        }
    
        override fun onReceive(context: Context?, intent: Intent?) {
            super.onReceive(context, intent)
            Log.i("TAG", "onReceive " + intent?.action)
    
            if (intent?.action == ACTION_REFRESH_CLICK) {
                val appWidgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
                Log.i("TAG", "onReceive appWidgetId $appWidgetId")
    
                val appWidgetManager = AppWidgetManager.getInstance(context)
                val views = RemoteViews(context!!.packageName, R.layout.example_appwidget)
    
                views.setTextViewText(R.id.text_data, "a " + (Math.random() * 9).roundToInt())
                appWidgetManager.updateAppWidget(appWidgetId, views)
            }
        }
    
        companion object {
            private const val ACTION_REFRESH_CLICK =  "com.example.androidwidgetbuttonclick.action.ACTION_REFRESH_CLICK"
        }
    }
    

    小部件初始布局

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/text_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="AA"
            android:textSize="20sp" />
    
        <Button
            android:id="@+id/button_refresh"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Refresh" />
    </LinearLayout>
    

    【讨论】:

      【解决方案6】:

      我尝试了 Sharon Haim Pour above 建议的解决方案,但我在 AppWidgetProvider 类中的 onReceive() 方法从未在按钮按下时被调用。

      Intent intent = new Intent(WIDGET_BUTTON);
      PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
      PendingIntent.FLAG_UPDATE_CURRENT);
      views.setOnClickPendingIntent(R.id.MY_BUTTON_ID, pendingIntent );
      

      经过一番研究,我可以通过更新以下代码来解决问题:

      Intent intent = new Intent(context, MY_APPWIDGETPROVIDER_CLASS.class);
      intent.setAction(WIDGET_BUTTON);
      PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
      PendingIntent.FLAG_UPDATE_CURRENT);
      views.setOnClickPendingIntent(R.id.MY_BUTTON_ID, pendingIntent );
      

      别忘了在下面放:

      appWidgetManager.updateAppWidget(appWidgetId, views);
      

      【讨论】:

        【解决方案7】:

        与此处使用onReceive() 的其他答案不同,我发现在onUpdate() 中执行所有操作实际上要干净得多。

        Android 官方代码实验室Advanced Android 02.1: App widgets 提供了这个解决方案。 Java 中的示例代码。这里我介绍一下 Kotlin 中的解决方案。

        class MyAppWidgetProvider : AppWidgetProvider() {
        
            override fun onUpdate(
                context: Context?,
                appWidgetManager: AppWidgetManager?,
                appWidgetIds: IntArray?
            ) {
                appWidgetIds?.forEach { appWidgetId ->
                    val views = RemoteViews(
                        context?.packageName,
                        R.layout.appwidget
                    )
                    // Coroutine to perform background IO task.
                    GlobalScope.launch(Dispatchers.IO) {
                        // Suspend function.
                        val apiData = Api.retrofitService.getData()
                        updateWidgetUI(views, apiData)
                        context?.let {
                            views.setOnClickPendingIntent(
                                R.id.widget_button,
                                getUpdatePendingIntent(it, appWidgetId)
                            )
                        }
                        appWidgetManager?.updateAppWidget(appWidgetId, views)
                    }
                }
            }
        
            private fun updateWidgetUI(views: RemoteViews, apiData: ApiData){
                views.apply {
                    setTextViewText(R.id.widget_value_textview, apiData.value)
                    setTextViewText(
                        R.id.widget_last_updated_value_textview,
                        DateFormat.getTimeInstance(DateFormat.MEDIUM).format(Date())
                    )
                }
            }
        
            private fun getUpdatePendingIntent(context: Context, appWidgetId: Int): PendingIntent {
                val intent = Intent(context, MyAppWidgetProvider::class.java).also {
                    it.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
                    // It's very important to use intArrayOf instead of arrayOf,
                    // as a primitive int array is expected.
                    it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(appWidgetId))
                }
                // Set the immutability flag for Android 12.
                val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
                } else {
                    PendingIntent.FLAG_UPDATE_CURRENT
                }
                return PendingIntent.getBroadcast(
                    context,
                    appWidgetId,
                    intent,
                    flags
                )
            }
        // No need for onReceive().
        }
        

        这里的关键是使用内置的 AppWidgetManager.ACTION_APPWIDGET_UPDATE 动作而不是自定义动作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-03-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-30
          • 2015-08-17
          • 1970-01-01
          相关资源
          最近更新 更多