您的位置:首页 > 移动开发 > Android开发

Android 屏幕适配问题

2013-12-02 11:56 363 查看
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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: