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) {
       //
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章