当我们要在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,则系统会去读取屏幕的密度,再根据密度比例来计算真正要展现在屏幕上的像素,效果会好很多。