美文网首页Android Other前端资源编程
Android-屏幕适配全攻略(绝对详细)(一)

Android-屏幕适配全攻略(绝对详细)(一)

作者: 食梦兽 | 来源:发表于2015-11-04 01:25 被阅读42695次

    关键字:屏幕适配 px dp dpi sp large限定符 .9.png

    前言:这篇文章依然是我在 [慕课网 ][h]学习 凯子哥 的同名视频 Android-屏幕适配全攻略 ,所记录下来的笔记---凯子哥讲得真的超详细。
    [h]: http://www.imooc.com/ "MOOC"

    2012年到2014年支持Android设备的种类从3997增长到18796。同时各大厂商定制的屏幕尺寸也非常多。这将非常不利于我们进行屏幕适配。这要求我们必须掌握屏幕适配技能,以便使我们的app可以适用于不同屏幕尺寸的设备上。

    Android设备分辨率比例

    从上图可以看出,主流的分辨率是前六种:1280×720、1920×1080、800×480、854×480、960×540、1184×720,不过我们有解决方案。看完这篇文章,想必你就可以解决常见的屏幕适配问题。

    接下来正式进入正题。

    一. 核心概念与单位详解

    介绍几个在Android屏幕适配上非常重要的名词:

    1. 什么是屏幕尺寸、屏幕分辨率、屏幕像素密度?

    屏幕尺寸是指屏幕对角线的长度。单位是英寸,1英寸=2.54厘米
    屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1像素点,一般是纵向像素横向像素,如1280×720
    屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写,像素密度和屏幕尺寸和屏幕分辨率有关

    屏幕像素密度计算公式
    例如:计算Nexus5的屏幕像素密度:
    屏幕尺寸:4.95inch、分辨率:1920×1080,屏幕像素密度:445

    和官方给出的一样,说明我们计算正确。

    2. 什么是dp、dip、dpi、sp、px?之间的关系是什么?

    dip:Density Independent Pixels(密度无关像素)的缩写。以160dpi为基准,1dp=1px
    dp:dip
    dpi:屏幕像素密度的单位,“dot per inch”的缩写

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

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

    dp的特点
    说明:如果A设备的参数为480×320,160dpi,B设置的参数为800×480,240dpi。我们要画出一条和屏幕宽度一样长的直线,如果使用px作为单位,必须在A设备上设置为320px,在B设备上设置480px。但是如果我们使用dp作为单位,由于以160dpi为基准,1dp=1px,所以A设备上设置为320dp就等于屏幕宽度(320px),在B设备上设置为320dp就等于320×(240/160)=480px,即B设备的屏幕宽度。这样,使用dp作为单位就可以实现简单的屏幕适配。这知识一种巧合,也有B设备的像素密度不是这样刚刚好的,就需要我们运用别的屏幕适配技术。

    3. 什么是mdpi、hdpi、xdpi、xxdpi、xxxdpi?如何计算和区分?

    用于区分不同的像素密度。

    名称 像素密度范围 图片大小
    mdpi 120dp~160dp 48×48px
    hdpi 160dp~240dp 72×72px
    xhdpi 240dp~320dp 96×96px
    xxhdpi 320dp~480dp 144×144px
    xxxhdpi 480dp~640dp 192×192px
    devices_displays_density@2x.png

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

    二. 解决方案-支持各种屏幕尺寸

    我们可以通过以下几种方式来支持各种屏幕尺寸:

    1. 使用wrap_content、math_parent、weight

    wrap_content:根据控件的内容设置控件的尺寸
    math_parent:根据父控件的尺寸大小设置控件的尺寸
    weight:权重,在线性布局中可以使用weight属性设置控件所占的比例

    例如,我们要实现下图所显示的效果:当屏幕尺寸改变时,new reader控件两边的控件大小不变,new reader控件会占完剩余的空间。

    效果图
    实现:通过给new reader控件设置属性:android:layout_width="wrap_content"android:layout_weight="1",两边两个控件为固定大小,android:layout_weight="0"

    具体布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent"    
    android:orientation="horizontal">    
    <TextView        
          android:layout_width="80dp"        
          android:layout_height="80dp"        
          android:layout_weight="0"        
          android:background="#028330"/>    
    <TextView        
          android:layout_width="wrap_content"        
          android:layout_height="80dp"        
          android:text="new
    reader"        
          android:textSize="22sp"        
          android:layout_weight="1"/>    
    <TextView        
          android:layout_width="160dp"        
          android:layout_height="80dp"       
          android:text="Politics"        
          android:textSize="18sp"        
          android:layout_weight="0"        
          android:background="#028330"/>
    </LinearLayout>
    

    小插曲:关于android:layout_weight属性

    公式:所占宽度=原来宽度+剩余空间所占百分比的宽度

    一般情况,我们都是设置要进行比例分配的方向的宽度为0dp,然后再用权重进行分配。如下:

    <Button    
          android:layout_width="0dp"    
          android:layout_height="wrap_content"    
          android:layout_weight="1"    
          android:text="Button1" />
    <Button    
         android:layout_width="0dp"    
         android:layout_height="wrap_content"    
         android:layout_weight="2"    
         android:text="Button2" />
    

    效果为:

    宽度为0dp时,所占比例
    设屏幕宽度为L,
    根据公式,
    button1宽度=0+L×1/(1+2)=1/3L
    button2宽度=0+L×2/(1+2)=2/3L
    但如果设置为match_parent
    <Button    
          android:layout_width="match_parent"    
          android:layout_height="wrap_content"    
          android:layout_weight="1"    
          android:text="Button1" />
    <Button    
         android:layout_width="match_parent"    
         android:layout_height="wrap_content"    
         android:layout_weight="2"    
         android:text="Button2" />
    

    效果为:


    宽度为match_parent时,所占比例

    button1宽度=L+(L-2L)×1/3=2/3L
    button2宽度=L+(L-2L)×2/3=1/3L

    当然,还有其他的方式,都可以运用此公式进行计算。
    在实际开发中,我们一般使用0dp的方式,而不使用其他方式。

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

    简单的布局一般都使用线性布局,而略微复杂点的布局,我们使用相对布局,大多数时候,我们都是使用这两种布局的嵌套。

    我们使用相对布局的原因是,相对布局能在各种尺寸的屏幕上保持控件间的相对位置。

    3. 使用限定符

    • 使用尺寸限定符
      当我们要在大屏幕上显示不同的布局,就要使用large限定符。例如,在宽的屏幕左边显示列表右边显示列表项的详细信息,在一般宽度的屏幕只显示列表,不显示列表项的详细信息,我们就可以使用large限定符。

    res/layout/main.xml 单面板:

    <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:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="match_parent" />
    </LinearLayout>
    

    res/layout-large/main.xml 双面板:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">
        <!-- 列表 -->
        <fragment android:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="400dp"
                  android:layout_marginRight="10dp"/>
        <!-- 列表项的详细信息 -->
        <fragment android:id="@+id/article"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.ArticleFragment"
                  android:layout_width="fill_parent" />
    </LinearLayout>
    

    如果这个程序运行在屏幕尺寸大于7inch的设备上,系统就会加载res/layout-large/main.xml 而不是res/layout/main.xml,在小于7inch的设备上就会加载res/layout/main.xml

    需要注意的是,这种通过large限定符分辨屏幕尺寸的方法,适用于android3.2之前。在android3.2之后,为了更精确地分辨屏幕尺寸大小,Google推出了最小宽度限定符。

    • 使用最小宽度限定符
      最小宽度限定符的使用和large基本一致,只是使用了具体的宽度限定。
      res/layout/main.xml,单面板(默认)布局:
    <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:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="match_parent" />
    </LinearLayout>
    

    res/layout-sw600dp/main.xml,双面板布局: Small Width 最小宽度

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">
        <fragment android:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="400dp"
                  android:layout_marginRight="10dp"/>
        <fragment android:id="@+id/article"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.ArticleFragment"
                  android:layout_width="fill_parent" />
    </LinearLayout>
    

    这种方式是不区分屏幕方向的。这种最小宽度限定符适用于android3.2之后,所以如果要适配android全部的版本,就要使用large限定符和sw600dp文件同时存在于项目res目录下。

    这就要求我们维护两个相同功能的文件。为了避免繁琐操作,我们就要使用布局别名。

    • 使用布局别名
      res/layout/main.xml: 单面板布局
      res/layout-large/main.xml: 多面板布局
      res/layout-sw600dp/main.xml: 多面板布局

    由于后两个文具文件一样,我们可以用以下两个文件代替上面三个布局文件:

    res/layout/main.xml 单面板布局
    res/layout/main_twopanes.xml 双面板布局

    然后在res下建立
    res/values/layout.xml
    res/values-large/layout.xml
    res/values-sw600dp/layout.xml三个文件。

    默认布局
    res/values/layout.xml:

    <resources>
        <item name="main" type="layout">@layout/main</item>
    </resources>
    

    Android3.2之前的平板布局
    res/values-large/layout.xml:

    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>
    

    Android3.2之后的平板布局
    res/values-sw600dp/layout.xml:

    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>
    

    这样就有了main为别名的布局。
    在activity中setContentView(R.layout.main);

    这样,程序在运行时,就会检测手机的屏幕大小,如果是平板设备就会加载res/layout/main_twopanes.xml,如果是手机设备,就会加载res/layout/main.xml 。我们就解决了只使用一个布局文件来适配android3.2前后的所有平板设备。

    • 使用屏幕方向限定符

    如果我们要求给横屏、竖屏显示的布局不一样。就可以使用屏幕方向限定符来实现。
    例如,要在平板上实现横竖屏显示不用的布局,可以用以下方式实现。
    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>
    

    4. 使用自动拉伸位图

    自动拉伸位图,即android下特有的.9.png图片格式。

    当我们需要使图片在拉伸后还能保持一定的显示效果,比如,不能使图片中的重要像素拉伸,不能使内容区域受到拉伸的影响,我们就可以使用.9.png图来实现。

    要使用.9.png,必须先得创建.9.png图片,androidSDK给我们提供了的工具就包含.9.png文件的创建和修改工具。双击SDK安装目录 oolsdraw9patch.bat,就会打开下图所示的窗口。

    .9.png图片制作器
    • 直接把图片拖进去,选择Filesave.9.png,即可保存为.9.png图片。
      不过,这和原图没有什么区别,我们要制作成自动拉伸的图片,还需要进行简单的处理。

    • 在左边和上边点下一个像素或多个像素点,是选择了拉伸的像素,即把选择的像素点进行拉伸。选择的像素点越多,拉伸的程度也越大。

    • 右边和下边是选择内容区域,在右边和下边画上直线,交叉的区域就是内容区域。

    • 什么事内容区域呢?
      比如,我们给Button设置了一个.9.png作为背景,还要设置其android:text属性,设置的text所占的位置就是内容区域。

    • 我们必须保证内容区域包含text文本,才会正常显示出text文本的内容。

    下面是一个例子:

    button背景.9.png
    上图是我们制作的.9.png,设置好了左上拉伸像素点和内容区域。

    Button属性设置:

    <Button    
        android:layout_width="300dp"    
        android:layout_height="300dp"    
        android:background="@drawable/button"    
        android:text="Button" />
    
    Button的显式效果

    如果我们选择的内容区域偏差太大,可能就不会显示出text值BUTTON


    好了,这篇文章写的有点多了,剩下的内容放在 下篇文章 记录吧。
    内容提要:
    解决方案-支持各种屏幕密度
    解决方案-实施自适应用户界面流程


    未完待续

    相关文章

      网友评论

      • ania:纠正一个问题~文中的像素密度范围 不应该以dp为单位,应该是以dpi作为单位吧?
      • 21ee2c8b4921:借楼 https://github.com/yatoooon/AndroidScreenAdaptation 这是我写的一个安卓屏幕适配库,希望能帮到大家,谢谢!
      • 73da8d43baaf:厉害!!!
      • 小白猿:视频我看到一半了,文章看完了,感觉文章算是对视频做了一个完整的记录,但是现在还是一头雾水
      • guodongAndroid:这不就是android官方文档么????
      • 黄油奥利奥:楼主,我对于你的双面板的想法有问题,如果加载了单面板布局,你这个acitivity只写了针对这一个单面板的逻辑代码。可是你要是加载了双面板,之前activity的代码你只是写了针对一个面板的,现在第二个面板在xml布局也有,这时候你的activity怎么办?
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&quot;
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">
        <!-- 列表 -->
        <fragment android:id="@+id/headlines"
        android:layout_height="fill_parent"
        android:name="com.example.android.newsreader.HeadlinesFragment"
        android:layout_width="400dp"
        android:layout_marginRight="10dp"/>
        <!-- 列表项的详细信息 -->
        <fragment android:id="@+id/article"
        android:layout_height="fill_parent"
        android:name="com.example.android.newsreader.ArticleFragment"
        android:layout_width="fill_parent" />
        </LinearLayout>
      • jsdevlei:很详细 :+1:
      • 反躺局局长:button1宽度=L+(L-2L)×1/3=2/3L
        button2宽度=L+(L-2L)×2/3=1/3L

        为什么要(L-2L) ? 有人知道吗
        反躺局局长:@世界是我的床 非常感谢哈!
        食梦兽:@ZHENGYUJUN 公式是 原来宽度+剩余空间所占百分比的宽度
        L是屏幕宽度,
        两个Button的宽度都是math_parent,即L,那么剩余的宽度就是L-2L,剩余的空间是指放好这两个button之后,宽度还会剩余多少,比如你设置button1的宽度为10dp,那么剩余宽度就是L-10dp-L
      • 扣子兮兮:视频全部解析啊,牛。
        食梦兽:@扣子兮兮 对啊,就是看着视频记录的
      • 雨果僧:超赞~~~
      • 微凉一季:我也是通过慕课网学到了好多实用的东西,目前已经工资翻番了
        食梦兽:@微凉一季 我还没毕业呢,签了一家广东的公司
        微凉一季:@世界是我的床 我在北京啊,你呢
        食梦兽:@微凉一季 你在哪个城市
      • JohnsonWoo:写的很好
      • 00d2135c32d1:超详细哎
      • 小厨笔记:写得很好~~~
      • 571acc71f1d1:一直觉得多文件资源适配方案很挫,太影响开发进度了。
      • FAIRY猫小狸:看晕了,最不喜欢这些代码
      • 5e332d571fef:看不懂,但是还是蛮不错的。可以慢慢试试
      • Brucetoo:不错
      • 4abe407992d2:太高深了,看不懂
        布鲁马:- -这已经讲的够通俗易懂了...总感觉你们太懒了,没仔细看吧
        食梦兽:@太阳当空照i 不太会组织语言,谢谢提醒,以后尽量写得易懂些
        ae9ffbae98fd:@弗拉基米尔刘斯特洛夫斯基 同感,,,,,,太科班化了
      • cf90f4e0eea2:脑洞大开
      • 19e7ffc8222a:涨知识了
      • 吕劼:好像走错地方了,看不懂
      • 对歌当酒:Mark一下,慢慢看
      • 3cd0244faf75:有用!
      • 肉团先生:期待下一篇
      • LITTLEDREAM:總結得蠻全的,不錯!
        食梦兽: @LITTLEDREAM thanks

      本文标题:Android-屏幕适配全攻略(绝对详细)(一)

      本文链接:https://www.haomeiwen.com/subject/ufdqhttx.html