Android屏幕适配:你需要了解的所有适配方法

引言    

    一直很想写文章,无奈自己才疏学浅,迟迟没有想写的冲动。而且相比较之下,大神们写的博客已经很好了,认为自己没必要再花费精力去写文章。话虽如此,但随着学习时间的推移,发现自己有时候记忆会产生碎片化。因此还是决定通过写文章来缕清思路,也为自己的学习留下笔记,毕竟好记性不如烂笔头。

1 为什么要进行Android屏幕适配?

官网解释:

    Android 可在各种具有不同屏幕尺寸和密度的设备上运行。对于应用,Android 系统在不同设备中提供一致的开发环境,可以处理大多数工作,将每个应用的用户界面调整为适应其显示的屏幕。 同时,系统提供 API,可用于控制应用适用于特定屏幕尺寸和密度的 UI,以针对不同屏幕配置优化 UI 设计。 例如,您可能想要不同于手机 UI 的平板电脑 UI。

    虽然系统为使您的应用适用于不同的屏幕,会进行缩放和大小调整,但您应针对不同的屏幕尺寸和密度优化应用。 这样可以最大程度优化所有设备上的用户体验,用户会认为您的应用实际上是专为他们的设备而设计,而不是简单地拉伸以适应其设备屏幕。

         通俗的讲就是Android系统手机屏幕碎片化严重,为了提高用户体验,需要尽可能的对所有产商的手机屏幕进行适配,让用户在感官上和使用上都有好的体验。

2 术语和概念

2.1 屏幕尺寸(size)

    按屏幕对角测量的实际物理尺寸。单位:英寸,1英寸 = 2.54cm。

Android屏幕适配:你需要了解的所有适配方法_第1张图片
图2-1 屏幕对角线

    Android 将所有实际屏幕尺寸分组为四种通用尺寸:小(small)、 正常(normal)、大(large)和超大(xlarge)。

图2-2 真实尺寸对应通用尺寸

         2018年市面上常见的手机屏幕尺寸有:5.0、5.2、5.5、6寸等等,它们所对应的通用尺寸为large。

2.2 屏幕密度dpi和ppi

    2.2.1 dpi

    屏幕物理区域中的像素量,通常称为 dpi(dots per inch,每英寸点数)。

    Android 将所有屏幕密度分组为六种通用密度: 低(ldpi)、中(mdpi)、高(hdpi)、超高(xhdpi)、超超高(xxhdpi)和超超超高(xxxhdpi)。

         一般情况下,240×320的屏幕是低密度120dpi,即ldpi;320×480的屏幕是中密度160dpi,即mdpi;480×800的屏幕是高密度240dpi,即hdpi;720×1280的屏幕是超高密度320dpi,即xhdpi;1080×1920的屏幕是超超高密度480dpi,即xxhdpi。

    2.2.2 ppi

   屏幕实际区域中的像素量,每英寸所拥有的像素点的数量ppi(pixels per inch),ppi数值越高显示越细腻。

   ppi可以通过图2-3公式计算。

Android屏幕适配:你需要了解的所有适配方法_第2张图片
图2-3 ppi计算公式

         如图2-4,通过公式计算得出:sqrt(1080*1080+1920*1920) / 5 = 440ppi。

Android屏幕适配:你需要了解的所有适配方法_第3张图片
图2-4 ppi计算


2.3 方向

    从用户视角看屏幕的方向,即横屏或竖屏,分别表示屏幕的纵横比是宽还是高。请注意, 不仅不同的设备默认以不同的方向操作,而且方向在运行时可随着用户旋转设备而改变。

2.4 分辨率

    屏幕上物理像素(px)的总数。

    例如:1080*1920px的屏幕,屏幕横向有1080个像素,屏幕纵向有1920个像素。像素总数为 1080*1920 = 2073600 个像素。

2.5 密度无关像素 (dp)

    在定义UI布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度或位置。

    密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。

    dp 单位转换为屏幕像素很简单:px = dp * (dpi / 160)。

    例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。

2.6 sp

    和dp类似,在定义字体大小时使用sp,字体可根据屏幕进行缩放。

3 系统如何显示最佳效果

1. 系统使用适当的备用资源

    根据当前屏幕的尺寸和密度,系统将使用您的应用中提供的任何尺寸和密度特定资源。例如,如果设备有高密度屏幕,并且应用请求可绘制对象资源,系统将查找与设备配置最匹配的可绘制对象资源目录。根据可用的其他备用资源,包含 hdpi 限定符(例如 drawable-hdpi/)的资源目录可能是最佳匹配项,因此系统将使用此目录中的可绘制对象资源。

2. 向上或向下扩展资源

    如果没有匹配的资源,系统将使用默认资源,并按需要向上或向下扩展,以匹配当前的屏幕尺寸和密度。

    “默认”资源是指未标记配置限定符的资源。例如,drawable/ 中的资源是默认可绘制资源。 系统假设默认资源设计用于基线屏幕尺寸和密度,即 正常屏幕尺寸和中密度。 因此,系统对于高密度屏幕向上扩展默认密度资源,对于低密度屏幕向下扩展。

    当系统查找密度特定的资源但在密度特定目录中未找到时,不一定会使用默认资源。系统在缩放时可能改用其他密度特定资源提供更好的效果。例如,查找低密度资源但该资源不可用时, 系统会缩小资源的高密度版本,因为系统可轻松以 0.5 为系数将高密度资源缩小至低密度资源,与以 0.75 为系数 缩小中密度资源相比,伪影更少。

    如需有关 Android 如何通过使配置限定符与设备配置匹配来选择备用资源的更多信息,请参阅Android 如何查找最佳匹配资源

4 如何支持多种屏幕

1.在 XML 布局文件中指定尺寸时使用 wrap_content、match_parent 或dp。

    为 XML 布局文件中的视图定义 android:layout_width 和 android:layout_height 时,使用 "wrap_content"、"match_parent" 或 dp 单位可确保在当前设备屏幕上为 视图提供适当的尺寸。

    例如,layout_width="100dp" 的视图在 中密度屏幕上测出宽度为 100 像素,在高密度屏幕上系统会将其扩展至 150 像素宽, 因此视图在屏幕上占用的物理空间大约相同。

    类似地,您应选择 sp(缩放独立的像素)来定义文本大小。sp 缩放系数取决于用户设置,系统会像处理 dp 一样缩放大小。

2.不要在应用代码中使用硬编码的像素值(px)

    由于性能的原因和简化代码的需要,Android 系统使用像素作为表示尺寸或坐标值的标准单位。这意味着, 视图的尺寸在代码中始终以像素表示,但始终基于当前的屏幕密度。 例如,如果 myView.getWidth() 返回 10,则表示视图在 当前屏幕上为 10 像素宽,但在更高密度的屏幕上,返回的值可能是 15。如果在应用代码中使用像素值来处理预先未针对当前屏幕密度缩放的位图,您可能需要缩放代码中使用的像素值,以与未缩放的位图来源匹配。

3.在 XML 布局文件中指定时使用weight。

   weight是权重属性,通过设置它可以改变控件在布局中的权重。 Google官方推荐,当使用weight属性时,将android:layout_width设为0dp,这样就可以理解为自动占比。

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="horizontal">

        android:id="@+id/textView"

        android:layout_width="0dp"

        android:layout_height="wrap_content"

        android:layout_weight="1"

        android:text="TextView" />

        android:id="@+id/textView2"

        android:layout_width="0dp"

        android:layout_height="wrap_content"

        android:layout_weight="2"

        android:text="TextView" />

Android屏幕适配:你需要了解的所有适配方法_第4张图片
图4-1 在布局中使用weight

        在ConstraintLayout中,spread 和 spread inside Chain 链可以设置每个组件的 weight 权重,这跟 LinearLayout 的 weight 权重设置很像,它可以设置为小数。

4.为不同屏幕密度提供替代位图可绘制对象(备用资源)

   如果需要精确控制应用在不同 屏幕配置上的外观,请在配置特定的资源目录中调整您的布局和位图可绘制对象。

    例如,考虑要显示在中密度和高密度屏幕上的图标。只需创建两种不同大小的图标 (例如中密度使用 100x100,高密度使用 150x150),然后使用适当的限定符以适当的方向放置两个变体:

res/drawable-mdpi/icon.png   //for medium-density screens

res/drawable-hdpi/icon.png   //for high-density screens

    也有的情况是会在根据sw最小宽度限定符去写很多的dimens,例如一种非常好用的Android屏幕适配

    但是dimens多了会影响启动速度,因此dimens的使用是根据实际需求来写,并不是越详细越好。这是我反编译一个软件得到的,仅供参考:


Android屏幕适配:你需要了解的所有适配方法_第5张图片
图4-2 values下都有dimens

    常见的限定符有尺寸限定符最小宽度限定符,屏幕方向限定符。例如,以下应用资源目录为不同屏幕尺寸对象提供不同的布局设计。

res/layout/my_layout.xml              // layout for normal screen size ("default")

res/layout-large/my_layout.xml        // layout for large screen size

res/layout-xlarge/my_layout.xml      // layout for extra-large screen size

res/layout-xlarge-land/my_layout.xml  // layout for extra-large in landscape orientation

    如需了解如何使用备用资源的更多信息以及配置限定符的完整列表(不只是屏幕配置),请参阅提供备用资源。

    我们还可以根据需要使用配置限定符,常见的配置限定符如图3-2所示:

Android屏幕适配:你需要了解的所有适配方法_第6张图片
图4-3 常见的配置限定符

    总结为两点:1.使用备用资源

                          2.使用配置限定符

    5. 使用.9图

    如果您在可能改变尺寸的组件上使用简单图像,您很快会发现效果有些差强人意,因为运行组件会均匀地拉伸或缩小您的图像。 解决方案是使用.9图,这种特殊格式的 PNG 文件会指示哪些区域可以拉伸,哪些区域不可以拉伸。

5. 常用的工具类

5.1 单位转换

public class DensityUtil {

    public static int dp2px(Context context, float dpValue) {

        final float scale = context.getResources().getDisplayMetrics().density;

        return (int) (dpValue * scale + 0.5f);

    }

    public static int px2dp(Context context, float pxValue) {

        final float scale = context.getResources().getDisplayMetrics().density;

        return (int) (pxValue / scale + 0.5f);

    }

    public static int px2sp(Context context, float pxValue) {

        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;

        return (int) (pxValue / fontScale + 0.5f);

    }

    public static int sp2px(Context context, float spValue) {

        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;

        return (int) (spValue * fontScale + 0.5f);

    }

}

5.2 获取屏幕尺寸和密度

public class GetScreenParameter {

    //方法一:已过时,可使用,但不建议使用 

    public static void getResolution1(Context mContext) { 

        Display mDisplay = ((Activity) mContext).getWindowManager() 

        .getDefaultDisplay(); 

        int W = mDisplay.getWidth(); 

        int H = mDisplay.getHeight(); 

    } 

    //方法二:通过getWindowManager来获取屏幕尺寸的 

    public static void getResolution2(Context mContext) { 

        DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 

        ((Activity) mContext).getWindowManager().getDefaultDisplay()  .getMetrics(mDisplayMetrics); 

        int W = mDisplayMetrics.widthPixels; 

        int H = mDisplayMetrics.heightPixels; 

        float density = mDisplayMetrics.density; //获取屏幕密度

        int densityDpi = mDisplayMetrics.densityDpi;//获取dpi

    } 

    //方法三:通过getResources来获取屏幕尺寸的,大部分用这个

    public static void getResolution3(Context mContext) { 

        DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 

        mDisplayMetrics = mContext.getResources().getDisplayMetrics(); 

        int W = mDisplayMetrics.widthPixels; 

        int H = mDisplayMetrics.heightPixels; 

        float density = mDisplayMetrics.density; 

        int densityDpi = mDisplayMetrics.densityDpi;

    } 

}

你可能感兴趣的:(Android屏幕适配:你需要了解的所有适配方法)