当我们要在Layout 中定义控件的长宽等维度时,Android的推荐是:
1)所有长宽的单位都定义成dip,除了字体的大小
2)字体的大小用sp来表示
这篇文章我们讨论一下,为什么不用像素 px, 而要用dip 表示呢? dip 跟 px 之前的关系是什么呢?
实践出真知,代码来展示。
我们先来定义两个按钮,其中一个的宽度是160 dip, 另外一个的宽度是160 px,(注意单位不同)然后来看看在不同的模拟器上有什么不一样。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView android:id="@+id/textViewSw"
android:layout_width="320dip"
android:layout_height="wrap_content"
android:background="#00CC77"
/>
<Button
android:layout_width="160dip"
android:layout_height="wrap_content"
android:text="160dip">
</Button>
<Button
android:layout_width="160px"
android:layout_height="wrap_content"
android:text="160px">
</Button>
<TextView android:id="@+id/textViewDp"
android:layout_width="160dip"
android:layout_height="wrap_content"
android:background="#00CC77"
/>
<TextView android:id="@+id/textViewPx"
android:layout_width="160dip"
android:layout_height="wrap_content"
android:background="#00CC77"
/>
</LinearLayout>
同时也定义了几个TextView, 来展示屏幕的宽度,屏幕密度比例 和 屏幕的密度。
public class DipActivity extends Activity{
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
float density = displayMetrics.density;
int densityDpi = displayMetrics.densityDpi;
int screenWidth = displayMetrics.widthPixels;
setContentView(R.layout.dip_layout);
TextView textViewSw = (TextView)findViewById(R.id.textViewSw);
textViewSw.setText("screen Width (px):" + screenWidth);
TextView dpView = (TextView)findViewById(R.id.textViewDp);
dpView.setText("Density :" + density);
TextView pxView = (TextView)findViewById(R.id.textViewPx);
pxView.setText("Density Dpi : " + String.valueOf(densityDpi));
}
}
然后我们运行代码,看看效果怎么样?如下,上面的是在480*800的模拟器上,下面是在320*480的模拟器上。
我们先来看屏幕为480*800的机器,
Button的宽度为160dip的很明显要比160px的要长,事实上,160dip的按钮占据了一半的宽度(240px),而160px的Button,就是160px.
为什么会这样呢?其实就是跟图上展示的Density有关了,我把它称为密度比例,它的值是当前屏幕的dpi (Dot Per Inch,不是dip)跟 160 的一个比例。
在屏幕密度 Density dpi 为240的手机上,密度比例Density = 240 / 160 = 1.5.
而dip 跟 px 的之间的比例 则为 px = dip * 1.5, 即当我们在布局中设置按钮的宽为160dip时,当它经过系统的换算,展现在屏幕上的已经是像素,而其值就是160 * 1.5 = 240px.
在Android手机中,一般有下面4个级别的屏幕密度,
1)120 - Low对应的density = 120 / 160 = 0.75
2)160 - Medium 对应的density = 160 / 160 = 1.0
3)240 - High 对应的density = 240 / 160 = 1.5
4)320 - Extra High 对应的density = 320 / 160 =2.0
这个值可以通过DisplayMetrics来拿到,如下:
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
float density = displayMetrics.density;
int densityDpi = displayMetrics.densityDpi;
int screenWidth = displayMetrics.widthPixels;
当我们在布局中以dip为单位的时候,中间其实会经过系统的换算,根据不同的屏幕密度,将其转换成对应屏幕的像素,从而达到适应不同屏幕分辨率的问题。
在320*480的机器上,可以看到Density Dpi = 160, 那么其Density = 160/160 = 1.0, 那么160dip 转换成像素就是 160 * 1.0 = 160px.
所以160dip的按钮跟160px的按钮在屏幕上展现是一样宽的。
综合两个图来看,当我们定义160dip的时候,无论是在上边的机器还是下边的机器,按钮都是占据屏幕的一半宽度,而如果定义成160px的话,则达不到这样的效果。
我想这也就是为什么Android会推荐用dip而不推荐px了,因为不同分辨率的手机屏幕实在是太多了,用像素的话,定死了,真不好看。
而用dip,则系统会去读取屏幕的密度,再根据密度比例来计算真正要展现在屏幕上的像素,效果会好很多。