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
这个是切图的参照,但是问题又来了,我给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。
=================================分割线,下面给出在代码中如何获得屏幕相关信息
=================================分割线,在这里补充一点,对于版本大于等于 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>