美文网首页优秀案例
安卓屏幕适配及实验

安卓屏幕适配及实验

作者: fastcv | 来源:发表于2020-02-20 23:12 被阅读0次

    前言

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

    测试机配置及项目相关配置
    • 测试机为谷歌原生机(模拟器):10801920-420、10802160_400、1080*2220_440(分辨率_dpi)
    • 开发语言为kotlin
    • 代码地址:屏幕适配测试代码

    理论知识

    理论知识是实践的基础

    先来理一理这个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)

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

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


    相关文章

      网友评论

        本文标题:安卓屏幕适配及实验

        本文链接:https://www.haomeiwen.com/subject/rqefqhtx.html