1. DisplayMetrics基础解析
在Android开发中,DisplayMetrics类是我们获取设备屏幕物理特性的重要工具。这个类位于android.util包下,提供了屏幕尺寸、密度和字体缩放等关键参数。理解这些参数对于实现适配不同设备的UI至关重要。
1.1 核心参数说明
DisplayMetrics包含几个关键字段:
- widthPixels:屏幕的绝对宽度(像素)
- heightPixels:屏幕的绝对高度(像素)
- densityDpi:屏幕密度(每英寸点数)
- density:密度比例因子(相对于基准密度160dpi)
- scaledDensity:字体缩放比例因子
获取DisplayMetrics实例的标准方式:
DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics);注意:在Android 4.2(API 17)及以上版本,推荐使用getRealMetrics()替代getMetrics(),因为后者不包含系统装饰(如导航栏)占用的空间。
1.2 屏幕密度详解
Android将屏幕密度分为几个标准级别:
- ldpi(低密度):~120dpi
- mdpi(中密度):~160dpi(基准密度)
- hdpi(高密度):~240dpi
- xhdpi(超高密度):~320dpi
- xxhdpi(超超高密度):~480dpi
- xxxhdpi(超超超高密度):~640dpi
密度比例因子(density)的计算公式:
density = densityDpi / 160f例如,在240dpi的设备上:
density = 240 / 160 = 1.52. 分辨率获取实践
2.1 基础获取方法
最直接的屏幕分辨率获取方式:
DisplayMetrics metrics = getResources().getDisplayMetrics(); int width = metrics.widthPixels; // 屏幕宽度(px) int height = metrics.heightPixels; // 屏幕高度(px)2.2 考虑系统UI的影响
实际可用显示区域可能小于物理分辨率:
Rect rect = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); int usableWidth = rect.width(); int usableHeight = rect.height();2.3 多窗口模式处理
在分屏模式下,需要动态监听尺寸变化:
View rootView = getWindow().getDecorView(); rootView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { int newWidth = right - left; int newHeight = bottom - top; // 处理尺寸变化 } });3. 像素单位转换
3.1 dp与px转换
Android推荐使用dp(密度无关像素)作为布局单位。转换方法:
dp转px:
public static int dpToPx(float dp, Context context) { return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5f); }px转dp:
public static float pxToDp(int px, Context context) { return px / context.getResources().getDisplayMetrics().density; }专业提示:加0.5f是为了四舍五入,避免精度损失导致的尺寸偏差。
3.2 sp与px转换
处理字体大小时使用sp(缩放无关像素):
public static int spToPx(float sp, Context context) { return (int) (sp * context.getResources().getDisplayMetrics().scaledDensity + 0.5f); }4. 多屏适配策略
4.1 最小宽度限定符
在res/values-sw dp目录中定义尺寸:
res/ values-sw320dp/ // 适用于最小宽度≥320dp的设备 dimens.xml values-sw600dp/ // 适用于平板 dimens.xml4.2 百分比布局
使用PercentRelativeLayout或ConstraintLayout的百分比约束:
<android.support.constraint.ConstraintLayout> <Button app:layout_constraintWidth_percent="0.5" app:layout_constraintHeight_percent="0.3"/> </android.support.constraint.ConstraintLayout>4.3 动态调整策略
根据屏幕特性动态调整UI:
DisplayMetrics metrics = getResources().getDisplayMetrics(); float diagonalInches = Math.sqrt(Math.pow(metrics.widthPixels/metrics.xdpi, 2) + Math.pow(metrics.heightPixels/metrics.ydpi, 2)); if (diagonalInches >= 7) { // 平板布局 } else if (diagonalInches >= 4.5) { // 大手机布局 } else { // 小手机布局 }5. 常见问题排查
5.1 获取值不正确的情况
可能原因及解决方案:
- 未考虑系统装饰:使用getRealMetrics()替代getMetrics()
- 未处理配置变更:在Activity中添加configChanges属性
<activity android:configChanges="orientation|screenSize|smallestScreenSize"/>- 多窗口模式未适配:实现OnLayoutChangeListener监听尺寸变化
5.2 内存带宽优化
高分辨率设备上图形性能问题:
- 使用RGB_565替代ARGB_8888减少内存占用
BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565;5.3 动态图标适配
根据密度动态加载图标资源:
String densitySuffix; switch(getResources().getDisplayMetrics().densityDpi) { case DisplayMetrics.DENSITY_XXXHIGH: densitySuffix = "xxxhdpi"; break; // 其他case... } int resId = getResources().getIdentifier( "icon_" + densitySuffix, "drawable", getPackageName());6. 高级应用场景
6.1 自定义View的尺寸处理
正确处理wrap_content和精确尺寸:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int desiredWidth = dpToPx(100, getContext()); // 默认100dp int finalWidth; if (widthMode == MeasureSpec.EXACTLY) { finalWidth = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { finalWidth = Math.min(desiredWidth, widthSize); } else { finalWidth = desiredWidth; } // 类似处理高度... setMeasuredDimension(finalWidth, finalHeight); }6.2 跨进程分辨率同步
在Service中获取正确分辨率:
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); DisplayMetrics metrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(metrics);6.3 动态主题适配
根据屏幕特性加载不同主题:
if (getResources().getConfiguration().smallestScreenWidthDp >= 600) { setTheme(R.style.TabletTheme); } else { setTheme(R.style.PhoneTheme); }在实际项目中,我发现正确处理DisplayMetrics数据的关键在于:
- 始终考虑实际可用显示区域而非物理分辨率
- 对dp/sp单位的转换保持一致性
- 动态监听配置变化,特别是多窗口场景
- 高分辨率设备上要注意内存使用优化
一个常见的坑是忘记处理横竖屏切换时的布局调整,这会导致UI显示异常。建议在开发初期就建立完善的屏幕适配测试方案,覆盖各种密度和尺寸的设备。