进阶之 -- 屏幕自适应

作者:夏至 欢迎转载,也请保留这段申明,谢谢
尊重劳动成果,请保留这份申明:
http://blog.csdn.net/u011418943/article/details/51247828

以前在做小东西的时候,觉得只要适应了自己的手机就可以了,发现真的给自己挖了很多坑。今天终于好好地研究了一下,这是个痛苦的事情,但是却又真的很有趣。
首先,先理解几个概念。

1、屏幕尺寸

 屏幕尺寸指屏幕的对角线的长度,单位是英寸 : 1 英寸 =  2.54 mm
 常见的尺寸有: 2.4 、2.8、3.5 、3.7,4.2、5.0等等

2、屏幕分辨率

 屏幕分辨率是指在横纵向上的像素点数,单位是px ,1px = 1个像素点

3、屏幕像素密度

屏幕像素密度是指每 英寸上的像素点数,单位是dpi (也叫dp),屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小,分辨率越高,像素密度越大,反之越小。

4、dp、dip、dpi、sp、px

dip 和 dp 是一个意思,即屏幕密度。至于 dpi 是什么?可以这样理解,假如 1 英寸 里面有160个像素,那么这个屏幕的像素密度就是160dpi。在这种情况下,dp 和 px 换算非常简单,如下图所示:
这里写图片描述

比如你在一个480x320的手机上布好了局,一看霸道炫酷,但你的单位是用px的,一到了800x480或者更高分辨率的手机的时候,就是东倒西歪的了。所以,我们一般不建议用px做单位。

5、mdpi 、hdpi、xdpi、xxdpi

上图中还有个ldpi 的,不过现在基本没人用这么惨不忍睹的分辨率了,所以就可以不用去管它了。用eclipse的我们经常会在 res 文件夹中看到这样定义的 drawable 文件夹,那么它们究竟是什么意思呢?Google 官方指定按照下列标准进行区分:
这里写图片描述

在进行开发的时候,我们也需要对应不同的文件夹放入不同的图片:
这里写图片描述

如果是对缩放不是特别重要的,我们可以把它放在drawable文件夹中,比如.9图等

6、支持屏边屏幕尺寸

回到我们的重点,我们今天要讲的就是屏幕的自适应。

- 使用wrap_content、match_parent、weight

为了确保布局的灵活性并适应各种不同的屏幕,我们一般建议使用 wrap_content 内容包裹,和match_parent 填充整个容器,这样就是不是绝对布局,而是可以根据屏幕的大小而自适应,但我们布局那么多,不可能每个都用这两个属性,所以,局限性也很大。
至于 weight 权重这个属性呢,好用是好用,不过一般是在线性布局中,但线性布局有个尴尬的地方就是,如果你的布局很多,那么它的树根就会很长,这个对于 UI 的优化是很不利的,而谷歌一般建议我们的布局层数不要超过10层,所以,如果是相对比较复杂的,都是建议使用 Reativelayout 相对布局。所以,局限也很大。

- 使用 dp 和百分比

这个时候,你可能会说,使用 dp 和 sp 啊,dp 是密度,你分辨率不同,用 dp 也能很好的适应。 在网上找到两张图。使用相同的代码在两种不同的机型上运行的效果如下:
这里写图片描述

很尴尬是不是,都使用了 dp 了,为什么还会出现这样的问题?其实很简单,因为屏幕的多样性,并不是所有的屏幕都是相同的 dp 长度,比如 Nexus One 属于hdpi ,屏幕宽度是320dp,而 Nexus 5属于xxhdpi ,屏幕宽度是360dp。所以,就算你用了dp也一样存在问题。比如,你在320 dp 里,写了一个TextView ,字体大小为24 sp,在两个不同的设备下,大小显而易见的不一样。
这个时候,你是不是很绝望,感觉生无可恋的感觉。分辨率不一样,所以不能用px;屏幕宽度不一样,所以不能用 dp ,还让不让人活了。别急,还是有解决方法的。参考两位大神的神作:
鸿洋大神的:http://blog.csdn.net/lmj623565791/article/details/45460089
一叶飘舟的:http://blog.csdn.net/jdsjlzx/article/details/45891551

基本看了它们的你就会了。我自己只是把我的理解也写一下而已。
当然,在安卓 5.0 之后,安卓也支持百分比的了。这无疑是个好消息。大家可以看到,为什么我们在电脑浏览网页什么的,那些控件大小,总能根据我们屏幕大小的不同而变化,就是人家用了百分比的效果啊,百分比就是占据整个屏幕的比例,这个很容易懂吧。但是它的局限性就是在也在这里,需5.0系统,我自己试了一下,效果真心不错,但是放到4.3的系统,就乱套了。所以,可以适当了解。如果你想更近一步的了解,请参考鸿洋大神的作品:http://blog.csdn.net/lmj623565791/article/details/46695347

好了,言归正传,我们的方案很简单,就是在项目中添加你的分辨率的不同文件夹,如:

这里写图片描述
自动生成 分辨率包的 Java 代码如下:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
/**
 * Created by zhy on 15/5/3.
 */
public class GenerateValueFiles {
    private int baseW;
    private int baseH;
    private String dirStr = "./res";
    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";
    /**
     * {0}-HEIGHT
     */
    private final static String VALUE_TEMPLATE = "values-{0}x{1}";
    private static final String SUPPORT_DIMESION = "320,480;480,800;480,854;540,960;600,1024;720,1184;720,1196;720,1280;768,1024;800,1280;1080,1812;1080,1920;1440,2560;";
    private String supportStr = SUPPORT_DIMESION;
    public GenerateValueFiles(int baseX, int baseY, String supportStr) {
        this.baseW = baseX;
        this.baseH = baseY;
        if (!this.supportStr.contains(baseX + "," + baseY)) {
            this.supportStr += baseX + "," + baseY + ";";
        }
        this.supportStr += validateInput(supportStr);
        System.out.println(supportStr);
        File dir = new File(dirStr);
        if (!dir.exists()) {
            dir.mkdir();
        }
        System.out.println(dir.getAbsoluteFile());
    }
    /**
     * @param supportStr
     *            w,h_...w,h;
     * @return
     */
    private String validateInput(String supportStr) {
        StringBuffer sb = new StringBuffer();
        String[] vals = supportStr.split("_");
        int w = -1;
        int h = -1;
        String[] wh;
        for (String val : vals) {
            try {
                if (val == null || val.trim().length() == 0)
                    continue;
                wh = val.split(",");
                w = Integer.parseInt(wh[0]);
                h = Integer.parseInt(wh[1]);
            } catch (Exception e) {
                System.out.println("skip invalidate params : w,h = " + val);
                continue;
            }
            sb.append(w + "," + h + ";");
        }
        return sb.toString();
    }
    public void generate() {
        String[] vals = supportStr.split(";");
        for (String val : vals) {
            String[] wh = val.split(",");
            generateXmlFile(Integer.parseInt(wh[0]), Integer.parseInt(wh[1]));
        }
    }
    private void generateXmlFile(int w, int h) {
        StringBuffer sbForWidth = new StringBuffer();
        sbForWidth.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        sbForWidth.append("<resources>");
        float cellw = w * 1.0f / baseW;
        System.out.println("width : " + w + "," + baseW + "," + cellw);
        for (int i = 1; i < baseW; i++) {
            sbForWidth.append(WTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellw * i) + ""));
        }
        sbForWidth.append(WTemplate.replace("{0}", baseW + "").replace("{1}",
                w + ""));
        sbForWidth.append("</resources>");
        StringBuffer sbForHeight = new StringBuffer();
        sbForHeight.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        sbForHeight.append("<resources>");
        float cellh = h *1.0f/ baseH;
        System.out.println("height : "+ h + "," + baseH + "," + cellh);
        for (int i = 1; i < baseH; i++) {
            sbForHeight.append(HTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellh * i) + ""));
        }
        sbForHeight.append(HTemplate.replace("{0}", baseH + "").replace("{1}",
                h + ""));
        sbForHeight.append("</resources>");
        File fileDir = new File(dirStr + File.separator
                + VALUE_TEMPLATE.replace("{0}", h + "")//
                        .replace("{1}", w + ""));
        fileDir.mkdir();
        File layxFile = new File(fileDir.getAbsolutePath(), "lay_x.xml");
        File layyFile = new File(fileDir.getAbsolutePath(), "lay_y.xml");
        try {
            PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
            pw.print(sbForWidth.toString());
            pw.close();
            pw = new PrintWriter(new FileOutputStream(layyFile));
            pw.print(sbForHeight.toString());
            pw.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static float change(float a) {
        int temp = (int) (a * 100);
        return temp / 100f;
    }
    public static void main(String[] args) {
        int baseW = 320;
        int baseH = 400;
        String addition = "";
        try {
            if (args.length >= 3) {
                baseW = Integer.parseInt(args[0]);
                baseH = Integer.parseInt(args[1]);
                addition = args[2];
            } else if (args.length >= 2) {
                baseW = Integer.parseInt(args[0]);
                baseH = Integer.parseInt(args[1]);
            } else if (args.length >= 1) {
                addition = args[0];
            }
        } catch (NumberFormatException e) {
            System.err
                    .println("right input params : java -jar xxx.jar width height w,h_w,h_..._w,h;");
            e.printStackTrace();
            System.exit(-1);
        }
        new GenerateValueFiles(baseW, baseH, addition).generate();
    }
}

要修改的就一个地方,即基准,比如上面的是以 480 x 320 为基准的。这里写图片描述,我们可以改成我们所需要的。当然,你也可以生成 jar 包,然后在命令行下,生成不同的代码:

先把过程打包成jar包,然后输入:

Java -jar xx.jar width height width,height_width,height

这里写图片描述

什么意思呢,前面两个是基准的意思。后面的是你要额外扩展的包。比如
我要一个1280x720 的基准,再额外扩展 一个 1366x768的包。就可以这样写:
java -jar test.jar 1280x720 1366,768

后面也是要扩展的,这里可以根据不同而选择不同。、

当然你也可以直接你要的基准的包:
这里写图片描述

但这里也有局限性,看到网上很多屏幕说,如果带有虚拟键的手机,这个方案也不适合了,只能跟着虚拟键来重新适配,当然这里还是适用于很多记性的。这里把常用的两个基准 480x320 、800x480,和1280x720 基准的res包发给大家,当然我建议你自己弄。CSDN 的附件太蛋疼了,用360好了:
160 基准 https://yunpan.cn/cPFiutXhwhdpB 访问密码 1bc7
240 基准 https://yunpan.cn/cPFiFXihU7ytz 访问密码 29df
320 基准 https://yunpan.cn/cPFixBsSQqrrX 访问密码 91da

如果消失了,请联系我。

7、安卓 TV 跟手机的不同

这里我们要 讲讲安卓TV 跟手机的不同。在手机上,我们可以以一个480x320的基准来设计屏幕大小,因为手机撑死的就5.0寸,所以,这个基准还是非常不错的。但是安卓TV不一样,都是平板类的,而且是比较大的液晶屏。所以,我们的基准就是要变一下。要用1280x720 的为基准,当然,这个只是个人理解。有错误也欢迎指出。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值