多个小部件实例 - 需要分别更新每个实例

Multiple widget instances - need to update each instance separately

我为它创建了简单的示例小部件和简单的配置 activity。我需要在用户单击 widets 后为每个小部件实例调用配置 activity。每个实例都应该有单独的设置,点击后,配置 activity 应该填充从用于调用配置对话框的这个特定实例获得的设置,只允许更改这个特定实例的设置(其他现有的小部件实例应该保持不变) .

所以,我的小部件代码:

package com.example.michal.widgettest2;

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


/**
 * Implementation of App Widget functionality.
 * App Widget Configuration implemented in {@link MyWidgetConfigureActivity MyWidgetConfigureActivity}
 */
public class MyWidget extends AppWidgetProvider
{

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
    {
        // There may be multiple widgets active, so update all of them
        final int N = appWidgetIds.length;
        for (int i = 0; i < N; i++)
        {
            int appWidgetId = appWidgetIds[i];

            Intent intent = new Intent(context, MyWidgetConfigureActivity.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

            RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.my_widget);
            views.setOnClickPendingIntent(R.id.widget_container, pendingIntent);

            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds)
    {
        // When the user deletes the widget, delete the preference associated with it.
        final int N = appWidgetIds.length;
        for (int i = 0; i < N; i++)
        {
            MyWidgetConfigureActivity.deleteTitlePref(context, appWidgetIds[i]);
        }
    }

    @Override
    public void onEnabled(Context context)
    {
        // Enter relevant functionality for when the first widget is created
    }

    @Override
    public void onDisabled(Context context)
    {
        // Enter relevant functionality for when the last widget is disabled
    }

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId)
    {
        CharSequence widgetText = MyWidgetConfigureActivity.loadTitlePref(context, appWidgetId);
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);
        Intent intent = new Intent(context, MyWidgetConfigureActivity.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
        views.setOnClickPendingIntent(R.id.widget_container, pendingIntent);
        appWidgetManager.updateAppWidget(appWidgetId, views);

    }
}

我的配置activity代码:

package com.example.michal.widgettest2;

import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;


/**
 * The configuration screen for the {@link MyWidget MyWidget} AppWidget.
 */
public class MyWidgetConfigureActivity extends Activity
{

    int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    EditText mAppWidgetText;
    private static final String PREFS_NAME = "com.example.michal.widgettest2.MyWidget";
    private static final String PREF_PREFIX_KEY = "appwidget_";

    public MyWidgetConfigureActivity()
    {
        super();
    }

    @Override
    public void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);

        // Set the result to CANCELED.  This will cause the widget host to cancel
        // out of the widget placement if the user presses the back button.
       setResult(RESULT_CANCELED);

        setContentView(R.layout.my_widget_configure);
        mAppWidgetText = (EditText) findViewById(R.id.appwidget_text);
        findViewById(R.id.add_button).setOnClickListener(mOnClickListener);

        // Find the widget id from the intent.
        Intent intent = getIntent();
        Bundle extras = intent.getExtras();
        if (extras != null)
        {
            mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        }

        // If this activity was started with an intent without an app widget ID, finish with an error.
        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID)
        {
            finish();
            return;
        }

        mAppWidgetText.setText(loadTitlePref(MyWidgetConfigureActivity.this, mAppWidgetId));
    }

    View.OnClickListener mOnClickListener = new View.OnClickListener()
    {
        public void onClick(View v)
        {
            final Context context = MyWidgetConfigureActivity.this;

            // When the button is clicked, store the string locally
            String widgetText = mAppWidgetText.getText().toString();
            saveTitlePref(context, mAppWidgetId, widgetText);

            // It is the responsibility of the configuration activity to update the app widget
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            MyWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);

            // Make sure we pass back the original appWidgetId
            Intent resultValue = new Intent();
            resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
            setResult(RESULT_OK, resultValue);
            finish();
        }
    };

    // Write the prefix to the SharedPreferences object for this widget
    static void saveTitlePref(Context context, int appWidgetId, String text)
    {
        SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
        prefs.putString(PREF_PREFIX_KEY + appWidgetId, text);
        prefs.commit();
    }

    // Read the prefix from the SharedPreferences object for this widget.
    // If there is no preference saved, get the default from a resource
    static String loadTitlePref(Context context, int appWidgetId)
    {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
        String titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null);
        if (titleValue != null)
        {
            return titleValue;
        } else
        {
            return context.getString(R.string.appwidget_text);
        }
    }

    static void deleteTitlePref(Context context, int appWidgetId)
    {
        SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
        prefs.remove(PREF_PREFIX_KEY + appWidgetId);
        prefs.commit();
    }
}

不幸的是,它不能正常工作。配置 activity 上显示的设置不是来自用于调用配置 activity 的小部件,更新会转到不正确的小部件实例。

可能是我遗漏了一些东西,你能提供任何提示吗?

[编辑] 更新代码:

 @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
    {
        // There may be multiple widgets active, so update all of them
        final int N = appWidgetIds.length;
        for (int i = 0; i < N; i++)
        {
            int appWidgetId = appWidgetIds[i];

            //Intent intent = new Intent(context, MyWidgetConfigureActivity.class);
            //intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
            //PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

            PendingIntent pendingIntent = getUniquePI(context,null,appWidgetId);

            RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.my_widget);
            views.setOnClickPendingIntent(R.id.widget_container, pendingIntent);

            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }


static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId)
    {
        CharSequence widgetText = MyWidgetConfigureActivity.loadTitlePref(context, appWidgetId);
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);


        //Intent intent = new Intent(context, MyWidgetConfigureActivity.class);
        //intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
        //PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
        PendingIntent pendingIntent = getUniquePI(context,null,appWidgetId);

        views.setOnClickPendingIntent(R.id.widget_container, pendingIntent);
        appWidgetManager.updateAppWidget(appWidgetId, views);

    }

现在我的小部件不响应点击事件(配置 activity 未显示)。怎么了?

逐步使用下面的示例。

步骤 1

创建服务UpdateWidgetService.java

public class UpdateWidgetService extends Service {
    public UpdateWidgetService() {
    }
    public static final String SKIP = "skip";
    public static final String OPENAPP = "openapp";

    @Override
    public int onStartCommand(Intent pIntent, int flags, int startId) {

        String command = pIntent.getAction();

        int appWidgetId = pIntent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
        RemoteViews remoteView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.widget);
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());

            if(command.equals(SKIP)){
                /**Do the SKIP click work here*/
                return START_NOT_STICKY;
            }else if(command.equals(OPENAPP)){
             /**Do the OPENAPP click work here*/
                Intent mAct=new Intent(this, OPENAPP.class);
                mAct.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(mAct);
                return START_NOT_STICKY;
            }

            remoteView.setTextViewText(R.id.widet_ibtn_skip, "MyTextHere");
            remoteView.setTextViewText(R.id.widget_tv_title, "Title");

            remoteView.setOnClickPendingIntent(R.id.widet_ibtn_skip,WallWidgetProvider.makeControlPendingIntent(getApplicationContext(),SKIP,appWidgetId));
            remoteView.setOnClickPendingIntent(R.id.widget_tv_title,WallWidgetProvider.makeControlPendingIntent(getApplicationContext(),OPENAPP,appWidgetId));

            appWidgetManager.updateAppWidget(appWidgetId, remoteView);


        return super.onStartCommand(pIntent, flags, startId);
    }


    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

步骤 2 创建 WidgetProvider 为 WallWidgetProvider.java

public class WallWidgetProvider extends AppWidgetProvider{

    public static final int UPDATE_RATE = 1000;

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        for (int appWidgetId : appWidgetIds) {
            setCounterAlarm(context, appWidgetId, -1);
        }
        super.onDeleted(context, appWidgetIds);
    }

    @Override
    public void onDisabled(Context context) {
        context.stopService(new Intent(context,UpdateWidgetService.class));
        super.onDisabled(context);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        PrefrenceSettings ps=new PrefrenceSettings(context);

        for (int appWidgetId : appWidgetIds) {
                setCounterAlarm(context, appWidgetId, UPDATE_RATE);
            }

        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

    public static void setCounterAlarm(Context context, int appWidgetId, int updateRate) {
        PendingIntent newPending = makeControlPendingIntent(context,UpdateWidgetService.UPDATE,appWidgetId);
        AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        if (updateRate >= 0) {
            alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), updateRate, newPending);
        } else {
            // on a negative updateRate stop the refreshing
            alarms.cancel(newPending);
        }
    }

    public static PendingIntent makeControlPendingIntent(Context context, String command, int appWidgetId) {
        Intent active = new Intent(context,UpdateWidgetService.class);
        active.setAction(command);
        active.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        //this Uri data is to make the PendingIntent unique, so it wont be updated by FLAG_UPDATE_CURRENT
        //so if there are multiple widget instances they wont override each other
        Uri data = Uri.withAppendedPath(Uri.parse("widget://widget/id/#"+command+appWidgetId), String.valueOf(appWidgetId));
        active.setData(data);
        return(PendingIntent.getService(context, 0, active, PendingIntent.FLAG_UPDATE_CURRENT));
    }

}

希望你应该知道如何在你的项目中使用这两个 class,这些 class 会生成每个 Widget 的唯一标识。