【问题标题】:How to detect orientation change in home screen widget?如何检测主屏幕小部件中的方向变化?
【发布时间】:2010-03-12 20:10:11
【问题描述】:

我正在编写一个主屏幕小部件,并希望在设备方向从纵向更改为横向或其他方式时更新主屏幕小部件。我怎样才能做到?

目前,我尝试像下面的代码一样重新设置 CONFIGURATION_CHANGED 操作,但 Android 不允许我通过说“不允许 IntentReceiver 组件注册接收意图”来执行此操作。有人能帮我吗?谢谢。

public class MyWidget extends AppWidgetProvider {

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        this.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
    }

【问题讨论】:

    标签: android


    【解决方案1】:

    我知道这是一个老问题,但确实有比将更新委托给 IntentService 更简单的方法。 处理方向变化的方法是监听Application中的配置变化,然后向widget广播一个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, MyWidget.class);
    
            // retrieve all appWidgetIds for the widget & put it into the Intent
            AppWidgetManager appWidgetMgr = AppWidgetManager.getInstance(this);
            ComponentName cm = new ComponentName(this, MyWidget.class);
            int[] appWidgetIds = appWidgetMgr.getAppWidgetIds(cm);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
    
            // update the widget
            sendBroadcast(intent);
        }
    }
    

    每当发生配置更改时都会调用该方法,其中之一是方向更改。您可以通过仅在方向更改而不是例如方向更改时进行广播来改进该方法系统语言的改变。 该方法基本上向您的小部件的所有实例发送更新广播。

    在清单中定义 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 框架杀死,它就无法运行。
    【解决方案2】:

    您可能不需要这样做。您可以使用 layout 和 layout-land 文件夹通过 xml 在每个方向上提供不同的布局,而无需手动检测旋转。但是,您不应该在每个方向上做任何功能上不同的事情,因为许多设备无法旋转主屏幕,并且您放入的任何功能都将无法访问。

    【讨论】:

    • -land 和-port 文件夹解决方案的问题在于,它通常需要您复制整个布局文件以进行一些或在我的情况下为几个字符的更改。话虽如此,它似乎是唯一的解决方案。
    • 有时您可以使用在纵向和横向之间变化的资源(整数、样式等)自动控制这一点。您甚至可以在单一布局中拥有两个平行视图,这些视图会根据方向自动变为可见/消失。在您的更新方法中,您填写两者,小部件容器将根据方向使用正确的容器。
    【解决方案3】:

    虽然我有点同意 jqpubliq 的回答,但只要您考虑自己在做什么并且设计得当,欢迎您针对不同的方向实施不同的布局。只是会很复杂。

    您收到的错误消息应该是不言自明的:BroadcastReceivers(如AppWidgetProvider)无法注册其他广播。毕竟,清单注册的BroadcastReceiver 实例应该在内存中停留几毫秒,而不是永远。

    因此,要实现此逻辑,您需要:

    1. 创建一个处理实际应用小部件更新的IntentService,并让您的AppWidgetProvider 将其工作委派给它。 Here is an example
    2. 创建一个单独的BroadcastReceiver实现,在清单中注册以接收CONFIGURATION_CHANGED广播Intents
    3. 让新的BroadcastReceiver 也使用IntentService(例如,致电startService())。
    4. IntentService 处理来自两个接收器的两个方向的更新。

    【讨论】:

    • 感谢您的重播。非常有帮助。您的建议中的第三步存在一个问题。 Android 规范提到了 ACTION_CONFIGURATION_CHANGED:“你不能通过清单中声明的​​组件接收这个,只能通过 Context.registerReceiver() 显式注册它。”这就是我尝试在我的 AppWidgetProvider 中使用 registerReceiver 的原因。有什么想法吗?
    • 糟糕。忘了那个。那么,这更加强调jqpubliq的答案。要接收该广播,您需要将服务无限期地保留在内存中。这对企业不利,更不用说不可避免地无效了——无论你喜欢与否,Android 或你的用户都会扼杀你的服务。
    • OP 想要获取有关主启动器方向更改的事件。 ACTION_CONFIGURATION_CHANGED 是否提供这个或只是设备的方向(不是相同的信号,因为家庭启动器可以设置为不改变方向)。
    • @CommonsWare 那么,即使我关闭了应用程序(返回键或从最近的任务)?他们做了什么来克服它?
    • @androiddeveloper:如果我是启动器的开发人员,并且我检测到暗模式发生了变化,我会要求我的所有应用小部件进行更新。所以,我不会认为 Calendar 有什么特别之处。
    【解决方案4】:

    Android 3.x及以上,有RemoteViewsService的,可以使用

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
       super.onConfigurationChanged(newConfig);
       //call widget update methods/services/broadcasts 
    }
    

    在 RemoteViewsService 子项中。 如果小部件不会被更新,小部件 UI 可以被清理或只是挂起并停止对外部事件做出反应。

    【讨论】:

    • 像 RemoteViewsService.onConfigurationChanged 这样的 Lools 仅在 Android ICS (4.x) 上正确调用。 Android Honeycomb 不时调用 onConfigurationChanged(不稳定)。
    • 我还发现,AppWidgetProvider 在旋转事件上调用了方法 onReceive,intent.getAction() == "com.sec.android.widgetapp.APPWIDGET_RESIZE" 但未调用更新。因此,此操作的手动更新小部件将有助于正确刷新小部件
    • 有人成功获得 APPWIDGET_RESIZE 事件吗?试图在我的 AppWidgetProvider 中接收它们但没有(我在 AndroidManifest 中启用了它们)。我正在使用 API 8。
    【解决方案5】:

    从 Android API 16 开始,有一个新的覆盖可以接收有关小部件选项更改的信息,主要是最小/最大宽度/高度。

    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)
    

    当它被调用时,可以检查当前的设备方向并适当地更新小部件,或者“简单地”调用必须获取方向信息的 onUpdate() 方法。

    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)
    {
        int width = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
        int height = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
        int orientation = context.getResources().getConfiguration().orientation;
    
        if (BuildConfig.DEBUG) logWidgetEvents("UPDATE " + width + "x" + height + " - " + orientation, new int[] { appWidgetId }, appWidgetManager);
    
        onUpdate(context, appWidgetManager, new int[] { appWidgetId });
    }
    

    onUpdate() 可以做这样的事情:

    int rotation = context.getResources().getConfiguration().orientation;
    if (rotation == Configuration.ORIENTATION_PORTRAIT)
    {
        ... portrait ...
    }
    else
    {
        ... landscape ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多