Android屏幕适配总结和思考

前言

其实网上已经有很多人总结了Andorid 屏幕适配的知识. 这里总结了适配的主流方案, 通过分析思考适配的本质, 再来思考各个适配方案的优劣. 弄清楚为什么有适配问题.

一.什么是屏幕适配?

这里说的屏幕适配就是在Android众多机型上,能有一个相对一致的显示表现.Android机型分辨率,尺寸,宽高比太多了,如果不做适配后果就是显示效果差异比较大. 举个栗子
xml布局:

    <TextView
        android:layout_width="300px"
        android:layout_height="50px"
        android:background="@color/colorAccent"
        android:gravity="center_vertical"
        android:text="文本"/>

左侧是nexus4 右侧是nexus5
屏幕都是等分十等分的, 便于我们观察差异.

图一.png

二. 为什么需要做屏幕适配?

刚才可以看到是用px做单位的. 其实谷歌有默认的适配方案,就是采用dp做单位来适配. 我们来看看使用dp做单位的情况是什么样的.


图二.png

从图中可以看到其实使用dp做适配,基本解决了问题, 只有一些小差异,如果公司要求不高,使用dp做适配其实也可以的. 没有问题, 甚至还有些优势 这个后面再分析.
现在主要分析为什么dp适配产生了一些差异, 谷歌这么牛x的公司为什么设计的方案是这这个样子 ,好像并没有完全解决问题, 还需要我们继续操心适配问题. 要了解这些,就先需要了解一下基本概念.

三. 基本概念

相信各位大佬肯定猜到我要说什么了. 老生长谈的几个概念, 但是我还是说下,便于之后的分析理解

  • 像素 px: 像素就是对应屏幕的分辨率上的像素, 比如手机是分辨率是1080*1920的,那么手机横向就是1080个像素点. 我们看图一,右侧的手机就是这个分辨率, 在布局中写300px自然显示效果大约横向屏幕的30%宽度.不管什么适配, 其实最后都是转为px, 因为px是对应的屏幕物理像素.
  • 密度无关像素dp: 这个是谷歌为Android定制的, px = dpi/160 *dp 看到转化公式, 问题来了,dpi是什么.
  • 屏幕像素真实密度: 就是用屏幕对象线的像素点数量/对角线英寸 就可以计算得出.
  • 逻辑像素密度 dpi : 这个也是密度, 和上面的密度的区别就是这个密度是厂家rom定义的. 和屏幕像素真实密度有一定的差异. getResources().getDisplayMetrics().densityDpi 可以获取到该值;

我们看看nexus5 nexus5x 手机参数


image.png

实际运行效果 如下图: 这个显示效果和上面表格计算出来的一致


图三.png

可以看出使用dp做单位影响因素最大的就是dpi这个值了

适配问题的产生核心本质就一句话:

  • 因为dpi和实际像素密度的差异导致使用dp做单位,没有很好的适配.

四. Android各个适配方案对比

一. dp 适配

dp适配是谷歌原始的适配方案, 上文也做了分析, 大家再来思考一下之前提出的问题 为什么谷歌这样设计. 设个一个dpi的概念, 而不使用真实像素密度的?
其实dpi是为了给手机厂商灵活配置显示效果的. 有两个手机一个是5寸 一个是6寸 , 如果直接采用真实像素密度那么6寸手机就是5寸手机的完全放大版本.很多时候我们想着的是大屏手机可以看到更多的内容. 而不是单纯的等比例放大.
缺点: 在不同的手机上表现不同, 不符合公司设计图标准.

二. 直接资源引用适配

我们在布局中写的单位可以引用dimens资源文件. 配置不同的分辨率然后手机运行的时候可以根据不同分辨率去对应不同的单位. 参考链接:
http://blog.csdn.net/lmj623565791/article/details/45460089
优点: 可以很好的控制不同分辨率的显示效果.
缺点: Andorid手机分辨率越来越多, 维护这套多分辨率的文件十分繁琐. 更糟糕的是, 如果没有对应的分辨率 不会去自动匹配相似的分辨率.
这种方案不做推荐

三. 最小宽度限定符

和方案一类似, 但是不是指定分辨率的,而是指定屏幕宽度的, 这样一来文件就要少很多了
目标屏幕的最小宽度都大于480dp时,屏幕就会自动到带sw480dp后缀的资源文件里去寻找相关资源文件,这里的最小宽度是指屏幕宽高的较小值,每个屏幕都是固定的,不会随着屏幕横向纵向改变而改变。

image.png

参考使用链接: https://www.jianshu.com/p/759375113de9
优点: 可以很好的控制不同分辨率的显示效果. 缺少对应宽度的引用资源文件 ,可以默认去匹配相近的文件.
缺点: 还是要去维护较多的文件. 略繁琐.

四. 谷歌退出的新的百分比设置支持库

这个库提供了PercentRelativeLayut percentFrameLayout 支持常用的属性. 使用的时候设置百分比就可以用了.
实现原理: 通过Layoutparams 获取child设置的百分比. 通过计算获取这百分比应该是多少. 测量出控件的大小.
缺点: 使用起来比较麻烦. 以为设计图标注的是px.每次设置百分比的时候需要去计算.
设置百分比还是依赖父容器的.导致scrollView ListView 内的高度无法使用百分比.
这种方案用的人比较少 不推荐使用.

五. 张鸿洋提出的 AutoLayout 全新适配方式.

使用px单位并不是像素.内部经过处理.变成相应的百分比. 宽和高都是百分比. 宽的1px和高的1px不相同;
https://github.com/hongyangAndroid/AndroidAutoLayout
鸿洋大神的这套方案用的很多 . 虽然最后停止维护了,
优点: 配置简单, 使用起来可以直接用px对着设计稿直接写.
缺点: 在一些复杂布局, 带一些自定义控件的布局时候容易出现适配问题.
而且在实际项目使用的时候 有偶发失效的情况. 失效后, 界面由于px做单位. 界面元素变的很小.
这方案可以考虑使用. 毕竟在项目中运用广泛. 需要注意的是一些自定义布局的处理, 在列表中itemview需要通过工具类额外处理一下.

六. 今日头条方案:

其实以上几种方案都不是很好, 在项目中有各自缺点. 目前最推荐的还是头条的方案;
参考链接: https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

工具类如下:

    /**
     * @param activity
     * @param application
     * @param isLandscape 是否是横屏
     */
 public class ScreenUtils {

    /** 设计稿标准 */
    private static final float width = 750f;
    private static final float high = 1334f;


    private static float textDensity = 0;
    private static float textScaledDensity = 0;


    /**
     * 今日头条的屏幕适配方案
     * 根据当前设备物理尺寸和分辨率去动态计算density、densityDpi、scaledDensity
     * 同时也解决了用户修改系统字体的情况
     * @param activity
     */
    public static void setCustomDensity(@NonNull Activity activity) {
        setCustomDensity(activity, false);
    }


    /**
     * @param activity
     * @param isLandscape 是否是横屏
     */
    public static void setCustomDensity(@NonNull final Activity activity, boolean isLandscape) {
        final Application application = activity.getApplication();
        final DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
        if (textDensity == 0) {
            textDensity = displayMetrics.density;
            textScaledDensity = displayMetrics.scaledDensity;
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration configuration) {
                    if (configuration != null && configuration.fontScale > 0) {
                        textScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }

        final float targetDensity;
        if (isLandscape) {//横屏
            targetDensity = displayMetrics.widthPixels / (high / 2); //当前UI标准750*1334
        } else {
            targetDensity = displayMetrics.widthPixels / (width / 2); //当前UI标准750*1334
        }


        final float targetScaledDensity = targetDensity * (textScaledDensity / textDensity);
        final int targetDpi = (int) (160 * targetDensity);

        displayMetrics.density = targetDensity;
        displayMetrics.scaledDensity = targetScaledDensity;
        displayMetrics.densityDpi = targetDpi;

        final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
        activityDisplayMetrics.density = targetDensity;
        activityDisplayMetrics.scaledDensity = targetScaledDensity;
        activityDisplayMetrics.densityDpi = targetDpi;
    }

}

使用如下:

public class MainActivity extends AppCompatActivity {
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //在设置布局之前调用工具类方法
        ScreenUtils.setCustomDensity(this);

        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        int densityDpi = getResources().getDisplayMetrics().densityDpi;
        tv.setText("逻辑像素密度 dpi  " + densityDpi);

    }
}

看看实际运行效果:
实际效果中可以看到很好的完成了适配


图片四.png

之前我们分析了dp不太适配的原因. 所以今日头条方案简答暴力.
既然rom中设置的dpi不是实际像素密度. 那么我直接把dpi给你改成实际像素密度.
果然够简单暴力的...
工具类比较简单,复制就可以用. 就不上传github了.

五. 最后总结和思考:

实际开发中我们直接使用今日头条方案就可好了, 可以满足需求.
似乎我们已经找到了完美的适配方案了.
但是? 但是了. 但是这个所谓的适配的真的就是谷歌的个各个手机厂商想看到的么?
厂商定义dpi没有按照实际的来定义 而是做了调整, 调整之后的好处就是就是希望在大屏手机上可以看更多的内容. 在图三中我们发现nexus-5x 比nexus-5 屏幕大一些 dpi值厂商设置的也diy低一些, 更具公式px=dpi/160*dp 得知这样px就变小, 内容占的地方就小, 可以容纳更多内容. 这就是为了让大屏手机看到更多的内容. 为了验证这个猜想我们再看看更大手机的显示情况

图五.png

图五中左侧是通过工具类转换后的显示, 完成了所谓的适配.
右侧是本来的样子. 可以看到右侧中textview占据的屏幕空间确实要小一些.

图六.png

图六中的表格看到越是大屏手机, 厂商对dpi的标定比实际小的越多, 这样就可以让屏幕显示更多的内容.特别是看电子书之类的内容时候体验更明显, 大屏手机一页可以显示更多的内容. 大屏幕的优势就凸显出来了.

但是我们开发的普通应用为了在大屏小屏上一致的体验. 就不得不去修改dpi为实际值了.
以上是我的对Android中适配的一些思考和总结, 希望对大家有帮助~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容