工具 AndroidStudio3.0.1
1 明确几个概念
- 平时称呼的 1920*1080 是指的分辨率 px,既 1920*1280px
- 相同的分辨率在不同尺寸的设备上会产生不同的像素密度 dpi
- 谷歌目前(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,下面是获取屏幕最下宽度的代码
1
2
3Configuration config = getResources().getConfiguration(); int smallestScreenWidth = config.smallestScreenWidthDp;
2. 在每个values** 目录下创建 dimens.xml 文件
1
2
3<?xml version="1.0" encoding="utf-8"?> <resources> </resources>
3. 先生成标准尺寸 sw380 下的dimens 文件
3.1 首先创建 java 类 GenerateDimen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38package 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>rn"); } 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 右键运行上面的类
结果如下:
1
2
3
4
5
6
7
8
9... <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 文件
1
2
3
4
5
6
7
8
9
10for (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>rn"); } swdef.append("</resources>"); //这里是文件名,注意修改 sw 后面的值,和转换值一一对应 String filedef = "./app/src/main/res/values-sw420dp/dimens.xml";
1
2
3
4
5
6
7
8
9
10
11
12
13
14.... <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占据宽度一半
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<?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,正好占据一半。
增加文件夹之后的效果
最后
以上就是勤劳飞机最近收集整理的关于利用不同 values 文件下的 dimens.xml 适配安卓屏幕的全部内容,更多相关利用不同内容请搜索靠谱客的其他文章。
发表评论 取消回复