优秀案例

安卓屏幕适配及实验

2020-02-20  本文已影响0人  fastcv

前言

关于屏幕适配,我看到了很多的笔记和文章,就现在来说,比较流行的有今日头条和sw两种,可能是我的段位太低,在之前的工作中,基本没有处理过屏幕适配的工作,一个dp适配所有,但是最近关注到了这个上面,于是抽时间了解了一下流行的这两种适配方案及其基本原理,写个demo试验并记录。

测试机配置及项目相关配置

理论知识

理论知识是实践的基础

先来理一理这个db、px、dpi 之间的关系

 px = desity * dp
 desity = dpi / 160
 px = dp * (dpi /160)
dpi ^ 2 = (width ^ 2 + height ^ 2 ) / (屏幕尺寸 ^ 2)

举个栗子:
一部手机,屏幕尺寸为6寸,分辨率为1920*1080,那么

dpi ^ 2 = (1080 ^ 2 + 1920 ^ 2) / 36 = (1166400 + 3686400) / 36 = 134800
dpi = 367 

那么,我们计算可以得出宽度的dp为

 dp_w = 1080 / (367 / 160) = 470.8

所以,这个手机控件宽度为470.8可以布满。了解以上的计算公式后,我们再来想一个问题。现在的设备基本上dpi是固定的,那么,假如我们现在手机的dpi为420,分辨率为1080*1920,那么计算得到宽度的dp值为

dp_w = (1080 * 160 / 420) = 411.43

那我们现在这样子写

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="411.43dp"
        android:text="这是一个按钮"
        android:layout_height="50dp"/>

</LinearLayout>

可以看到,刚好布满,但是,如果我们换成一个dpi为440的设备,我们再看看效果


从预览图上可以看出,我们这个控件超出了界线,会导致界面显示不全,那我们换成dpi为400的试试



可以看到,我们同样的布局代码,在不同的dpi的设备上,显示的效果差别还是很大的,假如现在的UI设计图的尺寸就是以411.43dp设计的,那么,在适配的时候就会出现上面的情况。

那么,按照这么说,是不是不能适配呢? 毕竟出厂的手机的dpi不同呀,但是,我们看上面的一个公式:

px = dp * density   ==>  dp = px / desity
desity = dpi / 160

在dp和dpi之间,靠的是desity来转换的,那么这个desity是什么,density 是 DisplayMetrics 中的成员变量,而 DisplayMetrics 实例通过 Resources#getDisplayMetrics 可以获得,而Resouces通过Activity或者Application的Context获得。(这里请看一种极低成本的Android屏幕适配方式 详细讲解)

那么,我们就可以通过修改density 的值即可适配我们的UI设计图,比如,我们的设计图为420dp,那么我们只需要把density 的值修改为

density = px_w / 420

即可,这里直接借用头条的适配代码

object HalloScreenAdapter {

    // 系统的Density
    private var sNoncompatDensity: Float = 0.toFloat()
    // 系统的ScaledDensity
    private var sNoncompatScaledDensity: Float = 0.toFloat()

    public fun setCustomDensity(activity: Activity, application: Application) {
        val displayMetrics = application.resources.displayMetrics
        if (sNoncompatDensity == 0f) {
            sNoncompatDensity = displayMetrics.density
            sNoncompatScaledDensity = displayMetrics.scaledDensity
            // 监听在系统设置中切换字体
            application.registerComponentCallbacks(object : ComponentCallbacks {
                override fun onConfigurationChanged(newConfig: Configuration?) {
                    if (newConfig != null && newConfig!!.fontScale > 0) {
                        sNoncompatScaledDensity = application.resources.displayMetrics.scaledDensity
                    }
                }

                override fun onLowMemory() {

                }
            })
        }
        // 此处以420dp的设计图作为例子
        val targetDensity = (displayMetrics.widthPixels / 420).toFloat()
        val targetScaledDensity = targetDensity * (sNoncompatScaledDensity / sNoncompatDensity)
        val targetDensityDpi = (160 * targetDensity).toInt()
        displayMetrics.density = targetDensity
        displayMetrics.scaledDensity = targetScaledDensity
        displayMetrics.densityDpi = targetDensityDpi

        val activityDisplayMetrics = activity.resources.displayMetrics
        activityDisplayMetrics.density = targetDensity
        activityDisplayMetrics.scaledDensity = targetScaledDensity
        activityDisplayMetrics.densityDpi = targetDensityDpi
    }
}

接下来,我们去验证一下,我们的代码的效果。


实践操作

实验是检验真理的唯一标准

我们编辑这样的一个布局,然后在不同dpi的设备上观察一下效果(假设设计图给的宽度dp为411.43)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_screen_adaption_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="今日头条适配方案"
        android:textSize="22sp" />

   <Button
       android:layout_width="200dp"
       android:layout_height="40dp"
       android:layout_marginTop="30dp"
       android:background="@drawable/shape_bt_bg"
       android:layout_gravity="center_horizontal"
       android:text="Bt1"/>

    <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="20dp"
        android:layout_gravity="center_horizontal"
        android:src="@drawable/img_test"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_marginTop="20dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >

        <View
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_marginLeft="10dp"
            android:background="#B32F2F"/>

        <View
            android:layout_width="150dp"
            android:layout_height="50dp"
            android:layout_marginLeft="10dp"
            android:background="#1CB6B1"/>

        <View
            android:layout_width="30dp"
            android:layout_height="50dp"
            android:layout_marginLeft="10dp"
            android:background="#1CB6B1"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_marginTop="20dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >

        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:layout_marginLeft="10dp"
            android:background="#B32F2F"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_marginLeft="10dp"
            android:background="#1CB6B1"/>
    </LinearLayout>

    <TextView
        android:layout_width="411.43dp"
        android:layout_height="50dp"
        android:gravity="right|center_vertical"
        android:text="我是一个文本框"
        android:background="#80E20E"/>

</LinearLayout>

这里,我们使用不同dpi的设备跑起来看一下效果。

未适配时(从左往右设备分辨率及dpi参数为:10801920-420、10802160_400、1080*2220_440)

显示的效果不尽人意,添加上我们的适配代码,再次运行看看效果

适配之后(从左往右设备分辨率及dpi参数为:10801920-420、10802160_400、1080*2220_440)

现在,看起来,效果好很多了吧,最后,借用大佬说过的一句话

所有的适配方案都不是用来取代match_parent,wrap_content的,而是用来完善他们的

所以,我们能使用match_parent,wrap_content的,还是使用它们比较好。

SW(smallestWidth)

在使用了头条的适配方案之后,我们再来试试smallestWidth的适配方案的效果,它的原理相对来说比较容易理解,就是让系统根据识别到的dp(宽度)值去资源文件中寻找对应限定符的文件夹下的资源文件。

举个栗子,假设我们的设备分辨率为1080*1920,dpi为480,那么,我们的dp_w的值就为 1080 / (480 / 160) = 360 , 那么,系统就会找到如下的资源目录(工具地址:生成资源目录):

未适配时(从左往右设备分辨率及dpi参数为:10801920-420、10802160_400、1080*2220_440)

显示的效果不尽人意,添加上我们的适配代码,再次运行看看效果

适配之后(从左往右设备分辨率及dpi参数为:10801920-420、10802160_400、1080*2220_440)

可以看到,适配效果还是不错的。详细的可以看这里

好了,这篇文章先到这里,后期有新的的再补充(客套话)。


上一篇下一篇

猜你喜欢

热点阅读