Android小部件 - 当按钮被点击时更新

3

你好。 我正在尝试创建一个小部件,它可以在屏幕上显示余额。 我创建了两个按钮并设置了监听器来确定哪个按钮被按下。

困难: 如何通过点击按钮更新数据或重新加载小部件以更新数据?

import android.app.AlarmManager;
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.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.DateFormat;
import java.util.Date;

public class EthWidgetProvider extends AppWidgetProvider {
    private static final String SYNC_CLICKED    = "automaticWidgetSyncButtonClick";
    private static final String SYNC_CLICKED2    = "automaticWidgetSyncButtonClick2";


    static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager, final int appWidgetId) {
        RequestSingleton.getInstance(context).fetchData(new VolleyCallback() {
            @Override
            public void onSuccessRequest(JSONArray result) {
                Log.i("Response", result.toString());
                String price = "";
                try {
                    JSONObject etherObject = result.getJSONObject(0);
                    price = etherObject.getString("price_usd");
                } catch (JSONException e) {
                    Log.e("JSONException", e.toString());
                }
                Log.i("Price", price);
                RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_widget);
                views.setTextViewText(R.id.gprs, "$" + price);

                String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
                views.setTextViewText(R.id.timeofday, currentDateTimeString);

                appWidgetManager.updateAppWidget(appWidgetId, views);
            }
        });
    }
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {


        RemoteViews remoteViews;
        ComponentName watchWidget;

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

        remoteViews.setOnClickPendingIntent(R.id.refresh, getPendingSelfIntent(context, SYNC_CLICKED));
        remoteViews.setOnClickPendingIntent(R.id.example_widget_button, getPendingSelfIntent(context, SYNC_CLICKED2));
        appWidgetManager.updateAppWidget(watchWidget, remoteViews);

        for(int appId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appId);
        }
    }

    @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.example_widget);
            watchWidget = new ComponentName(context, EthWidgetProvider.class);

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

            appWidgetManager.updateAppWidget(watchWidget, remoteViews);

        }

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

            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

            RemoteViews remoteViews;
            ComponentName watchWidget;

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

            remoteViews.setTextViewText(R.id.timeofday, "TESTING2");

            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);
    }
}

通过按下按钮1,我需要展示此活动。通过按下第二个按钮,您需要更新小部件,以便数据得到更新,也就是说需要再次向服务器发出请求并且数据会被更新。
非常感谢您的帮助。

请查看我对相关问题的解决方案此处 - makata
3个回答

2
我不理解你对广播的想法。
1. 广播不应该是内部类。 2. 广播应该扩展BroadcastReceiver类。 3. 广播应该在清单中定义。
因此,当你这样做时,你的接收器应该在按钮被点击后被调用。
但是,你想要一些东西来唤醒你的小部件。然后,Widget#onUpdate将获取更新的数据(你在小部件点击时下载的数据)并显示它们。 要请求更新你的小部件,你可以使用像这样的东西:
public static void updateAppWidget(@NonNull Context applicationContext,
        @NonNull Class<? extends AppWidgetProvider> widgetProviderClass) {
    final ComponentName component = new ComponentName(applicationContext, widgetProviderClass);
    final int[] widgetIds;
    try {
        widgetIds = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(component);
    } catch (RuntimeException re) {
        Log.d(re, "Unable to obtain widget IDs.");
        return;
    }

    if (widgetIds.length == 0) {
        Log.d("There is no widget to be updated.");
        return;
    }

    final Intent intent = new Intent(applicationContext, widgetProviderClass);
    intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);

    applicationContext.sendBroadcast(intent);
}

所以总结一下:
在小部件设置视图中,监听器和填充上次已知数据。
在监听器广播中,打开Activity或运行某些下载。
不要忘记BroadcastReceivers正在UI线程上运行;使用goAsync()方法。
下载的数据应存储在小部件可以获取的某个位置。
调用提到的函数来触发小部件更新。
我相信,这是一个相当具体和复杂的主题,与Android的其他部分不同,因此请随时询问我可能错过的任何内容 :)

我的目标是创建一个小部件,它将显示特定资源的余额。小部件在启动时发送请求并显示接收到的响应。小部件中会有一个按钮,通过向服务器发送请求来更新数据。我根据在网络上找到的视频课程和记录编写了这段代码。如果您能帮助我完成这个任务,我将非常感激。 - Ahmadjoni Muhamad
@AhmadjoniMuhamad 我认为这个概念上是错误的想法。小部件是非常敏感的东西。而更新按钮也是小部件使用的反模式。而持续不断的更新也是错误的。 所以对我来说,我建议您使用SyncAdapter、一些持久性存储,并通过小部件从存储中获取数据并显示它们。 然后您可以完全省略按钮。 请参见:https://developer.android.com/training/sync-adapters/creating-sync-adapter - l0v3
为了避免服务器压力过大,我决定在合适的时间手动更新用户数据。你能帮我吗? - Ahmadjoni Muhamad
那你是想把余额转到用户手上,而不是服务器?你为什么还要创建小部件?只要在打开时与服务器同步的应用程序就可以达到相同的目的。这样还只需要用户点击一次即可,而且对用户没有任何电池影响。小部件通常用于快速操作或作为预览,不需要任何操作。 - l0v3
为了让平衡器始终显示在屏幕上,我决定创建一个小部件。我知道您有创建小部件并在网络上工作的经验,如果可以帮忙就太好了。不要考虑这是否正确。只是请帮帮忙,我会非常感激您的帮助。 - Ahmadjoni Muhamad
显示剩余2条评论

1

有一份非常有用的官方指南,名为“构建应用程序小部件”:(https://developer.android.com/guide/topics/appwidgets)

请仔细阅读此页面,以便了解相关概念,并针对您的问题重点关注AppWidgetProvider部分。虽然在该部分中,他们使用了PendingIntent.getActivity(),但您应该使用Service作为PendingIntent.getService(),以便在按钮单击时调用您的Service,然后由Service更新您的小部件。

基本上,您需要执行以下操作:

  1. Create a Service

  2. Create a PendingIntent:

    Intent intent = new Intent(context, YourService.class);
    PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
    
  3. Set the onClickPendingIntent listener on your RemoteViews object like this:

    views.setOnClickPendingIntent(R.id.button, pendingIntent);
    

0

调用Activity:

remoteViews.setOnClickPendingIntent( R.id.button1, PendingIntent.getActivity(context, 0, new Intent( context, Main.class), 0) );

从另一个类更新小部件:

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance( context );
int ids[] = appWidgetManager.getAppWidgetIds( componentName );
for( int id : ids ){
    //update textView here
    appWidgetManager.updateAppWidget( id, views);
}

谢谢你的回答。我需要在小部件本身的按钮上单击,而不是从另一个类中更新小部件。在这种情况下该怎么办?你的代码在我的情况下没有起作用。 - Ahmadjoni Muhamad
很简单:您可以调用服务来更新小部件。然后使用PendingIntent.getService()。 - Style-7
我尝试过这个,但是没成功,我可以把我的项目代码发给你看一下吗? - Ahmadjoni Muhamad

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