【问题标题】:How to specify Activities that are only for phones or tablets on Android如何在 Android 上指定仅适用于手机或平板电脑的活动
【发布时间】:2014-06-29 20:02:52
【问题描述】:

我在查看 Google I/O Session 2012 app 时遇到了这个 TODO

// TODO: use <meta-data> element instead
private static final Class[] sPhoneActivities = new Class[]{
        MapActivity.class,
        SessionDetailActivity.class,
        SessionsActivity.class,
        TrackDetailActivity.class,
        VendorDetailActivity.class,
};

// TODO: use <meta-data> element instead
private static final Class[] sTabletActivities = new Class[]{
        MapMultiPaneActivity.class,
        SessionsVendorsMultiPaneActivity.class,
};

public static void enableDisableActivities(final Context context) {
    boolean isHoneycombTablet = isHoneycombTablet(context);
    PackageManager pm = context.getPackageManager();

    // Enable/disable phone activities
    for (Class a : sPhoneActivities) {
        pm.setComponentEnabledSetting(new ComponentName(context, a),
                isHoneycombTablet
                        ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                        : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
    }

    // Enable/disable tablet activities
    for (Class a : sTabletActivities) {
        pm.setComponentEnabledSetting(new ComponentName(context, a),
                isHoneycombTablet
                        ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }
}

这让我想知道如何执行该 TODO。

【问题讨论】:

    标签: android android-activity android-manifest


    【解决方案1】:

    我想出了这个方法(注意:这是仿照Google I/O Session 2012 appUIUtilis.java):

    AndroidManifest.xml 中定义Activitys 以包括&lt;meta-data&gt;

    <!-- Note: specify the target device for Activities with target_device meta-data of "universal|phone|tablet"
               see UIUtils.java (configureDeviceSpecificActivities) for more details. -->
    
    <!-- Activities for both phones and tablets -->
    <activity android:name=".ui.AccountActivity" 
              android:configChanges="orientation|keyboardHidden" 
              android:label="@string/app_name"
              android:theme="@style/Theme.Accounts">
              <meta-data android:name="target_device" android:value="universal"/>
    </activity>
    
    <!-- Activities for tablets -->
    <activity android:name=".ui.CoolMultipaneActivity"
              android:label="@string/app_name">
              <meta-data android:name="target_device" android:value="tablet"/>
    

    辛苦都放在方法configureDeviceSpecificActivities(Context context)

    /**
     * Enables and disables {@linkplain android.app.Activity activities} based on their "target_device" meta-data and
     * the current device. Add <meta-data name="target_device" value="tablet|phone|universal" /> to an activity to
     * specify its target device.
     * @param context the current context of the device
     * @see #isHoneycombTablet(android.content.Context)
     */
    public static void configureDeviceSpecificActivities(Context context) {
        final PackageManager package_manager = context.getPackageManager();
        final boolean is_honeycomb_tablet = isHoneycombTablet(context);
        try {
            final ActivityInfo[] activity_info = package_manager.getPackageInfo(context.getPackageName(),
                    PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA).activities;
            for (ActivityInfo info : activity_info) {
                final String target_device = info.metaData.getString("target_device");
                if (target_device == null) break;
                target_device = target_device.toLowerCase(Locale.US);
                final boolean is_for_tablet = target_device.equals("tablet");
                final boolean is_for_phone = target_device.equals("phone");
                final String class_name = info.name;
                package_manager.setComponentEnabledSetting(new ComponentName(context, Class.forName(class_name)),
                        is_honeycomb_tablet && is_for_phone
                                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                                : !is_honeycomb_tablet && is_for_tablet
                                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                                : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                        PackageManager.DONT_KILL_APP);
            }
        } catch (PackageManager.NameNotFoundException error) {
            Ln.w(error.getCause());
        } catch (ClassNotFoundException error) {
            Ln.w(error.getCause());
        }
    }
    

    有趣的事实:如果没有 GET_META_DATA 标志,它将无法工作,因为如果您不包含该标签,metaData 将始终返回 null。

    最后一点是调用这个方法,可能在你最初的ActivityonCreate

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
    
        // Anything else you want to do in the onCreate callback
    
        // Set up to use the appropriate Activities for the given device
        UIUtils.configureDeviceSpecificActivities(this);
    }
    

    现在您可以拥有专为手机和平板电脑设计的Activitys,以应对仅更改布局并可能包含更多Fragments 还不够的时代。

    注意:如果您看到警告,final String class_name = info.packageName + info.name; 可能必须是 final String class_name = info.name;

    注意(2):final String target_device = info.metaData.getString("target_device", "").toLowerCase(); 应该是为了向后兼容 API 12。

    String target_device = info.metaData.getString("target_device");
    if (target_device == null) break;
    target_device = target_device.toLowerCase();
    

    注意(3):target_device.toLowerCase(); 隐式使用默认语言环境。请改用target_device.toLowerCase(Locale.US)。并对上面的代码进行了所有修改。

    【讨论】:

    • 我认为 Application.onCreate 会比 Activity.onCreate 更合适..?
    • @android.weasel “可能在 onCreate ...”中,如果您有其他适合您的位置,那就去吧。这只是从 iosched 应用程序中实现“TODO”以使用 标记。将它放在 Activity 中的好处是,您无需扩展 Application 类即可。
    • @DandreAllison 我已经有一个Application,所以这并不麻烦,但我确实想到将它放在每个Activity 中可能会有好处:如果我在res/values-w600dp 中使用布尔值sw600dp 以区分平板电脑和手机,用户旋转设备使其不再那么宽,活动响应并可能更改为使用“手机”模式。将其放入Application.onCreate 会在启动时冻结平板电脑/手机的决策,这对我来说很合适,因为我使用的是永远不会改变的sw600dp
    • 哦,另外:我发现我无论如何都不能使用它,因为我想在具有相同名称的 &lt;activity-alias&gt;es 之间进行选择:首先,任何活动别名的存在都会破坏 @987654350 @ 代码,其次,即使我修改了一些东西以允许这样做,ComponentName 也无法区分具有相同名称的两个别名(我试图使用它们来使给定的意图神奇地最终出现在电话或平板电脑感知活动)。我将不得不编写委托 Activity 类和/或向自己发出不同的意图 - 不幸的是,但没什么大不了的。
    猜你喜欢
    • 1970-01-01
    • 2019-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多