Android屏幕适配总结和思考

前言

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

一.什么是屏幕适配?

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

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

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

图一.png

二. 为何须要作屏幕适配?

刚才能够看到是用px作单位的. 其实谷歌有默认的适配方案,就是采用dp作单位来适配. 咱们来看看使用dp作单位的状况是什么样的. bash

图二.png
从图中能够看到其实使用dp作适配,基本解决了问题, 只有一些小差别,若是公司要求不高,使用dp作适配其实也能够的. 没有问题, 甚至还有些优点 这个后面再分析. 如今主要分析为何dp适配产生了一些差别, 谷歌这么牛x的公司为何设计的方案是这这个样子 ,好像并无彻底解决问题, 还须要咱们继续操心适配问题. 要了解这些,就先须要了解一下基本概念.

三. 基本概念

相信各位大佬确定猜到我要说什么了. 老生长谈的几个概念, 可是我仍是说下,便于以后的分析理解app

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

咱们看看nexus5 nexus5x 手机参数 ide

image.png

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

图三.png

能够看出使用dp作单位影响因素最大的就是dpi这个值了布局

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

  • 由于dpi和实际像素密度的差别致使使用dp作单位,没有很好的适配.

四. Android各个适配方案对比

一. dp 适配

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

二. 直接资源引用适配

咱们在布局中写的单位能够引用dimens资源文件. 配置不一样的分辨率而后手机运行的时候能够根据不一样分辨率去对应不一样的单位. 参考连接: blog.csdn.net/lmj62356579… 优势: 能够很好的控制不一样分辨率的显示效果. 缺点: Andorid手机分辨率愈来愈多, 维护这套多分辨率的文件十分繁琐. 更糟糕的是, 若是没有对应的分辨率 不会去自动匹配类似的分辨率. 这种方案不作推荐

三. 最小宽度限定符

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

image.png
参考使用连接: www.jianshu.com/p/759375113… 优势: 能够很好的控制不一样分辨率的显示效果. 缺乏对应宽度的引用资源文件 ,能够默认去匹配相近的文件. 缺点: 仍是要去维护较多的文件. 略繁琐.

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

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

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

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

六. 今日头条方案:

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

工具类以下:

/**
     * @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中适配的一些思考和总结, 但愿对你们有帮助~

相关文章
相关标签/搜索