推荐1倍效果图,即采用 720 * 360 大小( 1280 *720:两倍图 \ 1920 * 1080: 三倍图),最主要的原因就是1px = 1dp,效果图标多大的 px,布局就写多大 dp。

定义

  • 像素 - px:
    一个小黑点就是像素。
  • 尺寸:
    屏幕的对角线的长度。
  • 分辨率:
    整个屏幕一共有多少个点,也就是像素。
  • 像素密度 - dpi:
  1. 每英寸中的像素数。
  2. 假如设备分辨率为320*240,屏幕长2英寸宽1.5英寸,dpi=320/2 = 240/1.5 =160。
  3. 对应于DisplayMetrics类中属性densityDpi的值。
  4. 当然这种宽和高的dpi都相同的情况现在已经很少见。
  • 设备独立像素 - dip - dp:
    1.不同设备有不同的显示效果,不依赖像素。
    2.dp = px / density
    3.dp = px / (dpi / 160)
    4.dpi(像素密度)为160 的设备上1dp = 1px。
  • 放大像素 - sp:
    用于字体显示。
  • dp转px、px转dp
public class Dp2Px {
public static int dp2px(Context context, int dp) {
return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5);
}

public static int px2dp(Context context, int px) {
return (int) (px / context.getResources().getDisplayMetrics().density + 0.5);
}
}

常见设备的 dp、px、density 的关系

  • ldpi:
density:0.75
分辨率:240*320
关系:dp = px / 0.75
  • mdpi:
density:1
分辨率:320 * 480
关系:dp = px / 1
  • hdpi:
density:1.5
分辨率:480 * 800
关系:dp = px / 1.5
  • xhdpi:
density:2.0
分辨率:720 * 1280
关系:dp = px / 2
  • xxhdpi:
density:3
分辨率:1080 * 1920
关系:dp = px / 3
  • 获取参数
//以1280*720为基准:
//获取手机屏幕的宽和高
int widthPixels = getResources().getDisplayMetrics().widthPixels;
int heightPixels = getResources().getDisplayMetrics().heightPixels;
//density
float density = getResources().getDisplayMetrics().density;
//dpi
int densityDpi = getResources().getDisplayMetrics().densityDpi;
//1dp = 多少px
int px = Dp2Px.dp2px(this, 1);
//1px = 多少dp
int dp = Dp2Px.px2dp(this, 1);

输出结果:

E/MainActivity: widthPixels: 720
E/MainActivity: heightPixels: 1280
E/MainActivity: density: 2.0
E/MainActivity: densityDpi: 320
E/MainActivity: px:2
E/MainActivity: dp:1

常见适配方法

1. 图片适配
1.1 图片的查找顺序

  • 注意:一般手机 ldpi<drawable<mdpi<hdpi<xhdpi<xxhdpi 先找自己对应的文件夹,再找大分辨率,再找小分辨率
  • 注意:mdpi手机 ldpi<mdpi<drawable<hdpi<xhdpi<xxhdpi 先找自己对应的文件夹,找drawable文件夹,再找大分辨率,再找小分辨率
  • 适配主流手机,1920 * 1080 1080 * 720 800 * 480,高清图、背景图(全屏展示)准备多套 。小图片 准备一套高分辨率的;比如按钮,图标等
  • 为了是 apk 瘦身,有些图片不要准备多套,Android分辨率种类太多了;即使适配主流手机,展示比较清楚的背景图(比如:欢迎界面),可以准备多套

1.2 在小分辨率展示高清图,放到大分辨率会出现什么情况呢?

  • 比如:你针对 800480 分辨率手机做了背景图图片,正好完全展示;如果把它放到大分辨率1280720上,会对图片进行拉伸,会使像素点变大,可能会看到锯齿或者模糊的东西

2. 尺寸适配
2.1 布局文件设置宽高

  • 宽高设置参数:有的时候用dp,有的时候用px,大多数用dp
  • dp:dp(dip):px = dp * 密度比,与屏幕像素有对应关系,设置成dp后,在不同分辨率的手机上有可能尺寸会不一样 px:像素,比如小分辨率手机上一像素和大分辨率手机上一像素,所显示的图像是不一样的 理解dp和px之间对应的关系,不同分辨率的手机用不同的dp值来适配
    2.2 密度比
  • 密度比是固定的,可以查询文档Develop—>API Guides—>Best Practices—>Supporting Multiple mdpi手机:160dpi 是基准线,1px = 1dp * 1,其他手机的密度比 = 自己的dpi/160
  • 代码获取密度比:getResources().getDisplayMetrics().density
ldip:120px = 160dp * 0.75
mdpi:160px = 160dp * 1
hdpi:240px = 160dp * 1.5
xhdpi:360px = 180dp * 2

3. 布局适配

  • 位置不一样
    不同的手机在运行的时候选择不同的布局(布局名称一样,类似于dimens),比如:
  • 控件不一样
    不能用布局适配了;为什么?
    布局能够实现界面效果,但是完成布局后在代码中,由于控件都不一样,所以会找这两套布局的id,还要做判断,根据不同的布局做两套代码(如果页面复杂,给控件设置参数等十分繁琐)
  • 适用场景
    不同的手机的控件的位置不一样,发生了位置变化才会用到布局适配,实际开发中用的很少

4. dimen 适配

1.在默认的values中的dimens文件下声明(类似于Strings.xml)
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="harlWidth">160dp</dimen>

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- values-hdpi 480X800 -->
<dimen name="imagewidth">120dip</dimen>
</resources>

<resources>
<!-- values-hdpi-1280x800 -->
<dimen name="imagewidth">220dip</dimen>
</resources>


<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- values-hdpi 480X320 -->
<dimen name="imagewidth">80dip</dimen>
</resources>


2.在布局文件中引用
<TextView
android:layout_width="@dimen/harlWidth"
android:layout_height="wrap_content"
android:background="#0ff"
android:text="@string/hello_world" />

3.新建需要适配的values-XXX(比如values-1280x720,注意规范大值在前)

4.在新建values-1280x720中的dimens.xml文件中
* <dimen name="harlWidth">180dp</dimen>

5.所有手机适配找对应的默认的dimens
* 思考:如何计算dpi?如何计算手机密度比?能够用dp适配所有手机吗?
* dp不能适配所有手机;
* 举个例子:按钮占屏幕宽度一半,把宽度设置成160dp,120px和160px和240px可以占屏幕一半,但是360px则小于屏幕一半;
* 如果把宽度设置成180dp,那么360dp可以占屏幕一半,但其他几个又不行。
* 如果要适配所有手机的控件宽度为屏幕宽度的一半,该怎么做呢?用dimen

常用适配框架

​今日头条适配​

1) 添加依赖

allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
------------------------------------------------------
implementation 'me.jessyan:autosize:1.2.1'

2) 基本使用
AndroidAutoSize 在使用上非常简单,只需要填写设计图尺寸这一步即可接入项目;

<manifest>
<application>
...
<meta-data
android:name="design_width_in_dp"
android:value="540"/>
<meta-data
android:name="design_height_in_dp"
android:value="960"/>
...
</application>
</manifest>

有两种类型的布局单位可以选择,一个是 主单位 (dp、sp),一个是 副单位 (pt、in、mm)

  • 主单位: 使用 dp、sp 为单位进行布局,侵入性最低,会影响其他三方库页面、三方库控件以及系统控件的布局效果,但 AndroidAutoSize 也通过这个特性,使用 ExternalAdaptManager 实现了在不修改三方库源码的情况下适配三方库的功能
  • 副单位: 使用 pt、in、mm 为单位进行布局,侵入性高,对老项目的支持比较好,不会影响其他三方库页面、三方库控件以及系统控件的布局效果,可以彻底的屏蔽修改 density 所造成的所有未知和已知问题,但这样 AndroidAutoSize 也就无法对三方库进行适配

说明:在使用主单位时,design_width_in_dp 和 design_height_in_dp 的单位必须是 dp,计算公式 dp = px / (DPI / 160) 将 px 尺寸转换为 dp 尺寸,如果实在找不到设备的 DPI 那就直接将 px 尺寸除以 3 或者 2 。

3) 进阶使用
在 AndroidManifest.xml 中填写的设计尺寸,是整个项目的全局设计图尺寸,但是如果某些 Activity 页面由于某些原因,这个页面的设计图尺寸和在 AndroidManifest.xml 中填写的设计图尺寸不一样该怎么办呢?则可以让这个页面的 Activity 实现 CustomAdapt ,CustomAdapt 接口的第一个方法可以修改当前页面的设计尺寸。

  • 自定义 Activity
public class CustomAdaptActivity extends AppCompatActivity implements CustomAdapt {

/**
* 是否按照宽度进行等比例适配 (为了保证在高宽比不同的屏幕上也能正常适配, 所以只能在宽度和高度之中选择一个作为基准进行适配)
*
* @return {@code true} 为按照宽度进行适配, {@code false} 为按照高度进行适配
*/
@Override
public boolean isBaseOnWidth() {
return false;
}

/**
* 设计图尺寸为 1080px * 1920px, 高换算成 dp 为 960 (1920px / 2 = 960dp)
* <p>
* 返回的设计尺寸, 单位 dp
* {@link #getSizeInDp} 须配合 {@link #isBaseOnWidth()} 使用, 规则如下:
* 如果 {@link #isBaseOnWidth()} 返回 {@code true}, {@link #getSizeInDp} 则应该返回设计图的总宽度
* 如果 {@link #isBaseOnWidth()} 返回 {@code false}, {@link #getSizeInDp} 则应该返回设计图的总高度
* 如果您不需要自定义设计图上的设计尺寸, 想继续使用在 AndroidManifest 中填写的设计图尺寸, {@link #getSizeInDp} 则返回 {@code 0}
*
* @return 单位 dp
*/
@Override
public float getSizeInDp() {
return 667;
}
}

如果某个 Activity 想放弃适配,让这个 Activity 实现 CancelAdapt 接口即可。

  • 自定义 Fragment
    Fragment 的自定义方式和 Activity 是一样的,只不过在使用前需要先在 App 初始化时开启对 Fragment 的支持
AutoSizeConfig.getInstance().setCustomFragment(true);
  • 实现 CustomAdapt
public class CustomAdaptFragment extends Fragment implements CustomAdapt {

@Override
public boolean isBaseOnWidth() {
return false;
}

@Override
public float getSizeInDp() {
return 667;
}
}

4) 万能解决方案
在任何情况下本来适配正常的布局突然出现适配失效,适配异常等问题,只要重写 Activity 的 getResources() 方法即可,如果是 Dialog、PopupWindow 等控件出现适配失效或适配异常,同样在每次 show() 之前调用 AutoSize#autoConvertDensity() 即可。

@Override
public Resources getResources() {
//需要升级到 v1.1.2 及以上版本才能使用 AutoSizeCompat
AutoSizeCompat.autoConvertDensityOfGlobal((super.getResources());//如果没有自定义需求用这个方法
AutoSizeCompat.autoConvertDensity((super.getResources(), 667, false);//如果有自定义需求就用这个方法
return super.getResources();
}