因为安卓设备的开放性,有很多的厂家都可以对安卓系统进行定制,市场上就出现了碎片化,也出现了各种各样的设备,不同的设备就有不同的需求,如果把所有的设备都使用的是一套方案肯定是不行的
下面我就结合着对慕课网视频的学习来写如何进行屏幕适配,慕课网视频地址
然后这个视频的主讲老师也写了一篇博客整理的非常清楚
http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023
最后我通过我的理解整理了下面的内容
这次博客的适配内容主要包括以下几个部分
- 相关概念
-解决方案
- 实施自适应用户界面流程
相关概念
- 屏幕尺寸
- 分辨率
- 屏幕像素密度
- 单位介绍
屏幕尺寸
概念:屏幕尺寸就是对角线的长度
单位是英寸,1英寸=2.54cm,比如说 4.7,5.0,5.5一些尺寸
我以前误解以为屏幕的适配主要和屏幕的尺寸有关,其实和下面的分辨率也有很大的关系,这个作为一个了解
分辨率
概念:横纵方向上的像素点数
一般一个屏幕的分辨率越高,屏幕显示的就会越清晰
单位是px 1px=1个像素点
1920 x 1080 就是指 纵向像素*横向像素
像素密度
概念:每英寸上的像素点数
单位是dpi
屏幕像素密度与屏幕尺寸和屏幕分辨率有关
计算公式举例:(√(1920²+1080²))/4.9
这个就是对角线上的像素点数/屏幕尺寸
单位介绍
1.dp,dip
密度无关像素,以160dpi为基准,1dip=1px
如果密度是320dpi,则1dip=2px,以此类推
2.px
这个是像素的单位
为什么我们常用dp很少用px作为长度单位
假如同样都是画一条320px的线,在480x800分辨率手机上显示为2/3屏幕宽度,在320x480的手机上则占满了全屏,如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度。
3.sp
sp设置文字的大小,最好不用奇数,一般会造成精度的丢失
4.mdip,dip,xdip,xxdip,不同的像素密度区分
所以我们可以计算出手机的像素密度,就知道该手机会调用哪个包下的图片了
解决方案
- 支持各种屏幕尺寸
- weight的使用
- 限定符
- 图片的自动拉伸
- 支持各种屏幕密度
- 实施自适应用户界面流程
支持屏幕尺寸
注意事项
- 使用wrap_content,math_parent,weight
- 使用相对布局,禁用绝对布局
- 使用限定符 large,sw600dp,小案例
- 使用自动拉伸图
1.weight方法的使用
只能在LinearLayout使用,对于想要改变的那边使用0dp
那么weight的宽度是如何给出的
设:
一个Button1 宽=match_parent , weight= 2
一个Button2 宽match_parent, weight= 1
这时候可能会认为第一个weight设置的大,所以会占据2/3的空间,但其实不是这样的
为什么第一个的weight大但是占据的宽度却小呢
显示的宽度=父控件的宽度+剩余空间所占百分比的宽度
首先设父控件的宽度是L,两个btn的宽度都为match_parent,所以两个空间的宽度和为2L,剩余的为L
显示宽度=L+(L-2L)*1/3=2/3L
由此可知如果设置成宽度为0dp则就是正常的比例了
2.使用限定符
有的时候我们的设备是平板,平板的宽度是比普通的手机要宽的,所以在设计的时候常常把它分成两栏,如图
左边一栏可以看做一个导航,右边显示的是正文内容
首先我们要为不同的设备创建好不同的布局
单面板布局:res/layout/main.xml
多面板布局:res/layout-large/main.xml 3.2之前的版本
多面板布局:res/layout-sw600dp/main.xml 3.2之后的版本
或者最后两个可以整合为:res/layout/main_twopanes.xml
内容的话普通手机和平板也会不一样
手机中的布局,只有一个显示内容的区域,就是一个Fragment
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<!--单面板-->
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/headlines"
android:name="show.xbl.com.shipeidemo.HeadlinesFragment"
/>
</LinearLayout>
平板的布局,一边是导航栏一边是显示内容的区域,所以有两个Fragment
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<!--多面板,宽屏-->
<fragment
android:layout_width="400dp"
android:layout_height="match_parent"
android:id="@+id/headlines"
android:name="show.xbl.com.shipeidemo.HeadlinesFragment"/>
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="show.xbl.com.shipeidemo.ArticleFragment"
android:id="@+id/article"/>
</LinearLayout>
有这么多的资源文件而且名字不同,怎样才能归结到一起,整成相同的名字
—那么就要使用资源文件来调布局的文件,这样直接调用大家相同的名字让手机系统自动适配下面的布局
res/values/layout.xml
<resources>
<item name="main" type="layout">@layout/main</item>
</resources>
res/values-large/layout.xml
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
res/values-sw600dp/layout.xml
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
同样还有屏幕方向限定符
横向:res/values-sw600dp-land/layouts.xml
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
垂直:res/values-sw600dp-port/layouts.xml
<resources>
<item name="main" type="layout">@layout/main</item>
</resources>
最后调用,让系统自动匹配就好啦
setContentView(R.layout.main);
比如:
适配的屏幕要求如下
/ | 小屏幕 | 7英寸平板电脑 | 10英寸平板电脑 | 电视 |
---|---|---|---|---|
纵向 | 单面板 | 单面板,带操作栏 | 双面板,窄,带操作栏 | / |
横向 | 单面板 | 双面板,宽,带操作栏 | 双面板,宽,带操作栏 | 双面板,宽,带操作栏 |
3. 图片的拉伸
①eclipse所带的图片拉伸软件
位置在:安装包下SDK文件→tools→draw9patch.bat
②AndroidStudio自带的图片拉伸软件
双击点击点击想要拉伸的.9图片就可以自动拉伸了
注意事项
再使用.9图片进行拉伸的时候,点点的位置不要经过图片的内容区域,否则内容区域也会被拉伸
将图片想要拉伸的上边框,左边框描黑边,为可拉伸的部分
图片的四个顶点不要拉伸
如果是黑边在左边,则左边向右边相同长度的整个区域都可以被上下拉伸
支持各种屏幕密度
- 使用非密度制约像素 —使用dp和sp
- 提供备用的位图
1.因为分辨率不一样,所以不能用px;因为屏幕宽度不一样,用dp的话长度也会不一样,那么我们可不可以用另外一种方法来统一单位,不管分辨率是多大,屏幕宽度用一个固定的值的单位来统计呢?
答案是:当然可以。
我们假设手机屏幕的宽度都是320某单位,那么我们将一个屏幕宽度的总像素数平均分成320份,每一份对应具体的像素就可以了。
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class MakeXml {
private final static String rootPath = "C:\\Users\\Administrator\\Desktop\\layoutroot\\values-{0}x{1}\\";
private final static float dw = 320f;
private final static float dh = 480f;
private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n";
private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n";
public static void main(String[] args) {
makeString(320, 480);
makeString(480,800);
makeString(480, 854);
makeString(540, 960);
makeString(600, 1024);
makeString(720, 1184);
makeString(720, 1196);
makeString(720, 1280);
makeString(768, 1024);
makeString(800, 1280);
makeString(1080, 1812);
makeString(1080, 1920);
makeString(1440, 2560);
}
public static void makeString(int w, int h) {
StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sb.append("<resources>");
float cellw = w / dw;
for (int i = 1; i < 320; i++) {
sb.append(WTemplate.replace("{0}", i + "").replace("{1}",
change(cellw * i) + ""));
}
sb.append(WTemplate.replace("{0}", "320").replace("{1}", w + ""));
sb.append("</resources>");
StringBuffer sb2 = new StringBuffer();
sb2.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sb2.append("<resources>");
float cellh = h / dh;
for (int i = 1; i < 480; i++) {
sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",
change(cellh * i) + ""));
}
sb2.append(HTemplate.replace("{0}", "480").replace("{1}", h + ""));
sb2.append("</resources>");
String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");
File rootFile = new File(path);
if (!rootFile.exists()) {
rootFile.mkdirs();
}
File layxFile = new File(path + "lay_x.xml");
File layyFile = new File(path + "lay_y.xml");
try {
PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
pw.print(sb.toString());
pw.close();
pw = new PrintWriter(new FileOutputStream(layyFile));
pw.print(sb2.toString());
pw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static float change(float a) {
int temp = (int) (a * 100);
return temp / 100f;
}
}
代码应该很好懂,我们将一个屏幕宽度分为320份,高度480份,然后按照实际像素对每一个单位进行复制,放在对应values-widthxheight文件夹下面的lax.xml和lay.xml里面,这样就可以统一所有你想要的分辨率的单位了,下面是生成的一个320*480分辨率的文件,因为宽高分割之后总分数和像素数相同,所以x1就是1px,以此类推
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">1.0px</dimen>
<dimen name="x2">2.0px</dimen>
<dimen name="x3">3.0px</dimen>
<dimen name="x4">4.0px</dimen>
<dimen name="x5">5.0px</dimen>
<dimen name="x6">6.0px</dimen>
<dimen name="x7">7.0px</dimen>
<dimen name="x8">8.0px</dimen>
<dimen name="x9">9.0px</dimen>
<dimen name="x10">10.0px</dimen>
...省略好多行
<dimen name="x300">300.0px</dimen>
<dimen name="x301">301.0px</dimen>
<dimen name="x302">302.0px</dimen>
<dimen name="x303">303.0px</dimen>
<dimen name="x304">304.0px</dimen>
<dimen name="x305">305.0px</dimen>
<dimen name="x306">306.0px</dimen>
<dimen name="x307">307.0px</dimen>
<dimen name="x308">308.0px</dimen>
<dimen name="x309">309.0px</dimen>
<dimen name="x310">310.0px</dimen>
<dimen name="x311">311.0px</dimen>
<dimen name="x312">312.0px</dimen>
<dimen name="x313">313.0px</dimen>
<dimen name="x314">314.0px</dimen>
<dimen name="x315">315.0px</dimen>
<dimen name="x316">316.0px</dimen>
<dimen name="x317">317.0px</dimen>
<dimen name="x318">318.0px</dimen>
<dimen name="x319">319.0px</dimen>
<dimen name="x320">320px</dimen>
</resources>
那么1080*1960分辨率下是什么样子呢?我们可以看下,由于1080和320是3.37倍的关系,所以x1=3.37px
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">3.37px</dimen>
<dimen name="x2">6.75px</dimen>
<dimen name="x3">10.12px</dimen>
<dimen name="x4">13.5px</dimen>
<dimen name="x5">16.87px</dimen>
<dimen name="x6">20.25px</dimen>
<dimen name="x7">23.62px</dimen>
<dimen name="x8">27.0px</dimen>
<dimen name="x9">30.37px</dimen>
<dimen name="x10">33.75px</dimen>
...省略好多行
<dimen name="x300">1012.5px</dimen>
<dimen name="x301">1015.87px</dimen>
<dimen name="x302">1019.25px</dimen>
<dimen name="x303">1022.62px</dimen>
<dimen name="x304">1026.0px</dimen>
<dimen name="x305">1029.37px</dimen>
<dimen name="x306">1032.75px</dimen>
<dimen name="x307">1036.12px</dimen>
<dimen name="x308">1039.5px</dimen>
<dimen name="x309">1042.87px</dimen>
<dimen name="x310">1046.25px</dimen>
<dimen name="x311">1049.62px</dimen>
<dimen name="x312">1053.0px</dimen>
<dimen name="x313">1056.37px</dimen>
<dimen name="x314">1059.75px</dimen>
<dimen name="x315">1063.12px</dimen>
<dimen name="x316">1066.5px</dimen>
<dimen name="x317">1069.87px</dimen>
<dimen name="x318">1073.25px</dimen>
<dimen name="x319">1076.62px</dimen>
<dimen name="x320">1080px</dimen>
</resources>
无论在什么分辨率下,x320都是代表屏幕宽度,y480都是代表屏幕高度。
那么,我们应该如何使用呢?
首先,我们要把生成的所有values文件夹放到res目录下,当设计师把UI高清设计图给你之后,你就可以根据设计图上的尺寸,以某一个分辨率的机型为基础,找到对应像素数的单位,然后设置给控件即可。
2.提供备用的位图
如果一个ImageView调用当前手机分辨率下的drawable分辨率下的图片,这个时候占用的内存是最小的
如果在当前分辨率下没有所对应的drawable文件夹,ImageView调用与分辨率不符的图片的时候会对图片进行压缩处理,导致占用内存
实施自适应用户界面流程
- 确定当前布局—判断当前是单面板还是双面板,并且每一块的布局是怎样的
- 根据当前布局做出响应—单面板的话首先要点击不同的item跳转到不同的界面,双面板左边是导航右边直接是界面
- 重复使用其他活动中的片段—fragment的使用,集中处理界面
- 处理屏幕适配变化—屏幕宽的时候顶部的导航可以全部显示出来,手机屏幕的时候可以将顶部的导航做成一个popupWindow