进阶之 -- 屏幕自适应

作者:夏至 欢迎转载,也请保留这段申明,谢谢

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

首先,先理解几个概念。

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 的为基准,当然,这个只是个人理解。有错误也欢迎指出。

时间: 2024-10-22 06:09:53

进阶之 -- 屏幕自适应的相关文章

firemonkey 手机屏幕自适应程序问题

我是新手.在我才学了2个星期的时候,那个白痴老板说什么手机屏幕自适应程序,我当时不能理解呀,觉得用Delphi的布局设计不就行了吗.结果他说:我就是想让控件内容什么的放在小屏幕手机上也不出来.我就说,那用布局layout.结果那个傻x又是画控件关于屏幕的位置,又是记录控件的位置,整了一大套.整个把我给整晕了,新手伤不起啊,我不知道Delphi xe5有一个控件布局叫ScaledLayout,结果捣鼓了很长时间.我在Delphi交流群里说这个功能时,大家都说我想多了.唉 ,没办法自己又把控件布局什

css语法规则屏幕自适应及条目应用优先权

1. !important 提升指定样式条目的应用优先权. div { color: #f00 !important; color: #000;}在上述代码中,IE6及以下浏览器div的文本颜色为#000,!important并没有覆盖后面的规则:其它浏览器下div的文本颜色为#f00 2.可以让屏幕自适应的方法: /* 样式代码导入 样式文件 */ 第一种方式: <link media="screen and (width:800px)" rel="styleshee

关于android屏幕自适应的个人心得

这是一些博主自己对安卓的屏幕自适应的理解,有什么不到之处也别喷. 首先,观察新建的项目res文件夹,大家估计都能找到drawable文件夹有很多个,如drawable-hdpi.drawable-ldpi.drawable-mdpi.drawable-xhdpi,这些文件夹是谷歌公司为屏幕自适应做的准备,意思说各位朋友在做屏幕自适应之前应该准备5套资源图片来出来该问题.相应的比例关系大概是 1 : 0.5 : 0.75 : 1.5 : 2 ,而现在大部分4.0的机子不再适用于240*320和32

4种必须知道的Android屏幕自适应解决方案

文章来源:http://blog.csdn.net/shimiso/article/details/19166167 demo下载:http://www.eoeandroid.com/forum.php?mod=attachment&aid=NjE0Njh8ZTIyZDA2M2N8MTMzODgyOTQxN3w1NzAwOTV8MTczOTcz一.细说layout_weight    目前最为推荐的Android多屏幕自适应解决方案.    该属性的作用是决定控件在其父布局中的显示权重,一般用于

HTML5工具做屏幕自适应的两种方法

近一两年,HTML5在中国很火,也出了不少HTML5工具和模板.别的先不说,对于不同的H5工具,解决屏幕自适应问题的区别是什么? 简单来说,感应式设计是当用不同设备访问时,能够根据设备的宽度和高度对设备进行动态的适配.例如,用iPhone4.iPhone5.iPhone6.PC.iPad访问同一个H5网页,都能打开一个适合该设备的一个HTML5页面,不留白边,不变形. 由于设备分辨率和长宽比都不同,如果设计一次就能适应所有的屏幕(包括移动端和PC端),几乎不可能.原因有三: 1. 如果等比例缩放

NGUI屏幕自适应

NGUI确实是非常棒的一个做界面的插件,比起U3D自带的GUI要好很多,当然也有一些不好之处,毕竟什么都不可能那么完美. 最近在用Unity写游戏使用NGUI遇到了一个很多人都在遇到的问题,就是关于屏幕自适应,谷哥度娘里面写关于适应屏幕的帖子很多,但是大多都是一样抄来抄去,都是说是Panel里面加个UIStretch里面加个 if 分支,什么OnWidth什么的,还要把UIAnchor的side放置在Bottom等等步骤,过于麻烦,而且效果也一般. 不知道大家在用最新的Unity的时候在UISt

Unity屏幕自适应

1.首先设置一个分辨率,1920*1080,用这个分辨率来设置UI 2. 3. 4.这种模式不好屏幕自适应,只能通过match去调节缩放. 5.最好的屏幕自适应设置方式 6. 7. 8. 9. 10.

virtualbox内部的客户机Fedora25安装扩展增强工具-实现文件共享、屏幕自适应等功能

virtualbox内部的客户机Fedora25安装扩展增强工具-实现文件共享.屏幕自适应等功能 20 如题,为了实现虚拟机和宿主机的文件共享.屏幕全屏等功能,需要给virtulbox内的客户机安装增强工具.请注意前提:虚拟机是Fedora25,其他发行版也可以参考,但请灵活运用.本文宿主机是win7 64,fedora25 LXDE,且二者都是联网状态. 第一步 在客户机开机前,在vbox设置里设置共享目录. 请点选自动挂载,完全控制也是可选的,如果不选客户机无法更改(只读模式)共享目录的内容

.Net语言 APP开发平台——Smobiler学习日志:开发APP时,如何快速地实现屏幕自适应

最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 一.属性介绍 设置控件在客户端屏幕可见并超出客户端屏幕时,是否自动调节高度以适应屏幕高度 二.举例介绍 当AutoHeight属性为"True"时,Mobile Form的Scrollable属性将失去效果,以GridView控件的下面两种情况为例(以下两种情况的Mobile Form的Scrollable属性都为"True"): 情况一: Smobil