android中一种方便的适配方案

使用姿势

  • 依赖

    implementation 'me.jessyan:autosize:1.1.2'

  • manifest配置

    <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的适配

    public class CustomAdaptActivity extends AppCompatActivity implements CustomAdapt {
    
        @Override
        public boolean isBaseOnWidth() {
            return false;
        }
    
        @Override
        public float getSizeInDp() {
            return 667;
        }
    }
    

    某个activity要取消适配

    public class CancelAdaptActivity extends AppCompatActivity implements CancelAdapt {
    
    }
    
  • fragment的适配

    首先要设置支持fragment适配AutoSizeConfig.getInstance().setCustomFragment(true);

    public class CustomAdaptFragment extends Fragment implements CustomAdapt {
    
        @Override
        public boolean isBaseOnWidth() {
            return false;
        }
    
        @Override
        public float getSizeInDp() {
            return 667;
        }
    }
    

    取消fragment的适配

    public class CancelAdaptFragment extends Fragment implements CancelAdapt {
    
    }
    
  • 使用过程遇到的问题

    在接入第三方库的时候,如果第三方库有activity和fragment那么也需要适配,否则会报如下异常

    “java.lang.RuntimeException: Unable to start activity ComponentInfo{com.wdg.tradecenter/com.yanzhenjie.permission.PermissionActivity}:java.lang.IllegalArgumentException: you must set design_width_in_dp in your AndroidManifest file”

    处理方式如下:

    AutoSizeConfig.getInstance()
                    .getExternalAdaptManager()
                    .addCancelAdaptOfActivity(PermissionActivity.class);
    
  • 代码中实际使用

image.png

然后就可以copy上图右下的xml代码了。

实现原理

  • 一些重要的单位
名称 一些重要的单位
px pixels(像素),屏幕上的实际像素点,无论控件或文字最终都会转化为px单位来显示其大小。
dp 与dip雷同,指的是设备独立像素,在不同分辨率和尺寸的手机上代表了不同的真实像素,计算公式:px = dp(dpi/160)
dpi 像素密度,指的是在系统软件上指定的单位尺寸的像素数量,它往往是写在系统出厂配置文件的一个固定值。
sp 全称scaled pixels,放大像素的缩写,专门用于处理字体的大小。它不仅与屏幕dpi有关,还与系统的默认字体大小有关。

通过上面的单位介绍及之间的转换,我们可以得到如下结论:

px= dp(dpi/160), density = dpi/160 => px = dp*density => dp = px/density;
明白上面这个结论,下面我们来讨论为什么我们日常对控件设置的宽/高为某一dp时,无法做到各个手机屏幕的适配。

设备 A , 屏幕宽度为 720px, dpi为160,则屏幕总dp为 720/(160/160) = 720 dp
设备 B , 屏幕宽度为 720px, dpi为320,则屏幕总dp为 720/(320/160) = 360 dp
可以看到屏幕的总 dp 宽度在不同的设备上是会变化的,但是我们在布局中填写的 dp 值却是固定不变的,这就导致我们设置的固定宽度在不同的设备上显示的比例不一样。 例如我们布局中有一个View设置固定宽度为180dp,在设备A中会占屏幕宽度的1/4,但是在设备B中只会占屏幕宽度的1/2,这种差别是十分巨大的。

这时我们要想完美适配,那就必须保证这个 View 在任何分辨率的屏幕上,与屏幕的比例都是相同的。

  • 解决方案

    由公式: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/今日头条 就是基于这个原理来实现屏幕适配的。

  • AndroidAutoSize是怎么以最小侵入代价应用上述原理的

    • 利用ContentProvider初始化该库—— ContentProvider的onCreate的调用时机介于Application的attachBaseContext和onCreate之间。

    • 在初始化的时候注册了一个Application.ActivityLifecycleCallbacks接口类回调

      application.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
      

      在接口Application.ActivityLifecycleCallbacks的实现类中修改density,以达到适配目的。

总结

这个适配方案优点明显

  • 使用成本低,简单。
  • 代码侵入性低
  • 没有性能损耗
  • 可适配activity/fragment/dialog/toast等

缺点

  • 既然AndroidAutoSize库可以修改density,其它库也可以修改,那么这样就会导致冲突。

参考博客

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容