Android Widget在方向更改时被破坏

时间:2014-04-21 13:31:54

标签: android android-widget super

应用程序的快速说明。这是一个简单的小部件,其中4个值通过SharedPreferences保存。唯一的活动视图是设置首选项的位置。完成后,将保存变量,并更新窗口小部件。小部件每90,000ms更新一次。这一切都很好。我遇到的问题是方向改变时。发生这种情况时,窗口小部件将被销毁,然后重新制作。我得到的是原始XML,其中变量Text fields设置为default。它会保持这样,直到下一次手动或自动更新。

修改 这个问题现在已大大改变了。我得到的两个回答指出了一些错误的事情。阅读所有评论,看看所有这些。在最后一次尝试(尝试在清单中实现标记)之后,我得到了更糟糕的结果。该程序根本无法运行。

我在提出这个想法的同一个答案中说了最后一句话。我的onUpdate()一定有问题。以下是整个班级的代码:

public class WatchWidget extends AppWidgetProvider
{

    public void onCreate(Context context, Bundle savedInstanceState) 
    {

        RemoteViews remoteViews;
        remoteViews = new RemoteViews( context.getPackageName(), R.layout.main );
        remoteViews.setTextViewText( R.id.widget_elapsedtime, "Time");

    }

    @Override
    public void onUpdate( Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
    {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        RemoteViews remoteViews;
        ComponentName watchWidget;
        DateFormat format = SimpleDateFormat.getTimeInstance( SimpleDateFormat.MEDIUM, Locale.getDefault() );

        remoteViews = new RemoteViews( context.getPackageName(), R.layout.main );
        watchWidget = new ComponentName( context, WatchWidget.class );
        //remoteViews.setTextViewText( R.id.widget_textview, "" + format.format( new Date()));

        //widget_date
        Calendar c = Calendar.getInstance();
        SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
        String formattedDate = df.format(c.getTime());
        //remoteViews.setTextViewText( R.id.widget_date, formattedDate); 

        SharedPreferences prefs = context.getSharedPreferences("qsPrefs", context.MODE_PRIVATE);
        String sdayTextEdit = prefs.getString("dayTextEdit", "23");
        //String sdayTextEdit ="23";
        String smonthTextEdit = prefs.getString("monthTextEdit", "3");
        String syearTextEdit = prefs.getString("yearTextEdit", "2014");
        String scostTextEdit = prefs.getString("costTextEdit", "8.5");
        String spacksTextEdit = prefs.getString("packsTextEdit", ".95");


        //Starting day
        Calendar pastdate = Calendar.getInstance();
        //int inputDay = 25;
        int inputDay = Integer.valueOf(sdayTextEdit);
        int inputMonth = 2;  //starts at 0
        inputMonth = Integer.valueOf(smonthTextEdit);
        int inputMonthAdj = inputMonth-1;
        int inputYear = 2014;
        inputYear = Integer.valueOf(syearTextEdit);
        pastdate.set(Calendar.DAY_OF_MONTH, inputDay);
        pastdate.set(Calendar.MONTH, inputMonthAdj);
        pastdate.set(Calendar.YEAR, inputYear);
        double packsPer = .95;
        packsPer = Double.parseDouble(spacksTextEdit.toString());
        double perPack = 8.57;
        perPack = Double.parseDouble(scostTextEdit.toString());

        inputMonthAdj = inputMonth;
        remoteViews.setTextViewText( R.id.widget_date, "From:  " + inputMonthAdj +"/"+ inputDay +"/"+ inputYear );

        SfinalDate = "From:  " + inputMonthAdj +"/"+ inputDay +"/"+ inputYear;


        Calendar today = Calendar.getInstance();
        DecimalFormat dform = new DecimalFormat("0.00");

        long dateDiff = today.getTimeInMillis() - pastdate.getTimeInMillis();

        long resultDays = dateDiff / (24*60*60*1000);
        remoteViews.setTextViewText( R.id.widget_elapsedtime, ""+ resultDays);

        double savedmoney = packsPer * perPack * resultDays;
        String finalMoney = dform.format(savedmoney);
        remoteViews.setTextViewText( R.id.widget_savedmoney, "$"+ finalMoney);

        double notSmoked = resultDays * 20 * packsPer;
        int finalNotSmoked = (int) Math.round(notSmoked);
        remoteViews.setTextViewText( R.id.widget_cigsnot, ""+ finalNotSmoked);

        SfinalMoney = "$"+ finalMoney;
        SfinalNS = ""+ finalNotSmoked;
        SfinalDays = ""+ resultDays;

        appWidgetManager.updateAppWidget( watchWidget, remoteViews );

        //Show Prefs screen
        Intent intent = new Intent(context, preferences.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
        views.setOnClickPendingIntent(R.id.LinearLayout01, pendingIntent);
        appWidgetManager.updateAppWidget( watchWidget, views );
    }

}

发布了......可能会有一些额外的行试图设置不再相关的东西。但代码是有效的。 onUpdate()以自动间隔(90000ms)激发。并且启动以设置首选项的活动也会在关闭时进行调用,这也会更新它。这就是让我相信整个空白必须正常的原因。但一定有些不妥。因为当屏幕方向更改时,onUpdate()不会运行。而且,根据第二个答案,它无论如何都不应该,因为系统应该保留它自己的UI。但事实并非如此。

2 个答案:

答案 0 :(得分:4)

您尝试执行的操作适用于活动但不适用于BroadcastReceiver。 AppWidgetProvider是BroadcastReceiver,没有您尝试覆盖的方法(onSaveInstanceState,onRestoreInstanceState)。如果将@Override注释添加到它告诉您的方法

  

YourWidgetProvider类型的方法xyz()必须覆盖或实现   超类型方法

那是因为AppWidgetProvider类没有onSaveInstanceState或onRestoreInstanceState方法,你不能覆盖那里没有的东西。只是删除@Override注释将摆脱编译错误,但你只需要添加一个永远不会被调用的方法(除非你自己自己做,但这没有意义)。

解决此问题的方法是在应用程序中侦听配置更改,并向窗口小部件广播ACTION_APPWIDGET_UPDATE。

将应用类添加到您的应用

public class MyApplication extends Application {

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // create intent to update all instances of the widget
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidgetProvider.class);

        // retrieve all appWidgetIds for the widget & put it into the Intent
        AppWidgetManager appWidgetMgr = AppWidgetManager.getInstance(this);
        ComponentName cm = new ComponentName(this, MyWidgetProvider.class);
        int[] appWidgetIds = appWidgetMgr.getAppWidgetIds(cm);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);

        // update the widget
        sendBroadcast(intent);
    }
}

只要发生配置更改,就会调用该方法,其中一个是方向更改。您可以通过仅在方向改变时进行广播而不是例如广播来改进方法。改变系统语言。 该方法基本上向您的窗口小部件的所有实例发送更新广播(将您用于窗口小部件提供程序的名称替换为MyWidgetProvider)。

在清单中定义MyApplication

<application
    android:name="yourpackagename.MyApplication"
    android:description="@string/app_name"
    android:label="@string/app_name"
    android:icon="@drawable/app_icon">

    <!-- here go your Activity definitions -->

</application>

重要只有在横向和纵向使用不同的布局时才需要这样做。这不是必需的,因为Android负责保存和恢复小部件的ui状态。如果你得到一个带有默认值的小部件,那么你在onUpdate方法中做错了什么。 现在我看看你的onUpdate方法,有一个主要问题(还有其他问题,但我只会指出一个肯定会破坏你的小部件的方法)。

在使用已初始化的TextViews调用updateAppWidget后,您将创建新的RemoteView:

appWidgetManager.updateAppWidget( watchWidget, remoteViews );

//Show Prefs screen
Intent intent = new Intent(context, preferences.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

// this line gets rid of all the work you have done above
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);

views.setOnClickPendingIntent(R.id.LinearLayout01, pendingIntent);
appWidgetManager.updateAppWidget( watchWidget, views );

因此,在创建视图并设置值之后,您将它全部抛弃并创建新的RemoteViews,它们显然具有空/未初始化的TextView。也没必要两次调用appWidgetManager.updateAppWidget,每次onUpdate调用就足够了。 所以你要做的就是:

remoteViews.setTextViewText( R.id.widget_cigsnot, ""+ finalNotSmoked);

SfinalMoney = "$"+ finalMoney;
SfinalNS = ""+ finalNotSmoked;
SfinalDays = ""+ resultDays;

//Show Prefs screen
Intent intent = new Intent(context, preferences.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.LinearLayout01, pendingIntent);
appWidgetManager.updateAppWidget( appWidgetIds, remoteViews );

请注意我正在使用updateAppWidget(int [] appWidgetIds,RemoteViews视图)而不是updateAppWidget(ComponentName提供程序,RemoteViews视图),因为它允许您仅更新某些窗口小部件实例,而您不需要保存的ComponentName你是另一行代码。

答案 1 :(得分:0)

仅在创建第一个窗口小部件时才会调用onCreate()的{​​{1}}。小部件应该在AppWidgetProvider中更新,而不是在活动中更新。

小部件的生命周期与Activity的生命周期无关。

onUpdate()