大家好,又见面了,我是你们的朋友全栈君。
目录
https://github.com/JessYanCoding/AndroidAutoSize
compile 'me.jessyan:autosize:1.0.1'
<manifest>
<application>
<meta-data
android:name="design_width_in_dp"
android:value="360"/>
<meta-data
android:name="design_height_in_dp"
android:value="640"/>
</application>
</manifest>
取消适配只需要我们的Activity实现CancelAdapt即可,具体如下:
public class TestActivity extends Activity implements CancelAdapt{
}
效果图如下:
取消适配后可以看到宽度360dp 已经不能占满整个宽屏。
如果我们的Activity不能使用Manifest中配置的宽/高,需要个性化配置。Activity只需要实现CustomAdapt 接口并实现isBaseOnWidth和getSizeInDp两个函数即可,具体代码如下:
public class TestActivity extends Activity implements CustomAdapt {
@Override
public boolean isBaseOnWidth() {
// true: 以宽度等比例缩放 false: 以高度等比例缩放
return true;
}
@Override
public float getSizeInDp() {
// 对应的宽/高
return 540;
}
}
效果图如下:
通过自定义总宽度值,可以看到宽度180dp只占有整个屏幕宽度的1/3。
(略)
屏幕分辨率 | dpi | 屏幕宽度dp | |
---|---|---|---|
手机 A | 720×1280 | 160 | 720(px=dp*(dpi/160)) |
手机 B | 720×1280 | 320 | 360(px=dp*(dpi/160)) |
可以发现,仅仅通过dp来适配,不同的设备显示的差异非常大。
为了与上图有一个比较,此处将Manifest中meta-data的design_width_in_dp设置为400。
适配过后,不同分辨率的设备显示非常相似。
名称 | 简介 |
---|---|
px | pixels(像素),屏幕上的实际像素点,无论控件或文字最终都会转化为px单位来显示其大小。 |
dp | 与dip雷同,指的是设备独立像素,在不同分辨率和尺寸的手机上代表了不同的真实像素,计算公式:px = dp(dpi/160) |
dpi | 像素密度,指的是在系统软件上指定的单位尺寸的像素数量,它往往是写在系统出厂配置文件的一个固定值。 |
sp | 全称scaled pixels,放大像素的缩写,专门用于处理字体的大小。它不仅与屏幕dpi有关,还与系统的默认字体大小有关。 |
public class DisplayMetrics {
public static final int DENSITY_MEDIUM = 160;
public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;
public static final float DENSITY_DEFAULT_SCALE = 1.0f / DENSITY_DEFAULT;
public static int DENSITY_DEVICE = getDeviceDensity();
public int widthPixels; // 屏幕像素(宽)
public int heightPixels; // 屏幕像素(高)
public float density; // 屏幕密度, density = dpi/160, dp与px之间的转化就是用此参数
public int densityDpi; // dpi
public float scaledDensity; // 字体大小转换会用到此参数 px = sp*scaledDensity
public float xdpi;
public float ydpi;
......
public void setToDefaults() {
widthPixels = 0;
heightPixels = 0;
density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
densityDpi = DENSITY_DEVICE;
scaledDensity = density;
xdpi = DENSITY_DEVICE;
ydpi = DENSITY_DEVICE;
......
}
}
这里面有几个重要的参数: density、densityDpi、scaledDensity,AndroidAutoSize的原理主要就是修改这几个参数值来实现屏幕的适配。下面还有一个系统提供的单位转化API,系统内部基本上都是调用此API来实现单位转化。
public static float applyDimension(int unit, float value,
DisplayMetrics metrics){
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
根据unit,将value转化为px值。
通过上面的单位介绍及之间的转换,我们可以得到如下结论:
明白上面这个结论,下面我们来讨论为什么我们日常对控件设置的宽/高为某一dp时,无法做到各个手机屏幕的适配。
可以看到屏幕的总 dp 宽度在不同的设备上是会变化的,但是我们在布局中填写的 dp 值却是固定不变的,这就导致我们设置的固定宽度在不同的设备上显示的比例不一样。 例如我们布局中有一个View设置固定宽度为180dp,在设备A中会占屏幕宽度的1/4,但是在设备B中只会占屏幕宽度的1/2,这种差别是十分巨大的。
这时我们要想完美适配,那就必须保证这个 View 在任何分辨率的屏幕上,与屏幕的比例都是相同的。
要做到在任何分辨率的屏幕上显示比例相同,我们该怎么做呢?
由于每种设备的宽度dp值是不同的,为使得View能够在不同设备上显示的比例一致,可以通过代码计算动态的设置每个View的dp值,这种方式显然是不合适的,工作量太大,太复杂。
由公式:dp = px/density 可知,由于px是屏幕分辨率,这个值有硬件确定,我们是无法改变的,那么我们可以通过修改density 的值使得不同分辨率的手机宽度dp值是相同的,这样当我们对View设置为某一特定的dp宽度时,占总宽度的dp比例是相同的,这样也就达到占屏幕的比例相同。
那么问题来了,我们如何确定density 的值呢?
由dp = px/density => 屏幕的总 px 宽度 / density = 屏幕的总 dp 宽度
屏幕的总 px 宽度 / density = 屏幕的总 dp 宽度 => 当前设备屏幕总宽度(单位为像素)/ 设计图总宽度(单位为 dp) = density
如果我们将一套设计图的总宽度(dp)作为最终手机屏幕的中宽度(dp), 从而达到修改density的目的,同时又可以保证最终不同分辨率手机的屏幕总宽度是相同的,这也就完成了适配。
AndroidAutoSize/今日头条 就是基于方案二的原理来实现屏幕适配的。
假设设计图中宽度为300dp,一个View在设计图上的尺寸为 100dp * 100dp,那么这个View的宽度占整个设计图宽度的33.3%,那么接下来我们来验证下通过方案二的适配方案,这个View能否做到不同分辨率的设备还能保持与设计图中一致的比例。
屏幕总宽度为 1080 px,根据上面公式求出density, 1080/300 = 3.6(density),那么这个尺寸为 100dp * 100dp的View,系统最后会将都换算成px,也就是 px=100*3.6=360(px), 然后 360/1080=0.333=33.3%,与设计图上的比例一致。
问题一
某些设备总宽度为1080px,但设备的dpi不同,是否对该方案适配产生影响?
答案当然是不会的,其实这个方案根本没有根据 设备提供的dpi 求出 density,是根据自己的公式求出的 density,所以这对该方案是没有影响的。
刚才我们验证的是宽度为1080px的设备,现在我们用另外一种分辨率的设备720px来验证。
屏幕总宽度为 720 px,根据上面公式求出density, 720 /300 = 2.4(density),那么这个尺寸为 100dp * 100dp的View,系统最后会将都换算成px,也就是 px=100*2.4=24.(px), 然后 240/720=0.333=33.3%,与设计图上的比例一致。
通过计算,该方案是可行的。
其实 AndroidAutoSize开源库已经很大程度上解决了如上两个缺点,如前面已经给出Activity的用法,适配粒度可以达到Activity。
设备的dpi值并不是任意指定的,它是通过 sqrt(screenWpx2 + screenHpx2) / 屏幕尺寸 计算出的结果(上面模拟器参数是我特意设置,为了很明显的演示所需) , 因此在大多数设备上对View的宽/高设以dp为单位进行设置值,差别并不是十分大,当然这只是大多数设备,因此要适配每种设备还是很难做到的。
开发者先在项目中根据主流屏幕的 最小宽度 (smallestWidth) 生成一系列 values-swdp 文件夹 (含有 dimens.xml 文件),当把项目运行到设备上时,系统会根据当前设备屏幕的 最小宽度 (smallestWidth) 去匹配对应的 values-swdp 文件夹,而对应的 values-swdp 文件夹中的 dimens.xml 文字中的值,又是根据当前设备屏幕的 最小宽度 (smallestWidth) 而定制的,所以一定能适配当前设备。
├── src/main
│ ├── res
│ ├── ├──values
│ ├── ├──values-sw320dp
│ ├── ├──values-sw360dp
│ ├── ├──values-sw400dp
│ ├── ├──values-sw411dp
│ ├── ├──values-sw480dp
│ ├── ├──...
│ ├── ├──values-sw600dp
│ ├── ├──values-sw640dp
详情可参考: https://juejin.im/post/5ba197e46fb9a05d0b142c62
https://juejin.im/post/5b7a29736fb9a019d53e7ee2
https://mp.weixin.qq.com/s/X-aL2vb4uEhqnLzU5wjc4Q
https://juejin.im/post/5b6250bee51d451918537021
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/135023.html原文链接:https://javaforall.cn
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有