屏幕适配经验(二)

屏幕适配问题的本质:

  • 使得“布局”、“布局组件”、“图片资源”、“用户界面流程”匹配不同的屏幕尺寸

  • 使得布局、布局组件自适应屏幕尺寸;

  • 根据屏幕的配置来加载相应的UI布局、用户界面流程

  • 使得“图片资源”匹配不同的屏幕密度

解决方案

1:使得布局元素自适应屏幕尺寸
开发中,我们使用的布局一般有:

  • 线性布局(Linearlayout)
  • 相对布局(RelativeLayout)
  • 帧布局(FrameLayout)
  • 表格布局(TabLayout)
  • 绝对布局(AbsoluteLayout)

常用的就是线性布局(Linearlayout)、相对布局(RelativeLayout)和帧布局(FrameLayout)需要根据需求进行选择,但要记住:

  • RelativeLayout
    布局的子控件之间使用相对位置的方式排列,因为RelativeLayout讲究的是相对位置,即使屏幕的大小改变,视图之前的相对位置都不会变化,与屏幕大小无关,灵活性很强

  • LinearLayout
    通过多层嵌套LinearLayout和组合使
    用”wrap_content”和”match_parent”以及”weight”来构建布局。但是LinearLayout无法准确地控制子视图之间的位置关系,只能简单的一个挨着一个地排列

所以,对于屏幕适配来说,使用相对布局(RelativeLayout)将会是更好的解决方案

2:根据屏幕的配置来加载相应的UI布局

做法:使用限定符

应用场景:需要为不同屏幕尺寸的设备设计不同的布局

  • 作用:通过配置限定符使得程序在运行时根据当前设备的配置(屏幕尺寸)自动加载合适的布局资源
    。限定符类型:
    。尺寸(size)限定符
    。最小宽度(Smallest-width)限定符
    。布局别名
    。屏幕方向(Orientation)限定符

尺寸(size)限定符

  • 使用场景:当一款应用显示的内容较多,希望进行以下设置:
    1.在平板电脑和电视的屏幕(>7英寸)上:实施“双面板”模式以同时显示更多内容
    2.在手机较小的屏幕上:使用单面板分别显示内容

因此,我们可以使用尺寸限定符(layout-large)通过创建一个文件来完成上述设定:

res/layout-large/main.xml
  • 让系统在屏幕尺寸>7英寸时采用适配平板的双面板布局
  • 反之(默认情况下)采用适配手机的单面板布局
    文件配置如下:

  • 适配手机的单面板(默认)布局: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>
  • 适配尺寸>7寸平板的双面板布局::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>

注意:
1.两个布局名称均为main.xml,只有布局的目录名不同:第一个布局的目录名为:layout,第二个布局的目录名为:layout-large,包含了尺寸限定符(large)

2.被定义为大屏的设备(7寸以上的平板)会自动加载包含了large限定符目录的布局,而小屏设备会加载另一个默认的布局

但要注意的是,这种方式只适合Android 3.2版本之前。

于是3.2之后推出了最小宽度(Smallest-width)限定符

  • 背景:上述提到的限定符“large”具体是指多大呢?似乎没有一个定量的指标,这便意味着可能没办法准确地根据当前设备的配置(屏幕尺寸)自动加载合适的布局资源
  • 例子:比如说large同时包含着5寸和7寸,这意味着使用“large”限定符的话我没办法实现为5寸和7寸的平板电脑分别加载不同的布局

于是,在Android 3.2及之后版本,引入了最小宽度(Smallest-width)限定符

定义:通过指定某个最小宽度(以 dp 为单位)来精确定位屏幕从而加载不同的UI资源

  • 使用场景

你需要为标准 7 英寸平板电脑匹配双面板布局(其最小宽度为 600 dp),在手机(较小的屏幕上)匹配单面板布局

解决方案:您可以使用上文中所述的单面板和双面板这两种布局,但您应使用 sw600dp 指明双面板布局仅适用于最小宽度为 600 dp 的屏幕,而不是使用 large 尺寸限定符。

  • sw xxxdp,即small width的缩写,其不区分方向,即无论是宽度还是高度,只要大于 xxxdp,就采用次此布局
  • 例子:使用了layout-sw 600dp的最小宽度限定符,即无论是宽度还是高度,只要大于600dp,就采用layout-sw 600dp目录下的布局

例子

  • 适配手机的单面板(默认)布局: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>
  • 适配尺寸>7寸平板的双面板布局:res/layout-sw600dp/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>

对于最小宽度≥ 600 dp 的设备,系统会自动加载 layout-sw600dp/main.xml(双面板)布局,否则系统就会选择 layout/main.xml(单面板)布局(这个选择过程是Android系统自动选择的)

然而,使用早于Android 3.2系统的设备将无法识别sw600dp这个限定符,所以你还是同时需要使用large限定符。这样你就需要在res/layout-large和res/layout-sw600dp目录下都添加一个相同的main.xml。

使用布局别名(也就是最后的解决办法)

Smallest-width限定符仅在Android 3.2及之后的系统中有效。因而,你也需要同时使用Size限定符(small, normal, large和xlarge)来兼容更早的系统。例如,你想手机上显示single-pane界面,而在7寸平板和更大屏的设备上显示multi-pane界面,你需要提供以下文件:

  • res/layout/main.xml: single-pane布局
  • res/layout-large: multi-pane布局
  • res/layout-sw600dp: multi-pane布局

最后的两个文件是完全相同的,为了要解决这种重复,你需要使用别名技巧。例如,你可以定义以下布局:

  • 适配手机的单面板(默认)布局 :res/layout/main.xml, single-pane布局
  • 适配尺寸>7寸平板的双面板布局: res/layout/main_twopanes.xml, two-pane布局

然后加入以下两个文件,以便进行Android 3.2前和Android 3.2后的版本双面板布局适配:

1.res/values-large/layout.xml(Android 3.2之前的双面板布局)

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

2.res/values-sw600dp/layout.xml(Android 3.2及之后的双面板布局)

<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
  • 最后两个文件有着相同的内容,但是它们并没有真正去定义布局,它们仅仅只是给main定义了一个别名main_twopanes。这样两个layout.xml都只是引用了@layout/main_twopanes,就避免了重复定义布局文件的情况。
  • 由于这些文件包含 large 和 sw600dp 选择器,因此,系统会将此文件匹配到不同版本的>7寸平板上:
    a. 版本低于 3.2 的平板会匹配 large的文件
    b. 版本高于 3.2 的平板会匹配 sw600dp的文件

屏幕方向(Orientation)限定符

  • 使用场景:根据屏幕方向进行布局的调整

小屏幕, 竖屏: 单面板
小屏幕, 横屏: 单面板
7 英寸平板电脑,纵向:单面板,带操作栏
7 英寸平板电脑,横向:双面板,宽,带操作栏
10 英寸平板电脑,纵向:双面板,窄,带操作栏
10 英寸平板电脑,横向:双面板,宽,带操作栏
电视,横向:双面板,宽,带操作栏

分2步来完成:

1.先定义类别:单/双面板、是否带操作栏、宽/窄

定义在 res/layout/ 目录下的某个 XML 文件中

2.再进行相应的匹配:屏幕尺寸(小屏、7寸、10寸)、方向(横、纵)

使用布局别名进行匹配

具体

1.在 res/layout/ 目录下的某个 XML 文件中定义所需要的布局类别
(单/双面板、是否带操作栏、宽/窄)
res/layout/onepane.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/onepane_with_bar.xml:(单面板带操作栏)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
    <LinearLayout android:layout_width="match_parent"   
                  android:id="@+id/linearLayout1"    
                  android:gravity="center"  
                  android:layout_height="50dp">  
        <ImageView android:id="@+id/imageView1"   
                   android:layout_height="wrap_content"  
                   android:layout_width="wrap_content"  
                   android:src="@drawable/logo"  
                   android:paddingRight="30dp"  
                   android:layout_gravity="left"  
                   android:layout_weight="0" />  
        <View android:layout_height="wrap_content"   
              android:id="@+id/view1"  
              android:layout_width="wrap_content"  
              android:layout_weight="1" />  
        <Button android:id="@+id/categorybutton"  
                android:background="@drawable/button_bg"  
                android:layout_height="match_parent"  
                android:layout_weight="0"  
                android:layout_width="120dp"  
                style="@style/CategoryButtonStyle"/>  
    </LinearLayout>  

    <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/twopanes.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>

res/layout/twopanes_narrow.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="200dp"
              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>

2.使用布局别名进行相应的匹配(屏幕尺寸(小屏、7寸、10寸)、方向(横、纵)),可为resources设置bool,通过获取其值来动态判断目前已处在哪个适配布局

  • res/values/layouts.xml:(默认布局)
<resources>  
    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>  
    <bool name="has_two_panes">false</bool>  
</resources>
  • res/values-sw600dp-land/layouts.xml(大屏、横向、双面板、宽-Andorid 3.2版本后)
<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>
  • res/values-large-land/layouts.xml(大屏、横向、双面板、宽-Andorid 3.2版本前)
<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>
  • res/values-large-port/layouts.xml(大屏、纵向、单面板带操作栏-Andorid 3.2版本前)
<resources>
    <item name="main_layout" type="layout">@layout/onepane</item>
    <bool name="has_two_panes">false</bool>
</resources>

使用自动拉伸位图:Nine-Patch的图片类型(也就是我们常说的.9png图片)

支持不同屏幕大小通常情况下也意味着,你的图片资源也需要有自适应的能力。例如,一个按钮的背景图片必须能够随着按钮大小的改变而改变。
如果你想使用普通的图片来实现上述功能,你很快就会发现结果是令人失望的,因为运行时会均匀地拉伸或压缩你的图片。解决方案是使用nine-patch图片,它是一种被特殊处理过的PNG图片,你可以指定哪些区域可以拉伸而哪些区域不可以。
因而,当你设计需要在不同大小的控件中使用的图片时,最好的方法就是用nine-patch图片

注意:

  • 必须要使用.9.png后缀名,因为系统就是根据这个来区别nine-patch图片和普通的PNG图片的;
  • 必须放在drawable下面
  • 当你需要在一个控件中使用nine-patch图片时,如
    android:background=”@drawable/button”系统就会根据控件的大小自动地拉伸你想要拉伸的部分
    原文作者:唠嗑008
    原文地址: https://www.jianshu.com/p/213962fdc446
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞