利用不同 values 文件下的 dimens.xml 适配安卓屏幕

工具 AndroidStudio3.0.1 

1 明确几个概念

 

  1. 平时称呼的 1920*1080 是指的分辨率 px,既 1920*1280px
  2. 相同的分辨率在不同尺寸的设备上会产生不同的像素密度 dpi
  3. 谷歌目前(2018.05.25)规定的像素密度,以及对应的 dp 转换比例下

 

ldpi

mdpi

tvdpi

hdpi

xhdpi

xxhdpi

1dp转换为px

0.75

1

1.33

1.5

2

3 

2 分析

    根据 1 中第三条可以知道 dp 的出现是谷歌提供给开发者的一种适配方案,并且 Android 系统会把形形色色的屏幕像素密度归并到上面提到的几种。如果屏幕的尺寸和分辨率不是特别奇葩的话,dp 能很好的实现自己的设计目的。利用 dp 可以保障: 同一dp长度的控件在不同设备上物理尺寸是相同的。

    设想一种情景:希望一个控件占据整个屏幕宽度的一半。这种场景可以用代码设置,但是实现起来比较麻烦。更何况我们希望方便快捷。我们希望只给控件在 xml 文件中标记尺寸,令其自动完成占据一半的逻辑。这时可以用到今天要讲的 dimens 适配。

 

 

 

3 dimens 适配的原理

  令控件占据屏幕的一半,必定是控件根据不同的屏幕来变化尺寸(dp值),这里的思路是在 xml 文件中填写一个占位符,系统运行的时候会根据不同的屏幕尺寸把占位符替换成不同的dp值

   为了实现上面的逻辑需要: 1 定义一个基准屏幕,例如 分辨率是760*1280 密度是 xhdpi 的屏幕,这样屏幕的宽度就是 760/2 = 380dp 。 2 创建不同的 dimes 文件,以便适应不同的屏幕。

   例如在 stuido 开发中 利用Nexus4   来开发 xml。也可以创建一个自定义屏幕完全和基准屏幕相同。 在xml 只需要设置 android:layout_width="@dimen/dp190" 即可。这样在基准屏幕上 dp190 会转换为 190dp 正好是 380dp 的一半。至于在其他屏幕上的转换就需要进行下一步 dimes 文件的配置了。

 

4 生成 dimens 文件

    1. 首先在 res 目录下新建各种尺寸的 values 文件

 

其中 sw320dp 代表屏幕的最小宽度是320dp,下面是获取屏幕最下宽度的代码

 

 Configuration config = getResources().getConfiguration();
 int  smallestScreenWidth = config.smallestScreenWidthDp;

2. 在每个values** 目录下创建 dimens.xml 文件

<?xml version="1.0" encoding="utf-8"?>
<resources>

</resources>

3. 先生成标准尺寸 sw380 下的dimens 文件

  3.1 首先创建 java 类 GenerateDimen

 

 

package com.example.hepan.adaptation;

/**
 * Created by hepan on 2018/5/25.
 */

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;

public class GenerateDimen {

    public static void genDimen() {

        StringBuilder swdef = new StringBuilder();
        PrintWriter out;

        try {

            swdef.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                    "<resources>");

            double value;
            for (int i = 0; i < 500; i++) {
                //这里控制对应转换的值,如果是标准尺寸就一对一转换
                value = (i + 1) * 1;
                value = Math.round(value * 100) / 100;
                swdef.append("<dimen name=\"dp" + (i + 1) + "\">" + value + "dp</dimen>\r\n");
            }
            swdef.append("</resources>");

            //这里是文件名,1 注意修改 sw 后面的值,和转换值一一对应  2 文件夹和文件要先创建好否则要代码创建
            String filedef = "./app/src/main/res/values-sw380dp/dimens.xml";


            out = new PrintWriter(new BufferedWriter(new FileWriter(filedef)));

            out.println(swdef.toString());


            out.close();

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

        }

    }


    public static void main(String[] args) {

        genDimen();

    }

}

  3.2 右键运行上面的类

 

结果如下:

...
<dimen name="dp379">379.0dp</dimen>
<dimen name="dp380">380.0dp</dimen>
<dimen name="dp381">381.0dp</dimen>
<dimen name="dp382">382.0dp</dimen>
<dimen name="dp383">383.0dp</dimen>
<dimen name="dp384">384.0dp</dimen>
<dimen name="dp385">385.0dp</dimen>
....

3.3  同理 修改 GenerateDime 类生成 sw420 文件

 

   for (int i = 0; i < 500; i++) {
                //这里控制对应转换的值,如果是标准尺寸就一对一转换
                value = (i + 1) * 420/380;
                value = Math.round(value * 100) / 100;
                swdef.append("<dimen name=\"dp" + (i + 1) + "\">" + value + "dp</dimen>\r\n");
            }
            swdef.append("</resources>");

            //这里是文件名,注意修改 sw 后面的值,和转换值一一对应
            String filedef = "./app/src/main/res/values-sw420dp/dimens.xml";

 

  ....
    <dimen name="dp380">420.0dp</dimen>
    <dimen name="dp381">421.0dp</dimen>
    <dimen name="dp382">422.0dp</dimen>
    <dimen name="dp383">423.0dp</dimen>
    <dimen name="dp384">424.0dp</dimen>
    <dimen name="dp385">425.0dp</dimen>
    <dimen name="dp386">426.0dp</dimen>
    <dimen name="dp387">427.0dp</dimen>
    <dimen name="dp388">428.0dp</dimen>
    <dimen name="dp389">429.0dp</dimen>
    <dimen name="dp390">431.0dp</dimen>
  ....

 

我们会发现,相对于 sw380 基准,sw420的值都会扩大 420/380 =1.10526 倍

 

3.4 同理我们生成其他 sw** 的文件,当设计的sw**文件越多,屏幕适配的越全面。

4 看看效果

设置如下布局,令 TextView占据宽度一半

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.hepan.adaptation.MainActivity">
    <!--因为基准是sw380,所以dp190代表一半-->
    <TextView
        android:layout_width="@dimen/dp190"
        android:layout_height="30dp"
        android:background="#ff0000"
        android:gravity="center"
        android:text="我占据了一半"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.469" />

    <!--中间线,在design模式下可确认textview占一半位置-->
    <android.support.constraint.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />


</android.support.constraint.ConstraintLayout>

 4.1 直接在 AndroidStudio 上查看效果

 

                 

选取了如下的布局显示

分别选取了 hdpi xhdpi xxhdpi 的三个屏幕,控件都很好的占据了一半。

4.2 特殊的bug

 当我选取上图对应参数 1080*1920 ,420dpi (Pixel 2)作为设计模板时出现了特殊情况

在 AndroidStudio 上显示的并不是一半,这里我们计算一下

补充知识: 基础 mdpi 对应密度是160dpi 比例是 1, hdpi 代表的密度其实是 240dpi 对应转换比例为 1.5(文章第一部分第三条)。

那么 420dpi 对应的转换比例应该是 420/160 = 2.625 也就是说 1dp=2.625px。 那么我们选取的屏幕 1080*1920px 对应的最小宽度 sw 就是 1080/2.625 = 411.2dp。 但是我们创建的文件夹并没有 values-sw411,这时系统会遵循向下兼容原则,找到比411小且最近的文件夹,也就是sw380,而sw380里面 dp190 = 190dp 比411.2/2 要小,理所当然的不会显示一半。

如何解决上述问题呢?

答案就是尽可能的多建文件夹特别是比较集中的范围,可把间隔缩小 。这里我们再建一个 sw410 文件夹,按照上面生成dimens.xml 文件,发现 dp190 = 205dp,正好占据一半。

增加文件夹之后的效果

    

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值