Android 适配语言、屏幕、图片、横竖屏、版本

目录

一、适配国家语言

(1)手机系统语言适配

(2)应用切换语言

二、屏幕适配

(1)图片适配

(2)、XML布局适配

(3)、横竖屏适配

三、适配不同系统版本


前言

智能手机的用户分布在不同国家,且偏好各异,这就要求开发阶段兼容适配;由于各厂家生产出的安卓设备分别率不同、屏幕大小和风格也存在各异,如果手机的用户设备各异,仅用一张图片可能会出现拉伸变形模糊,影响用户体验,因此对应屏幕的兼容适配是重中之重;随着Google不断更新Android版本,每个版本的代码也有区别。Android适配技能日益成为开发者必不可少的一项专业技能。

本篇文章分别讲解了语言适配、屏幕适配、版本适配三个内容。

 

一、适配国家语言

语言适配有两种场景:一种是在手机系统里的“设置”选项中更改了系统的语言,影响的是整个手机内应用,包括系统应用和非系统应用。另一种,是用户可以手动切换某一款应用的内部语言风格,影响的范围仅仅是自身应用。

(1)手机系统语言适配

工程的根目录有个res/的目录,该目录下存放的是资源文件:如drawable、anim、layout、values。其中,value目录下存放/strings.xml,它用来设置项目中需要的字符串对象,可以理解为应用文本显示的内容。如下,默认的应用名称。

<resources>

    <string name="app_name"> APP_NAME </string>

</resources>

 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:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="title">My Application</string>
    <string name="hello_world">Hello World!</string>
</resources>

西班牙语:/values-es/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="title">Mi Aplicación</string>
    <string name="hello_world">Hola Mundo!</string>
</resources>

法语:/values-fr/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="title">Mon Application</string>
    <string name="hello_world">Bonjour le monde !</string>
</resources>

最后,我们就可以在代码中使用R.string.<string_name>语法来引用字符串资源就可以了。配置好了就不怕手机系统语言切换了,但是APP内部语言切换就得换一种实现方式了。

(2)应用切换语言

首先,通过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设备是支持屏幕旋转功能的,如果不做适配那问题就大了,轻者布局错位重则生命周期重刷发生异常崩溃。这里归纳总结的屏幕适配有三个方向:图片适配、布局适配、旋转适配。

(1)图片适配

 

dp是一种基于屏幕密度的抽象单位,也叫虚拟像素,在不同的像素密度的设备上会自动适配。原理:我们知道px是像素单位,一款设备的屏幕上会散列着物理像素点,叫分别率。密度dpi表示在屏幕上每英寸的所占的像素点。规定当dpi为160时,1dp是等于1px的,从而实现dp可以自动适配的功能。

计算公式:px = dp * (dpi/160) ;   dp = px/dpi/160;        

手机设备的尺寸是☞屏幕对角线的长度,因此像素密度DPI的计算方式比较有趣:例如有个5寸且长宽比为4:3的手机设备。1寸 = 2.54厘米。知道了对角线长度和长宽比,就可以根据勾股定理很容易的计算出手机屏幕的长和宽了。

分辨率级别 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的图,那么hdpi150*150pxmdpi100*100pxldpi75*75px....然后,将这些文件放入相应的drawable资源目录中,当引用@drawable/image时系统会根据屏幕的分辨率选择恰当的bitmap。

  res/
        drawable-xhdpi/
              image.png
        drawable-hdpi/
               image.png
        drawable-mdpi/
              image.png
        drawable-ldpi/
               image.png

另外,解释一下为什么上面表格有两行标位红色,原因是2倍图和3倍图是主流占比,在友盟统计中显示如下:

一般通过dp适配时,是在如上表中比较标准的dip和分别率的情况下,可以完美适配,但出现特殊屏幕时就会出现问题。举个例子,已知MIX2的屏幕尺寸是5.5英寸,分辨率是2160*1080象素,DPI = resources.displayMetrics.densityDpi= 440。

此时,设计人员给的UI设计图是按照分别率1280*720也就是2倍图,换算dp后宽高是640dp*375dp。而在MIX2设备上,屏幕的宽度是1080/(440/160)=392.7dp,也就是说手机设备屏幕比设计图要宽,这种情况下要显示满屏的欢迎页图片,就无法达到和设计图相同的效果。

当然,还会存在手机设备宽度不足375dp,那么就会出现图片超出屏幕的情况。因此用dp进行适配也是差强人意,下面是字节跳动的屏幕适配方案:获取屏幕参数信息,进行动态适配

object ScreenUtil {
    fun adapterScreen(activity: Activity, targetDP: Int, isVertical: Boolean) {
        //系统的屏幕尺寸
        val systemDM = Resources.getSystem().displayMetrics
        //app整体的屏幕尺寸
        val appDM = activity.application.resources.displayMetrics
        //activity的屏幕尺寸
        val activityDM = activity.resources.displayMetrics

        if (isVertical) {
            // 适配屏幕的高度
            activityDM.density = activityDM.heightPixels / targetDP.toFloat()
        } else {
            // 适配屏幕的宽度
            activityDM.density = activityDM.widthPixels / targetDP.toFloat()
        }
        // 适配相应比例的字体大小
        activityDM.scaledDensity = activityDM.density * (systemDM.scaledDensity / systemDM.density)
        // 适配dpi
        activityDM.densityDpi = (160 * activityDM.density).toInt()
    }

    fun resetScreen(activity: Activity) {
        //系统的屏幕尺寸
        val systemDM = Resources.getSystem().displayMetrics
        //app整体的屏幕尺寸
        val appDM = activity.application.resources.displayMetrics
        //activity的屏幕尺寸
        val activityDM = activity.resources.displayMetrics

        activityDM.density = systemDM.density
        activityDM.scaledDensity = systemDM.scaledDensity
        activityDM.densityDpi = systemDM.densityDpi

        appDM.density = systemDM.density
        appDM.scaledDensity = systemDM.scaledDensity
        appDM.densityDpi = systemDM.densityDpi
    }
}

使用时需要注意以下几点:

  1. 尽量只在当前页面生效,包括activity,fragment,dialog,view,需要在setCOntentView()或者inflate之前调用这个方法,在结束onDestroy,onDismiss,onDetachWindow()的时候调用resetScreen()方法。
  2. 在页面中需要弹出toast和dialog的时候需要调用resetScreen,不然toast和dialog的页面大小和字体大小会被影响。
  3. 记住一点使用前调用adapterScreen,时候后调用resetScreen。
     

(2)、XML布局适配

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文件中设置多分辨率支持!!!

<Supports-screens
 android:largeScreens="true"
 android:normalScreen="true"
 android:anyDensity="true"  
 android:smalleScreen="true"/>

布局控件常用适配方法

  1. 尽量使用线性布局(LinearLayout)和相对布局(RelateLayout),尽量不使用绝对布局(AbsoluteLayout)和帧布局(FrameLayout)。
  2. 尽量使用wrap_content、mach_parent让view自适应或最大化,尽量不要写宽高的值。
  3. 使用线下布局的百分比weight权重时,要把宽度写成“0dp“,如果写成wrap_coent会使布局效果不佳等问题。
  4. 尽量使用android的Shape自定义view背景,这样会随之自适应。
  5. ImageView的ScaleType有五种方式(center,centerCrop,centerInside,fieCenter,fieXY),尽量使用fieCenter按比例扩大至view宽度,能取得较好适配和显示效果。

(3)、横竖屏适配

在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 、10.0新特性,DownloadManager踩坑记

首先,在项目清单文件中指定最小和目标API级别。具体来说,<uses-sdk>元素中的minSdkVersion和targetSdkVersion 属性,标明在设计和测试app时,最低兼容API的级别和最高适用的API级别(这个最高的级别是需要通过我们的测试的)。例如:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" />
    ...
</manifest>

其次,是在代码中判断检查版本信息。Android在Build常量类中提供了对每一个版本的唯一代号,在我们的app中使用这些代号可以建立条件,保证依赖于高级别的API的代码,只会在这些API在当前系统中可用时,才会执行。

private void setUpActionBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
       //
    }
}

 

发布了153 篇原创文章 · 获赞 755 · 访问量 100万+

猜你喜欢

转载自blog.csdn.net/csdn_aiyang/article/details/67640413