##PX和DIP的关系
最近在写一个新闻客户端,在做Splash页面切换的时候,需要在页面下方绘制几个点,随页面切换,点由黑色变为红色.当然,实际实现的时候是先绘制几个黑点,再把一个红点放在第一个黑点的位置,然后切换页面,红点去覆盖黑点.这时候,问题就来了,因为在布局里距离的单位是dp(dip),而在Java类中距离的单位是px,这样就会造成显示上的bug.
dp:是dip的简写,指密度无关的像素.指一个抽象意义上的像素,程序用它来定义界面元素.一个与密度无关的,在逻辑尺寸上,与一个位于像素密度为160DPI的屏幕上的像素是一致的.要把密度无关像素转换为屏幕像素,可以用这样一个简单的公式:pixels=dips*(density/160).举个例子,在DPI为240的屏幕上,1个DIP等于1.5个物理像素.
也就是说在这个例子里显示上红点会比第一个黑点要大,在页面从第一个页面切换到后面的页面之后,因为要适应布局文件的设置,红点会被强制压缩到和黑点一样的大小.
因此,我们在用Java代码定义程序布局界面的时候,一定要使用dip来定义.因为这样可以保证你的UI在各种分辨率的屏幕上都可以正常显示.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 根据手机的分辨率从 dip 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
当然,也有一个开源框架可以帮我们完成这个工作,项目地址是android-common,是trinea的写的一个android utils工具集,这是一个非常有用的工具集,主要分三大块:缓存类,公用的view,工具类,其中里面的utils ->sceenutils这个类解决了像素转换的问题.
##横竖屏的切换问题
Android应用程序的横竖屏切换,其原理很简单.Android中每次横竖屏切换都会重启Activity,为了保存用户使用应用程序的当前状态和数据,横竖屏切换时,必须要在Activity销毁(执行onPause()和onDestory()方法)之前保存应用程序的当前状态和数据,等Activity重新启动即横竖屏切换之后再载入配置.
###禁止切换横竖屏
实际上有的应用时不适合进行横竖屏切换的,这时候可以在清单文件对应的Activity配置的位置添加如下代码:1
android:screenOrientation="portrait"//landscape是横向,portrait是纵向
这样就可以保证屏幕一直是竖屏.
###切换横竖屏
当然,当需要进行横竖屏切换的时候,做如下的操作即可.
在清单文件对应的Activity配置的位置添加如下内容:
1
android:configChanges="keyboardHidden|orientation"
重写Activity的onConfigurationChanged()方法:
1
2
3
4
5
6
7
8
9
10@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int orientation = newConfig.orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {//竖屏
setContentView(R.layout.activity_main);
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {//横屏
setContentView(R.layout.activity_other);
}
}
##分辨率问题
分辨率适配是Andoid的一大难题,官方给出的解决办法是创建不同的layout文件夹,其实这是谷歌的一种偷懒和不负责任的做法.因为这意味着你需要对每一种分辨率都要写一个布局文件,看上去确实解决了分辨率问题,可一旦其中一个文件修改了,其他的所有的布局文件都要修改.这时候,我们就要寻求其他的解决方案.
###使用layout_weight
这是目前最为推荐的Android多屏幕自适应解决方案.
该属性的作用是决定控件在其父布局中的显示权重,一般用于线性布局中.其值越小,则对应的layout_width或layout_height的优先级就越高(一般到100作用就不太明显了);一般横向布局中,决定的是layout_width的优先;;纵向布局,,决定的是layout_height的优先级.
传统的layout_weight使用方法是将当前控件的layout_width和layout_height都设置成fill_parent,这样就可以把控件的显示比例完全交给layout_weight;这样使用的话,就出现了layout_weight越小,显示比例越大的情况(即权重越大,显示所占的效果越小).不过对于2个控件还好,如果控件过多,且显示比例也不相同的时候,控制起来就比较麻烦了,毕竟反比不是那么好确定的.于是就有了现在最为流行的0px设值法.看似让人难以理解的layout_height=0px的写法,结合layout_weight,却可以使控件成正比例显示,轻松解决了当前Android开发最为头疼的碎片化问题之一.
代码示例如下,将layout_width和layout_height两个属性封装成四个style,根据实际的布局情况,选择其中一种即可:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 全屏幕拉伸-->
<style name="layout_full">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">fill_parent</item>
</style>
<!-- 固定自身大小-->
<style name="layout_wrap">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
<!-- 横向分布-->
<style name="layout_horizontal" parent="layout_full">
<item name="android:layout_width">0px</item>
</style>
<!-- 纵向分布-->
<style name="layout_vertical" parent="layout_full">
<item name="android:layout_height">0px</item>
</style>
</resources>
###清单文件配置
实际上,不建议使用这种方式,因为需要对不同的界面写不同的布局
在AndroidManifest.xml文件的1
2
3
4
5
6<supports-screensandroid:largeScreens="true"
android:normalScreens="true"
android:anyDensity="true"
android:smallScreens="true"
android:xlargeScreens="true">
</supports-screens>
这就是适配大,中,小三种密度的屏幕的配置,其中Android:anyDensity=”true”的意思是当应用程序安装不同分辨率的手机上,程序会分别自动加载hdpi,mdpi,ldpi文件夹中的资源.如果值为false,那么即使我们在hdpi,mdpi,ldpi,xdpi中有同一资源,应用程序也不会自动去对应的文件夹中寻找资源,而只会在大分辨率和小分辨率的手机上加载mdpi文件夹中的资源.
有时候会根据需要在代码中动态地设置某个值,可以在代码中为这几种密度分别设置偏移量,但是这种方法最好不要使用,最好的方式是在xml文件中不同密度的手机进行分别设置.这里地图的偏移量可以在values-xpdi,values-hpdi,values-mdpi,values-ldpi四种文件夹中的dimens.xml文件进行设置.
###其他
说明:
- 在不同分辨率的手机模拟器下,控件显示的位置会稍有不同
- 通过在layout中定义的布局设置的参数,使用dp(dip),会根据不同的屏幕分辨率进行适配
- 但是在代码中的各个参数值,都是使用的像素(px)为单位的
技巧:
- 尽量使用线性布局,相对布局,如果屏幕放不下了,可以使用ScrollView(可以上下拖动)
- ScrowView使用的注意:
在不同的屏幕上显示内容不同的情况,其实这个问题我们往往是用滚动视图来解决的,也就是ScrowVie;需要注意的是ScrowView中使用layout_weight是无效的,既然使用ScrowView了,就把它里面的控件的大小都设成固定的吧.- 指定宽高的时候,采用dip的单位,dp单位动态匹配
- 由于android代码中写的单位都是像素,所有需要通过工具类进行转化
- 尽量使用9-patch图,可以自动的依据图片上面显示的内容被拉伸和收缩.其中在编辑的时候,灰色区域是被拉伸的,上下两个点控制水平方向的拉伸,左右两点控制垂直方向的拉伸.
其实关于屏幕适配,需要注意的地方非常多,而且基本都是细节上的东西,跟布局一样,都是细活,理解起来不复杂,甚至不需要什么理解,更多的还是平时积累的一些经验,因此多看多学多积累才是最佳的解决方案.