Android 屏幕适配问题

android 屏幕适配没有偷懒的方法,只有按照官方的方法来做才是最好,如果强行指定dp为单位也是可以的,但是要考虑内存占用的问题,例如:

你只用一套布局文件,只用一套icon,然后在布局文件中指定各个view的长度和宽度为 固定的dp,那么如果icon尺寸和分辨率过大在小屏手机上容易造成内存溢出;如果icon尺寸和分辨率过小,那么在大屏幕手机上就会显示的模糊。

Android的自动调整你的布局,以便正确适应屏幕。因此,你的布局不同的屏幕尺寸不用担心UI元素的绝对大小,而是着眼于布局结构,影响用户体验(例如尺寸和位置根据同级的views来指定)。

================================================分割线,适配不同屏幕的两个重要资源(layout和drawable)==========================

其实android要做到不同屏幕的适配只需要做两个事情就可以了,尺寸(屏幕方向的变化也被认为是尺寸的变化)和密度:

①尺寸(实际的物理尺寸,测量屏幕的对角线,而不是分辨率这个要特别谨记,例如:nexus 7 是7寸的平板分辨率是1280x800,现在有的手机屏幕 尺寸是5.0,但是分辨率也能达到1280x800),这个是针对布局文件的,android有四个常规尺寸:small, normal, large, xlarge 分别用不同的layout来指定就可以了,例如:

MyProject/
    res/
        layout/              # 默认布局 (竖屏)
            main.xml
        layout-land/         # 横屏
            main.xml
        layout-large/        # large (竖屏)
            main.xml
        layout-large-land/   # large 黑屏
            main.xml
②密度(屏幕的物理区域内的像素的数量,通常简称为dpi(每英寸像素点数)),这个是针对Bitmap来说的也就是程序的icon,你应该提供能够正确缩放到每个广义密度的位图资源:低,中,高和超高密度。这可以帮助你实现所有的屏幕密度良好的图形质量和性能。要生成这些图片,你应该使用 矢量格式的原始资源和按照以下尺寸规格的密度来生成图像:

  • xhdpi: 2.0
  • hdpi: 1.5
  • mdpi: 1.0 (标准参照)
  • ldpi: 0.75

这意味着,如果你为xhdpi设备设计了一个 200x200的图片,那么就应该在hdpi设备上生成150x150的相同图片,在mdpi设备上生成100x100的相同图片,在ldpi设备上生成75x75的相同图片。

然后将图片放到相应的资源文件下:

MyProject/
    res/
        drawable-xhdpi/
            awesomeimage.png
        drawable-hdpi/
            awesomeimage.png
        drawable-mdpi/
            awesomeimage.png
        drawable-ldpi/
            awesomeimage.png

每次通过 @drawable/awesomeimage 引用图片资源时,系统会基于当前屏幕密度来选择合适的Bitmap。

注意:ldpi并不是必须的,当你提供了hdpi资源,系统会自动取得hdpi的资源并缩放一半来适应ldpi的屏幕。

=======================================分割线,下面给出small, normal, large, xlarge 对应表和低,中,高和超高密度对应表===================



上图第一个是 尺寸对应表,第二个是 密度(dpi)对应表,这两个对应之间没有关系。


========================================分割线,下面给出dp和像素和dpi之间的关系=========================================


①像素(px),这个大家都知道,就是屏幕上的物理像素点;

②密度(dpi),这个在上面提到过,就是单位英寸下有多少个物理像素点;

③密度无关的像素(dp(dip)),这个是android独有的一个密度无关单位,密度无关的像素相当于一个160 dpi的屏幕上的一个物理像素,所以px和dp之间的换算公式就是 px = dp*(dpi/160) 例如,一个240 dpi的屏幕上,1个DP等于1.5物理像素。


================================分割线,到底该怎么切图======================================

相信大家都纠结过到底应该怎么切图的问题,反正我是纠结了好长时间,知道现在。。。我相信等我写完这篇文章后,我就不会再纠结了!

上面提到过

  • xhdpi: 2.0
  • hdpi: 1.5
  • mdpi: 1.0 (标准参照)
  • ldpi: 0.75
这意味着,如果你为xhdpi设备设计了一个 200x200的图片,那么就应该在hdpi设备上生成150x150的相同图片,在mdpi设备上生成100x100的相同图片,在ldpi设备上生成75x75的相同图片。

这个是切图的参照,但是问题又来了,我给480*800分辨率的手机切完图了,又想给1280*720分辨率的手机切图,那么应该怎么切?

那么首先我们要清楚android 中的 ldpi、mdpi、hdpi、xhdpi到底是什么,所谓的dpi就是单位英寸下的像素点数。接下来就好办了,我们先给出一个标准参照那就是 屏幕分辨率是 480*800 、屏幕尺寸是 4.0(对角线)那么它的dpi就是 233dpi,属于hdpi,那么切图的话就以这个为参照按照比例来切对应dpi下的图片就OK了。

在这里计算dpi的公式是: 480*480+800*800 再开方,得到对角线的像素数,然后再除以 4.0,得到dpi。

=================================分割线,相面罗嗦一大堆,直接看下面的就可以了。。。。

-----------图片资源文件dpi比例    icon图尺寸    dpi值
1.ldpi  -- 0.75 (36*36) 120dpi
2.mdpi  -- 1   (48*48) 160dpi
3.hdpi  -- 1.5   (72*72) 240dpi
4.xhdpi -- 2 (96*96) 320dpi
5.xxhdpi-- 3     (144*144) 480dpi



------------dpi是怎么计算出来的
  dpi就是屏幕的密度也就是 单位物理尺寸(英寸)有多少个像素。例如标准mdpi设备就是 2英寸*1.5英寸分辨率为
320px*240px  dpi = 320/2 = 240/1.5 = 160


------------dp和px之间的转换关系
1.对于mdpi(160dpi)来说,正好是 1dp == 1px。所以它们之间的转换关系就是  px = dp*(屏幕dpi/160)。
2.在代码中可以通过 final DisplayMetrics displayMetrics = new DisplayMetrics();
  activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
  float midu = (float)displayMetrics.densityDpi;
来获得屏幕dpi。


------------布局中view尺寸设置多少dp
根据切图给的 mdpi 中icon的尺寸设置,icon是多少分辨率(宽px*高px),那么在布局中设置icon对应的view就是多少dp。
同理也可以根据其它 密度的切图,自己算,例如 xhdpi icon是 96*96,那么设置icon对应view的dp就是 48dp*48dp。



=================================分割线,下面给出在代码中如何获得屏幕相关信息

DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int width = metric.widthPixels;   // 屏幕宽度(像素)
int height = metric.heightPixels;   // 屏幕高度(像素)
float density = metric.density;   // 屏幕密度(0.75 / 1.0 / 1.5)
int densityDpi = metric.densityDpi;   // 屏幕密度DPI(120 / 160 / 240)



=================================分割线,在这里补充一点,对于版本大于等于 android 3.2 ==============================

 在android 3.2 及以上版本 可以 使用最小宽度限定符  来进行更为精确的布局设计。

在版本低于 3.2 的 Android 设备上,开发人员遇到的问题之一是“较大”屏幕的尺寸范围,该问题会影响戴尔 Streak、早期的 Galaxy Tab 以及大部分 7 英寸平板电脑。即使这些设备的屏幕属于“较大”的尺寸,但很多应用可能会针对此类别中的各种设备(例如 5 英寸和 7 英寸的设备)显示不同的布局。这就是 Android 3.2 版在引入其他限定符的同时引入“最小宽度”限定符的原因。

最小宽度限定符可让您通过指定某个最小宽度(以 dp 为单位)来定位屏幕。例如,标准 7 英寸平板电脑的最小宽度为 600 dp,因此如果您要在此类屏幕上的用户界面中使用双面板(但在较小的屏幕上只显示列表),您可以使用上文中所述的单面板和双面板这两种布局,但您应使用 sw600dp 指明双面板布局仅适用于最小宽度为 600 dp 的屏幕,而不是使用 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,双面板布局:
    <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 版本低于 3.2 的设备不支持此技术,原因是这些设备无法将 sw600dp 识别为尺寸限定符,因此您仍需使用 large 限定符。这样一来,就会有一个名称为 res/layout-large/main.xml 的文件(与 res/layout-sw600dp/main.xml 一样)。您将在下一教程中了解到避免此类布局文件出现重复的技术。

使用布局别名


最小宽度限定符仅适用于 Android 3.2 及更高版本。因此,您仍需使用与较低版本兼容的概括尺寸范围(小、正常、大和特大)。例如,如果您要将用户界面设计成在手机上显示单面板,但在 7 英寸平板电脑、电视和其他较大的设备上显示多面板,请提供以下文件:

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

后两个文件是相同的,因为其中一个用于和 Android 3.2 设备匹配,而另一个则是为使用较低版本 Android 的平板电脑和电视准备的。

要避免平板电脑和电视的文件出现重复(以及由此带来的维护问题),您可以使用别名文件。例如,您可以定义以下布局:

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

然后添加这两个文件:

  • res/values-large/layout.xml:
    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>
  • res/values-sw600dp/layout.xml:
    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>

后两个文件的内容相同,但它们并未实际定义布局。它们只是将 main 设置成了 main_twopanes 的别名。由于这些文件包含 large 和 sw600dp 选择器,因此无论 Android 版本如何,系统都会将这些文件应用到平板电脑和电视上(版本低于 3.2 的平板电脑和电视会匹配 large,版本低于 3.2 的平板电脑和电视则会匹配 sw600dp)。

使用屏幕方向限定符


某些布局会同时支持横向模式和纵向模式,但您可以通过调整优化其中大部分布局的效果。在新闻阅读器示例应用中,每种屏幕尺寸和屏幕方向下的布局行为方式如下所示:

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

因此,这些布局中的每一种都定义在了 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>

既然您已定义了所有可能的布局,那就只需使用配置限定符将正确的布局映射到各种配置即可。您现在只需使用布局别名技术即可做到这一点:

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:

<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>

res/values-sw600dp-port/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/onepane</item>
    <bool name="has_two_panes">false</bool>
</resources>

res/values-large-land/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>

res/values-large-port/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/twopanes_narrow</item>
    <bool name="has_two_panes">true</bool>
</resources>



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值