多屏幕适配及PopupWindows
1.多屏幕适配的一些概念
谷歌Android官网对多屏幕适配术语和概念的介绍:
屏幕尺寸
按屏幕对角测量的实际物理尺寸。
为简便起见,Android 将所有实际屏幕尺寸分组为四种通用尺寸:小、 正常、大和超大。
屏幕密度(dpi)
屏幕物理区域中的像素量;通常称为 dpi(每英寸点数)。例如, 与“正常”或“高”密度屏幕相比,“低”密度屏幕在给定物理区域的像素较少。
为简便起见,Android 将所有屏幕密度分组为六种通用密度: 低、中、高、超高、超超高和超超超高。
分辨率
屏幕上物理像素的总数。添加对多种屏幕的支持时, 应用不会直接使用分辨率;而只应关注通用尺寸和密度组指定的屏幕 尺寸及密度。
密度无关像素 (dp)
密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单: px = dp * (dpi / 160)
。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。
另:
sp
sp 全称是 Scale-independent Pixels,用于字体大小,其概念与DP是一致的,也是为了保持设备无关。
2.如何对多屏幕进行适配?
总的说,因为android的设备屏幕尺寸,分辨率各不相同,
所以我们要在不同屏幕上正常显示资源的时候,必须保证以下四点原则:
- 在 XML 布局文件中指定尺寸时使用
wrap_content
、match_parent
或dp
单位 。 - 不要在应用代码中使用硬编码的像素值
- 不要使用
AbsoluteLayout
(已弃用) - 为不同屏幕密度提供替代位图可绘制对象
下文将提供更详细的信息。
(via :https://developer.android.com/guide/practices/screens_support.html?hl=zh-cn)
2.1 对布局尺寸使用 wrap_content、match_parent 或 dp 单位
为 XML 布局文件中的视图定义 android:layout_width
和 android:layout_height
时,使用 "wrap_content"
、 "match_parent"
或 dp
单位可确保在当前设备屏幕上为 视图提供适当的尺寸。
例如,layout_width="100dp"
的视图在 中密度屏幕上测出宽度为 100 像素,在高密度屏幕上系统会将其扩展至 150 像素宽, 因此视图在屏幕上占用的物理空间大约相同。
类似地,您应选择 sp
(缩放独立的像素)来定义文本 大小。sp
缩放系数取决于用户设置,系统 会像处理 dp
一样缩放大小。
2.2 不要在应用代码中使用硬编码的像素值
由于性能的原因和简化代码的需要,Android 系统使用像素作为 表示尺寸或坐标值的标准单位。这意味着, 视图的尺寸在代码中始终以像素表示,但始终基于当前的屏幕密度。 例如,如果 myView.getWidth()
返回 10,则表示视图在 当前屏幕上为 10 像素宽,但在更高密度的屏幕上,返回的值可能是 15。如果 在应用代码中使用像素值来处理预先未针对 当前屏幕密度缩放的位图,您可能需要缩放代码中使用的像素值,以与 未缩放的位图来源匹配。
如果应用在运行时操作位图或处理像素值,请参阅 下面有关其他密度注意事项的一节。
2.3 不要使用 AbsoluteLayout
与其他布局小工具不同,AbsoluteLayout
会强制 使用固定位置放置其子视图,很容易导致 在不同显示屏上显示效果不好的用户界面。因此,AbsoluteLayout
在 Android 1.5(API 级别 3)上便已弃用。
您应改用 RelativeLayout
,它会使用相对定位 来放置其子视图。例如,您可以指定按钮小部件显示在文本小工具的“右边”。
2.4 使用尺寸和密度特定资源
虽然系统会根据当前屏幕 配置扩展布局,但您在不同的屏幕尺寸上可能要调整 UI,以及提供 针对不同密度优化的可绘制对象。这基本上是重申 本文档前面的信息。
如果需要精确控制应用在不同 屏幕配置上的外观,请在配置特定的 资源目录中调整您的布局和位图可绘制对象。例如,考虑要显示在 中密度和高密度屏幕上的图标。只需创建两种不同大小的图标 (例如中密度使用 100x100,高密度使用 150x150),然后使用适当的限定符 以适当的方向放置两个 变体:
res/drawable-mdpi/icon.png //for medium-density screens
res/drawable-hdpi/icon.png //for high-density screens
注:如果密度限定符在目录名称中未定义, 系统会假设该目录中的资源是针对基线中 密度而设计,对于其他密度将会适当地缩放。
3.多屏幕适配在PopupWindow中的一些应用场景
下面举一个PopupWindow 的简单的用法:
// 加载layout_a ,使其成为一个view对象
View view = LayoutInflater.from(this).inflate(R.layout.layout_a, null);
PopupWindow popupwindow = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
popupwindow.setTouchable(true);
popupwindow.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
// 这里如果返回true的话,touch事件将被拦截
}
});
popupwindow.setBackgroundDrawable(new ColorDrawable(0x00000000));
// 设置显示位置,设为btn_b正左下方,无偏移。
popupwindow.showAsDropDown(btn_b);
PopupWindow 一共有四个显示函数:
// 相对某个控件的位置(正左下方),无偏移
1. showAsDropDown(View anchor):
// 相对某个控件的位置,有偏移;
// xoff表示x轴的偏移;
// yoff表示相对y轴的偏移;
2.showAsDropDown(View anchor, int xoff, int yoff):
// API19之后添加的函数,不常用。Displays the content view in a popup window anchored to the corner of another view. The window is positioned according to the specified gravity and offset by the specified x and y coordinates.
3.showAsDropDown(View anchor, int xoff, int yoff, int gravity)
//显示到父控件parent指定的位置,并附带偏移量(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等)
4. showAtLocation(View parent, int gravity, int x, int y):
上面显示函数的偏移量单位都是pixels,其坐标系参考View的坐标系而不是数学中的坐标系。
View的坐标都是相对于父控件而言的。
来自@gcssloop的图:
如果这时候,我们有一个新的需求,需要把PopupWindow 显示到某个控件的上方,
这时候只需要将y轴的偏移量改为负数,同时输入一个固定的dp,
根据dpi把dp转换成px(pixels),就完成了多屏幕的PopupWindow 适配。
转换代码如下:
/**
* dp转px
* @param context
* @param dpVal
* @return
*/
public static int dp2px(Context context, float dpVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getResources().getDisplayMetrics());
}