android屏幕适配的全攻略2--支持手机各种屏幕密度dpi

时间:2022-10-05 23:52:50

如何为不同密度的屏幕提供不同的资源和使用密度独立的单位。

1 使用密度无关像素

坚决杜绝在布局文件中使用绝对像素来定位和设置大小。因为不同的屏幕有不同的像素密度,所以使用像素来设置控件大小是有问题的,在不同的设备上同样的像素可能代表不同的物理屏幕尺寸,所以当使用尺寸的时候,总是使用dp或者sp,dp是相对于160dpi屏幕的密度独立的像素单位,而sp是同样的,只不过会相对于用户设定的文字大小去缩放,因此在定义文字大小的时候要用sp而不要在定义布局的时候使用sp。

例如当你设置两个按钮的间隔的时候使用dp 而不是 px:

<Buttonandroid:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/clickme"
android:layout_marginTop="20dp"/>

当定义文字大小的时候,总是使用 sp:

<TextViewandroid:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"/>

2 不同的屏幕密度使用不同的图片

由于Android设备的屏幕密度是各种各样的,您应该为不同密度的屏幕提供不同的图片资源,这样在各种像素密度的屏幕上都能获得最好的图形质量和性能。

在原有矢量图的基础上按照如下的缩放比例来分别生成不同屏幕密度的图片资源:

  • xhdpi: 2.0

  • hdpi: 1.5

  • mdpi: 1.0 (baseline)

  • ldpi: 0.75

这意味着对于xhdpi 的设备需要使用200×200的图片;对于hdpi的设备只需要 150×150 的图片;而对于mdpi的设备需要 100×100 的图片; 对于ldpi 的设备只需要75×75 的图片。

把这些图片放到位于res/目录下对于的图片目录中:

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

然后,当您使用@drawable/awesomeimage的时候, 系统会根据当前设备的屏幕密度为您选择最优的图片。

小插曲:
如果我们只提供一个图片来适配不同屏幕密度的设备的话,就要考虑放在哪个文件夹下了。

我们以Nexus5为例,如果我们把图片放在drawable-xxhdpi下,占用的内存最小(凯子哥的例子是11.65M),如果放在drawabledrawable-mdpi下,占用的内存将会非常大(凯子哥的例子是74.95M)。如果放在drawable-hdpi下占用的为35.38M(同一张图片),所以,我们要提供不同尺寸的图片来适配不同的屏幕密度,否则可能会很大程度上浪费内存。

虽然是以了dp这种屏幕密度无关单位,但还是存在下面的问题,看下面的一个列子

Nexu5手机,对应的宽度是360dp

我们现在在一个布局文件中,放置两个按钮,一个按钮在最左边宽度是150dp,另外一个按钮放置布局的最右边宽度是200dp,中间还剩余了10dp,我们来看下布局的显示

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#a79"
tools:context=".MainActivity"> <Button
android:layout_width="150dp"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:background="#779"
android:text="左边按钮"
/> <Button
android:layout_width="200dp"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:background="#179"
android:text="右边按钮"
/>
</RelativeLayout>

android屏幕适配的全攻略2--支持手机各种屏幕密度dpi

但是该布局在其他手机上的显示效果

android屏幕适配的全攻略2--支持手机各种屏幕密度dpi

即使我们使用了dp和sp,还是会存在上面的同一种布局文件在不同的手机上显示出不一样的效果,因为不同手机的宽度的大小对应的dp是不一样的。

解决上面问题的办法:

1、尽量不要直接写150dp这种固定值,可以使用weight、wrapcontent 相对布局来解决上面的问题,除了这种方法之外,还有没有其他的方式了

思路:把任何设备的手机宽度像素均分为320份,高度像素均分为480份,使用我们写好的程序自动生成资源values-***×***文件夹,里面包含lay_x.xmllay_y.xml,分别对应宽度和高度的像素。

MakeXml类:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter; /**
* Created by wei.yuan on 2016/5/10.
*/
public class MakeXml {
private final static String rootPath = "F:\\layoutroot\\values-{0}x{1}\\"; private final static float dw = 320f;
private final static float dh = 480f; private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n";
private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n"; public static void main(String[] args) {
makeString(320, 480);
makeString(480, 800);
makeString(480, 854);
makeString(540, 960);
makeString(600, 1024);
makeString(720, 1184);
makeString(720, 1196);
makeString(720, 1280);
makeString(768, 1024);
makeString(800, 1280);
makeString(1080, 1812);
makeString(1080, 1920);
makeString(1440, 2560);
} public static void makeString(int w, int h) { StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sb.append("<resources>");
float cellw = w / dw;
for (int i = 1; i < 320; i++) {
sb.append(WTemplate.replace("{0}", i + "").replace("{1}",
change(cellw * i) + ""));
}
sb.append(WTemplate.replace("{0}", "320").replace("{1}", w + ""));
sb.append("</resources>"); StringBuffer sb2 = new StringBuffer();
sb2.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sb2.append("<resources>");
float cellh = h / dh;
for (int i = 1; i < 480; i++) {
sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",
change(cellh * i) + ""));
}
sb2.append(HTemplate.replace("{0}", "480").replace("{1}", h + ""));
sb2.append("</resources>"); String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");
File rootFile = new File(path);
if (!rootFile.exists()) {
rootFile.mkdirs();
}
File layxFile = new File(path + "lay_x.xml");
File layyFile = new File(path + "lay_y.xml");
try {
PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
pw.print(sb.toString());
pw.close();
pw = new PrintWriter(new FileOutputStream(layyFile));
pw.print(sb2.toString());
pw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} } public static float change(float a) {
int temp = (int) (a * 100);
return temp / 100f;
} }

执行上面程序,

 private final static String rootPath = "F:\\layoutroot\\values-{0}x{1}\\";知道生成文件的路径

会在f盘盘的layoutroot目录下生成下面这些文件夹和文件

android屏幕适配的全攻略2--支持手机各种屏幕密度dpi

android屏幕适配的全攻略2--支持手机各种屏幕密度dpi

F:\layoutroot\values-480x320\lay_x.xml

<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">1.0px</dimen>
<dimen name="x2">2.0px</dimen>
<dimen name="x3">3.0px</dimen>
<dimen name="x4">4.0px</dimen>
<dimen name="x5">5.0px</dimen>
...省略...
<dimen name="x316">316.0px</dimen>
<dimen name="x317">317.0px</dimen>
<dimen name="x318">318.0px</dimen>
<dimen name="x319">319.0px</dimen>
<dimen name="x320">320px</dimen>
</resources>

F:\layoutroot\values-480x320\lay_y.xml

<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="y1">1.0px</dimen>
<dimen name="y2">2.0px</dimen>
<dimen name="y3">3.0px</dimen>
<dimen name="y4">4.0px</dimen>
<dimen name="y5">5.0px</dimen>
...省略...
<dimen name="y476">476.0px</dimen>
<dimen name="y477">477.0px</dimen>
<dimen name="y478">478.0px</dimen>
<dimen name="y479">479.0px</dimen>
<dimen name="y480">480px</dimen>
</resources>

在480×320的设备上,x2就代表2.0px,y2就代表3.0px。
在800×480的设备上,x2就代表3.0px,y2就代表3.33px。依次类推。

我们需要把上面生成的value文件夹拷贝到对应的工程目录下

如何使用:

<Button
android:background="#abd123"
android:layout_width="@dimen/x160"
android:layout_height="@dimen/y240"
android:text="Button" />

这样设置,在各种屏幕宽度的设备上,此Button的宽度和高度就都占屏幕的一半。

效果如下:

android屏幕适配的全攻略2--支持手机各种屏幕密度dpi

我们看到这种方式可以支持大部分屏幕宽度的设备,但是我们也看到了一些设备,如Nexus9、Nexus10上并没有显示出Button,这是因为我们生成的尺寸资源文件里没有对应分辨率的xml文件。