现在的位置: 首页 > 综合 > 正文

Android基础系列———– 资源适配

2018年02月16日 ⁄ 综合 ⁄ 共 5915字 ⁄ 字号 评论关闭

1、一些概念

屏幕尺寸:指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米
屏幕分辨率:指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素,如1960*1080。
屏幕像素密度:指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。
dp、dip、dpi、sp、px辨析

px:即像素,1px代表屏幕上一个物理的像素点,大多数情况下,比如UI设计、Android原生API都会以px作为统一的计量单位,不过px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同。偶尔用到px的情况,是需要画1像素表格线或阴影线的时候,用其他单位如dp会显得模糊。

pixels = dips * (density / 160)
density有160、240、320
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int width = metric.widthPixels;  // 屏幕宽度(像素)
int height = metric.heightPixels;  // 屏幕高度(像素)
float density = metric.density;  // 屏幕密度(0.75 / 1.0 / 1.5)
int densityDpi = metric.densityDpi;  // 屏幕密度DPI(120 / 160 / 240)

dp:这个是最常用但也最难理解的尺寸单位。它与“像素密度”密切相关,假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算出在这部手机的屏幕上,每英寸包含的像素点的数量为240/1.5=160dpi(横向)或320/2=160dpi(纵向),160dpi就是这部手机的像素密度,像素密度的单位dpi是Dots Per Inch的缩写,即每英寸像素数量。横向和纵向的这个值都是相同的,原因是大部分手机屏幕使用正方形的像素点。
        不同的手机/平板可能具有不同的像素密度,例如同为4寸手机,有480x320分辨率的也有800x480分辨率的,前者的像素密度就比较低。Android系统定义了四种像素密度:低(120dpi)、中(160dpi)、高(240dpi)和超高(320dpi),它们对应的dp到px的系数分别为0.75、1、1.5和2,这个系数乘以dp长度就是像素数。例如界面上有一个长度为“80dp”的图片,那么它在240dpi的手机上实际显示为80x1.5=120px,在320dpi的手机上实际显示为80x2=160px。如果你拿这两部手机放在一起对比,会发现这个图片的物理尺寸“差不多”,这就是使用dp作为单位的效果。
dip:与dp完全相同,只是名字不同而已。在早期的Android版本里多使用dip,后来为了与sp统一就建议使用dp这个名字了。

sp:与缩放无关的抽象像素(Scale-independent Pixel)。sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(在手机settings中可以设置系统字体大小,小、正常、大、超大等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。对于使用sp作为字体大小单位的app,如果不需要根据系统字大小的改变影响应用内字体大小,可以通过在自定义的所有Activity的基类BaseActivity中添加如下代码:

@Override  
public Resources getResources() {  
	Resources res = super.getResources();    
	Configuration config=new Configuration();    
	config.setToDefaults();    
	res.updateConfiguration(config,res.getDisplayMetrics() );  
	return res;  
}

/**
 * dp、sp 转换为 px 的工具类
 * 
 */
public class DisplayUtil {
	/**
	 * 将px值转换为dip或dp值,保证尺寸大小不变
	 * 
	 * @param pxValue
	 * @param scale
	 *            (DisplayMetrics类中属性density)
	 * @return
	 */
	public static int px2dip(Context context, float pxValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (pxValue / scale + 0.5f);
	}

	/**
	 * 将dip或dp值转换为px值,保证尺寸大小不变
	 * 
	 * @param dipValue
	 * @param scale
	 *            (DisplayMetrics类中属性density)
	 * @return
	 */
	public static int dip2px(Context context, float dipValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dipValue * scale + 0.5f);
	}

	/**
	 * 将px值转换为sp值,保证文字大小不变
	 * 
	 * @param pxValue
	 * @param fontScale
	 *            (DisplayMetrics类中属性scaledDensity)
	 * @return
	 */
	public static int px2sp(Context context, float pxValue) {
		final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
		return (int) (pxValue / fontScale + 0.5f);
	}

	/**
	 * 将sp值转换为px值,保证文字大小不变
	 * 
	 * @param spValue
	 * @param fontScale
	 *            (DisplayMetrics类中属性scaledDensity)
	 * @return
	 */
	public static int sp2px(Context context, float spValue) {
		final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
		return (int) (spValue * fontScale + 0.5f);
	}
}

mdpi、hdpi、xdpi、xxdpi
其实之前还有个ldpi,但是随着移动设备配置的不断升级,这个像素密度的设备已经很罕见了,所在现在适配时不需考虑。
mdpi、hdpi、xdpi、xxdpi用来修饰Android中的drawable文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值。
那么如何区分呢?Google官方指定按照下列标准进行区分:
名称 像素密度范围
mdpi 120dpi~160dpi
hdpi 160dpi~240dpi
xhdpi 240dpi~320dpi
xxhdpi 320dpi~480dpi
xxxhdpi 480dpi~640dpi

2、提供可选资源

        为应用程序提供多种可选资源是实现设备适配的基础。除了分辨率外,同种资源之间还可以有一下几种标签属性。
  • 语言和地区

           android系统采用的是ISO 639-1国际语言码,有两个字母组成。地区码采用的是ISO 3166-1-alpha-2,也是两个字母。其中地区码是可选的,如果加上的话,需要在前面加上“r”。例如:en(表示英语),en-rUS(表示英语和美国地区)。在程序中可以通过Configuration类的locale属性值来获取当前设备的语言地区信息。

  • 最小宽度(Smallest Width)

格式是sw<N>dp

如layout-sw600dp, values-sw600dp这里的sw代表smallwidth的意思,当你所有屏幕的最小宽度都大于600dp时,屏幕就会自动到带sw600dp后缀的资源文件里去寻找相关资源文件,这里的最小宽度是指屏幕宽高的较小值,每个屏幕都是固定的,不会随着屏幕横向纵向改变而改变。在AndroidManifest.xml中可以通过“android:requiresSmallestWidthDp”属性来表示此程序要求的最小宽度值。在程序中可以通过Configuration类smallestScreenWidthDp成员变量来获取当前设备的最小宽度值
  • 可用宽度(Available Width)

格式是w<N>dp

如layout-w600dp, values-w600dp带这样后缀的资源文件的资源文件制定了屏幕宽度的大于Ndp的情况下使用该资源文件,但它和sw<N>dp不同的是,当屏幕横向纵向切换时,屏幕的宽度是变化的,以变化后的宽度来与N相比,看是否使用此资源文件下的资源。在程序中可以通过Configuration类screenWidthDp成员变量来获取当前可用宽度值。
  • 可用高度(Available Height)

格式是h<N>dp 

如layout-h600dp, values-h600dp
这个后缀的使用方式和w<N>dp一样,随着屏幕横纵向的变化,屏幕高度也会变化,根据变化后的高度值来判断是否使用h<N>dp ,但这种方式很少使用,因为屏幕在纵向上通常能够滚动导致长度变化,不像宽度那样基本固定,因为这个方法灵活性不是很好,google官方文档建议尽量少使用这种方式。
  • 屏幕大小(Screen Size)

在Android系统中,将屏幕尺寸大致分为以下几类:

        1.small
         2.normal
        3.large
        4.xlarge
可以通过Configuration类中的screenLayout成员变量来获取当前设备的屏幕大小。
  • 屏幕的宽高外观(Screen Aspect)

这个属性是指当前屏幕的宽高比(aspect ratio)有两种结果:

      1.long
       2.notlong
可以通过Configuration类中的screenLayout成员变量来获知当前设备的屏幕是否为长屏。
  • 屏幕方向(Screen Orientation)

分为两种,即:竖屏(port)、横屏(land)

可以通过Configuration类中的orientation成员变量来获知当前设备的屏幕方向。
  • UI模式(UI Mode)

UI模式分为以下几种:car、desk、television、appliance

  • 夜间模式(Night Mode)

分为两种nigth、notnight。可以通过UiModeManager来开启或关闭这一功能。

  • 屏幕像素密度(dpi)
android系统将dpi粗略地分为以下几类:ldpi、mdpi、hdpi、xhdpi、xxhdpixxxhdpi、tvdpi(介于mdpi和hdpi之间)、nodpi(这些资源不希望被改变尺寸以适应屏幕)
  • 平台版本(Platform Version)

设备所支持的API等级值

3、屏幕适配要点

  • 为不同的屏幕尺寸提供多种layout布局

建议对尺寸跨度大的屏幕,应用程序应该提供多种layout布局

  • 为不同的屏幕方向提供多种layout布局
  • 为不同的屏幕密度提供相应的图片资源
  • 显式声明应用程序支持的屏幕类型
  • 布局时注意事项

使用wrap_content、match_parent、weight

要确保布局的灵活性并适应各种尺寸的屏幕,应使用 “wrap_content” 和 “match_parent” 控制某些视图组件的宽度和高度。
使用 “wrap_content”,系统就会将视图的宽度或高度设置成所需的最小尺寸以适应视图中的内容,而 “match_parent”(在低于 API 级别 8 的级别中称为 “fill_parent”)则会展开组件以匹配其父视图的尺寸。如果使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬编码的尺寸,视图就会相应地仅使用自身所需的空间或展开以填满可用空间。此方法可让布局正确适应各种屏幕尺寸和屏幕方向。
weight是线性布局的一个独特的属性,我们可以使用这个属性来按照比例对界面进行分配,完成一些特殊的需求。
android:layout_weight的真实含义是:如果View设置了该属性并且有效,那么该 View的宽度等于原有宽度(android:layout_width)加上剩余空间的占比。

使用自动拉伸位图

关于高清设计图尺寸

Google官方给出的高清设计图尺寸有两种方案,一种是以mdpi设计,然后对应放大得到更高分辨率的图片,另外一种则是以高分辨率作为设计大小,然后按照倍数对应缩小到小分辨率的图片。

根据经验,我更推荐第二种方法,因为小分辨率在生成高分辨率图片的时候,会出现像素丢失,我不知道是不是有方法可以阻止这种情况发生。
而分辨率可以以1280*720或者是1960*1080作为主要分辨率进行设计。


ImageView的ScaleType属性

设置不同的ScaleType会得到不同的显示效果,一般情况下,设置为centerCrop能获得较好的适配效果。
动态设置
有一些情况下,我们需要动态的设置控件大小或者是位置,比如说popwindow的显示位置和偏移量等,这个时候我们可以动态的获取当前的屏幕属性,然后设置合适的数值

public class ScreenSizeUtil {

    public static int getScreenWidth(Activity activity) {
        return activity.getWindowManager().getDefaultDisplay().getWidth();
    }

    public static int getScreenHeight(Activity activity) {
        return activity.getWindowManager().getDefaultDisplay().getHeight();
    }

}

抱歉!评论已关闭.