精华内容
下载资源
问答
  • 这个利用了percent库和图片的缩放处理,满足大多数屏幕适配的要求。
  • android 适配底部虚拟按键,屏幕处于全屏状态下,处理页面的展示。
  • android适配

    2015-06-26 15:15:52
    android适配,语言适配,屏幕适配,平台版本的适配(使应用程序在不同版本的系统中平稳运行)
  • Android适配之今日头条适配方案

    千次阅读 2019-05-21 14:43:00
    前介: 头条适配是根据设计图宽度,算出density(可以理解为密度),继而算出dpi,替换系统中的dpi.则XML即可按照UI所给设计图设置宽度...import android.app.Application; public class App extends Application{ @Ov...

    前介:

    头条适配是根据设计图宽度,算出density(可以理解为密度),继而算出dpi,替换系统中的dpi.则XML即可按照UI所给设计图设置宽度.达到适配目的.

    使用方式:

    直接在Application的onCreate方法中调用

    import android.app.Application;
    
    public class App extends Application{
        @Override
        public void onCreate() {
            super.onCreate();
            Density.setDensity(this,375f);
        }
    }

    这个地方我们需要注意 “ 375f ” 这个参数,针对这个参数,我们来好好说说。375这个值是一个UI图的参照值,单位是dp,我参照的是1334*750像素的图。

    px与dp的转换图

    Android单位:dp
    MDPI1px=1dp
    HDPI1.5px=1dp
    XHDPI2px=1dp
    XXHDPI3px=1dp
    XXXHDPI4px=1dp

     

     

     

     

     

     

     

    因为我们UI使用的是蓝湖的UI设计,可以根据这些不同的DPI来切换显示不同布局的不同宽度dp值

    然后我们继续看那个参数值375,因为我参照的是XHDPI的图进行绘制的,2px=1dp,375这个值就是宽度的像素750px/2px=375dp

    然后我们切换到XHDPI进行看看是不是375dp

    那么,我们就可以参照上面这个图给出的UI进行设置了,我这个地方给了个测试例子,375dp的话,要想能看出是否精准适配的话,我们可以这样,采用LineaLayout的vertical布局,靠左边设置一个宽度为200dp宽度的widget,靠右设置一个175dp宽度的widget,如果他们两个相交的地方刚好撘上,那么,这就是一个精准的适配

    有的UI给出的并不是像我们这种dp方式的,有的UI给的单位是px,其实,这也是很好解决的,比如UI给的是1080*1920的图,那我们就按照XHDPI 2px=1dp的方式来适配,那么,我们设置setDensity的值就为1080/2=540dp,然后UI凡是标注多少px,我们就在布局中给这个px除以2,然后设置到布局上即可

    注意:我们在做dp与px换算的时候,尽量保持这个结果dp值在320~560之间,为什么呢?看下面表述

    我们看上面的换算表格MDPI 1px=1dp,既然1px=1dp,那么我们怎么不直接给setDensity设置750呢,这样,我还省去了计算,直接参照MDPI的图来做,好,我们就这么来一次,给大家看看效果,最后再来说原因。

    确实能宽度适配,但大家看看,这个字体和页面明显有缩小的感觉,这种适配虽然宽度达到了,但明显不是我们想要的,那是什么原因导致的呢?这时候,我们就要引出一个关键因素像素密度dpi,我来列出两个公式

    dp=px/dpi*160
    
    dpi=px*160/dp

    这个计算公式是系统通过我们给定xml的值来计算出显示在屏幕上的布局,附录的代码就是利用的这个公式给系统设置计算参数,然后我们把刚刚MDPI适配的方式计算一下dpi

    750*160/750=160dpi

    160这个密度值明显过低,导致1密度下面占据太多的像素,界面看起来很拥挤,然后我们看下XHDPI的值,750的px等于375dp

    750*160/375=320dpi
    

    dpi的值是系统给的,大概查阅了流行的机子的dpi,就是我上面列举的范围内,在我们知道像素宽度的时候,尽量参考Xhdpi来做,这也是我们在适配图片的时候,喜欢参照xhdpi的原因

    其实,当我说到这的时候,原因其实差不多也都说出来了,虽然dpi是系统给的,但我们可以通过getDisplayMetrics替换掉这个dpi,上面的公式也给了,我们可以通过getDisplayMetrics拿到widthPixels屏幕的宽度像素值,然后再加上我们通过换算拿到的dp值,那dpi的值也就可以知道了,然后将这些值都设置到系统里面,让系统在解析xml布局的时候,按照我们给他设定的值来计算,然后显示在界面上,这就是适配的原理。

    有人说,我是老项目了,如果按照动态这么适配的话,那岂不之前的都要改?确实,但不要担心,还有另一种适配方式,smallWidth适配,修改系统的值属于动态方式来做,那么sw适配方式就属于静态适配了,sw也是根据上面的公式,计算出对应的dp值生成xml,生成xml的java代码也有,是一个哥们做的,大家可以看看他的文章,看这,也挺不错的,计算方式需要自己知道自己适配的机型的dpi,然后通过公式计算出dp值,然后将dp值设置到java代码中,然后运行一下,就可以了,将生成的values放到res文件夹下面就可以了

    附录

    Application里面调用

        @Override
        public void onCreate() {
         //记住,这个值需要自己根据UI图计算的哦,可不要直接抄
         Density.setDensity(this, 375);//375为UI提供设计图的宽度
        }
    
    Density.java
    import android.app.Activity;
    import android.app.Application;
    import android.content.ComponentCallbacks;
    import android.content.res.Configuration;
    import android.os.Bundle;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.util.DisplayMetrics;
    
    public class Density {
        private static float appDensity;
        private static float appScaledDensity;
        private static DisplayMetrics appDisplayMetrics;
        /**
         * 用来参照的的width
         */
        private static float WIDTH;
     
        public static void setDensity(@NonNull final Application application, float width) {
            appDisplayMetrics = application.getResources().getDisplayMetrics();
            WIDTH = width;
            registerActivityLifecycleCallbacks(application);
     
            if (appDensity == 0) {
                //初始化的时候赋值
                appDensity = appDisplayMetrics.density;
                appScaledDensity = appDisplayMetrics.scaledDensity;
     
                //添加字体变化的监听
                application.registerComponentCallbacks(new ComponentCallbacks() {
                    @Override
                    public void onConfigurationChanged(Configuration newConfig) {
                        //字体改变后,将appScaledDensity重新赋值
                        if (newConfig != null && newConfig.fontScale > 0) {
                            appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                        }
                    }
     
                    @Override
                    public void onLowMemory() {
                    }
                });
            }
        }
     
     
        private static void setDefault(Activity activity) {
            setAppOrientation(activity);
        }
     
        private static void setAppOrientation(@Nullable Activity activity) {
     
            float targetDensity = 0;
            try {
                targetDensity = appDisplayMetrics.widthPixels / WIDTH;
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
     
            float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);
            int targetDensityDpi = (int) (160 * targetDensity);
     
            /**
             *
             * 最后在这里将修改过后的值赋给系统参数
             *
             * 只修改Activity的density值
             */
     
            DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
            activityDisplayMetrics.density = targetDensity;
            activityDisplayMetrics.scaledDensity = targetScaledDensity;
            activityDisplayMetrics.densityDpi = targetDensityDpi;
        }
     
     
        private static void registerActivityLifecycleCallbacks(Application application) {
            application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                @Override
                public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                    setDefault(activity);
                }
                @Override
                public void onActivityStarted(Activity activity) {
                }
                @Override
                public void onActivityResumed(Activity activity) {
                }
                @Override
                public void onActivityPaused(Activity activity) {
                }
                @Override
                public void onActivityStopped(Activity activity) {
                }
                @Override
                public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
                }
                @Override
                public void onActivityDestroyed(Activity activity) {
     
                }
            });
        }
    }

    这样就可以完美的适配当前Android各个机型

    展开全文
  • 自动生成适配Android不同设备分辨率资源目录及文件 根据配置文件自动生成适配Android不同语言资源目录及文件
  • 解决Android适配问题
  • android 万能适配

    2016-04-13 13:32:16
    android 万能屏幕适配,支持手机,pad,针对pad处理是(屏幕大小大于6英寸,自动缩放到6英寸大小。),针对手机,自动全屏缩放。
  • 说起屏幕适配或者说UI适配应该是Android开发中必碰到的一个问题,在项目开发早期,UI小妹(当然了,也可能是UI小哥了)在问完IOS要多少分辨率的图片后来问你要多少分辨率的,此刻你要怎么回答;在面试中,面试官问你在...

    前言

    依赖库可访问我的Github:https://github.com/Mangosir/DimensMake
    使用起来很方便:

    • 在项目根目录下的build.gradle文件中添加如下代码

      allprojects {
        repositories {
          ...
          maven { url 'https://jitpack.io' }
        }
      }
      
    • 在moudle下的build.gradle文件添加如下依赖

      dependencies {
        compile 'com.github.Mangosir:DimensMake:1.0.4'
      }
      
    • 在工程中任何一个类添加如下代码,然后右键运行main方法即可

      public static void main(String[] args){
      
              //下面两种方法任选其一,具体参数信息见代码
              DimensBuild.buildDP();
              DimensBuild.buildPX();
      
      }
      

    说起屏幕适配或者说UI适配应该是Android开发中必碰到的一个问题,在项目开发早期,UI小妹(当然了,也可能是UI小哥了)在问完IOS要多少分辨率的图片后来问你要多少分辨率的,此刻你要怎么回答;在面试中,面试官问你在平时开发中是如何处理屏幕适配的问题的,这时候你要怎么应对;在开发过程中,碰到不同分辨率的手机,不同尺寸的手机出现的UI变形问题,你要怎么解决;今天我想用这篇文章来解决这些问题

    适配缘由

    做Android开发一定会碰到适配这个问题,在Android世界里,Android设备太多了,手机,平板,TV,手表等,光其中的手机这一项就有众多厂家发布的奇奇怪怪的手机,不仅分辨率各有不同,就是手机尺寸也是一言难尽,各种尺寸都有,更烦的是有的手机还在屏幕上搞个虚拟导航栏放在底部;厂家多也就算了,由于Android系统的开源,任何厂家,个人,OEM厂商,运营商都可以对Android进行定制,修改成他们想要的样子,这更加大了适配的难度(尤其是我在平时开发中经常会用到一些工程机);总之而言,Android的碎片化太严重了;不像ios适配机型有限,所以解决掉Android适配问题是做一个好的APP的第一步

    相关概念

    在开发过程中,UI给出的设计图上的所有元素尺寸都是以px作为单位的,但是我们在layout文件中定义相关View的长宽是用dp作为单位来指定其值的,定义文字大小是用sp作为单位,还可能会涉及到屏幕尺寸,屏幕分辨率,屏幕像素密度dpi

    这里说到了四个单位:px dp sp dpi,还有屏幕尺寸及屏幕分辨率:

    • 屏幕分辨率:指手机横纵方向上的像素点数,比如常见的720 * 1280 ,1080 * 1920,单位px,1px=一个像素点,

    • 屏幕尺寸:不是屏幕宽度,也不是长度,而是屏幕的对角线长度,比如常见的5.0尺寸,5.5尺寸,单位是英寸,1英寸=2.54厘米

    • dpi:全称dots per ich,也就是每英寸的像素点数(其实不是真正有多少像素点数),或者说屏幕像素密度;它是一个软件上的概念,是在系统软件上指定的单位尺寸的像素数量,它往往是写在系统出厂配置文件的一个固定值;我们可能还接触过一个单位叫ppi,它也是像素密度,这个是物理上的概念,它是客观存在的不会改变。dpi是软件参考了物理像素密度后,人为指定的一个值,这样保证了某一个区间内的物理像素密度在软件上都使用同一个值。这样会有利于我们的UI适配;比如,几部相同分辨率不同尺寸的手机的ppi可能分别是是430,440,450,那么在Android系统中,可能dpi会全部指定为480;ppi与屏幕尺寸和屏幕分辨率有关,它们的关系如下
      在这里插入图片描述

    • px:这个大家容易理解,也就是真实像素的单位,就跟s是时间秒的单位一样

    • sp:全称scale-independent pixel,也叫sip,即独立缩放像素,Android特有的用来描述文字大小的单位,建议使用12,14,18,20等偶数值,避免奇数和小数造成精度的丢失问题;为什么用这个不用dp呢?这是因为我们可以在手机系统设置中修改文字的大小,如果使用dp单位,文字大小不会被改变;而使用sp会随着修改而变化;具体原因可参考sp与dp的区别

    • dp:全称Density-independent pixel 也叫dip,即独立像素密度,它的一个特性就是与像素无关,或者说与终端上的实际物理像素点无关,是Android特有的用来描述View尺寸的单位,保证同一个分辨率的图片在不同分辨率的手机上保持同样的视觉大小(占用相识比例的空间)
      比如同样都是画一条长度是屏幕一半的线,如果使用px作为计量单位,那么在480x800分辨率手机上设置应为240px;在320x480的手机上应设置为160px,二者设置就不同了;如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度,原因在下方

    在Android中,规定以160dpi为基准,1dp=1px;320dpi下,1dp = 2px,计算公式:px = dp * (dpi / 160),屏幕密度越大,1dp对应 的像素点越多,如下表

    dp111111
    px11.5233.54
    dpi160240320480560640

    这里还有一个东西没有提,mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi等,它们是用来修饰Android中的mipmap(Eclipse中是drawable)文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值,它们与dpi的关系如下

    名称密度区间
    mdpi120dpi~160dpi
    hdpi160dpi~240dpi
    xhdpi240dpi~320dpi
    xxhdpi320dpi~480dpi
    xxxhdpi480dpi~640dpi

    通常情况下每个目录对应的屏幕分辨率如下

    名称代表分辨率
    mdpi320x480
    hdpi480x800
    xhdpi720x1280
    xxhdpi1080x1920
    xxxhdpi1440x2960

    但是这里要注意,同一个分辨率的dpi是可能不同的,比如1080x1920分辨率的dpi可以是420,也可以是480

    适配方案

    db适配

    在设计大图的时候,最好是同一个图片分别设计几种分辨率放到对应目录,以免变形和降低内存开销

    设计图标的时候同理,假如你的手机分辨率是1080x1920,手机屏幕密度是480,那1dp=3px;如果UI在设计过程中将这个图标设计为108x108px,那么转换为dp就是36x36dp,放在xxhdpi目录中,理论上来说在上表中对应的每个分辨率的Android手机里这个图标对应的大小都是36dp * 36dp;那么就需要为720x1280分辨率的手机设计一个72x72px的图标;为480x800分辨率的手机设计一个54x54的图标等,以此类推,即一套分辨率=一套位图资源,当然了这肯定会为UI设计师增加很多工作量,也会给APK增加大小

    如果没有设计其它屏幕分辨率的图标,那么Android会将这个目录的图标进行压缩或者放大展示在其它分辨率的设备上;比如在720x1280的设备,就会将这个图标进行压缩,展示后的像素就会变成72x72px;在1440x2960设备上,就会将这个图标进行放大,展示后的实际像素有可能会达到144x144px,这样虽然能达到类似的展示效果,比如占用相似比例的空间,但是图标内容是会变形的,只是变形的程度不同

    使用db作为尺寸单位可以基本解决不同手机上适配的问题,这可以说是最原始的Android适配方案

    使用这种方案能解决掉绝大部分的适配问题,但是使用这种方案还是存在一些问题,比如并不是所有分辨率为1080x1920手机的的dpi都是480,还有420的,甚至更低都有,那么1dp对应的px就会有不同,为什么呢?上面在介绍dpi的时候说到,dpi跟分辨率和屏幕尺寸有关系,虽然都是同样的分辨率,但是尺寸有5.5的,也有5.0,还有4.7的,那么1dp对应的px肯定是不同的,那么一个设置为360x360dp的ImageView在480dpi的手机上宽度就会铺满屏幕,但是在420dpi的屏幕,1dp=2.625px,实际展示出来的宽度只有945px,这样宽度就不能铺满屏幕,在更低的dpi屏幕上,差别就更明显了

    设计最大分辨率图片

    Android系统会根据屏幕密度自动选择对应的资源目录下的文件进行渲染加载,比如你的手机分辨率是480x800,那就会优先去hdpi目录下加载图片,如果没有,那就去更高的dpi的目录比如xhdpi,xxhdpi去加载,然后将原图片进行压缩展示,但是因为原图片像素很大,尽管进行了压缩,还是能展示的很清晰

    所以理论上来说只需要提供一种分辨率规格的图片资源就可以了,但是要提供尽量高分辨率的图片,现在的手机大部分的分辨率都是720x1280和1080x1920,所以提供一套xxhdpi规格的图片就可以了,这样就跟设计师说按照1080x1920分辨率设计就行了

    这只是一种比较偷懒的方法,比较适合那种用户基数小,用户群体固定的APP

    然后对layout文件中的ImageView的ScaleType属性的设置:

    设置不同的ScaleType会得到不同的显示效果,一般情况下,设置为centerCrop能获得较好的适配效果。

    屏幕分辨率限定符(宽高限定符)

    什么叫屏幕分辨率限定符呢?

    我们知道市面上不同分辨率的手机太多太多了,而这种方案就是尽可能多的将其列举出来,在res目录下面创建与各种分辨率一一对应的values-xxx 文件夹;然后选定一种基准分辨率,然后其它分辨率以该分辨率做参考,生成对应的dimens文件,如图:

    在这里插入图片描述

    其实分辨率方面1920x1080以及1280x720是应用适配占比最高,市场占有率有50%左右,至于这些数据从哪来,大家可以去友盟统计查看

    dimens文件生成原理

    我们选定一种分辨率作为基准分辨率,将宽高分成x份和y份,1/x份就是一个px;其它分辨率都以基准分辨率作为参考系,计算出一份等于多少px,最后编写对应的dimens文件

    比如我这里以1280x720作为基准分辨率,那么

    • 在values-1280x720目录下,创建lay_x和lay_y两个xml文件

    • 高度既然是1280px,那就将所有分辨率的高度都统一分为1280份,定义dimen个数是从y1到y1280,在当前分辨率下,分别取值为1px-1280px,存放在lay_y文件下

    • 宽度是720px,那就将所有分辨率的宽度统一划分为720份,定义dimen个数是从x1到x720,在当前分辨率下,分别取值为1px-720px,存放在lay_x文件下

    values-1280x720目录下文件搞定了,那么对于1920x1080分辨率的values-1920x1080目录下的lay_x和lay_y文件怎么写呢?

    • lay_x文件里还是定义dimen个数是从x1到x720,那么x1的值是(1080/720) * 1 = 1.5px ,x2的值是(1080/720) * 2 = 3.0px,以此类推,x720的值是(1080/720) * 720 = 1080.0px

    • lay_y文件里也是定义dimen个数是从y1到y1280,那么y1的值是(1920/1280) * 1 = 1.5px,y2的值是(1920/1280) * 2 = 3.0px,以此类推,y1280的值是(1920/1280) * 1280 = 1920.0px

    其它分辨率下的lay_x和lay_y文件都是如此计算,计算公式:(实际分辨率/基准分辨率)* X = px(X为份额)

    最终文件内容如图:

    在这里插入图片描述

    在这里插入图片描述

    使用样例1

    这时候假如你拿到的UI设计图上的一个控件宽度是540px,而且设计图是基于1920x1080分辨率设计的;而且控件是占屏幕宽度50%,那你在layout中可以对控件这样写

    android:layout_width="@dimen/x360" 
    

    至于这个360怎么得来的,就是根据上面的计算公式,(1080/720) * x = 540px,可以得出x = 360
    这样当app运行时,系统就会自动根据当前手机分辨率去相应的values目录下去找对应的值

    • 如果手机分辨率是1920x1080,那么系统就会去values-1920x1080目录下的lay_x文件找x360这个索引,获取到的值是540px,那控件就是如设计图一样占屏幕宽度的50%

    • 如果手机分辨率是1280x720,那么系统就会去values-1280x720目录下的lay_x文件找x360这个索引,获取到的值是360px,控件还是如设计图一样占屏幕宽度的50%

    这样在手机上的UI比例跟设计图一样,如此我们也就完成了UI适配的工作

    注意:这里有两个前提条件就是控件的大小要确定,设计图的大小也要确定

    使用样例2

    比如设计图上的控件宽度还是540px,但是设计图是基于1280x720设计的,那控件就是占屏幕宽度75%,那在layout里就得这样写

    android:layout_width="@dimen/x540" 
    

    540同样是根据上面的计算公式得出(720/720) * x = 540px,可以得出x = 540

    那么在1920x1080分辨率的手机上,系统就会去values-1920x1080目录下的lay_x文件找x540这个索引,得到的值是810px,这样同样是占屏幕宽度75%

    dimens文件生成脚本

    刚接触的童鞋是不是一看要写这么多文件,而且文件里面的内容这么多,几百上千条dimen,要是手写那不得写到天昏地暗啊;不用担心,作为程序猿,怎么能做这么体力活呢?写个脚本就可以完成这些工作

    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Author: Mangoer
     * Time: 2019/1/4 19:48
     * Version:
     * Desc:TODO()
     */
    public class DimenPXThread implements Runnable{
    
        private String path = "";
        private static List<DimensPX> dimensData = new ArrayList<>();
    
        static{
            dimensData.add(new DimensPX(480,800,720,1280));
            dimensData.add(new DimensPX(480,854,720,1280));
            dimensData.add(new DimensPX(540,960,720,1280));
            dimensData.add(new DimensPX(600,1024,720,1280));
            dimensData.add(new DimensPX(768,1024,720,1280));
            dimensData.add(new DimensPX(720,1184,720,1280));
            dimensData.add(new DimensPX(720,1196,720,1280));
            dimensData.add(new DimensPX(720,1280,720,1280));
            dimensData.add(new DimensPX(768,1280,720,1280));
            dimensData.add(new DimensPX(800,1280,720,1280));
            dimensData.add(new DimensPX(1080,1776,720,1280));
            dimensData.add(new DimensPX(1080,1794,720,1280));
            dimensData.add(new DimensPX(1080,1812,720,1280));
            dimensData.add(new DimensPX(1080,1920,720,1280));
            dimensData.add(new DimensPX(1200,1920,720,1280));
            dimensData.add(new DimensPX(1080,2016,720,1280));
            dimensData.add(new DimensPX(1080,2040,720,1280));
            dimensData.add(new DimensPX(1080,2160,720,1280));
            dimensData.add(new DimensPX(1440,2560,720,1280));
    
        }
    
        public void setPath(String path) {
            this.path = path;
        }
    
        @Override
        public void run() {
    
            if("".equals(path)) return;
    
            StringBuffer sb = new StringBuffer();
            for(int i=0; i<dimensData.size(); i++){
    
                DimensPX dimensPX = dimensData.get(i);
                String parentName = path + "values-"+dimensPX.getyPX() + "x" + dimensPX.getxPX();
                File file = new File(parentName);
                if(!file.exists()){
                    file.mkdirs();
                }
    
                /************************编写lay_x.xml文件*******************************/
    
                File lay_x = new File(file, "lay_x.xml");
                lay_x.delete();
                writeFile(lay_x, sb, dimensPX,"x");
    
                /**************************编写lay_y.xml文件********************************/
    
                File lay_y = new File(file, "lay_y.xml");
                lay_y.delete();
                writeFile(lay_y, sb, dimensPX,"y");
    
            }
    
        }
    
        private void writeFile(File lay, StringBuffer sb, DimensPX dimens, String type){
    
            //切勿使用FileWriter写数据,它是默认使用ISO-8859-1 or gb2312,不是utf-8,并且没有setEncoding方法
            BufferedWriter fw = null;
            try {
                fw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(lay,true),"UTF-8"));
    
                fw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>" +"\n");
                fw.write("<resources>" + "\n");
    
                int bound;
                if("x".equals(type)){
                    bound = dimens.getxBase();
                }else{
                    bound = dimens.getyBase();
                }
    
                for(int k=1; k<bound+1; k++){
    
                    sb.setLength(0);
                    sb.append("    <dimen name=\""+type+k+"\">");
    
                    float px = 0.0f;
                    if("x".equals(type)){
                        px = ((float)dimens.getxPX()/dimens.getxBase()) * k;
                    }else{
                        px = ((float)dimens.getyPX()/dimens.getyBase()) * k;
                    }
    
                    sb.append(px+"px</dimen>" + "\n");
                    fw.write(sb.toString());
                }
                fw.write("</resources>");
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    总结

    这个方案有一个难以避免的问题,就是容错率不高:

    列举出来的分辨率是有限的,而且是一对一的,手机是哪个分辨率就必须要找到对应的限定符;可是Android手机的分辨率太多了呀,这样没列举出来的分辨率就只能使用默认dimens了,不可避免的会导致UI变形;很恶心的是有的手机底部还有虚拟导航栏,这又得单独写一份dimens文件;还有平板的适配,也得增加dimens文件;大量的dimens.xml 文件能占到2M左右,增加了APK的体积

    不过该方案放弃Google推荐的dp适配方案,直接采用实际像素来适配,能解决绝大部分的适配问题,且能适配的较好,而且也有较多的团队使用这种方案,还是比较成熟的,前提是列举相当齐全的分辨率限定符

    最小宽度限定符

    顾名思义根据屏幕宽高中最小尺寸的dp值去对应的values-swdp目录下的dimens文件中获取结果,所以也叫smallestWidth限定符适配 或者 sw< N >dp限定符适配

    可以看到这里加了最小两个字,到底什么意思呢?我们知道手机屏幕是可以旋转的,这样宽高值就会互换,如果我们选定最小,那么该方案就不区分屏幕旋转了,只取宽高中最小的值

    最终目录如图
    在这里插入图片描述

    该方案和上面的屏幕分辨率限定符方案原理都是一样的,都是写好相关的文件,运行时由系统去寻找对应的资源文件;只不过上面那个方案是根据当前手机分辨率去找匹配到的values-xxx目录,而该方案是根据宽高中最小dp值去匹配对应的values-swxxxdp目录

    那这个最小dp值怎么算出来的呢?

    计算公式:(手机宽高最小像素值/(DPI/160) ) = dp

    用代码表达:

    		DisplayMetrics metrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(metrics );
            int heightP = dm.heightPixels;
            int widthP = dm.widthPixels;
            float density = dm.density;
            float smallestWidthDP;
            if(heightP < widthP ) {
                smallestWidthDP = heightP / density;
            }else {
                smallestWidthDP = widthP / density;
            }
    
    

    比如手机分辨率是1920 x 1080,显然最小值是1080,其DPI是480,那么dp = 1080 / (480/160) = 360dp;接下来系统就会根据这个360dp去匹配values-sw360dp这个目录下的dimens文件;如果找不到对应的values目录,系统就会向下找离values-sw360dp最近的一个目录,比如values-sw320dp

    dimens文件生成原理

    这里同样还是要选取一个最小宽度作为基准值,假如我选取360dp作为最小宽度基准值,也就是说将屏幕宽度等分为360份,这样从第一份到第360份所占的dp值的计算公式如下:

    dp = ( 实际最小宽度 / 最小宽度基准值 ) * X
    这里的X是份额,我们是以360dp作为基准,那X就是从1到360

    由此可知values-sw360dp目录下的dimens文件中内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <dimen name="dp_1">1.00000dp</dimen>
        <dimen name="dp_2">2.00000dp</dimen>
        <dimen name="dp_3">3.00000dp</dimen>
    
        <!--中间太多 省略-->
        <dimen name="dp_108">108.00000dp</dimen>
        
        <dimen name="dp_358">358.00000dp</dimen>
        <dimen name="dp_359">359.00000dp</dimen>
        <dimen name="dp_360">360.00000dp</dimen>
    </resources>
    

    那么其它目录下的dimens内容也就容易计算了,比如values-sw420dp目录,如果系统匹配到这个目录,说明该手机的最小宽度是420dp,套用上面的公式,可以得出从第一份到第360份所占的dp值如下:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <dimen name="dp_1">1.16667dp</dimen>
        <dimen name="dp_2">2.33334dp</dimen>
        <dimen name="dp_3">3.50001dp</dimen>
    
        <!--中间太多 省略-->
    
        <dimen name="dp_358">417.66786dp</dimen>
        <dimen name="dp_359">418.83453dp</dimen>
        <dimen name="dp_360">420.00000dp</dimen>
    </resources>
    

    使用样例

    假设你拿到的设计图是基于1920x1080的分辨率设计的,且DPI是480,设计图上的一个控件宽度是324x324px,控件所占宽度百分比是324/1080 = 30%,那我们应该在layout布局中给这个控件的宽高引用多少dimens合适呢?如下:

        <ImageView
            android:id="@+id/iv"
            android:layout_width="@dimen/dp_108"
            android:layout_height="@dimen/dp_108"
            android:src="@mipmap/ic_launcher"
            android:layout_centerInParent="true"/>
    

    为什么是dp_108呢?我们来算一下

    该设计图的最小宽度是1080px,那么最小宽度dp值根据开头的公式 px = px * (dpi / 160) 可以算出是 1080 / (480/160) = 360dp
    控件宽度 = 324 / (480/160) = 108dp
    套用【dimens文件生成原理】这一节的公式 dp = ( 实际最小宽度 / 最小宽度基准值 ) * X
    这个公式里实际最小宽度是360dp,最小宽度基准值是360dp,dp值是108dp,只有X未知
    所以X = 108dp / (360dp/360dp) = 108

    如此系统就会去values-sw360dp目录下的dimens文件中查找引用dp_108

    验证

    既然开发完了,那我们就举例验证下

    • 假如APP运行在一款1920x1080分辨率的手机上,DPI是400;可以得出该手机的最小宽度是1080px,dp值 = 1080 / (400/160) = 432,这样系统就会匹配到values-sw432dp目录下的dimens文件,该文件内容如下

      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <dimen name="dp_1">1.20000dp</dimen>
          <dimen name="dp_2">2.40000dp</dimen>
          <dimen name="dp_3">3.60000dp</dimen>
      
          <!--中间太多 省略-->
          <dimen name="dp_108">129.60000dp</dimen>
      
      
          <dimen name="dp_358">417.66786dp</dimen>
          <dimen name="dp_359">418.83453dp</dimen>
          <dimen name="dp_360">432.00000dp</dimen>
      </resources>
      

      可以看到在这个设备上该控件的实际dp值是129.6dp,同样占屏幕宽度432dp的30%,与设计图的比例是一样的

    • 假如APP运行在一款1280 * 720分辨率的手机上,DPI是320;可以得出该手机的最小宽度是720px,dp值 = 720 / (320/160) = 360dp,这样系统就会匹配到values-sw360dp目录下的dimens文件,该文件内容在上方以列出;可以得出这个设备上该控件的实际dp值是108dp,同样占屏幕宽度360dp的30%,与设计图的比例是一样的

    • 假如APP运行在一款1920x1080分辨率的手机上,DPI是420;可以得出该手机的最小宽度是1080px,dp值 = 1080 / (420/160) = 411.428571dp,这样系统就会找到一个小于等于411.428571dp的目录,而values-sw411dp这个目录是最接近它的,所以就是用该目录下的dimens文件,内容如下

      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <dimen name="dp_1">1.14167dp</dimen>
          <dimen name="dp_2">2.28333dp</dimen>
          <dimen name="dp_3">3.42501dp</dimen>
      
          <!--中间太多 省略-->
          <dimen name="dp_108">123.30036dp</dimen>
      
      
          <dimen name="dp_358">408.71786dp</dimen>
          <dimen name="dp_359">409.85953dp</dimen>
          <dimen name="dp_360">411.00000dp</dimen>
      </resources>
      

      可以看到在这个设备上该控件的实际dp值是123.30036dp,占屏幕宽度411.428571dp的29.97%,与设计图的比例差了一点点,原因就是虽然都是同样的分辨率,但是因为手机dpi的不同,导致算出来的最小宽度dp值可能出现小数这种情况,这样就没办法完全精准匹配values目录,只能匹配最近的目录,进而导致误差的出现;想优化这种情况只能是生成较全面的values目录,假如这里没有values-sw411dp目录,下面只有values-sw400dp目录,那误差就更大;不过1%的误差可以接受

    关于字体

    首先字体为什么官方推荐是使用sp作为单位呢?上面也说了,是为了响应用户在设置界面对手机全局字体大小的修改;这一点dp是做不到的,所以这里还是使用sp作为字体单位;那么我们就需要在dimens文件里添加字体相关的dimen引用,只不过单位是sp

    那这个大小是多少呢?要知道同一个手机上默认情况下的1dp和1sp大小是一样的,不相信吗?请看

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:layout_width="100sp"
            android:layout_height="100sp"
            android:text="Hello 1"/>
    
        <Button
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="Hello 2"
            android:layout_marginTop="100dp"/>
    
    </RelativeLayout>
    

    看预览效果
    在这里插入图片描述

    假如设计图是在【使用样例】这一节中的,某个文字的大小是36px,那sp值 = 36/(480/160) = 12sp,跟dp值计算方法一样;一般我们字体只需要设置12sp,14sp,16sp,18sp,20sp,22sp这些个就行了
    在values-sw360dp目录下的dimens文件如下:

        /**********字体适配***************/
    
        <dimen name="sp_12">12.0sp</dimen>
        <dimen name="sp_14">14.0sp</dimen>
        <dimen name="sp_16">16.0sp</dimen>
        <dimen name="sp_18">18.0sp</dimen>
        <dimen name="sp_20">20.0sp</dimen>
        <dimen name="sp_22">22.0sp</dimen>
        <dimen name="sp_24">24.0sp</dimen>
    

    在values-sw480dp目录下的dimens文件如下:

        /**********字体适配***************/
    
        <dimen name="sp_12">16.0sp</dimen>
        <dimen name="sp_14">18.666668sp</dimen>
        <dimen name="sp_16">21.333334sp</dimen>
        <dimen name="sp_18">24.0sp</dimen>
        <dimen name="sp_20">26.666668sp</dimen>
        <dimen name="sp_22">29.333334sp</dimen>
        <dimen name="sp_24">32.0sp</dimen>
    

    dimens文件生成脚本

    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Author: Mangoer
     * Time: 2019/1/4 19:50
     * Version:
     * Desc: TODO()
     */
    public class DimenDPThread implements Runnable{
    
        private String path = "";
        private static int wBaseDp = 360;
        private static List<DimensDP> dimensData = new ArrayList<>();
    
        static{
            dimensData.add(new DimensDP(240,wBaseDp));
            dimensData.add(new DimensDP(320,wBaseDp));
            dimensData.add(new DimensDP(360,wBaseDp));
            dimensData.add(new DimensDP(384,wBaseDp));
            dimensData.add(new DimensDP(392,wBaseDp));
            dimensData.add(new DimensDP(400,wBaseDp));
            dimensData.add(new DimensDP(410,wBaseDp));
            dimensData.add(new DimensDP(411,wBaseDp));
            dimensData.add(new DimensDP(420,wBaseDp));
            dimensData.add(new DimensDP(430,wBaseDp));
            dimensData.add(new DimensDP(432,wBaseDp));
            dimensData.add(new DimensDP(440,wBaseDp));
            dimensData.add(new DimensDP(480,wBaseDp));
            dimensData.add(new DimensDP(533,wBaseDp));
            dimensData.add(new DimensDP(592,wBaseDp));
            //600dp以后的基本是平板适配,如果你的APP不需要适配平板,那后面的就不需要加了
            dimensData.add(new DimensDP(600,wBaseDp));
            dimensData.add(new DimensDP(640,wBaseDp));
            dimensData.add(new DimensDP(662,wBaseDp));
            dimensData.add(new DimensDP(720,wBaseDp));
            dimensData.add(new DimensDP(768,wBaseDp));
            dimensData.add(new DimensDP(800,wBaseDp));
            dimensData.add(new DimensDP(811,wBaseDp));
    
        }
    
        public void setPath(String path) {
            this.path = path;
        }
    
        public void setwBaseDp(int wBaseDp) {
            this.wBaseDp = wBaseDp;
        }
    
        @Override
        public void run() {
    
            if("".equals(path)) return;
    
            StringBuffer sb = new StringBuffer();
            for(int i=0; i<dimensData.size(); i++){
    
                DimensDP dimensDP = dimensData.get(i);
                String parentName = path + "values-sw" + dimensDP.getSwDp() + "dp";
                File file = new File(parentName);
                if(!file.exists()){
                    file.mkdirs();
                }
    
                /************************编写dimens.xml文件*******************************/
                File dim = new File(file, "dimens.xml");
                dim.delete();
                writeFile(dim, sb, dimensDP);
    
            }
    
        }
    
        private void writeFile(File lay, StringBuffer sb, DimensDP dimens){
    
            //切勿使用FileWriter写数据,它是默认使用ISO-8859-1 or gb2312,不是utf-8,并且没有setEncoding方法
            BufferedWriter fw = null;
            try {
                fw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(lay,true),"UTF-8"));
    
                fw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>" +"\n");
                fw.write("<resources>" + "\n");
    
                StringBuffer sp = new StringBuffer();
                for(int k = 1; k<dimens.getwBaseDp()+1; k++){
                    sb.setLength(0);
                    sb.append("    <dimen name=\"dp_"+k+"\">");
                    float dp = ((float)dimens.getSwDp()/dimens.getwBaseDp()) * k;
                    sb.append(dp+"dp</dimen>" + "\n");
                    fw.write(sb.toString());
                    if (k >= 12 && k <=24 & k%2 == 0) {
                        sp.append("    <dimen name=\"sp_"+k+"\">");
                        float value = ((float)dimens.getSwDp()/dimens.getwBaseDp()) * k;
                        sp.append(value+"sp</dimen>" + "\n");
                    }
                }
                fw.write("\n");
                fw.write("    /**********字体适配***************/" + "\n");
                fw.write("\n");
                fw.write(sp.toString());
                fw.write("</resources>");
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    }
    

    总结

    该方案比较成熟,可以看做是屏幕分辨率限定符方案的升级版,容错率更高,只要values目录合理,即使手机对应的最小宽度值没有完全对应的资源文件,它也能向下兼容,寻找最接近的values目录;并且该方案产生的文件要更少;其实可以看出两种方案的最终目的都是以跟设计图相同的百分比去适配各个手机的屏幕

    疑问

    有人可能问了,设计师给的图上标的都是px,而该方案都是dp,那每次写的时候都要去算下每个view的dp值吗?

    不需要的,推荐几个工具:

    • 将设计图上传到蓝湖,上面会标注出dp
    • 使用像素大厨查看psd源文件,获取dp值
    • 没办法只能自己对着设计图的px算

    參考文章:
    https://www.jianshu.com/p/1302ad5a4b04
    https://www.jianshu.com/p/a4b8e4c5d9b0
    https://www.jianshu.com/p/2aded8bb6ede

    展开全文
  • Android 适配Dark Theme(暗黑模式)

    千次阅读 2020-06-11 20:36:33
    Android开发者应该如何让自己的app适配暗黑模式?接下来的文章将一一为你解答。 为什么我们需要暗黑模式 在Android 官方文档中,列举了暗黑模式的三个好处: 可以大大降低功耗(取决于设备的屏幕技术)。 提高了...

    暗黑模式

    在 2019 年的 Google I/O上,谷歌新发布的android 10终于从系统层级支持暗黑模式,那么为什么我们需要暗黑模式?Android开发者应该如何让自己的app适配暗黑模式?接下来的文章将一一为你解答。

    为什么我们需要暗黑模式

    在Android 官方文档中,列举了暗黑模式的三个好处:

    1. 可以大大降低功耗(取决于设备的屏幕技术)。
    2. 提高了弱视用户和对强光敏感的用户的可见性。
    3. 使任何人在昏暗的环境中都更容易使用设备。

    在OLED显示屏上,当一个像素是纯黑色(十六进制为#000000)的时候,该像素将会被关闭并且不消耗能量,这时如果显示屏显示的是大面积的黑色像素,将会大大降低显示屏消耗的电量。

    如何适配暗黑模式

    Force Dark自动适配

    Android 10 提供 Force Dark 功能。此功能可让开发者快速实现深色主题背景,只需要在 style.xml 中的应用主题中添加这一行代码android:forceDarkAllowed=“true” ,就可以完成自动适配。

    <resources>
        <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:forceDarkAllowed">true</item>
        </style>
    </resources>
    
    

    效果对比如下图所示。从结果来看,整体的界面风格好像确实变成了暗黑模式,但是菜单栏并未适配,所以这里我并不推荐你使用这种自动化的方式来实现深色主题,而是应该使用更加复杂一点的实现方式——自定义适配。

    图1 正常情况
    图2 Force Dark适配

    自定义适配

    1. 将App 使用的主题从之前默认的 Light 主题修改为 DayNight
    <resources>
    
        <!-- Base application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
        </style>
        
    </resources>
    
    1. 右击res目录 -> New -> Directory,创建一个values-night目录,然后右击values-night目录 -> New -> Values resource file,创建一个colors-night.xml文件。接着在这个文件中指定深色主题下的颜色值,颜色的命名要与colors.xml相同。
    --colors.xml
    <resources>
        <color name="colorPrimary">#008577</color>
        <color name="colorPrimaryDark">#00574B</color>
        <color name="colorAccent">#D81B60</color>
        <color name="colorTextView">#000</color>
    </resources>
    
    --colors-night.xml
    <resources>
        <color name="colorPrimary">#303030</color>
        <color name="colorPrimaryDark">#232323</color>
        <color name="colorAccent">#008577</color>
        <color name="colorTextView">#FFFFFF</color>
    </resources>
    
    1. 为那些在切换为暗黑模式时,需要改变颜色的控件适配,这里我们以TextView为例。
       <TextView
           android:id="@+id/name"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:textColor="@color/colorTextView"
           android:padding="10dp"
           android:textSize="20sp"
           app:layout_constraintLeft_toLeftOf="parent"
           app:layout_constraintTop_toTopOf="parent" />
    

    效果对比如下图。效果明显比自动适配好上不少,算是初步实现了对暗黑模式的适配。

    手动切换暗黑模式

    目前为止我们已经知道了如何适配暗黑模式,在完成适配之后,我们还需要为用户提供在运行时,切换主题的选项,切换的代码也很简单,在菜单监听中通过getDelegate().setLocalNightMode()来设置当前的模式。

        @Override
        public  boolean onOptionsItemSelected(MenuItem mi){
    
            if(mi.isCheckable()){
                mi.setChecked(true);
            }
            switch (mi.getItemId()){
                case R.id.mode_light:
                    getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
                    break;
                case R.id.mode_dark:
                    getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
                    break;
                case R.id.mode_system:
                    getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
                    break;
            }
            return true;
        }
    

    setLocalNightMode()方法接收一个mode参数,用于控制当前应用程序的夜间模式。mode参数主要有以下值可供选择:

    1. MODE_NIGHT_FOLLOW_SYSTEM:默认模式,表示让当前应用程序跟随系统设置来决定使用浅色主题还是深色主题。
    2. MODE_NIGHT_YES:脱离系统设置,强制让当前应用程序使用深色主题。
    3. MODE_NIGHT_NO:脱离系统设置,强制让当前应用程序使用浅色主题
    4. MODE_NIGHT_AUTO_BATTERY:根据手机的电池状态来决定使用浅色主题还是深色主题,如果开启了节点模式,则使用深色主题。

    需要注意的是,当调用setLocalNightMode()方法并成功切换主题时,应用程序中所有处于started状态的Activity都会被重新创建,那如果不想Activity重新创建Activity怎么切换主题呢?
    这时候我们可以在AndroidManifest中将configChanges设置为uiMode,使当前的Activity避免被重新创建。

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:configChanges="uiMode"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    

    现在当应用程序的主题发生变化时,MainActivity并不会重新创建,而是会触发onConfigurationChanged()方法的回调,你可以在回调当中手动做一些逻辑处理。

    override fun onConfigurationChanged(newConfig: Configuration) {
        val currentNightMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
        when (currentNightMode) {
            Configuration.UI_MODE_NIGHT_NO -> {} // 夜间模式未启用,使用浅色主题
            Configuration.UI_MODE_NIGHT_YES -> {} // 夜间模式启用,使用深色主题
        }
    }
    
    

    还有一点需要注意的是,切换逻辑仅在运行时生效,当我们重新启动 App 的时候,会与当前系统设置的模式保持一致。

    梁文锋

    原文地址:https://blog.csdn.net/weixin_44028826/article/details/106653038

    展开全文
  • 主要为大家详细介绍了Android适配安卓6.0蓝牙通讯实现过程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 一个便利于android 适配的dimens生成工具,分享给大家。
  • 主要介绍了Android 折叠屏适配攻略,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 如果你有更好的屏幕适配方案,请在评论区留下链接。非常感谢 一、最小宽度 限定符(推荐) 什么是最小宽度限定符,先看看我的截图把: 没错,以values-sw360dp 这种结尾的文件夹,和你项目里的values文件处于同一级...

    前言:关于这个问题就设计到了很多单位了,什么px、pt、dp、sp,手机尺寸,ppi等等,我这里只介绍我解除过比较靠谱的。如果你有更好的屏幕适配方案,请在评论区留下链接。非常感谢

    一、最小宽度 限定符(推荐)

    什么是最小宽度限定符,先看看我的截图把:

    没错,以values-sw360dp 这种结尾的文件夹,和你项目里的values文件处于同一级。

    我简单用我的话理解下:google推荐使用的单位是dp(就像苹果推荐使用的单位是pt一样),这里的意思是屏幕密度的意思。听到密度就知道了。虽然各大android尺寸分辨率很多,但是用密度衡量的话,会显得很少。简单的话来说就是:1寸的屏幕里,像素点是一样的

    1.1、感谢大神插件ScreenMatch

    详细链接,感谢

    当你会使用ScreenMatch插件后,我稍微讲下screenMatch.properties这个文件,是插件自动生成的:

    点开的是这样的

    ############################################################################
    #
    # You need to refresh or reopen the project every time you modify the configuration,
    # or you can't get the latest configuration parameters.
    #
    #############################################################################
    #
    # Base dp value for screen match. Cut the screen into [base_dp] parts.
    # Data type is double. System default value is 360.
    # I advise you not to modify the value, be careful !!!!!!!!! _^_  *_*
    # 设计图上最小的宽度的基准值。意思就是满屏时是360dp
    base_dp=360
    # Also need to match the phone screen of [match_dp].
    # If you have another dp values.
    # System default values is 320,360,384,392.7272,400,410,411.4285,432,480,533,592,600,640,662,720,768,800,811,820,960,961,1024,1280,1365
    # 要适配的最小宽度有哪些宽度
    match_dp=320,360,384,392.7272,400,410,411.4285,432,480,533,592,600,640
    # If you not wanna to match dp values above. Write some above values here, append value with "," .
    # For example: 811,961,1365
    # 需要忽略适配的宽度有哪些
    ignore_dp=662,720,768,800,811,820,960,961,1024,1280,1365
    # They're not android module name. If has more,split with , Symbol.
    # If you set, it will not show in SelectDialog.
    # If you have, write here and append value with "," .
    # For example: testLibrary,commonModule
    
    • base_dp=360 这里上面写的是你设计图上满屏的dp值。只要设置之后,手机上适配,控件的大小就可以温泉按照设计图走。超方便
    • match_dp=320,360,384,392.7272,400,410,411.4285,432,480,533,592,600,640 要适配的最小宽度有哪些宽度
    • ignore_dp=662,720,768,800,811,820,960,961,1024,1280,1365 需要忽略适配的宽度有哪些

    1.2、同时适配 手机 & 平板

    首先我们要知道一点,同时要适配手机和平板,这里应该有2套布局。做一套手机的做一套平板的。知道这个就好办了。我们都知道手机屏幕的大小一般在3英寸-5英寸之间,平板电脑的大小在7英寸-10英寸之间。所以手机会直接运行layout里的布局。我们再定义一个layout-sw600dp文件夹,与layout同一层。里面的布局就是平板布局。当然你要更精细的可以协商不同尺寸的文件夹。如下

    这样的话运行在手机上,会走layout。运行在平板会走layout-sw600dp。你可以放2套不同的布局试试。但是你的layout的xml名称要保持一致哦。

    我首先运行在手机上,再运行在平板上。同一个项目,看效果


    1.3、同时适配横竖屏

    当你会使用screenMatch插件里,是不是生成了很多最小宽度限定符swXXdp的文件夹。之前也说了最小宽度限定符,是不区分横竖屏,既最小那边是最小宽度。那么这样的话在切换横竖屏幕的时候是会混乱的。意思你再竖屏的时候设置了一个控件,占屏幕宽度一半,但切换到横屏时,却没有占一半。具体怎么做呢??

    将插件生成的文件夹,前面的swXXdp的s去掉,变成wXXdp。,大致意思,当前方向就是最小宽度的方向,这里区分了横竖屏了。看下面效果(这里配置好了,我什么都没干,只是旋转了手机,看适配效果):

    这里稍微提下,就不细讲了。如果你想单独为横屏或者竖屏幕适配一套,可以利用横竖限定符

    • values-sw400dp-land (最小宽度 400 dp 横向)
    • values-sw400dp-port (最小宽度 400 dp 纵向)

    关于以上demo链接

    二、屏幕分辨率限定符( 小心使用 )

    什么是屏幕分辨率限定符?

    首先感谢张鸿洋的自动生成工具

    但是经过我具体细致的测试下,我发现问题大大的。首先这本身就是屏幕分辨率限定符的弊端。先看我的测试,我使用的真机测试机,

    很明显我的手机是1080 x 1920的分辨率。这里我们从lay_x.xml分析就行了。首先我是以设计图里的宽度375去生成这些文件夹。
    之后我在Activity里放了2个控件,一个设置成@dimen/x375,另外一个设置成@dimen/x187。但是结果并不是,一个满屏,一个半屏。之后我很奇怪所以我在Activity里打印了当前@dimen/x375的长度,和屏幕当前宽度。结果是这样的:

    发现屏幕宽度确实是1080。但是@dimen/x375的值确是800。然后我那一堆values文件夹里去找,找到了values-1280x800文件夹里是800px。但是我就有点奇怪了。既然有values-1920x1080文件夹。为什么不走呢??!!我后面怒删values-1280x800文件夹。结果走的是values-1280x768。我整个人都震惊了,震惊了。我在想,我以前某些项目岂不是坑爹了。!!后面继续怒删values-1280x768文件夹。结果删了一个遍,系统一直去往上找。就是不找1920x1080。没办法继续去找资料了解这个机制。直接给链接吧

    被误用的屏幕分辨率限定符
    Android 屏幕适配,比较全的方案

    个人建议屏幕分辨率限定符还是小心使用吧。不像screenMathch那样能经受的住考验。如果你有更好的屏幕适配方法,请在评论区留下链接。一起加油进步。3q

    三、今日头条屏幕适配方案

    今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案

    字节跳动官方文章
    小结:经过实际测试和大量虚拟机测试,今日头条是以360dp为例,且以160dpi为例。不改代码直接去试。设置了360dp后,如果属于160dpi的手机确实正常,但是不是160的dpi却不是满屏。且如果动态获取dpi:appDisplayMetrics.densityDpi。却发现还是不能实现如官网文章所说的效果。是我忽略什么了吗?而且全it平台全是以官网文章为例子,360和160。大家真的用了多种机型测试过吗。希望有比较了解的,能解决我的疑惑!!!

    展开全文
  • A low-cost Android screen adaptation solution (今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案)
  • 一、适配国家语言 (1)手机系统语言适配 (2)应用切换语言 二、屏幕适配 (1)图片适配 (2)、XML布局适配 (3)、横竖屏适配 三、适配不同系统版本 前言 智能手机的用户分布在不同国家,且偏好各异,...
  • 主要介绍了Android 刘海屏适配总结(推荐),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了AndroidQ(10)黑暗模式适配的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • Android 适配pad屏幕

    千次阅读 2018-02-25 15:49:19
    Pad基本属于xlarge:资源目录配置修饰符物理尺寸的大小 layout-small(屏幕尺寸小于3英寸左右的布局),layout-normal(屏幕尺寸小于4.5英寸左右),layout-large(4英寸-7英寸之间),layout-xlarge(7-10英寸之间)...
  • Android dimens适配

    2015-12-28 11:22:05
    Android屏幕宽度不同的手机做的适配,可以适配95%以上的手机。
  • android适配自动生产xml

    2017-04-07 16:41:19
    android适配自动生产xml
  • Android大屏幕适配demo

    2018-02-02 10:51:18
    Android大屏幕适配demo,这个是仿照郭林文章中的例子,他的例子是用eclipse写的,直接运行会不兼容最新的sdk,我改成了AndroidStudio可以直接运行的demo.郭林此代码文章的连接 ...
  • Android适配曲面屏

    千次阅读 2018-08-02 10:54:08
    前面写了两篇适配 https://blog.csdn.net/qq_30711091/article/details/81332753 https://blog.csdn.net/qq_30711091/article/details/81318184 适配曲面屏很简单 曲面屏因为有曲面,导致布局有时会在曲面部分,...
  • Android适配方法之dimen适配-附件资源
  • 其中第一点是所有应用都需要适配的,对应下文的声明最大长宽比,而第二点,如果应用本身不需要全屏显示或使用沉浸式状态栏,是不需要适配的。 针对需要适配第二点的应用,需要获取刘海的位置和宽高,然后将显示内容...
  • android适配工具,生成相应的Value,value中对应的dimens
  • Android使用fitsSystemWindows属性实现--状态栏【status_bar】各版本适配方案 详情介绍:http://blog.csdn.net/ys408973279/article/details/49994407 Android使用fitsSystemWindows属性实现--状态栏【status_bar】各...
  • android 适配启动图变形

    千次阅读 2019-05-29 17:00:24
    一般的做法是在style中设置windowBackground为启动图,来避免冷启动时的黑屏,但是如果放一张尺寸的图在某些屏幕上就会出现拉伸,并且windowBackground还不能centerCrop,就算通过资源限定符也不能完美的适配 ...
  • Android适配:字体的大小适配

    千次阅读 2017-02-22 15:22:32
    Android适配:字体的大小适配 Android最让大家头疼的莫过于适配了,由于机型,屏幕等各种原因,适配是真心费劲,在这儿我简单的讲一下个人在项目中字体的一些简单适配方法: 假设需要适应320x240,480x320...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 106,245
精华内容 42,498
关键字:

android适配