Android N Display Size

Android 7.0引入屏幕缩放功能,允许用户调整显示尺寸以改善视觉体验。此功能考虑了不同屏幕密度下的应用行为变化,并提供了一系列推荐实践以确保应用兼容性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android N新特性-屏幕缩放

Android 7.0 支持用户设置显示尺寸,以放大或缩小屏幕上的所有元素,从而提升设备对视力不佳用户的可访问性。用户无法将屏幕缩放至低于最小屏幕宽度 sw320dp,该宽度是 Nexus 4 的宽度,也是常规中等大小手机的宽度。

当设备密度发生更改时,系统会以如下方式通知正在运行的应用:
如果是面向 API 级别 23 或更低版本系统的应用,系统会自动终止其所有后台进程。这意味着如果用户切换离开此类应用,转而打开 Settings 屏幕并更改 Display size 设置,则系统会像处理内存不足的情况一样终止该应用。如果应用具有任何前台进程,则系统会如处理运行时更改中所述将配置变更通知给这些进程,就像对待设备屏幕方向变更一样。
如果是面向 Android 7.0 的应用,则其所有进程(前台和后台)都会收到有关配置变更的通知,如处理运行时更改中所述。
大多数应用并不需要进行任何更改即可支持此功能,不过前提是这些应用遵循 Android 最佳做法。具体要检查的事项:

  • 在屏幕宽度为 sw320dp 的设备上测试您的应用,并确保其充分运行。
  • 当设备配置发生变更时,更新任何与密度相关的缓存信息,例如缓存位图或从网络加载的资源。当应用从暂停状态恢复运行时,检查配置变更。
    注:如果您要缓存与配置相关的数据,则最好也包括相关元数据,例如该数据对应的屏幕尺寸或像素密度。保存这些元数据便于您在配置变更后决定是否需要刷新缓存数据。
  • 避免用像素单位指定尺寸,因为像素不会随屏幕密度缩放。应改为使用与密度无关像素 (dp) 单位指定尺寸

Dpi,dp,ppi,sp,in&px参考

Android屏幕适配全攻略


Display Size分析

Display Size 对应手机的Settings->Display->Display size。用来缩放手机屏幕。
流程分析
1. screen_zoom_activity.xml
Display Size调整设置项对应的布局文件,通过seekbar来显示缩放等级,通过按钮来调整等级
2. CustomPreviewSeekBarPreferenceFragment.java

final int max = Math.max(1, mEntries.length - 1);
final LabeledSeekBar seekBar = (LabeledSeekBar) content.findViewById(R.id.seek_bar);
seekBar.setLabels(mEntries);
seekBar.setMax(max);
seekBar.setProgress(mInitialIndex);

mEntries数组长度决定了缩放调整的级数,数组内容为每个等级的标签,mInitialIndex决定了当前所在的级数,第一次获取的则为默认等级。
3. ScreenZoomSettings.java
该类继承自PreviewSeekBarPreferenceFragment.java,该类确定了mEntries和mInitialIndex的值。这两个值都是由DisplayDensityUtils对象获得。
4. DisplayDensityUtils.java
mEntries和mInitialIndex可以看到是通过DisplayDensityUtils的getEntries()和getCurrentIndex()方法获得。而该方法中返回值搜索代码即可看到是在该类的构造方法中确定。
源码如下

public class DisplayDensityUtils {
    /** Minimum increment between density scales. */
    private static final float MIN_SCALE_INTERVAL = 0.09f;
    /** Minimum density scale. This is available on all devices. */
    private static final float MIN_SCALE = 0.85f;
    /** Maximum density scale. The actual scale used depends on the device. */
    private static final float MAX_SCALE = 1.50f;
    /** Summary used for "default" scale. */
    public static final int SUMMARY_DEFAULT = R.string.screen_zoom_summary_default;
    /** Summary used for "custom" scale. */
    private static final int SUMMARY_CUSTOM = R.string.screen_zoom_summary_custom;
    /**
     * Summaries for scales smaller than "default" in order of smallest to
     * largest.
     */
    private static final int[] SUMMARIES_SMALLER = new int[] {
            R.string.screen_zoom_summary_small
    };
    /**
     * Summaries for scales larger than "default" in order of smallest to
     * largest.
     */
    private static final int[] SUMMARIES_LARGER = new int[] {
            R.string.screen_zoom_summary_large,
            R.string.screen_zoom_summary_very_large,
            R.string.screen_zoom_summary_extremely_large,
    };
    /**
     * Minimum allowed screen dimension, corresponds to resource qualifiers
     * "small" or "sw320dp". This value must be at least the minimum screen
     * size required by the CDD so that we meet developer expectations.
     */
    private static final int MIN_DIMENSION_DP = 320;

    private final String[] mEntries;
    private final int[] mValues;

    private final int mDefaultDensity;
    private final int mCurrentIndex;

    public DisplayDensityUtils(Context context) {
        final int defaultDensity = DisplayDensityUtils.getDefaultDisplayDensity(
                Display.DEFAULT_DISPLAY);
        if (defaultDensity <= 0) {
            mEntries = null;
            mValues = null;
            mDefaultDensity = 0;
            mCurrentIndex = -1;
            return;
        }

        final Resources res = context.getResources();
        final DisplayMetrics metrics = res.getDisplayMetrics();
        final int currentDensity = metrics.densityDpi;
        int currentDensityIndex = -1;

        // Compute number of "larger" and "smaller" scales for this display.
        final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels);
        final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
        final float maxScale = Math.min(MAX_SCALE, maxDensity / (float) defaultDensity);
        final float minScale = MIN_SCALE;
        final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL,
                0, SUMMARIES_LARGER.length);
        final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
                0, SUMMARIES_SMALLER.length);
        String[] entries = new String[1 + numSmaller + numLarger];
        int[] values = new int[entries.length];
        int curIndex = 0;

        if (numSmaller > 0) {
            final float interval = (1 - minScale) / numSmaller;
            for (int i = numSmaller - 1; i >= 0; i--) {
                // Round down to a multiple of 2 by truncating the low bit.
                final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
                if (currentDensity == density) {
                    currentDensityIndex = curIndex;
                }
                entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
                values[curIndex] = density;
                curIndex++;
            }
        }

        if (currentDensity == defaultDensity) {
            currentDensityIndex = curIndex;
        }
        values[curIndex] = defaultDensity;
        entries[curIndex] = res.getString(SUMMARY_DEFAULT);
        curIndex++;

        if (numLarger > 0) {
            final float interval = (maxScale - 1) / numLarger;
            for (int i = 0; i < numLarger; i++) {
                // Round down to a multiple of 2 by truncating the low bit.
                final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
                if (currentDensity == density) {
                    currentDensityIndex = curIndex;
                }
                values[curIndex] = density;
                entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
                curIndex++;
            }
        }

        final int displayIndex;
        if (currentDensityIndex >= 0) {
            displayIndex = currentDensityIndex;
        } else {
            // We don't understand the current density. Must have been set by
            // someone else. Make room for another entry...
            int newLength = values.length + 1;
            values = Arrays.copyOf(values, newLength);
            values[curIndex] = currentDensity;

            entries = Arrays.copyOf(entries, newLength);
            entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);

            displayIndex = curIndex;
        }

        mDefaultDensity = defaultDensity;
        mCurrentIndex = displayIndex;
        mEntries = entries;
        mValues = values;
    }

    public String[] getEntries() {
        return mEntries;
    }
    public int getCurrentIndex() {
        return mCurrentIndex;
    }
}

构造函数首先根据默认DPI来计算可缩放的等级数->填充smaller的mEntries标签内容->设置默认密度等级的下标和填充标签->填充larger的mEntries的标签内容

计算等级数

final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels);
final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
final float maxScale = Math.min(MAX_SCALE, maxDensity / (float) defaultDensity);
final float minScale = MIN_SCALE;
final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL,
        0, SUMMARIES_LARGER.length);
final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
        0, SUMMARIES_SMALLER.length);
String[] entries = new String[1 + numSmaller + numLarger];

defaultDensity是通过 DisplayDensityUtils.getDefaultDisplayDensity(Display.DEFAULT_DISPLAY)获取,该默认密度对应代码中ro.sf.lcd_density
numSmaller为最小为0,最大为1,此处值为1
numLarger则为0到3之间,具体取决于maxScale,它又取决于 maxDensity / (float) defaultDensity

此处以测试机5015为例,其ro.sf.lcd_density默认为240dpi,width为480px,height为854px
则defaultDensity为240  
maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP 
 ==>160*480/320 =240
则maxScale = 240/240 = 1
则numLarger = 0

修改Android DPI
代码路径:alps/device/sagetel/sr6580_we_n/system.prop,ro.sf.lcd_density修改该值则修改了手机的默认密度

填充smaller

设置默认密度

if (currentDensity == defaultDensity) {
    currentDensityIndex = curIndex;//设置默认下标
}
values[curIndex] = defaultDensity;
entries[curIndex] = res.getString(SUMMARY_DEFAULT);填充默认等级的描述

填充larger


等级客制化

可通过修改ro.sf.lcd_density值来改变缩放等级数,density越小,等级数越多,但最大5级。为了界面显示正常,我们需调整合适的density,density测试可通过

修改屏幕Dpi
adb shell wm density 240    //修改屏幕密度为240dpi
adb shell wm density reset //重置屏幕密度为默认密度
<think>我们正在处理用户的问题:如何在Android14中以编程方式更改屏幕分辨率?根据Android的安全模型,直接更改屏幕分辨率通常需要系统级权限或设备root。普通应用无法更改系统级别的设置(如屏幕分辨率),因为这会影响整个设备。但是,我们可以考虑以下几种可能的方法:1.在具有系统权限的应用中(例如系统应用或具有系统签名的应用),可以通过调用系统服务(如`DisplayManager`)来更改显示模式。2.在设备已root的情况下,通过执行shell命令来更改分辨率。3.对于普通应用,可能只能通过更改自己应用内的显示设置(例如调整SurfaceView的分辨率)来实现,但这并不是真正的更改系统屏幕分辨率。根据用户要求,我们需要提供代码示例。但请注意,以下方法可能仅适用于具有系统权限的应用或系统应用。参考引用[3]:Android支持多种屏幕配置,但通常应用程序不应该去更改系统屏幕分辨率,而是通过资源适配来适应不同的屏幕。然而,如果确实需要更改屏幕分辨率(例如在系统应用中),我们可以尝试以下步骤:在Android中,从Android5.0(API21)开始,可以使用`DisplayManager`来设置显示模式。具体步骤如下:1.获取`DisplayManager`系统服务。2.获取目标显示(默认显示)。3.获取显示支持的模式(Display.Mode),并选择其中一个模式(包含分辨率、刷新率等信息)。4.设置显示模式。注意:此操作需要权限`android.permission.MODIFY_PHONE_STATE`,该权限是系统权限,仅授予系统应用。代码示例(适用于具有系统权限的应用):```java//需要API21以上DisplayManagerdisplayManager=(DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);Displaydisplay=displayManager.getDisplay(Display.DEFAULT_DISPLAY);//获取当前支持的模式Display.Mode[]modes=display.getSupportedModes();//假设我们想设置的分辨率是1920x1080,我们需要在支持的模式中找到这个分辨率Display.ModetargetMode=null;for(Display.Modemode:modes){if(mode.getPhysicalWidth()==1920&&mode.getPhysicalHeight()==1080){targetMode=mode;break;}}if(targetMode!=null){//创建一个新的Mode对象,通常我们使用相同的刷新率//注意:setUserPreferredDisplayMode需要API23以上if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){//设置用户首选的显示模式displayManager.setUserPreferredDisplayMode(display.getDisplayId(),targetMode);}}```另一种方法是通过执行shell命令(需要root权限):使用`adbshellwmsize1920x1080`命令可以临时更改分辨率。在代码中,我们可以通过`Runtime.getRuntime().exec()`来执行这个命令。代码示例(需要root权限):```javatry{Processprocess=Runtime.getRuntime().exec("su");DataOutputStreamos=newDataOutputStream(process.getOutputStream());os.writeBytes("wmsize1920x1080\n");os.writeBytes("exit\n");os.flush();process.waitFor();}catch(Exceptione){e.printStackTrace();}```但是,请注意:-第一种方法需要系统权限,普通应用无法使用。-第二种方法需要设备root,并且应用需要获取root权限。在Android14中,由于权限和安全性的进一步限制,普通应用几乎不可能更改屏幕分辨率。因此,建议用户重新考虑需求:是否真的需要更改系统屏幕分辨率?或者可以通过调整应用内部的显示方式(例如使用不同的布局或缩放)来适应?相关问题:1.在Android中如何获取当前屏幕分辨率?2.如何为不同屏幕尺寸和密度的Android设备进行应用适配?3.在Android中如何动态更改应用显示密度(dpi)?4.如何以编程方式更改Android设备的屏幕方向?注意:以上代码示例仅供参考,实际使用时请考虑设备兼容性和权限问题。</think>在 Android 14 中以编程方式更改屏幕分辨率需要系统级权限(如 `WRITE_SECURE_SETTINGS` 或设备 ROOT),普通应用无法实现此操作。以下是两种实现方式及注意事项: ### 方法 1:通过系统服务(需系统权限) ```java // 需要声明权限:<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> // 仅系统/特权应用可用 DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); // 获取支持的显示模式 Display.Mode[] modes = display.getSupportedModes(); Display.Mode targetMode = null; // 查找目标分辨率(如 1920x1080) for (Display.Mode mode : modes) { if (mode.getPhysicalWidth() == 1920 && mode.getPhysicalHeight() == 1080) { targetMode = mode; break; } } if (targetMode != null) { // 设置用户首选的显示模式(API 23+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { displayManager.setUserPreferredDisplayMode(display.getDisplayId(), targetMode); } } ``` ### 方法 2:通过 ADB 命令(需 ROOT) ```java // 需要 ROOT 权限 try { Process process = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(process.getOutputStream()); // 设置分辨率(临时生效) os.writeBytes("wm size 1920x1080\n"); // 设置密度(可选) os.writeBytes("wm density 480\n"); os.writeBytes("exit\n"); os.flush(); process.waitFor(); } catch (Exception e) { e.printStackTrace(); } ``` ### 关键限制 1. **权限要求**: - `WRITE_SECURE_SETTINGS` 仅授予系统应用 - ROOT 权限需要解锁 Bootloader 2. **Android 版本限制**: - `setUserPreferredDisplayMode()` 仅支持 Android 6.0+ - Android 11 后权限控制更严格[^3] 3. **恢复默认**: ```bash adb shell wm size reset adb shell wm density reset ``` ### 替代方案(应用级适配) 普通应用应通过资源系统适配不同分辨率: ```xml <!-- 不同屏幕尺寸的布局 --> res/layout-sw600dp/ <!-- 7英寸平板 --> res/layout-sw720dp/ <!-- 10英寸平板 --> ``` 使用 `Configuration` 检测变化: ```java @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 处理屏幕配置变化 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值