Android学习笔记四十一之屏幕适配

来源:互联网 发布:什么牌的网络机顶盒好 编辑:程序博客网 时间:2024/05/29 18:15

1、屏幕适配原因

  我们都知道Android系统是开发源代码的,任何用户、开发者、OEM厂商、运营商都可以对android进行定制,修改成他们喜欢的样子。这样就导致Android碎片化非常严重,具体严重到什么程度呢?我们来看一张图

这里的每一个矩形都对应一种Android设备,根据统计,截止到2014年,支持Android的设备共有18796种,具体的统计数据请猛戳这里。

  随着搭载Android系统的设备不断增多,设备碎片化、品牌碎片化、系统碎片化、传感器碎片化和屏幕碎片化的程度在不断地加深,今天我们需要学习的就是对我们开发影响非常大的—屏幕碎片化。Android设备的屏幕尺寸很多,为了让我们开发的程序能够美观地显示在不同分辨率、不同尺寸、不同像素密度的设备上,我们在开发过程中需要进行屏幕适配。当然,我们不可能适配全部的屏幕尺寸和分辨率,我们只需要保证我们的程序在主流的屏幕尺寸和分辨率中运行就可以了。

学习屏幕适配之前,我们需要了解一些重要的概念。

2、重要概念

屏幕尺寸

屏幕尺寸是指屏幕对角线的长度,单位是英寸,1英寸=2.54厘米

屏幕分辨率

屏幕分辨率是指屏幕横向和纵向上的像素点数,单位是px,1px=1像素,一般格式是纵向X横向,例如1920X1080

屏幕像素密度

屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写,像素密度和屏幕尺寸和屏幕分辨率有关。计算公式是:

dip、dp、dpi、sp、px

  • dip:Density Independent Pixels(密度无关像素)的缩写。以160dpi为基准,1dp=1px

  • dp:与dip相同

  • dpi:屏幕像素密度的单位,“dot per inch”的缩写

  • sp:Scale-Independent Pixels的缩写,可以根据文字大小首选项自动进行缩放。Google推荐我们使用12sp以上的大小,通常可以使用12sp,14sp,18sp,22sp,最好不要使用奇数和小数。

  • px:像素,物理上的绝对单位

mdpi、hdpi、xdpi、xxdpi、xxxdpi

mdpi、hdpi、xdpi、xxdpi、xxxdpi的划分我们来看一张表:

在Google官方开发文档中,说明了 mdpi:hdpi:xhdpi:xxhdpi:xxxhdpi=2:3:4:6:8 的尺寸比例进行缩放。例如,一个图标的大小为48×48dp,表示在mdpi上,实际大小为48×48px,在hdpi像素密度上,实际尺寸为mdpi上的1.5倍,即72×72px,以此类推。

3、通过支持各种屏幕尺寸解决

1、使用wrap_content、math_parent、weight

  • wrap_content:根据控件的内容设置控件的尺寸

  • math_parent:根据父控件的尺寸大小设置控件的尺寸

  • weight:权重,在线性布局中可以使用weight属性设置控件所占的比例,计算公式是:所占宽度=原来宽度+剩余空间所占百分比的宽度

2、使用相对布局,禁用绝对布局

在实际开发中,我们简单一点的布局使用LinearLayout布局,复杂一点可以使用RelativeLayout,相对布局可以在各种尺寸的屏幕上保持控件之间的相对位置,还会使用这两种布局的嵌套。

3、使用限定符

可以使用尺寸限定符、最小宽度限定符,使用布局别名和使用屏幕方向限定符等。

4、使用.9patch图片

.9.png图片是Android系统下特有的,这个可以保持图片在拉伸之后还能保持一定的效果,比如,图片的重要像素不会被拉伸,使内容区域不受到拉伸影响等。使用.9.png,必须要创建.9.png图片,AndroidSDK给我们提供了工具中就包含了.9.png图片的创建和修改工具,当然也可以直接在Android studio中使用。

通过支持各种屏幕密度解决

把任何设备的手机宽度像素均分为320份,高度像素均分为480份,使用我们写好的程序自动生成资源values-×文件夹,里面包含lay_x.xml和lay_y.xml,分别对应宽度和高度的像素。

程序代码如下:

public class MakeXml {    private final static String rootPath = "F:\\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;}}

运行上面的代码会生成:

在480×320的设备上,x2就代表2.0px,y2就代表3.0px。

在800×480的设备上,x2就代表3.0px,y2就代表3.33px。依次类推。

使用:

<Button    android:background="#abd123"    android:layout_width="@dimen/x160"    android:layout_height="@dimen/y240"    android:text="Button" />

效果如下:

这样也可以实现屏幕适配。

通过设定百分比解决

  在Web页面设计中,从来没有听说过需要适配的,这是为什么呢?因为浏览器支持百分比设计,这样就可以适配不同的分辨率和尺寸,那么我们Android开发也是不是可以支持百分比设计,那么这样,我们的屏幕适配就没有那么头疼了。以Web页面的设计引出一种适配方案,通过百分比控制控件的大小,达到适配的目的。

  在这里推荐使用Google推出的一个支持百分比设计的库android-percent-support,但是这个库只有两种布局供我们使用,分别是PercentRelativeLayout、PercentFrameLayout,通过名字就可以看出,这是继承自FrameLayout和RelativeLayout两个容器类。

这两个布局支持的属性有:

  • layout_widthPercent

  • layout_heightPercent

  • layout_marginPercent

  • layout_marginLeftPercent

  • layout_marginTopPercent

  • layout_marginRightPercent

  • layout_marginBottomPercent

  • layout_marginStartPercent

  • layout_marginEndPercent

可以看到,支持的有宽高和margin,使用的时候直接用PercentRelativeLayout、PercentFrameLayout替换FrameLayout、RelativeLayout即可。

But没有LinearLayout的扩展,这是非常不好的,因为LinearLayout也是我们常用的布局之一啊,虽然LinearLayout有weight属性,但是weight只支持一个方向的。不用急,鸿洋大神早就已经给我们提供了自定义的一个PercentLinearLayout,具体请猛戳这里:Android 百分比布局库(percent-support-lib) 解析与扩展,里面做了使用的介绍和详细分析了源码,就不在做很多的介绍了。

下面是鸿洋大神提供的android-percent-support扩展,猛戳进入:android-percent-support-extend

Android屏幕适配就简单介绍到这里了,推荐几篇大神的文章:

鸿洋大神的:Android 屏幕适配方案

还有:Android屏幕适配全攻略(最权威的官方适配指导)

原创粉丝点击