窗外的山竹,在电线杆上多嘴 你说这一句 很有末日的感觉。山竹来了,连饭都没得吃,简直不要太恐怖。
说在前面
为什么要做屏幕适配?我前几天在郭霖大大的公众号里面看到一张图片,我觉得很有说明性的。
由于Android系统的开放性,任何用户、开发者、硬件厂商、运营商都可以对Android系统和硬件进行定制,修改成他们想要的样子。 上图就代表了各大品牌的手机碎片化的现状。随着Android设备的增多,设备碎片化、系统碎片化、屏幕尺寸碎片化、屏幕碎片化的程度也在不断加深。
当Android系统、屏幕尺寸、屏幕密度出现碎片化的时候,就很容易出现同一元素在不同手机上显示不同的问题。有的时候可能在4.3寸屏幕上面的样子是完美的,但是当其安装到5.7屏幕上面的就会出现很大的问题。
为了保证不同分辨率、屏幕大小的用户体验效果一致,从而引发了适配的课题
基本概念想·
1、 像素:
-
含义:通常所说的像素,就是CCD/CMOS上光电感应元件的数量,一个感光元件经过感光,光电信号转换,A/D转换等步骤以后,在输出的照片上就形成一个点,我们如果把影像放大数倍,会发现这些连续色调其实是由许多色彩相近的小方点所组成,这些小方点就是构成影像的最小单位“像素”(Pixel)。简而言之,像素就是手机屏幕的最小构成单元。
-
单位:px(pixel),1px = 1像素点 一般情况下UI设计师的设计图会以px作为统一的计量单位。
2、 分辨率:
-
含义:手机在横向、纵向上的像素点数总和 一般描述成 宽*高 ,即横向像素点个数 * 纵向像素点个数(如1080 x 1920)。
-
单位:px(pixel),1px = 1像素点
3、 屏幕尺寸(in):
-
含义:手机对角线的物理尺寸
-
单位 英寸(inch),一英寸大约2.54cm 常见的尺寸有4.7寸、5寸、5.5寸、6寸
4、 屏幕像素密度(dpi):
-
含义:每英寸的像素点数。 例如每英寸内有160个像素点,则其像素密度为160dpi。
-
单位:dpi(dots per inch)
-
计算公式: 像素密度 = 像素 / 尺寸 (dpi = px / in)
-
标准屏幕像素密度(mdpi): 每英寸长度上还有160个像素点(160dpi),即称为标准屏幕像素密度(mdpi)。
-
屏幕尺寸、分辨率、像素密度三者关系:
一部手机的分辨率是宽x高,屏幕大小是以寸为单位,那么三者的关系是:
5、 密度无关像素(dp):
-
含义:density-independent pixel,叫dp或dip,与终端上的实际物理像素点无关
-
单位:dp,可以保证在不同屏幕像素密度的设备上显示相同的效果,是安卓特有的长度单位。
6、 独立比例像素(sp):
-
含义:scale-independent pixel,叫sp或sip
-
单位:sp,字体大小专用单位 Android开发时用此单位设置文字大小,可根据字体大小首选项进行缩放; 推荐使用12sp、14sp、18sp、22sp作为字体大小,不推荐使用奇数和小数,容易造成精度丢失,12sp以下字体太小。
7、sp 与 dp 的区别:
-
dp只跟屏幕的像素密度有关;
-
sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。
追到android源码,发现系统内部用applyDimension() (路径:android.util.TypedValue.applyDimension())将所有单位都转换成px 再处理:
/**
* Converts an unpacked complex data value holding a dimension to its final floating
* point value. The two parameters <var>unit</var> and <var>value</var>
* are as in {@link #TYPE_DIMENSION}.
*
* @param unit The unit to convert from.
* @param value The value to apply the unit to.
* @param metrics Current display metrics to use in the conversion --
* supplies display density and scaling information.
*
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/
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;
}
可以发现dp和sp的区别在于density和scaledDensity两个值上;
/**
* The logical density of the display. This is a scaling factor for the
* Density Independent Pixel unit, where one DIP is one pixel on an
* approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen),
* providing the baseline of the system's display. Thus on a 160dpi screen
* this density value will be 1; on a 120 dpi screen it would be .75; etc.
*
* <p>This value does not exactly follow the real screen size (as given by
* {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of
* the overall UI in steps based on gross changes in the display dpi. For
* example, a 240x320 screen will have a density of 1 even if its width is
* 1.8", 1.3", etc. However, if the screen resolution is increased to
* 320x480 but the screen size remained 1.5"x2" then the density would be
* increased (probably to 1.5).
*
* @see #DENSITY_DEFAULT
*/
public float density;
/**
* A scaling factor for fonts displayed on the display. This is the same
* as {@link #density}, except that it may be adjusted in smaller
* increments at runtime based on a user preference for the font size.
*/
public float scaledDensity;
屏幕适配的方案:
-
图片适配
关于图片适配呢?我两个点我要提及:
1、 资源位置:
前面的介绍我们可以知道:在AndroidStudio的资源目录res下有五个层级图片文件夹,分别用来存放不同分辨率的图片:
drawable-ldpi :低分辨率(用的少了,一般不再用)
drawable-mdpi:中分辨率
drawable-hdpi:高分辨率
drawable-xdpi:较高分辨率
drawable-xxdpi:超级高分辨率
drawable-xxxhpi:顶级分辨率
在对应的文件夹下放置不同分辨率的图片就可以很好的对图片进行适配。
随着屏幕越来越大,推荐xxdpi的一套切图,这样就可以向下和向上兼容,节省资源。
2、 图片拉伸:
如果对于Android有一定的了解的开发者,都可能会晓得imageview有一个scaleType用来适配图片的大小:
-
android:scaleType=“center” 保持原图的大小,显示在ImageView的中心。当原图的size大于ImageView的size时,多出来的部分被截掉。
-
android:scaleType=“center_inside” 以原图正常显示为目的,如果原图大小大于ImageView的size,就按照比例缩小原图的宽高,居中显示在ImageView中。如果原图size小于ImageView的size,则不做处理居中显示图片。
-
android:scaleType=“center_crop” 以原图填满ImageView为目的,如果原图size大于ImageView的size,则与center_inside一样,按比例缩小,居中显示在ImageView上。如果原图size小于ImageView的size,则按比例拉升原图的宽和高,填充ImageView居中显示。
-
android:scaleType=“matrix” 不改变原图的大小,从ImageView的左上角开始绘制,超出部分做剪切处理。
-
androd:scaleType=“fit_xy” 把图片按照指定的大小在ImageView中显示,拉伸显示图片,不保持原比例,填满ImageView.
-
android:scaleType=“fit_start” 把原图按照比例放大缩小到ImageView的高度,显示在ImageView的start(前部/上部)。
-
android:sacleType=“fit_center” 把原图按照比例放大缩小到ImageView的高度,显示在ImageView的center(中部/居中显示)。
-
android:scaleType=“fit_end” 把原图按照比例放大缩小到ImageView的高度,显示在ImageVIew的end(后部/尾部/底部)
-
布局适配
关于布局适配呢?我也有两点要进行提及:
1、 建议:
不要使用绝对布局,使用相对布局和线性布局来代替绝对布局。
2、 资源位置:
在屏幕适配中,我们也可以和图片一样:根据手机大小不一样的手机建立不同的布局,比如说创建两个文件夹:
- layout-800 * 480
- layout-1280 * 720
手机会根据分辨率去找设定的不同大小的layout的布局。
-
Contractlayout适配:
我在这里呢? 就不讲这个了,详情呢?请查看我前面的文章ConstraintLayout用法详解。顺便也关注一下
-
尺寸适配(dimens适配)
在屏幕适配中,我们也可以和图片一样:根据手机大小不一样的手机建立不同的dimens,比如说创建两个文件夹:
- values-400*320
- values-800*480
手机会根据分辨率去找设定的不同大小的dimens的参数。
-
权重适配:
当布局占满屏幕宽或高的时候,子布局可以使用权重适配。例如LinearLayout中的weight属性。
-
dp dp dp
android的单位dp本身就有适配的功能,如下图所示:
所以用好dp,本身就是一种适配,下面附一下dp转px的方法 供日后查看:
fun Int.dp2Px(context: Context): Int =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this.toFloat(),
context.resources.displayMetrics
).toInt()
fun Int.px2Dp(context: Context): Int =
(this.toFloat() / context.resources.displayMetrics.density).toInt()
fun Int.sp2Px(context: Context): Int =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
this.toFloat(),
context.resources.displayMetrics
).toInt()
fun Int.px2Sp(context: Context): Int =
(this.toFloat() / context.resources.displayMetrics.scaledDensity).toInt()
fun Float.dp2Px(context: Context): Int =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
context.resources.displayMetrics
).toInt()
fun Float.sp2Px(context: Context): Int =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
this,
context.resources.displayMetrics
).toInt()
-
代码的适配:
//获取手机屏幕的宽和高
val widthPixels = resources.displayMetrics.widthPixels
val heightPixels = resources.displayMetrics.heightPixels
//给button设置宽和高
val layoutParams = bt_main_button.getLayoutParams()
layoutParams.width = widthPixels / 2
layoutParams.height = heightPixels / 2
bt_main_button.setLayoutParams(layoutParams)
由上面代码可知:我们可以计算屏幕的大小 来确定控件的大小 这样就能达到适配的效果。
说在最后:
适配是一门大学问,无论是刚入行的新手 还是入行多年的老手 对于适配其实或多或少都有点怵的。算了,我不知道哔哔什么了,窗外的那个吊车被台风吹到了我的窗前,我现在有点虚 时刻都在注意着它的“走位”,生怕他一个e闪将我留在这里。不写了不写了。
热门评论
感谢分享,什么时候可以分享测试方面的,安卓兼容这块