一、适配国家语言
二、屏幕适配
(1)页面控件属性常用适配方法
(2)手机横竖屏切换兼容适配
三、适配不同系统版本
智能手机的用户分布在不同国家,且偏好各异,这就要求开发阶段兼容适配;由于各厂家生产出的安卓设备分别率不同、屏幕大小和风格也存在各异,如果手机的用户设备各异,仅用一张图片可能会出现拉伸变形模糊,影响用户体验,因此对应屏幕的兼容适配是重中之重;随着Google不断更新Android版本,每个版本的代码也有区别。Android适配技能日益成为开发者必不可少的一项专业技能。
本篇文章分别讲解了语言适配、屏幕适配、版本适配三个内容。
语言适配有两种场景:一种是在手机系统里的“设置”选项中更改了系统的语言,影响的是整个手机内应用,包括系统应用和非系统应用。另一种,是用户可以手动切换某一款应用的内部语言风格,影响的范围仅仅是自身应用。
工程的根目录有个res/的目录,该目录下存放的是资源文件:如drawable、anim、layout、values。其中,value目录下存放/strings.xml,它用来设置项目中需要的字符串对象,可以理解为应用文本显示的内容。如下,默认的应用名称。
APP_NAME
value目录会根据手机语言的改变而加载不同String.xml。因此,在res/目录下创建多个values/strings.xml文件,且values目录需要改名,例如:
res/
values/ 默认
strings.xml
values-en/ 英文
strings.xml
values-es/ 西班牙
strings.xml
values-fr/ 法语
strings.xml
英语:/values-en/strings.xml:
My Application
Hello World!
西班牙语:/values-es/strings.xml:
Mi Aplicación
Hola Mundo!
法语:/values-fr/strings.xml:
Mon Application
Bonjour le monde !
最后,我们就可以在代码中使用R.string.
语法来引用字符串资源就可以了。配置好了就不怕手机系统语言切换了,但是APP内部语言切换就得换一种实现方式了。
首先,通过Configuration(配置信息)这个类,获取应用当前的语言,然后修改该配置中的语言,最后更新配置,重启一下Activity才能生效。其次,还有一个比较重要的类Locale(地点),如下。
public final class Locale implements Cloneable, Serializable {
static private final Cache LOCALECACHE = new Cache();
/** 中文
*/
static public final Locale CHINESE = createConstant("zh", "");
/**英文
*/
static public final Locale ENGLISH = createConstant("en", "");
/** 法文
*/
static public final Locale FRENCH = createConstant("fr", "");
/** 德文
*/
static public final Locale GERMAN = createConstant("de", "");
/**印度文
*/
static public final Locale ITALIAN = createConstant("it", "");
/** 日文
*/
static public final Locale JAPANESE = createConstant("ja", "");
/** 韩文
*/
static public final Locale KOREAN = createConstant("ko", "");
.....
}
点击切换按钮,实现中英文语言切换。
@Override
public void onClick(View v) {
//1、获取当前语言
String current_language;
// 注:Locale.getDefault().getLanguage();该方法获取系统语言,对于应用内切换不适用。
Configuration config = getResources().getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){//api24 ,android 7.0
current_language = config.getLocales().toLanguageTags();
}else{
current_language = config.locale.getLanguage();
}
//2、切换中英语言
if (current_language.equals(Locale.CHINESE.getLanguage()) || current_language.equals("zh-CN") ){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
config.getLocales();
config.setLocale(Locale.ENGLISH);
}else{
config.locale = Locale.ENGLISH;
}
}else if (current_language.equals(Locale.ENGLISH.getLanguage())){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
config.getLocales();
config.setLocale(Locale.CHINESE);
}else{
config.locale = Locale.CHINESE;
}
}
//3、更新应用配置
getBaseContext().getResources().updateConfiguration(config,getBaseContext().getResources().getDisplayMetrics());
//4、重启Activity
recreate();
}
为什么做屏幕适配?
目前市场上的安卓手机设备的分别率各不相同、屏幕大小和风格(9.0 刘海屏适配方案)也存在各异。例如,项目中仅用一张图片,在多款不同大小的手机上表现可能会出现拉伸变形模糊,影响用户体验。其次,Android设备是支持屏幕旋转功能的,如果不做适配那问题就大了,轻者布局错位,重则生命周期重刷发生异常崩溃。
这里归纳总结的屏幕适配有三个方向:图片适配、布局适配、旋转适配。
在项目开发的过程中经常会用到图片,因此有一个重要的沟通,就是与UI设计师的图片大小分别率的沟通。一般UI设计师画图的实现步骤:①在photoshop、coreldraw、Sketch中制作好图标,设置图标的内外边距,单位是px ;②保存输出为PNG位图图片③而开发者在实际开发中“dp”做为单位的更加常见,所以就会涉及到“px”“dp”互转的问题。
dp是一种基于屏幕密度的抽象单位,也叫虚拟像素,在不同的像素密度的设备上会自动适配。原理:我们知道px是像素单位,一款设备的屏幕上会散列着物理像素点,也叫分别率,在屏幕上每英寸160像素点时,1dp是等于1px的。不同手机屏幕尺寸大小不同,分辨率也不同。
Google在系统基本对于设备的适配是以dp为单位的,db会跟着屏幕大小仍保持相对应的px大小。参考下表:
计算公式:px = dp * (dpi/160) ; dp = dpi/160/px;
分辨率级别 | DPI | 比率 | 分别率(px) | dp 换算 px关系 |
ldpi(Low:低) | 120 | 0.75 | 240*320 | 1dp = 0.75px |
mdpi(Middle:中) | 160 | 1 | 320*480 | 1dp = 1px |
hdpi(High:高) | 240 | 1.5 | 480*800 | 1dp =1.5px |
xhdpi(超高,2倍图) | 320 | 2 | 1280*720 | 1dp = 2px |
xxhdpi(3倍图) | 480 | 3 | 1920*1080 | 1dp =3px |
xxxhdpi(4倍图) | 640 | 4 | 3840*2160 | 1dp =4px |
因此,我们可以根据dp与px的换算关系,让UI设计师给出计算后的多套图。例如,xhdpi 级别的设备画了一张200*200px的图,那么hdpi的150*150px、mdpi的100*100px、ldpi的75*75px....然后,将这些文件放入相应的drawable资源目录中,当引用
@drawable/image
时系统会根据屏幕的分辨率选择恰当的bitmap。
res/
drawable-xhdpi/
image.png
drawable-hdpi/
image.png
drawable-mdpi/
image.png
drawable-ldpi/
image.png
另外,解释一下为什么上面表格有两行标位红色,原因是2倍图和3倍图是主流占比,在友盟统计中显示如下:
Layout适配尺寸有4种:小(small),普通(normal),大(large),超大(xLarge)
我们在资源文件layout目录创建不同尺寸布局文件,系统会根据运行的设备屏幕尺寸,选择在与之对应的layout目录中加载layout。如下:
res/
layout(-normal)/默认
main.xml
layout-large/大
main.xml
layout-xlarge/超大
main.xml
layout-small/小
main.xml
注意:记得在AndroidManifest.xml文件中设置多分辨率支持!!!
其他屏幕适配常用适配方法
- 尽量使用线性布局(LinearLayout)和相对布局(RelateLayout),尽量不使用绝对布局(AbsoluteLayout)和帧布局(FrameLayout)。
- 尽量使用wrap_content、mach_parent让view自适应或最大化,尽量不要写宽高的值。
- 使用线下布局的百分比weight权重时,要把宽度写成“0dp“,如果写成wrap_coent会使布局效果不佳等问题。
- 尽量使用android的Shape自定义view背景,这样会随之自适应。
- ImageView的ScaleType有五种方式(center,centerCrop,centerInside,fieCenter,fieXY),尽量使用fieCenter按比例扩大至view宽度,能取得较好适配和显示效果。
- 获取屏幕分辨率信息,进行动态适配:DisplayMetrics metrics =newDisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics);
在AndroidMaifest.xml中activity中的属性 android:screenOrientation="",可以设置屏幕为固定横屏或竖屏。值有三种类型:属性landscape是横向,portrait是纵向,"sensor"是由物理的感应器来决定。
如果用户手动旋转设备,此时不仅要注意Activity会经过销毁到重建的问题,还要注意布局兼容问题。 适配横向屏幕,首先再创建一份layout-land布局文件,系统会根据运行的设备屏幕方向情况自动加载对应的layout。如下:
res/
layout-port/ 竖屏
main.xml
layout-land/ 横屏
main.xml
layout-large-land/ 也是可以与不同屏幕大小一起使用
main.xml
也可以只在layout文件夹下创建不同xml布局,通过Configuration().orientation来判断当前是横屏landscape还是竖屏portrait,然后加载相对应的布局文件即可。
重建问题:
如果不需要重新走一遍Activity的生命周期,则在AndroidManifest.xml中activity标签下设置android: configChanges="orientation|keybordHidden|screenSize",这样的话就不会重复调用activity的生命周期方法,切换时只会调用 onConfigurationChanged(Configuration newconfig)方法。
如果一切让它销毁在重建,只不过这过程中把需重要的值保存起来。重建后在取出来就行了。
//onResume之后调用,onPause()之前执行
@Override
protected void onSaveInstanceState(Bundle outBundle) {
super.onSaveInstanceState(outBundle);
outBundle.putBoolean("RoadChange", mChange);
}
//onResume之前调用 ,onStart之后执行
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mChange = savedInstanceState.getBoolean("RoadChange");
}
新的Android版本会为我们的app提供更棒的API,但我们的app仍应支持旧版本的Android,直到更多的设备升级到新版本为止。Android 5.0、6.0、7.0、8.0、9.0 新特性,DownloadManager踩坑记
首先,在项目清单文件中指定最小和目标API级别。具体来说,
...
其次,是在代码中判断检查版本信息。Android在Build常量类中提供了对每一个版本的唯一代号,在我们的app中使用这些代号可以建立条件,保证依赖于高级别的API的代码,只会在这些API在当前系统中可用时,才会执行。
private void setUpActionBar() {
// Make sure we're running on Honeycomb or higher to use ActionBar APIs
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
最后,使用项目的风格和主题跟随系统版本改变外观。Android提供了用户体验主题,为app提供基础操作系统的外观和体验。这些主题可以在manifest文件中被应用于app中。通过使用内置的风格和主题,我们的app自然地随着Android新版本的发布,自动适配最新的外观和体验.
使activity看起来像对话框:
使activity有一个透明背景:
应用在/res/values/styles.xml
中定义的自定义主题:
使整个app应用一个主题(全部activities)在元素中添加android:theme
属性:
更多关于创建和使用主题,详见Styles and Themes。