其实网上已经有不少人总结了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
刚才能够看到是用px作单位的. 其实谷歌有默认的适配方案,就是采用dp作单位来适配. 咱们来看看使用dp作单位的状况是什么样的. bash
相信各位大佬确定猜到我要说什么了. 老生长谈的几个概念, 可是我仍是说下,便于以后的分析理解app
咱们看看nexus5 nexus5x 手机参数 ide
实际运行效果 以下图: 这个显示效果和上面表格计算出来的一致 工具
能够看出使用dp作单位影响因素最大的就是dpi这个值了布局
适配问题的产生核心本质就一句话:字体
dp适配是谷歌原始的适配方案, 上文也作了分析, 你们再来思考一下以前提出的问题 为何谷歌这样设计. 设个一个dpi的概念, 而不使用真实像素密度的? 其实dpi是为了给手机厂商灵活配置显示效果的. 有两个手机一个是5寸 一个是6寸 , 若是直接采用真实像素密度那么6寸手机就是5寸手机的彻底放大版本.不少时候咱们想着的是大屏手机能够看到更多的内容. 而不是单纯的等比例放大.
缺点: 在不一样的手机上表现不一样, 不符合公司设计图标准.ui
咱们在布局中写的单位能够引用dimens资源文件. 配置不一样的分辨率而后手机运行的时候能够根据不一样分辨率去对应不一样的单位. 参考连接: blog.csdn.net/lmj62356579… 优势: 能够很好的控制不一样分辨率的显示效果. 缺点: Andorid手机分辨率愈来愈多, 维护这套多分辨率的文件十分繁琐. 更糟糕的是, 若是没有对应的分辨率 不会去自动匹配类似的分辨率. 这种方案不作推荐
和方案一相似, 可是不是指定分辨率的,而是指定屏幕宽度的, 这样一来文件就要少不少了 目标屏幕的最小宽度都大于480dp时,屏幕就会自动到带sw480dp后缀的资源文件里去寻找相关资源文件,这里的最小宽度是指屏幕宽高的较小值,每一个屏幕都是固定的,不会随着屏幕横向纵向改变而改变。
这个库提供了PercentRelativeLayut percentFrameLayout 支持经常使用的属性. 使用的时候设置百分比就能够用了. 实现原理: 经过Layoutparams 获取child设置的百分比. 经过计算获取这百分比应该是多少. 测量出控件的大小. 缺点: 使用起来比较麻烦. 觉得设计图标注的是px.每次设置百分比的时候须要去计算. 设置百分比仍是依赖父容器的.致使scrollView ListView 内的高度没法使用百分比. 这种方案用的人比较少 不推荐使用.
使用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);
}
}
复制代码
看看实际运行效果: 实际效果中能够看到很好的完成了适配
以前咱们分析了dp不太适配的缘由. 因此今日头条方案简答暴力. 既然rom中设置的dpi不是实际像素密度. 那么我直接把dpi给你改为实际像素密度. 果真够简单暴力的... 工具类比较简单,复制就能够用. 就不上传github了.
实际开发中咱们直接使用今日头条方案就可好了, 能够知足需求.
彷佛咱们已经找到了完美的适配方案了. 可是? 可是了. 可是这个所谓的适配的真的就是谷歌的个各个手机厂商想看到的么? 厂商定义dpi没有按照实际的来定义 而是作了调整, 调整以后的好处就是就是但愿在大屏手机上能够看更多的内容. 在图三中咱们发现nexus-5x 比nexus-5 屏幕大一些 dpi值厂商设置的也diy低一些, 更具公式px=dpi/160*dp 得知这样px就变小, 内容占的地方就小, 能够容纳更多内容. 这就是为了让大屏手机看到更多的内容. 为了验证这个猜测咱们再看看更大手机的显示状况
图六中的表格看到越是大屏手机, 厂商对dpi的标定就比实际越偏小. 这样就可让屏幕显示更多的内容.特别是看电子书之类的内容时候体验更明显, 大屏手机一页能够显示更多的内容. 大屏幕的优点就凸显出来了.
可是咱们开发的普通应用为了在大屏小屏上一致的体验. 就不得不去修改dpi为实际值了. 以上是个人对Android中适配的一些思考和总结, 但愿对你们有帮助~