前言:android数以千计的分辨率令我们开发者头痛不已,那么我们能不能解开它的面纱来欣赏一下它的本质呢?以下就带大家进行这么一场美妙的观光。
一、概述
Android的屏幕适配是一个比较受关注的问题,再加上UI、UE一般是按照IOS出一套然后Android也是对照着去做,给美工小妹妹想要讲清楚dp是一件比登天还难的事情。先来了解下这几个点位之间的关系。
二、 PX、PT、PPI、DPI、DP
术语
| 说明 |
备注
|
PX
|
(pixel),像素,屏幕上显示数据的最基本的点 |
|
PT
|
(point), 点,印刷行业常用单位 |
1pt=1/72英寸 |
PPI
|
(pixel per inch),每英寸像素数 | |
DPI
|
(dot per inch),每英寸点数 | |
DP
|
即dip(Density-independent pixel), 设备独立像素 |
1dp=160dpi时1px长度
|
其中px, pt, dp为长度单位,ppi和dpi为密度单位。安卓端屏幕大小各不相同,根据其像素密度,分为以下几种规格:
dp为安卓开发时的长度单位,根据不同的屏幕分辨率,与px有不同的对应关系。
三、获取屏幕宽高
public static int getScreenHeight1(Activity activity) {
return activity.getWindowManager().getDefaultDisplay().getHeight();
}
public static int getScreenHeight2(Activity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.heightPixels;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public static int getScreenHeight3(Activity activity) {
Point point = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(point);
return point.y;
}
在 Android 4.2(Api17) 之后提供了如下方法,与第三种类似也是将数据封装到Point中,然后返回款高度信息。
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static int getScreenHeight4(Activity activity) {
Point realSize = new Point();
activity.getWindowManager().getDefaultDisplay().getRealSize(realSize);
return realSize.y;
}
那么我们运行一把,看下获取的具体信息,以下为MI3和海尔平板(S1001LS)获取的数据:
在MI3上四个方法获取到的数据都为1920px,但是在海尔平板(S1001LS)上面前三个为1848px,最后一个为1920px,出现了分歧,我们看官方说分辨率是多少:
通过查找发现,在Android 4.0(Api14)就提供了getRealSize()这个方法,只不过是系统隐藏了,我们不能直接去调用。那么能不能通过反射的方式来使用呢?
public static int getScreenHeight5(Activity activity) {
Point realSize = new Point();
Display display = activity.getWindowManager().getDefaultDisplay();
try {
Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
} catch (Exception e) {
e.printStackTrace();
}
return realSize.y;
}
/**
* Gets the raw width of the display, in pixels.
* <p>
* The size is adjusted based on the current rotation of the display.
* </p>
* @hide
*/
public int getRawHeight() {
int h = getRawHeightNative();
if (DEBUG_DISPLAY_SIZE) Slog.v(
TAG, "Returning raw display height: " + h);
return h;
}
private native int getRawWidthNative();
当然我们可以通过反射的方式来调用它:
public static int getScreenHeight6(Activity activity) {
int heightPixels = 0;
Display display = activity.getWindowManager().getDefaultDisplay();
try {
heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
} catch (Exception e) {
e.printStackTrace();
}
return heightPixels;
}
Android 版本 |
版本号
| getRawHeight() |
getRealSize(Point p)
|
Android 3.2
|
13
|
包含(隐藏)
|
|
Android 4.0,4.0.1,4.0.2
|
14
|
包含(隐藏) |
包含(隐藏) |
Android 4.0.3,4.0.4
|
15
|
包含(隐藏) |
包含(隐藏) |
Android 4.1,4.1.1
|
16
|
包含(隐藏) |
包含(隐藏) |
Android 4.2,4.2.2
|
17
|
包含(隐藏) | 包含 |
... ...
|
... ...
|
... ...
|
... ...
|
综上,我们可以得到一个比较完整的获取系统屏幕高度的方法:
public int getRealHeight(Activity activity) {
int heightPixels = 0;
Display display = activity.getWindowManager().getDefaultDisplay();
final int VERSION = Build.VERSION.SDK_INT;
if(VERSION < 13) {
display.getHeight();
}else if (VERSION == 13) {
try {
heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
} catch (Exception e) {
}
} else if (VERSION >= 14 && VERSION < 17) {
Point realSize = new Point();
try {
Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
heightPixels = realSize.y;
} catch (Exception e) {
}
} else {
Point realSize = new Point();
display.getRealSize(realSize);
heightPixels = realSize.y;
}
return heightPixels;
}
简化一下就是:
public int getRealHeight(Activity activity) {
int heightPixels = 0;
Display display = activity.getWindowManager().getDefaultDisplay();
final int VERSION = Build.VERSION.SDK_INT;
if(VERSION < 13) {
display.getHeight();
}else if (VERSION == 13) {
try {
heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
} catch (Exception e) {
}
} else {
Point realSize = new Point();
try {
Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
heightPixels = realSize.y;
} catch (Exception e) {
}
}
return heightPixels;
}
四、获取屏幕尺寸
/**
* The exact physical pixels per inch of the screen in the X dimension.
*/
public float xdpi;
/**
* The exact physical pixels per inch of the screen in the Y dimension.
*/
public float ydpi;
没错,这就是我们要找的在宽度和高度上的ppi(每英寸内的像素点数目)。
/**
* 获取屏幕宽度ppi
*
* @param activity
* @return 屏幕宽度ppi
*/
public static float getWidthPpi(Activity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.xdpi;
}
/**
* 获取屏幕高度ppi
*
* @param activity
* @return 屏幕高度ppi
*/
public static float getHeightPpi(Activity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.ydpi;
}
那么获取宽高的物理尺寸也就简单了:
/**
* 获取屏幕宽度物理尺寸
*
* @param activity
* @return
*/
public static float getWidthInch(Activity activity) {
int realWidth = getRealWidth(activity);
return (float)realWidth / getWidthPpi(activity);
}
/**
* 获取屏幕高度物理尺寸
*
* @param activity
* @return
*/
public static float getHeightInch(Activity activity) {
int realHeight = getRealHeight(activity);
return (float)realHeight / getHeightPpi(activity);
}
根据勾股定理,宽度和高度的尺寸都知道了,对角线的长度就是长度平方加上高度平方再开平方:
/**
* 获取屏幕物理尺寸
*
* @param activity
* @return 屏幕物理尺寸
*/
public static float getScreenInch(Activity activity) {
return (float)Math.sqrt(Math.pow(getWidthInch(activity), 2) + Math.pow(getHeightInch(activity), 2));
}
五、获取长宽DP
/**
* 获取屏幕密度
*
* @return
*/
public static float getScreenDensity(Activity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.density;
}
然后用屏幕宽高除以 屏幕密度就是宽高上的dp数目:
/**
* 获取屏幕可操作区域宽度dp数目
*
* @param activity
* @return
*/
public static float getScreenWidthDp(Activity activity) {
return getScreenWidth(activity) / getScreenDensity(activity);
}
/**
* 获取屏幕高度可操作区域dp数目
*
* @param activity
* @return
*/
public static float getScreenHeightDp(Activity activity) {
return getScreenHeight(activity) / getScreenDensity(activity);
}
/**
* 获取屏幕真实宽度dp数目
*
* @param activity
* @return
*/
public static float getRealWidthDp(Activity activity) {
return getRealWidth(activity) / getScreenDensity(activity);
}
/**
* 获取屏幕真实高度dp数目
*
* @param activity
* @return
*/
public static float getRealHeightDp(Activity activity) {
return getRealHeight(activity) / getScreenDensity(activity);
}
六、判断是手机还是平板
/**
* 判断屏幕是否大于6英寸
*
* @param activity
* @return
*/
public static boolean isMoreThan6Inch(Activity activity) {
return getScreenInch(activity) >= 6.0;
}
第三种判断是否为大尺寸设备:
/**
* 判断设备是否为大尺寸屏幕
*
* @param context
* @return
*/
public static boolean isScreenSizeLarge(Context context) {
return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
我们开看下SCREENLAYOUT_SIZE_LARGE 是怎么定义的:
/** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
* value indicating the screen is at least approximately 480x640 dp units,
* corresponds to the
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
* resource qualifier.
* See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information. */
public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
可以看到是判断的屏幕高度和宽度的dp数目,他要求最小为 480X640 dp,我觉得最好这两个都满足才是平板:
/**
* 判断设备是否为平板
*
* @param activity
* @return true 平板;
* false 手机;
*/
public static boolean isTablet(Activity activity) {
return isMoreThan6Inch(activity) && isScreenSizeLarge(activity);
}
七、源码及示例
结语: