.

作者 :万境绝尘

转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/19698511

.

最近遇到了一系列的屏幕适配问题, 以及屏幕画图像素密度相关的问题, 索性在这里全部总结下;

1. 名词解析

在之前写过的 AndroidUI设计之 布局管理器 - 详细解析布局实现 中的 第七 小节已经说明了一部分;

(1) 通用名词

屏幕尺寸(screen size): 按照屏幕的对角线测量的实际大小;

--屏幕尺寸分类: 屏幕尺寸分为 小(small), 普通(normal), 大(large), 超大(extra large) 四种;

--自动渲染 : Android SDK根据屏幕实际尺寸, 选择一种方式(四选一)对布局进行渲染, 这是人为不可控的, 对程序员透明;

屏幕尺寸界线 : 屏幕的尺寸是按照dp计算的, dp越大, 尺寸越大;

--small(小屏) : 最少 320dp * 426dp;

--normal(普通) : 最少 320dp * 470dp;

--large(大屏) : 最少 480dp * 640dp;

--xlarge(超大) : 最少 720dp * 960dp;

屏幕长宽比(aspect ratio) : 手机屏幕物理宽度和物理高度比例关系, 程序中可以为指定长宽比屏幕提供布局资源;

屏幕分辨率(resolution) : 屏幕上显示的物理像素总和, 如 320 * 480;

--注意 : 分辨率不等于屏幕宽高比, 在Android程序中尽量避免直接使用px;

像素(px) : 实际的分辨率, 例如在 320 * 480分辨率手机上, 320 和 480 就是像素点;

分辨率(px)与设备独立像素(dip)比较: dip越大, 屏幕的尺寸越大, 分辨率越高, 越清晰, 屏幕大分辨率不一定大, 如电脑;

(2) Android设备相关名词

密度(density) : 在物理宽高范围内显示的像素数量, 同样屏幕大小的手机, 低密度显示的像素点少, 高密度显示的像素点多;

-- 资源分类 :固定像素宽高的UI资源(图片资源的宽高是按照像素确定的), 在低密度显得很大, 在高密度显示的很小, 因此为了使UI组件显示大致统一(不是绝对), 美工需要一种资源设置成4份不同像素的资源, 放到对应目录中去;

设备独立像素(dip/dp) : 该像素与设备硬件有关, 不同的设备显示效果不同, 与 实际密度 和 像素 无关;

-- 密度(dpi)无关 : 密度是每英寸包含像素个数, dip是基于屏幕物理密度的抽象单位;

-- dip与px等效情况 : 在密度为160dip的屏幕上, 1dip == 1px,320*480分辨率手机 宽2英寸 高3英寸, 那么手机密度为160dpi;

-- 屏幕不变分辨率改变 : 如果上面 2 * 3 英寸屏幕不变, 分辨率改成 480 * 800 分辨率, 这时每英寸的像素数量明显增加了, 即密度增加, 为240dpi, 2英寸有480像素; 屏幕不变的前提下 , 如果在160pi下100dip像素的实际长度 与 240dip下 100dip像素的实际长度是一样的;

-- 实际尺寸计算 : view组件使用dip作为单位, 如果在160dpi下直接按照像素点画出, 如果密度不是160dpi, 那么会计算一个转换比例, 这个比例与实际尺寸相乘得到新的像素点个数;

-- 计算公式: px = dip * density / 160; 当密度为160的时候, 屏幕的 px == dip;

-- Google建议: 在布局文件设置组件属性的时候, 尽量使用dip作为单位, 字体大小统一使用 sp 作为单位;

px与dip区别: 下面的情况是以屏幕尺寸不变为前提的;

-- px绘图 : 在320像素宽的手机上, 100px的长度 是 480宽度像素手机上长度的 2/3;

-- dip绘图 : 屏幕大小不变的情况下, 100dip 在320 480 像素手机上实际尺寸长度是一样的;

px与dip, px与sp之间转化工具类 :

public class DisplayUtils {

	public static int px2dip(float pxValue, float scale) {
		return (int) (pxValue / scale + 0.5f);
	}

	public static int dip2px(float dipValue, float scale) {
		return (int) (dipValue * scale + 0.5f);
	}

	public static int px2sp(float pxValue, float fontScale) {
		return (int) (pxValue / fontScale + 0.5f);
	}

	public static int sp2px(float spValue, float fontScale) {
		return (int) (spValue * fontScale + 0.5f);
	}

}

.

(3) 获取密度相关方法示例

注意 : 区分屏幕密度单个方向精确密度;

package shuliang.han.displaytest;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;

public class MainActivity extends Activity {

	//屏幕的宽高, 单位像素
	private int screenWidth;
	private int screenHeight;

	//屏幕的密度
	private float density;	//只有四种情况 : 0.75/ 1.0/ 1.5/ 2.0
	private int densityDpi;	//只有四种情况 : 120/ 160/ 240/ 320

	//水平垂直精确密度
	private float xdpi;	//水平方向上的准确密度, 即每英寸的像素点
	private float ydpi;	//垂直方向上的准确密度, 即没音村的像素点

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		//getPixelWindowManager();
		//getPixelDisplayMetrics();
		//getPixelDisplayMetricsII();

		System.out.println("宽:" + screenWidth + ", 高:"+screenHeight);
		System.out.println("密度 density:" + density + ",densityDpi:" +densityDpi);
		System.out.println("精确密度 xdpi:" + xdpi + ", ydpi:" + ydpi);
	}

	private void getPixelWindowManager() {
		screenWidth = getWindowManager().getDefaultDisplay().getWidth();
		screenHeight = getWindowManager().getDefaultDisplay().getHeight();
	}

	private void getPixelDisplayMetrics() {
		DisplayMetrics dm = new DisplayMetrics();
		dm = getResources().getDisplayMetrics();

		screenWidth = dm.widthPixels;
		screenHeight = dm.heightPixels;

		density = dm.density;
		densityDpi = dm.densityDpi;

		xdpi = dm.xdpi;
		ydpi = dm.ydpi;
	}

	private void getPixelDisplayMetricsII() {
		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);

		screenWidth = dm.widthPixels;
		screenHeight = dm.heightPixels;

		density = dm.density;
		densityDpi = dm.densityDpi;

		xdpi = dm.xdpi;
		ydpi = dm.ydpi;
	}
}

执行 getPixelWindowManager() 方法结果:

02-22 16:19:38.925: I/System.out(29606): 宽:1280, 高:752
02-22 16:19:38.925: I/System.out(29606): 密度 density:0.0,densityDpi:0
02-22 16:19:38.925: I/System.out(29606): 精确密度 xdpi:0.0, ydpi:0.0

执行 getPixelDisplayMetrics() 方法结果

02-22 16:20:40.225: I/System.out(29763): 宽:1280, 高:752
02-22 16:20:40.225: I/System.out(29763): 密度 density:1.0,densityDpi:160
02-22 16:20:40.225: I/System.out(29763): 精确密度 xdpi:149.82489, ydpi:150.51852

执行 getPixelDisplayMetricsII() 方法结果

02-22 16:21:11.230: I/System.out(29911): 宽:1280, 高:752
02-22 16:21:11.230: I/System.out(29911): 密度 density:1.0,densityDpi:160
02-22 16:21:11.230: I/System.out(29911): 精确密度 xdpi:149.82489, ydpi:150.51852

.

2. 真实密度(像素计算)和归一化密度(物理长度计算)

px与dp换算公式 : px = dip * density / 160;

计算像素点使用的是归一化密度, 计算实际尺寸使用的是精确的物理密度;

真实密度 : 每英寸含有的像素点数, 拿我使用的三星GT-N8000为例, 水平方向上的真实密度为 每英寸149.82像素, 垂直方向上的真实密度为 每英寸150.51像素;

-- 运算不按照该方式 : 按照该密度计算 1280dp对应的是 1280 * 149.82 / 160 = 1198.4 个像素;

举例 :

给一个Textview控件设置1280dp的宽度, 然后可以看到该组件横向沾满宽度, 按照实际运算该1280dp对应的是1198个像素, 是无法占满整个屏幕的;

XML布局文件 :

<LinearLayout 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"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="1280dp"
        android:layout_height="wrap_content"
        android:background="#FF0000"
        android:text="@string/hello_world" />

</LinearLayout>

效果图

归一化密度 : 在Android中从DisplayMetrics中获取的density 和 densityDpi 就是归一化密度;

-- 固定值 : 归一化的密度是有固定值的, 这个固定值是 120dpi(ldpi) , 160dpi(mdpi), 240dpi(ldpi), 320dpi(xldpi), 480dpi(xxldpi) Android中计算像素使用的密度是这五个值之一;

-- 实际尺寸不准确 : 如果想要在屏幕上划出1英寸的直线, 使用归一化密度计算这个值是错误的;

下面计算三星GT-N8000中水平方向上100dip所占有的像素个数和实际长度 :

-- 计算像素个数: 计算像素个数需要使用归一化密度, 该设备的归一化密度为 160dpi, 因此根据 px = dip * densityDpi / 160 , 进行计算, px = 100 * 160 / 160, 对应的像素个数为100px;

-- 计算实际尺寸: 按照英寸计算, 先计算出像素个数, 然后根据像素个数 和 精确物理密度 计算实际尺寸, 上面计算出了像素个数为100px, 水平方向上每英寸149.82489 个像素, 100px / 149.82489px/inch * 1inch = 0.6674inch, 因此100dpi对应的实际尺寸为 0.6674英寸;

.

3. Android中资源适配

(1) 图片资源适配

图片资源失真问题: 图片资源的大小是按照像素计算的, 在密度不同的时候显示大小也不相同, 因此会根据密度的不同制作不同像素的图片, 以避免失真;

-- 低密度手机显示 : 如果在低密度的手机上, 分辨率低, 图片占用像素个数不变, 图片会显得很大;

-- 高密度手机显示 : 如果在高密度的手机上, 分辨率高, 图片占用像素个数不变, 图片会显得很小;

根据密度选择资源 : 根据屏幕密度选择资源, 这种方式是Android默认的, 在res下有以下文件 :

-- 密度为120时 : 使用drawable-ldpi目录中的资源;

-- 密度为160时 : 使用drawable-mdpi目录中的资源;

-- 密度为240时 : 使用drawable-hdpi目录中的资源;

-- 密度为320时 : 使用drawable-xdpi目录中的资源;

-- 密度为480时 : 使用drawable-xxdpi目录中的资源;

保持图片不失真 : 从这个角度来讲, 可以只定义高密度资源, 然后使用dip单位限制图片显示父容器的大小, 也可以有很好的效果, 不过这样效率会很低;

根据屏幕尺寸适配 :

-- small小屏幕 : 使用drawable-small目录中的图片资源;

-- normal普通屏幕 : 使用drawable-normal目录中的图片资源;

-- large大屏幕 : 使用drawable-large目录中的图片资源;

-- xlarge超大屏幕 : 使用drawable-xlarge目录中的图片资源;

同时根据屏幕尺寸和密度适配 : 如适配大屏幕的中等密度 使用 drawable-large-mdpi目录下的图片资源;

(2) 布局文件适配

横竖屏布局适配 : 手机屏幕横竖屏切换的时候, 显然竖屏时的布局不能适配横屏的情况;

-- 竖屏布局 : 竖屏的情况下会自动加载 res/layout-port 目录下的布局文件;

-- 横屏布局 : 横屏的情况下会自动加载 res/layout-land 目录下的布局文件;

如果只设置一个布局 : 禁用自动切换, 只是用横屏 或者 只是用竖屏 进行布局;

-- 横竖屏设置 : 在AndroidManifest.xml 文件中设置activity的android:screenOrientation, 属性值为portrait的时候是竖屏显示, 属性值为landscape时是横屏显示;

分辨率布局适配 : Android中可以根据不同的分辨率自动适配对应的布局文件;

-- 例320*480分辨率: 使用res/layout-320x480目录下的布局文件;

-- 例480*800分辨率 : 使用res/layout-480x800目录下的布局文件;

综合情况: 分辨率320*480情况下分横竖屏两种情况;

-- 320*480分辨率横屏: 使用res/layout-land-320x480目录下的布局文件;

-- 320*480分辨率竖屏 : 使用res/layout-port-320x480目录下的布局文件;

根据屏幕尺寸选择布局文件 : 与适配图片资源文件类似;

(3) 精确适配

精确适配 : 3.2以上版本可以设置精确适配, 可以任意设置宽高的独立像素;

-- 宽320dp高480dp密度160dpi: drawable-w320dp-h480dp-160dpi, 其中w320dp表示屏幕宽度320dip, h480dp表示屏幕高度480dp, 160dpi表示密度;

.

作者 :万境绝尘

转载请注明出处  : http://blog.csdn.net/shulianghan/article/details/19698511

.

【Android 应用开发】Android屏幕适配解析 - 详解像素,设备独立像素,归一化密度,精确密度及各种资源对应的尺寸密度分辨率适配问题的更多相关文章

  1. Android游戏开发之旅 View类详解

    Android游戏开发之旅 View类详解 自定义 View的常用方法: onFinishInflate() 当View中所有的子控件 均被映射成xml后触发 onMeasure(int, int) ...

  2. Android屏幕适配解析 - 详解像素,设备独立像素,归一化密度,精确密度及各种资源对应的尺寸密度分辨率适配问题

    . 作者 :万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/19698511 . 最近遇到了一系列的屏幕适配问题, 以及 ...

  3. 动画_ _ Android应用开发之所有动画使用详解

    转载: http://blog.csdn.net/yanbober/article/details/46481171 题外话:有段时间没有更新博客了,这篇文章也是之前写了一半一直放在草稿箱,今天抽空把 ...

  4. Android应用开发之所有动画使用详解

    题外话:有段时间没有更新博客了,这篇文章也是之前写了一半一直放在草稿箱,今天抽空把剩余的补上的.消失的这段时间真的好忙,节奏一下子有些适应不过来,早晨七点四十就得醒来,晚上九点四十才准备下班,好像最近 ...

  5. 分享我开发的网络电话Android手机APP正式版,图文详解及下载

    分享我开发的网络电话Android手机APP正式版,图文详解及下载 分享我开发的网络电话Android手机APP正式版 实时语音通讯,可广域网实时通讯,音质清晰流畅! 安装之后的运行效果: 第一次安装 ...

  6. Android开发:文本控件详解——TextView(一)基本属性

    一.简单实例: 新建的Android项目初始自带的Hello World!其实就是一个TextView. 在activity_main.xml中可以新建TextView,从左侧组件里拖拽到右侧预览界面 ...

  7. Android开发:文本控件详解——TextView(二)文字跑马灯效果实现

    一.需要使用的属性: 1.android:ellipsize 作用:若文字过长,控制该控件如何显示. 对于同样的文字“Android开发:文本控件详解——TextView(二)文字跑马灯效果实现”,不 ...

  8. Android编程之LayoutInflater的inflate方法详解

    LayoutInflater的inflate方法,在fragment的onCreateView方法中经常用到: public View onCreateView(LayoutInflater infl ...

  9. Android消息传递之EventBus 3.0使用详解

    前言: 前面两篇不仅学习了子线程与UI主线程之间的通信方式,也学习了如何实现组件之间通信,基于前面的知识我们今天来分析一下EventBus是如何管理事件总线的,EventBus到底是不是最佳方案?学习 ...

随机推荐

  1. 给定 n×n 的实数矩阵,每行和每列都是递增的,求这 n^2 个数的中位数。

    #define COL 4 #define ROW 4 int findMedian(int matrix[][COL], int row, int col) { int* arr = new int ...

  2. mysql和postgresql转义字符探究

    总结 mysql依靠反斜杠\转义, postgresql 依靠单引号转义 mysql 客户端 mysql> create table usr (name varchar(), age integ ...

  3. rbac 概念

    1 权限管理 1.1 什么是权限管理 分享牛原创,分享牛系列.基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户 ...

  4. Python 3 函数自由变量的大坑

    Python中函数是一个对象, 和整数,字符串等对象有很多相似之处,例如可以作为其他函数的参数或返回对象, Python中的函数还可以携带自由变量, 两者无疑极大增进了Python的表达力. 但是Py ...

  5. [ExtJS5学习笔记]第三十六节 报表组件mzPivotGrid

    mzPivotGrid 是一个报表组件,采用这个组件之后,可以令你的应用体现更多的价值. 什么是pivot grid 什么是mzPivotGrid 学习资源 与图表组件的融合 什么是pivot gri ...

  6. 使用spark ml pipeline进行机器学习

    一.关于spark ml pipeline与机器学习 一个典型的机器学习构建包含若干个过程 1.源数据ETL 2.数据预处理 3.特征选取 4.模型训练与验证 以上四个步骤可以抽象为一个包括多个步骤的 ...

  7. sql group句子

    rollup SELECT employee_id,department_id,job_id,SUM(salary) FROM employees WHERE department_id <60 ...

  8. listener.ora--sqlnet.ora--tnsnames.ora的关系以及手工配置举例(转载:http://blog.chinaunix.net/uid-83572-id-5510.ht)

    listener.ora--sqlnet.ora--tnsnames.ora的关系以及手工配置举例 ====================最近看到好多人说到tns或者数据库不能登录等问题,就索性总结 ...

  9. lucene索引库的增删改查操作

    1. 索引库的操作 保持数据库与索引库的同步 说明:在一个系统中,如果索引功能存在,那么数据库和索引库应该是同时存在的.这个时候需要保证索引库的数据和数据库中的数据保持一致性.可以在对数据库进行增.删 ...

  10. 利用CocoaHTTPServer实现wifi局域网传输文件到iphone

    背景 近日在做一个代码阅读器,其中涉及到代码文件的上传,之前看到过许多app支持局域网传文件,因此就通过查询和研究实现了此功能,我是用的框架是CocoaHTTPServer. 原理 CocoaHTTP ...