Android 屏幕适配之 dimens 适配

 

 

1 参考链接

http://blog.csdn.net/qq_34161388/article/details/73469319

首先需要明白一些概念,比如什么是 px,什么是 dp,什么是 dpi,讲这些概念的文章很多,推荐一个:

http://www.jianshu.com/p/ec5a1a30694b

 

2 为什么要适配

Google 官方推荐在写布局文件的时候使用 dp,在分辨率相差不大的情况下,dp 确实能自己进行一定适配,但是如果分辨率比较大的时候,还是会有问题,所以仍然需要适配。而且如果不是大公司的话,UI 设计图通常都是以 iOS 为标准分辨率的,这让我们 Android 写起布局来还是挺不顺手的。对于适配这个问题,网上众说纷纭,以前虽然有零零散散的看过,但是没有实践过,也是在最近遇到这个需求的情况下才研究了一下,现在做个记录。

 

2.1 情况一




    

 

上面是随便写的一个布局,就在 RelativeLayout 中放了一个 ImageView,ImageView 左边和顶部给了一定 margin,运行一下看看效果:

Android 屏幕适配之 dimens 适配_第1张图片      Android 屏幕适配之 dimens 适配_第2张图片      Android 屏幕适配之 dimens 适配_第3张图片

 

从左到右分别是 800x480、1920x1080、2560x1440 的分辨率的手机上的效果,可以从 ImageView 右边和底部与手机屏幕的距离很明显的看出来差异。

 

2.2 情况二




    

        

            

            

            

            
        
    

 

这是在一个 HorizontalScrollView 中放置了一个 LinearLayout,里面放置了 4 个 TextView,给了固定宽度为  90dp,运行一下看看效果:

Android 屏幕适配之 dimens 适配_第4张图片     Android 屏幕适配之 dimens 适配_第5张图片     Android 屏幕适配之 dimens 适配_第6张图片

 

从左到右仍然是 800x480、1920x1080、2560x1440,这下区别更明显,800x480 已经装不下这 4 个 TextView 了,1920x1080 则是刚好平分,2560x1440 则还有富余。

现在我在项目中遇到的情况就是情况二,在 HorizontalScrollView 中需要放置几个 TextView,每个 TextView 的宽度需要刚好为屏幕的 1./4。其实不适配也可以做,就是在 Activity 中使用 Java 代码获取屏幕宽度,动态修改每个 TextView 的宽度。不过这样的代码,写出来还是有点恶心的:

        HorizontalScrollView horizontalScrollView = findViewById(R.id.horizontal_scroll_view);
        LinearLayout linearLayout = (LinearLayout) horizontalScrollView.getChildAt(0);
        for (int i = 0; i < linearLayout.getChildCount(); i++) {
            linearLayout.getChildAt(i).getLayoutParams().width = getResources().getDisplayMetrics().widthPixels / 4;
            linearLayout.getChildAt(i).requestLayout();
        }

 

3 适配

针对上面这两种情况,我们就应该考虑适配了,适配的方式有很多,比如鸿洋大神的 AutoLayout(已经停止维护了,不建议在项目中使用),还有写多个 layout 布局呀,还有 dimens 适配呀等等,在对比了多种方法后,最后我采用了 dimens 适配这种方式。这样的适配方式虽然会加入很多不同分辨率的 dimens 文件,但是比起写多个 layout 布局,要修改 UI 的时候需要修改很多 layout 的情况,还是要方便一点,最主要的是写布局的时候会更方便!

首先我们要建立多个不同分辨率的 values 文件夹,主流的分辨率有:values-800x480、values-1280x720、values-1920x1080、values-2560x1440,而在 Android 中是以 480x320 为基准的,所以这个分辨率的 dimens 文件可以放到默认的 values 文件夹下,最后的文件夹结构如下:

Android 屏幕适配之 dimens 适配_第7张图片

 

每个文件夹下放置一个 dimens.xml 文件,dimens 中的值是按照基准分辨率进行等比例扩大或缩小的,如 480x320 的分辨率作为基准分辨率,那么 values 下的 dimens.xml 文件内容就应该是这样:



    1px
    2px
    3px
    4px
    ......
    1397px
    1398px
    1399px
    1400px


 

values-1280x720 文件夹下面的 dimens.xml 文件中的内容就应该是这样的:



    2.25px
    4.5px
    6.75px
    9px
    ......
    3143.25px
    3145.5px
    3147.75px
    3150px


 

values-1920x1080 文件夹下面的 dimens.xml 文件中的内容就应该是这样的:



    3.375px
    6.75px
    10.125px
    13.5px
    ......
    4714.875px
    4718.25px
    4721.625px
    4725px


 

你没看错,就是 px,这样我们可以直接用 px 来写布局文件了,而不用对着设计图将 px换算成 dp 了。而且既然可以将 480x320 的分辨率作为基准,那么同样也可以以我们的设计图的分辨率作为基准,比如我拿到的设计图是 720x1080 分辨率的,那么我们就可以以 1280x720 作为基准,480x320 的等比例缩小:



    0.4444444px
    0.8888889px
    1.333333px
    1.777778px
	......
    620.8889px
    621.3333px
    621.7778px
    622.2222px

 

1920x1080 等比例扩大:



    1.5px
    3px
    4.5px
    6px
    ......
    2095.5px
    2097px
    2098.5px
    2100px

 

这样适配后,再来对上面两种情况修改布局(以 720x1280 为基准分辨率,在 720x1280 分辨率下,1dp=2px):




    

 

Android 屏幕适配之 dimens 适配_第8张图片     Android 屏幕适配之 dimens 适配_第9张图片     Android 屏幕适配之 dimens 适配_第10张图片

 




    

        

            

            

            

            
        
    

 

Android 屏幕适配之 dimens 适配_第11张图片     Android 屏幕适配之 dimens 适配_第12张图片     Android 屏幕适配之 dimens 适配_第13张图片

 

4 特殊适配

在上面的 values 文件夹结构示意图中会有那么两个奇怪的分辨率,1720x1080 和 2360x1440,这个分辨率的手机应该是没有的,那么为什么要写这么两个奇怪的分辨率呢,里面的文件每一个 px 的值应该是多少呢?

先来说一种情况。有的手机是有虚拟按键的,比如 Nexus、华为,这些有虚拟按键的手机分辨率是将虚拟按键的高度算进去了的,但是 Android 在对 values 适配的时候却不会将虚拟按键的高度算进去,比如一个手机的分辨率本来是 1920x1080,但是虚拟按键栏占了144,所以它在适配的时候并不会去找 1920x1080 这个分辨率,而是去找 values-1776-1080,要是这个分辨率没有,那么就会去找低一级的分辨率 1280x720,再没有就再找更低一级的。所以布局又会出问题了,怎么办呢?其实虚拟按键栏的高度我们是可以获取的:

    /**
     * Description:获取虚拟按键栏高度
     * Date:2018/8/1
     */
    public static int getNavigationBarHeight(Context context) {
        int result = 0;
        if (hasNavBar(context)) {
            Resources res = context.getResources();
            int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android");
            if (resourceId > 0) {
                result = res.getDimensionPixelSize(resourceId);
            }
        }
        return result;
    }

    /**
     * Description:检查是否存在虚拟按键栏
     * Date:2018/8/1
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public static boolean hasNavBar(Context context) {
        Resources res = context.getResources();
        int resourceId = res.getIdentifier("config_showNavigationBar", "bool", "android");
        if (resourceId != 0) {
            boolean hasNav = res.getBoolean(resourceId);
            // check override flag
            String sNavBarOverride = getNavBarOverride();
            if ("1".equals(sNavBarOverride)) {
                hasNav = false;
            } else if ("0".equals(sNavBarOverride)) {
                hasNav = true;
            }
            return hasNav;
        } else { // fallback
            return !ViewConfiguration.get(context).hasPermanentMenuKey();
        }
    }

 

在得到虚拟按键栏的高度后,如为 144,再用分辨率去减这个高度,如 1920-144=1776,那么我们再增加一个 values-1776x1080 文件夹,里面的 dimens.xml 文件同 1920x1080 即可。但是不同手机的虚拟按键的高度也可能是不一样的,不可能写 values-1776x1080、values-1780x1080 这么多文件夹,上面说了找不到这个分辨率的时候,就会去找低一级的分辨率,所以我们写一个最低的就行了,比如 1720x1080,那么 1776x1080、1780x1080 等都会去找 1720x1080 了。

所以就多了 1720x1080 和 2360x1440 这两个文件夹,1720x1080 与 1920x1080 是一样的,2360x1440 则与 2560x1440 是一样的,把高写小了 200 就是为了尽可能的适配有虚拟按键栏的手机。

 

5 Dimens 文件生成器

但是这么多 dimens 文件不可能手写,推荐一个 dimens 文件自动生成器,已上传资源:

Dimens 文件生成器

使用方法:

Android 屏幕适配之 dimens 适配_第14张图片

 

其中 Dpi 一定要选择 160,最后生成的文件的值后面是 dp,全局替换成 px 就好。如果是以 iPhone6 的设计图 750x1334 为基准分辨率的话,比例会有点误差 Height 要写成1333.3333。

 

6 总结

屏幕适配其实说麻烦也麻烦,说简单也简单,麻烦是因为文件太多,简单是因为步骤固定,这也就是我为什么选择用 dimens 适配的原因,生成一下 dimens 文件的麻烦程度我还是可以接受的,只要将正确的 dimens 文件放到工程中,后面也就不用考虑其他的了。

你可能感兴趣的:(走在自己的Android之路上)