使用集合更新应用小部件的问题

Posted

技术标签:

【中文标题】使用集合更新应用小部件的问题【英文标题】:Update issue of an app widget using collection 【发布时间】:2013-03-27 14:54:32 【问题描述】:

我为我的应用创建了一个使用集合的应用小部件,该小部件显示该特定日期的日期和项目列表。一切正常,并且小部件正在根据需要进行更新,但有时通过单击下一个和上一个按钮更改小部件中的日期时会发生什么情况,列表未刷新意味着项目未在该特定日期更新。这种行为是随机的,它有时只发生。那么为什么会发生这种情况,我的代码有什么问题。

我使用过的代码:

WidgetProvider.class

public class WidgetProvider extends AppWidgetProvider 
   
    private ThemeManager    m_ThemeManagerObject;

    private static String   WIDGET_NEXT_BUTTON = "in.test.widgetApp.WIDGET_NEXT_BUTTON";

    private static String   WIDGET_PREV_BUTTON = "in.test.widgetApp.WIDGET_PREV_BUTTON";    

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

        // Set Date to current Date
        NoteManager.getSingletonObject().setWidgetToCurrentDate();

        // Code to update the widget by current date 
        updateAppWidget(context, AppWidgetManager.getInstance(context), appWidgetIds);
       
    
    @Override
    public void onReceive(Context context, Intent intent) 
               
        super.onReceive(context, intent);

        int numOfDays = 1;
        
        ComponentName thisWidget = new ComponentName(context, WidgetProvider.class);
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
        
        if (intent.getAction().equals(WIDGET_NEXT_BUTTON)) 
        
            // Increase no of days by one
            // Update the widget by new date 
            NoteManager.getSingletonObject().setWidgetDate(numOfDays);          
            updateAppWidget(context, AppWidgetManager.getInstance(context), appWidgetIds);
           
        else if (intent.getAction().equals(WIDGET_PREV_BUTTON)) 
        
            // Decrease no of days by one
            // Update the widget by new date 
            NoteManager.getSingletonObject().setWidgetDate(-numOfDays);         
            updateAppWidget(context, AppWidgetManager.getInstance(context), appWidgetIds);
                           
    

        public void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)

    // Get the folder path of all-page-view
    ContextWrapper cw = new ContextWrapper(context.getApplicationContext());
    File customDirectoryPath = cw.getDir(Utilities.CUSTOM_DIRECTORY_NAME_PREFIX, Context.MODE_PRIVATE);
    File allPageDirectoryPath = new File(customDirectoryPath.getPath() + "/" + Utilities.All_PAGE_DIRECTORY_NAME_PREFIX); 

    if (!(allPageDirectoryPath.exists()))
        allPageDirectoryPath.mkdirs();

    // Create an singleton object of ThemeManager class
    m_ThemeManagerObject = ThemeManager.getSingletonObject();
    m_ThemeManagerObject.readTheme(allPageDirectoryPath.getPath());

    // Create an instance of SimpleDateFormat class
    SimpleDateFormat dateFormater = new SimpleDateFormat("dd-MMM, EEE", Locale.US);

    /* loop through all widget instances */
    for (int widgetId : appWidgetIds) 
     
        // Create an instance of remote view class
        RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.widget_list);       
        Intent svcIntent = new Intent(context, WidgetService.class);
        svcIntent.setData(Uri.fromParts("content", String.valueOf(widgetId), null));        
        remoteView.setRemoteAdapter(R.id.widget_list, svcIntent);   

        // Show day, month and week day inside the widget
        remoteView.setTextViewText(R.id.txt_date, dateFormater.format(NoteManager.getSingletonObject().getWidgetDate().getTime()));

        // If the list is empty. Show empty widget with juswrite-icon & empty text to the user          
        remoteView.setEmptyView(R.id.widget_list, R.id.widget_empty_text);              

        // On click of next button
        Intent nextButtonIntent = new Intent(WIDGET_NEXT_BUTTON);
        /* use widgetId as second parameter - it helped me to better address particular widget instance */
        PendingIntent nextButtonPendingIntent = PendingIntent.getBroadcast(context, widgetId, nextButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.btn_next_month, nextButtonPendingIntent);
        remoteView.setInt(R.id.btn_next_month, "setBackgroundResource", m_ThemeManagerObject.getNextButtonBgImage());

        // On click of previous button
        Intent prevButtonIntent = new Intent(WIDGET_PREV_BUTTON);
        /* use widgetId as second parameter - same as above */
        PendingIntent prevButtonPendingIntent = PendingIntent.getBroadcast(context, widgetId, prevButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.btn_prev_month, prevButtonPendingIntent);
        remoteView.setInt(R.id.btn_prev_month, "setBackgroundResource", m_ThemeManagerObject.getPrevButtonBgImage());

        // Open application on click of app widget
        Intent clickIntent = new Intent(context, AllPageViewActivity.class);
        PendingIntent clickPI = PendingIntent.getActivity(context, 0,clickIntent,PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.widget_empty_text, clickPI);
        remoteView.setOnClickPendingIntent(R.id.txt_date, clickPI);
        
        /* update one widget instance at a time*/
        appWidgetManager.updateAppWidget(widgetId, remoteView);
    


WidgetService.class

public class WidgetService extends RemoteViewsService 

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) 
    
        return(new WidgetDisplay(this.getApplicationContext(), intent));
    

WidgetDisplay.class

public class WidgetDisplay implements RemoteViewsService.RemoteViewsFactory

    private File m_CustomDirectoryPath, m_AllPageDirectoryPath;
    
    private NoteManager m_NoteManagerObject;
    
    private ThemeManager m_ThemeManagerObject;
    
    private ArrayList<String>   m_AlarmItemNameArrayList;
    
    private ArrayList<Integer>  m_ItemIndexArray;
    
    private Context ctxt=null;
    
    int appWidgetId;
    
    Bitmap canvasBackground;

    public WidgetDisplay(Context ctxt, Intent intent) 
    
        this.ctxt=ctxt;
        
        appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
        
        setImageInView(this.ctxt);

    
    
    private void setImageInView(Context context) 
    
        ContextWrapper cw = new ContextWrapper(ctxt.getApplicationContext());
        m_CustomDirectoryPath = cw.getDir(Utilities.CUSTOM_DIRECTORY_NAME_PREFIX, Context.MODE_PRIVATE);
        m_AllPageDirectoryPath = new File(m_CustomDirectoryPath.getPath() + "/" + Utilities.All_PAGE_DIRECTORY_NAME_PREFIX); 

        m_NoteManagerObject = NoteManager.getSingletonObject();
        m_ThemeManagerObject = ThemeManager.getSingletonObject();
        
        m_NoteManagerObject.readSettings(m_AllPageDirectoryPath.getPath());
        m_NoteManagerObject.readAllPageChangesFromFile(m_AllPageDirectoryPath.getPath());
        m_NoteManagerObject.readAlarmFromFile(m_AllPageDirectoryPath.getPath());
        m_ThemeManagerObject.readTheme(m_AllPageDirectoryPath.getPath());

        m_AlarmItemNameArrayList = new ArrayList<String>(m_NoteManagerObject.getAlarmCount());
        m_ItemIndexArray = new ArrayList<Integer>(m_NoteManagerObject.getAlarmCount());

        SimpleDateFormat sdFormatter = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);      
        String selectedDate = sdFormatter.format(m_NoteManagerObject.getWidgetDate());
        
        for(int i=0; i<m_NoteManagerObject.getAlarmCount(); i++)
        
            String ArrayDate = sdFormatter.format(m_NoteManagerObject.getAlarmTime(i));         
            if(selectedDate.equals(ArrayDate))
            
                File noteDirectoryPath = new File(m_CustomDirectoryPath.getPath() + "/" + m_NoteManagerObject.getAlarmFolder(i));
                m_AlarmItemNameArrayList.add(noteDirectoryPath.getPath() + "/" + m_NoteManagerObject.getAlarmItem(i));

                m_ItemIndexArray.add(i);
            
        
    

    @Override
    public int getCount() 
    
        return(m_AlarmItemNameArrayList.size());
    
    
    @Override
    public RemoteViews getViewAt(int position) 
           
        new ImageLoaderTask(position).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

        // Set combine image to the image view using remote view instance
        RemoteViews remoteView = new RemoteViews(ctxt.getPackageName(), R.layout.widget_list_item);
        remoteView.setImageViewBitmap(R.id.image_view, canvasBackground);
        
        // Set time text view using remote view instance
        SimpleDateFormat timeFormater;
        
        if(m_NoteManagerObject.get24HourFormat())
        
            timeFormater = new SimpleDateFormat("HH:mm", Locale.US);
        
        else
        
            timeFormater = new SimpleDateFormat("hh:mm a", Locale.US );
        
        
        // Show time on the top of each image view
        String time = timeFormater.format(m_NoteManagerObject.getAlarmTime(m_ItemIndexArray.get(position)));            
        remoteView.setTextViewText(R.id.text_alarm_time,  time);        
                
        Intent clickIntent = new Intent(ctxt, AllPageViewActivity.class);
        PendingIntent clickPI=PendingIntent.getActivity(ctxt, 0,clickIntent,PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.image_view, clickPI);

        return(remoteView);
    

    class ImageLoaderTask extends AsyncTask<URL, Integer, Long>
    
        private int position;
        
        ImageLoaderTask(int position)
        
            this.position = position;
        

        @Override
        protected void onPreExecute()
        
            // Get foreground and background image
            Bitmap bitmapImage = BitmapFactory.decodeFile(m_AlarmItemNameArrayList.get(position)).copy(Bitmap.Config.ARGB_8888, true);
            canvasBackground = BitmapFactory.decodeResource(ctxt.getResources(), m_ThemeManagerObject.getWidgetListItemBgImage(m_ItemIndexArray.get(position), bitmapImage)).copy(Bitmap.Config.ARGB_8888, true);
            
            // Scaled foreground image and combine with the background image
            bitmapImage = Bitmap.createScaledBitmap(bitmapImage, 380, bitmapImage.getHeight() / 2, true);               
            Canvas comboImage = new Canvas(canvasBackground);
            comboImage.drawBitmap(bitmapImage, 0f, 0f, null);
        

        @Override
        protected Long doInBackground(URL... urls)
        
            return null;
        

        @Override
        protected void onProgressUpdate(Integer... progress)
        

        

        @Override
        protected void onPostExecute(Long result)
        

        
    
    
    @Override
    public void onCreate() 
    

    @Override
    public void onDestroy()
    
    
    @Override
    public RemoteViews getLoadingView()
    
        return(null);
    

    @Override
    public int getViewTypeCount()
        return(1);
    

    @Override
    public long getItemId(int position)
        return(position);
    

    @Override
    public boolean hasStableIds()
        return(true);
    

    @Override
    public void onDataSetChanged()
    

【问题讨论】:

任何人都知道这个答案的线索 经过大量搜索,我终于得到了答案。实际上我必须调用 appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list); onReceive() 方法。我已经发布了我更新的代码作为这个问题的答案以供将来使用。 【参考方案1】:

WidgetProvider.class

public class WidgetProvider extends AppWidgetProvider 
   
    private NoteManager         m_NoteManagerObject;

    private ThemeManager        m_ThemeManagerObject;

    private SimpleDateFormat    m_DateFormater;

    private static String       WIDGET_NEXT_BUTTON = "in.test.widgetApp.WIDGET_NEXT_BUTTON";

    private static String       WIDGET_PREV_BUTTON = "in.test.widgetApp.WIDGET_PREV_BUTTON";    

    public WidgetProvider()
    
        // Create an singleton object of NoteManager class
        m_NoteManagerObject = NoteManager.getSingletonObject();
        // Create an singleton object of ThemeManager class
        m_ThemeManagerObject = ThemeManager.getSingletonObject();
        // Create an instance of SimpleDateFormat class
        m_DateFormater = new SimpleDateFormat("dd-MMM, EEE", Locale.US);
    

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

        // Set Date to current Date
        m_NoteManagerObject.setWidgetToCurrentDate();

        // Get the folder path of all-page-view
        ContextWrapper cw = new ContextWrapper(context.getApplicationContext());
        File customDirectoryPath = cw.getDir(Utilities.CUSTOM_DIRECTORY_NAME_PREFIX, Context.MODE_PRIVATE);
        File allPageDirectoryPath = new File(customDirectoryPath.getPath() + "/" + Utilities.All_PAGE_DIRECTORY_NAME_PREFIX); 

        if (!(allPageDirectoryPath.exists()))
            allPageDirectoryPath.mkdirs();

        m_ThemeManagerObject.readTheme(allPageDirectoryPath.getPath());     

        // Set up the intent that starts the WidgetService, which will
        // provide the views for this collection.   
        // When intents are compared, the extras are ignored, so we need to embed the extras
        // into the data so that the extras will not be ignored.
        Intent intent  = new Intent(context, WidgetService.class);
        intent.setData(Uri.fromParts("content", String.valueOf(appWidgetIds), null));

        // Instantiate the RemoteViews object for the App Widget layout.
        // Set up the RemoteViews object to use a RemoteViews adapter. 
        // This adapter connects to a RemoteViewsService  through the specified intent.
        // This is how you populate the data.
        RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.widget_list);           
        remoteView.setRemoteAdapter(R.id.widget_list, intent);  

        // Show day, month and week day inside the widget
        remoteView.setTextViewText(R.id.txt_date, m_DateFormater.format(m_NoteManagerObject.getWidgetDate().getTime()));

        // The empty view is displayed when the collection has no items.        
        remoteView.setEmptyView(R.id.widget_list, R.id.widget_empty_text);              

        // On click of next button
        // This section makes it possible for items to have individualized behavior.
        // Set the action for the intent.
        // When the user touches a particular view, it will have the effect of
        // broadcasting ACTION.
        Intent nextButtonIntent = new Intent(context, WidgetProvider.class);            
        nextButtonIntent.setAction(WIDGET_NEXT_BUTTON);
        PendingIntent nextButtonPendingIntent = PendingIntent.getBroadcast(context, 0, nextButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.btn_next_month, nextButtonPendingIntent);
        remoteView.setInt(R.id.btn_next_month, "setBackgroundResource", m_ThemeManagerObject.getNextButtonBgImage());

        // On click of previous button
        // This section makes it possible for items to have individualized behavior.
        // Set the action for the intent.
        // When the user touches a particular view, it will have the effect of
        // broadcasting ACTION.
        Intent prevButtonIntent = new Intent(context, WidgetProvider.class);
        prevButtonIntent.setAction(WIDGET_PREV_BUTTON);
        PendingIntent prevButtonPendingIntent = PendingIntent.getBroadcast(context, 0, prevButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.btn_prev_month, prevButtonPendingIntent);
        remoteView.setInt(R.id.btn_prev_month, "setBackgroundResource", m_ThemeManagerObject.getPrevButtonBgImage());

        // Open application on click of app widget
        Intent clickIntent = new Intent(context, AllPageViewActivity.class);
        PendingIntent clickPI = PendingIntent.getActivity(context, 0,clickIntent,PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.widget_empty_text, clickPI);
        remoteView.setOnClickPendingIntent(R.id.txt_date, clickPI); 

        appWidgetManager.updateAppWidget(appWidgetIds, remoteView); 
       

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

        int numOfDays = 1;

        ComponentName thisWidget = new ComponentName(context, WidgetProvider.class);
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

        if (intent.getAction().equals(WIDGET_NEXT_BUTTON)) 
        
            // Increase no of days by one
            m_NoteManagerObject.setWidgetDate(numOfDays);

            // Update remote view
            RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.widget_list);               
            remoteView.setTextViewText(R.id.txt_date, m_DateFormater.format(m_NoteManagerObject.getWidgetDate().getTime()));
            appWidgetManager.updateAppWidget(appWidgetIds, remoteView); 

            // Update list content of the widget
           // This will call onDataSetChanged() method of WidgetDisplay class
            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);
           
        else if (intent.getAction().equals(WIDGET_PREV_BUTTON)) 
        
            // Decrease no of days by one
            m_NoteManagerObject.setWidgetDate(-numOfDays);  

            // Update remote view
            RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.widget_list);               
            remoteView.setTextViewText(R.id.txt_date, m_DateFormater.format(m_NoteManagerObject.getWidgetDate().getTime()));
            appWidgetManager.updateAppWidget(appWidgetIds, remoteView); 

            // Update list content of the widget
              // This will call onDataSetChanged() method of WidgetDisplay class
            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);
                           
    

【讨论】:

对您自己的问题的出色回答,赞!你为我节省了大量研究时间;)【参考方案2】:

您可以尝试在您的 nextButtonPendingIntentprevButtonPendingIntent 待定意图中将 PendingIntent.FLAG_UPDATE_CURRENT 更改为 PendingIntent.FLAG_CANCEL_CURRENT 会有所帮助。

【讨论】:

它已经在 nextButtonPendingIntent 和 prevButtonPendingIntent 中设置为 PendingIntent.FLAG_UPDATE_CURRENT。 我在原始答案中犯了错误(我在粘贴时切换了标志的顺序)。立即编辑。 不工作有时它会更新列表,但有时它不会,但日期会发生变化,这意味着点击按钮,从 onReceive() 方法转到 updateAppWidget(),但从那里它无法调用 WidgetService 类。结果它将无法调用 WidgetDisplay 类并且数据没有更新,但有时它就像一个魅力。 所以在为下一个和上一个按钮设置 pendingIntents 之前它会中断。我觉得可疑的一件事是: svcIntent.setData(Uri.fromParts("content", String.valueOf(appWidgetIds), null));执行 String.valueOf(int[]) 会产生类似“[I@142bece”的东西,也许你应该更新你的 updateAppWidget 方法来迭代 appWidgetIds 并为每个 single 执行它的逻辑 小部件实例(设置 pendingIntents 等)。顺便说一句,我发现在 PendingIntent.getBroadcast(...) 调用中使用当前的 widgetId 作为第二个参数而不是 0 是个好主意。 你能告诉我一些代码行吗,实际上我无法得到我需要在我的代码中更改的内容。【参考方案3】:

当您在updateAppWidget 中创建PendingIntents 时,为每个小部件创建以下两个:

PendingIntent nextButtonPendingIntent = PendingIntent.getBroadcast(context, widgetId, nextButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent prevButtonPendingIntent = PendingIntent.getBroadcast(context, widgetId, prevButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);

对于两者,您将第二个参数设置为 widgetId。 PendingIntent 系统不会查看 Intent 参数是否不同。 (事实上​​,它绝对不应该这样做——否则你无法将现有的 PendingIntent 更新为新的 Intent。)这意味着 nextButtonPendingIntentprevButtonPendingIntent 最终结果相同。

解决方案是将已知的、不同的数字放入该参数中,并将其他有用的信息(如小部件 ID)放入意图中:

nextButtonIntent.putExtra("widget_id", widgetId);

并在onReceive():中检索它

int widgetId = intent.getIntExtra("widget_id");

【讨论】:

如果您有时间查看我的问题,我也会遇到类似的问题:我有点茫然***.com/questions/25944639/… 我已经回答过了,希望对你有帮助。

以上是关于使用集合更新应用小部件的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何创建具有配置活动的应用小部件,并首次对其进行更新?

使用应用程序更新 ListView 小部件

应用更新后小部件消失了

Android 应用小部件未更新

一些第 3 方小部件在应用程序升级后停止更新

在 Android 中更新多个应用小部件