小项目控制中心(一)之Android屏幕分辨率

最近接的一个小项目,在手机桌面实现一个扇形的悬浮窗,里面包含一些功能按键,用以对手机进行控制,比如gprs的开关,wifi的开关,音乐播放器的播放,下一首,上一首,对手机音量和亮度的调节。这个项目中,碰到了很多问题,也涉及到了很多平时没有接触过的知识点。

1、桌面悬浮窗的实现问题

2、扇形的UI

3、自定义seekbar,而且需要定义成1/4的圆。

4、对系统功能的操作,gprs、wifi、系统播放器的调用、音量亮度调节、屏幕旋转控制。

这个小项目基本框架已经完成,目前还处于调试阶段,很多UI,功能问题都没有解决。大致效果如下:


可以看到现在的效果还很差,所以带着问题,边Google,边完善项目。


第一个碰到的问题,也是自定义UI经常会碰到的,屏幕分辨率。

上面的截图是在真机调试中的效果,实际上同样的一个程序,运行在Genymotion虚拟机上的效果如下图:


对比两张截图,效果明显不一样,什么原因导致的呢?就是虚拟机和真机的屏幕分辨率不一样导致的。

刚开始去查阅相关资料的时候,很是模糊,完全搞不清楚基本概念问题,px和dp的区别和他们之间的换算问题,displaymetrics这样一个类的作用,屏幕分辨率和密度的概念,还有更纠结的是明明在这个扇形的UI对应的布局文件中(会在下面结合项目详细讲到),写到了width和height都是220dp,为什么用getLayoutParams().width和getLayoutParams().height得到的却是330dp(真机)和440dp(虚拟机)等等问题。

其实归根到底就一个问题:如何让你的APP支持于多种不同的设备。http://developer.android.com/training/basics/supporting-devices/index.html

在1.5及更早的Android版本中,在设定的时候,假定系统只会运行在一种分辨率的设备上----HVGA(320X480)分辨率。尺寸为3.2寸。由于系统只能工作在一种屏幕上,所以那是开发app无需考虑其他屏幕的显示问题。

这里解释下VGA的含义:

VGA:Video Graphics Array,显示绘图矩阵,相当于640X480像素

HVGA:Halrf-Size VGA,即VGA的一半,分辨率为480X320

QVGA:Quarter VGA,即VGA的四分之一,分辨率为320X240

WQVGA:Wide Quarter VGA,扩大的QVGA,分辨率比QVGA高,比VGA低。一般是400X240,480X272。

WVGA:Wide Video Graphics Array,扩大的VGA,分辨率为800X480。

具体的可以参看下面的表格:


事实上,程序中并不需要关注这些,这些知识作为科普常识,了解下便可。

自从Android1.6版本之后,系统引入了对多种尺寸,多种分辨率屏幕的支持。这就意味着1.6之后的程序需要在多种分辨率屏幕上良好显示做出额外的设计

Android本身也做了这方面的考虑,他把屏幕按尺寸和密度分为四类:

按尺寸分为:small,normal,large,xlarge

按密度分为:low(ldpi),medium(mdpi),high(hdpi),extra high(xhdpi)


所以,为了能写出适应于各种屏幕尺寸的应用程序,先看一看关于尺寸的一些基本概念:

1、屏幕的尺寸:屏幕的物理尺寸,以屏幕的对角线长度作为依据。(比如2.8寸,3.5寸,都是指的屏幕对角线)。

2、分辨率:可以理解为屏幕上拥有的像素总数,大多数情况下会被写成“宽度X长度”。不过标准的定义是:屏幕上每英寸上的像素点数,单位是dpi

3、像素:pixel,单位px。

4、密度:每平方英寸的像素数,同样像素大小的控件或图片,在密度大的设备上,显示的就会比较小,在密度小的设备上,显示的较大。Android设备依据密度可以分为四种:ldpi(120),mdpi(160),hdpi(240),xhdpi(320)。这些数值表示的屏幕密度(120,160,240,320)和用像素比例来表示屏幕密度是一个意思(0.75,1,1.5,2.0)。在px和dp换算的过程中,会用到屏幕比例(0.75,1.0,1.5,2.0)。

5、密度无关的像素,Device Independent Pixels(DIP,或者被简写成dp)。和像素的换算关系可以用下面公式:pixels = dips*(density/160)。density的取值包括120,160,240,320。


说到这里,基本上就可以理解为什么项目中,同样在布局文件中设置为220dp(float_window_big.xml配置文件中,设置的RelativeLayout的宽高都是220dp),为什么在模拟器和真机显示的大小不一样。

先贴两段代码:

public FloatWindowBigView(final Context context) {
		super(context);
		this.context = context;
		LayoutInflater.from(context).inflate(R.layout.float_window_big, this);
		view = findViewById(R.id.big_window_layout);
		viewWidth = view.getLayoutParams().width;
		viewHeight = view.getLayoutParams().height;
//		viewWidth = 220;
//		viewHeight =220;
		Log.i("width&height====", "width"+viewWidth+"height"+viewHeight+"");

上面这段是自定义FloatWindowBigView的构造函数,对布局的初始化,在配置文件中,对布局的宽高都是220dp,但是在view.getLayoutParams.width和view.getLayoutParams.height中得到的宽高单位确实px(像素)。所以在屏幕密度为2.0的模拟器上得到的viewWidth和viewHeight是440px,在屏幕密度为1.5的真机上,得到的宽高却是330px。

然后在windowManager中创建的窗口,在指定窗口大小过程中,如下:

	public static void createBigWindow(Context context) {
		WindowManager windowManager = getWindowManager(context);
		int screenWidth = windowManager.getDefaultDisplay().getWidth();
		int screenHeight = windowManager.getDefaultDisplay().getHeight();
		Log.i("screen width height", "screenwidth:" + screenWidth
				+ "screenheight" + screenHeight);
		if (bigWindow == null) {
			bigWindow = new FloatWindowBigView(context);
			if (bigWindowParams == null) {
				bigWindowParams = new LayoutParams();
				// bigWindowParams.x = screenWidth -
				// FloatWindowBigView.viewWidth;
				// 大悬浮窗位置为屏幕底部
				// bigWindowParams.y = screenHeight
				// - FloatWindowBigView.viewHeight;
				bigWindowParams.x = 0;
				bigWindowParams.y = 0;
				bigWindowParams.type = LayoutParams.TYPE_PHONE;
				bigWindowParams.format = PixelFormat.RGBA_8888;
				bigWindowParams.gravity = Gravity.RIGHT | Gravity.BOTTOM;
				bigWindowParams.width = FloatWindowBigView.viewWidth;
				bigWindowParams.height = FloatWindowBigView.viewHeight;
			}
			windowManager.addView(bigWindow, bigWindowParams);
		}
	}

bigWindowParams.width和bigWindowParams.height两个参数指定了当前悬浮窗口的大小,模拟器和真机得到的宽高分别是440px和330px,实际上,再根据各自密度再换算回去,两者的悬浮窗口都是220dp,所以,呈现在两台设备中的窗口大小实际上是一样大小的,之所以UI出现问题,是因为窗口中的空间的大小都是用px来指定的,这样,在模拟器中的控件显得更小的。(好吧,我承认,我是写到这里才发现问题所在的。)


Android中有个类可以得到分辨率等信息:

DisplayMetrics

用法如下:

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		WindowManager windowManager = getWindowManager();
		DisplayMetrics dm = new DisplayMetrics();
		windowManager.getDefaultDisplay().getMetrics(dm);
		System.out.println("density:"+dm.density);
		System.out.println("widthPixels:"+dm.widthPixels);
		System.out.println("heightPixels:"+dm.heightPixels);
		System.out.println("xdpi:"+dm.xdpi);
		System.out.println("ydpi:"+dm.ydpi);
		System.out.println("densityDpi"+dm.densityDpi);
		System.out.println(dm.toString());
		int screenwidth = windowManager.getDefaultDisplay().getWidth();
		int screenheight = windowManager.getDefaultDisplay().getHeight();
		System.out.println("widht"+screenwidth+"height"+screenheight);
		setContentView(R.layout.activity_main);
	}

DisplayMetrics有多个属性包括density(取值0.75,1.0,1.5,2.0),densityDpi(取值120,160,240,320),宽(width),高(height),x方向和y方向的dpi(分辨率)。

另外还可以通过windowManager.getDefaultDisplay.getWidth()、getHeight(),来得到屏幕的宽高,不过此方法好像已经弃用。

最后贴下上面的输出结果(用真机调试的,可以看出真机的屏幕密度为1.5即240)。



如何修改在类文件中定义的控件大小呢?因为类文件自定义的控件,其长度单位都是使用的px,如果想让这些控件支持多种不同像素的设备,有下面的工具类实现从px和dp的相互转换:

DensityUtils.java

public class DensityUtils {

	public static int px2dp(Context context, float pxValue) {
		final float scale = context.getResources().getDisplayMetrics().density;

		return (int) (pxValue / scale + 0.5f);
	}

	public static int dp2px(Context context, float dpValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dpValue * scale + 0.5f);
	}

}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值