Android开发一步到位屏幕适配解决方案

看简书上和CSDN都没有一篇特别有针对性的Android屏幕适配解决方案,大部分都是基于官方的基本的,或者挪来挪去的,最近自己刚好处理一个老项目需要适配大小屏,这里就贴一个自己的屏幕适配解决方案,有问题大家一起沟通。

大家都知道Android屏幕适配是件非常头疼的事,目前全世界安卓设备的大大小小分辨率,大大小小的尺寸,最终形成的设备屏幕大小种类不计其数,但就这一项就给开发者造成了不少困难,总是照顾住这种屏幕照顾不了那种屏幕。

先来放一种很熟悉的图:

image

针对Android屏幕适配,除了我们按照“灵活运用布局”、“尺寸限定符”、“布局相关属性”、“.9图”、“屏幕密度适配”等官方标准,但是却发现还远远不够,要么添加很多图片(从加载性能说似乎是必要的)使得apk变得很大,要么有多套布局工作量变大难以维护。针对屏幕适配,google官方也是系统默认支持的是采用屏幕像素密度来进行匹配相关资源和长度的,这一块不做过多的阐述了,建议参看官方文档。

这里我提供一个最牛、最简单、一步到位的方案,github地址:https://github.com/toperc/ScreenAdaptation

大概其优点如下:

  • 只需要提供一套最优图片,用于减小apk包大小。当然为了加载性能和图片更精细化显示采用多套也可以。
  • 无论大小屏如果布局确定只需要一套布局,当然如果横竖屏变换不同的屏幕展示,多套也是必要的。
  • 没有重叠的现象发生,按照默认布局方法可能在大屏上适配很好,到小屏上却出现重叠的现象,很常见,但这里我不允许他有!
  • 无论大小屏整体看过去布局几乎相同,因为是按比例来的。
  • 在大屏上展示不会随着完全比例放大而显得傻、大、憨。

其原理也很简单,就是根据基准屏幕像素密度来进行适当缩放后得到相对屏幕像素,然后给系统的像素密度重新赋值。

准备工作:
确定一套基准屏幕参数,然后布局时根据这套参数布局,单位仍然采用dp,并尽可能确定其布局控件的长度。

        //绘制页面时参照的设计图尺寸
        final float DESIGN_WIDTH = 1080f;
        final float DESIGN_HEIGHT = 1920f;
        final float DESTGN_INCH = 5.0f;

三步走:

  1. 确定放大缩小比例
  2. 确定参考屏幕密度
  3. 确定相对屏幕密度并重新赋值给系统的像素密度

主要方法:

    /**
     * 重置屏幕密度
     */
    public static void resetDensity(Context context) {
        //绘制页面时参照的设计图尺寸
        final float DESIGN_WIDTH = 800f;
        final float DESIGN_HEIGHT = 1280f;
        final float DESTGN_INCH = 5.0f;
        //大屏调节因子,范围0~1,因屏幕同比例放大视图显示非常傻大憨,用于调节感官度
        final float BIG_SCREEN_FACTOR = 0.8f;

        DisplayMetrics dm = context.getResources().getDisplayMetrics();

        //确定放大缩小比率
        float rate = Math.min(dm.widthPixels, dm.heightPixels) / Math.min(DESIGN_WIDTH, DESIGN_HEIGHT);
        //确定参照屏幕密度比率
        float referenceDensity = (float) Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT) / DESTGN_INCH / DisplayMetrics.DENSITY_DEFAULT;
        //确定最终屏幕密度比率
        float relativeDensity = referenceDensity * rate;

        if (ORIGINAL_DENSITY == -1) {
            ORIGINAL_DENSITY = dm.density;
        }
        if (relativeDensity > ORIGINAL_DENSITY) {
            relativeDensity = ORIGINAL_DENSITY + (relativeDensity - ORIGINAL_DENSITY) * BIG_SCREEN_FACTOR;
        }
        dm.density = relativeDensity;
        dm.densityDpi = (int) (relativeDensity * DisplayMetrics.DENSITY_DEFAULT);
        dm.scaledDensity = relativeDensity;
    }

上边放大缩小比例是根据屏幕的长宽和参考屏幕的长宽对应设定的,这样计算出来的屏幕密度是固定的。
注意最终赋值的三个成员变量的含义:

  • dm.density
    屏幕密度比率,不同设备的屏幕视图的长宽大小都是根据屏幕密度比率与屏幕密度基准值(160dp)的乘积。此成员变量控制拥有固定长宽值视图的缩放。
  • dm.densityDpi
    实际屏幕密度比率,但是单独设定此值并不一定起到缩放效果,需要配合density一起设定,此成员变量控制长宽自动wrapConent形式的缩放,因为wrapContent形式下系统自动为其分配默认的屏幕密度,如果不对其重新赋值,则不能根据规则进行缩放。
  • dm.scaledDensity
    独立像素密度,主要处理字体大小缩放,目前比较流行的字体单位为dp,第一能随着应用适应不同的设备缩放,第二避免了跟随系统缩放使得布局展示错乱。但是某些系统机开发者不能控制的例如toast字体大小等默认使用的单位仍然是sp,所以此成员变量也需要进行重新赋值。系统默认应用sp和dp二者的比率为1,但是某些情况下又想根据系统缩放,还要保持整体缩放比率,这是就要系统缩放和应用缩放结合着得出一个比率来最终赋值了。

配置地方:
DisplayMetrics相关参数的值有两种,一种是默认的,一种是动态调整后的。
获取默认的:

        DisplayMetrics display = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(display);

获取动态调整后的:

        DisplayMetrics dm = context.getResources().getDisplayMetrics();

应用展示最终使用的DisplayMetrics相关参数就是调整后的。android8.0之前,整个应用长宽缩放比率均是采用一套,无论时Activity和Application两者任意其一配置均可,但是在Android8.0的时候,系统架构调整,由原来的统一现在重点分配到每个Activity中和Application中,为了兼顾全局,两者都要设置,尤其是Activity。Activity中设置的时候要注意一定要设置在setContentView()之前,Application设置在onCreat()即可。
还应注意一点,当屏幕旋转时有时我们为了保存相关状态,所以不想让Activity的onCreat()方法重走一遍,但是屏幕旋转会使得屏幕密度参数进行重置,不得已我们还要在屏幕旋转的时候在重新设置一下屏幕密度。
源码中屏幕旋转所作的工作:

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
        if (mResources != null) {
            // The real (and thus managed) resources object was already updated
            // by ResourcesManager, so pull the current metrics from there.
            final DisplayMetrics newMetrics = super.getResources().getDisplayMetrics();
            mResources.updateConfiguration(newConfig, newMetrics);
        }
    }

为了使这个应用产生效果,建议将Activity形式的配置在BaseActivity中。
Activity中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //一定要写在setContentView之前
        ScreenUtil.resetDensity(this);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        //屏幕旋转时会使一些参数初始化,所以也需要在此重置一下
        ScreenUtil.resetDensity(this);
    }

Application中:

    @Override
    public void onCreate() {
        super.onCreate();
        ScreenUtil.resetDensity(this);
    }

这些配置完毕后,一旦app启动优先处理这件事,其他均按照默认处理,就是这么简单。

使用和不使用截图直观感受
采用默认布局方式截图对比:

image

采用此方案截图对比:
image

转载请注明出处,我是toperc.

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

推荐阅读更多精彩内容