想要Android應(yīng)用適應(yīng)不同尺寸的屏幕,思路如下:
創(chuàng)新互聯(lián)長期為上千多家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為南崗企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、網(wǎng)站制作,南崗網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
不同的layout
Android手機屏幕大小不一,有480x320, 640x360, 800x480,讓app適應(yīng)不同屏幕,需要在res目錄下創(chuàng)建不同的layout文件夾,比如layout-640x360,layout-800x480,所有的layout文件在編譯之后都會寫入R.java里,而系統(tǒng)會根據(jù)屏幕的大小自己選擇合適的layout進行使用。
hdpi、mdpi、ldpi
在之前的版本中,只有一個drawable,而2.1版本中有drawable-mdpi、drawable-ldpi、drawable-hdpi三個,這三個主要是為了支持多分辨率。
drawable- hdpi、drawable- mdpi、drawable-ldpi的區(qū)別:
(1)drawable-hdpi里面存放高分辨率的圖片,如WVGA (480x800),FWVGA (480x854)
(2)drawable-mdpi里面存放中等分辨率的圖片,如HVGA (320x480)
(3)drawable-ldpi里面存放低分辨率的圖片,如QVGA (240x320)
系統(tǒng)會根據(jù)機器的分辨率來分別到這幾個文件夾里面去找對應(yīng)的圖片。
更正:應(yīng)該是對應(yīng)不同density 的圖片
在開發(fā)程序時為了兼容不同平臺不同屏幕,建議各自文件夾根據(jù)需求均存放不同版本圖片。
屏幕方向:
橫屏豎屏自動切換:
可以在res目錄下建立layout-port-800x600和layout-land兩個目錄,里面分別放置豎屏和橫屏兩種布局文件,這樣在手機屏幕方向變化的時候系統(tǒng)會自動調(diào)用相應(yīng)的布局文件,避免一種布局文件無法滿足兩種屏幕顯示的問題。
不同分辨率橫屏豎屏自動切換:
以800x600為例 :
可以在res目錄下建立layout-port-800x600和layout-land-800x600兩個目錄
不切換:
以下步驟是網(wǎng)上流傳的,不過我自己之前是通過圖形化界面實現(xiàn)這個配置,算是殊途同歸,有空我會把圖片貼上來。
還要說明一點:每個activity都有這個屬性screenOrientation,每個activity都需要設(shè)置,可以設(shè)置為豎屏(portrait),也可以設(shè)置為無重力感應(yīng)(nosensor)。
要讓程序界面保持一個方向,不隨手機方向轉(zhuǎn)動而變化的處理辦法:
在AndroidManifest.xml里面配置一下就可以了。
加入這一行android:screenOrientation="landscape"。
例如(landscape是橫向,portrait是縱向):
以下是JAVA代碼:
?xml?version="1.0"?encoding="utf-8"??
manifest?xmlns:android=""?
package="com.ray.linkit"?
android:versionCode="1"?
android:versionName="1.0"?
application?android:icon="@drawable/icon"?android:label="@string/app_name"?
activity?android:name=".Main"?
android:label="@string/app_name"?
android:screenOrientation="portrait"?
intent-filter?
action?android:name="android.intent.action.MAIN"?/?
category?android:name="android.intent.category.LAUNCHER"?/?
/intent-filter?
/activity?
activity?android:name=".GamePlay"?
android:screenOrientation="portrait"/activity?
activity?android:name=".OptionView"?
android:screenOrientation="portrait"/activity?
/application?
uses-sdk?android:minSdkVersion="3"?/?
/manifest
另外,android中每次屏幕的切換動會重啟Activity,所以應(yīng)該在Activity銷毀前保存當(dāng)前活動的狀態(tài),在Activity再次Create的時候載入配置,那樣,進行中的游戲就不會自動重啟了!?
有的程序適合從豎屏切換到橫屏,或者反過來,這個時候怎么辦呢?可以在配置Activity的地方進行如下的配置android:screenOrientation="portrait"。這樣就可以保證是豎屏總是豎屏了,或者landscape橫向。
而有的程序是適合橫豎屏切換的。如何處理呢?首先要在配置Activity的時候進行如下的配置:android:configChanges="keyboardHidden|orientation",另外需要重寫Activity的 onConfigurationChanged方法。實現(xiàn)方式如下,不需要做太多的內(nèi)容:
@Override?
public?void?onConfigurationChanged(Configuration?newConfig)?{?
super.onConfigurationChanged(newConfig);?
if?(this.getResources().getConfiguration().orientation?==?Configuration.ORIENTATION_LANDSCAPE)?{?
//?land?do?nothing?is?ok?
}?else?if?(this.getResources().getConfiguration().orientation?==?Configuration.ORIENTATION_PORTRAIT)?{?
//?port?do?nothing?is?ok?
}?
}
寫一個支持多分辨的程序,基于1.6開發(fā)的,建立了三個資源文件夾drawable-hdpi drawable-mdpi drawable-ldpi,里面分別存放72*72 48*48 36*36的icon圖標(biāo)文件。當(dāng)我在G1(1.5的系統(tǒng))上測試時,圖標(biāo)應(yīng)該自適應(yīng)為48*48才對啊,但實際顯示的是36*36。怎么才能讓其自適應(yīng) 48*48的icon圖標(biāo)呢 ?
解決辦法 drawable-hdpi drawable-mdpi drawable-ldpi改成drawable-480X320 drawable-800X480的多分辨支持的文件夾。
Android屏幕分辨率千奇百怪,怎么讓app在不同的分辨率的設(shè)備上“看起來一樣”呢?
你也許還有以下疑惑:
這篇文章將會針對以上問題一一解答。
Pixels 我們看到屏幕上的圖像由一個個像素組成,像素里包含色彩信息。
如常說的手機分辨率:1080 x 1920 指的是手機寬度可展示1080像素,高度可展示1920像素。
Pixels Per Inch 每英寸長度所具有的像素個數(shù),單位面積內(nèi)像素越多,圖像顯示越清晰。
ppi一般用在顯示器、手機、平板等描述屏幕精細(xì)度。
Dots Per Inch 每英寸長度所具有的點數(shù)。
dpi一般用來描述打?。〞?、雜志、電報)的精細(xì)度
density-independent pixels (device-independent pixels 我查了一下,官網(wǎng)更多時候使用前者,有的時候也顯示后者),dip是縮寫,也可以更簡單些稱作dp。該單位的目的是屏蔽不同設(shè)備密度差異,后面細(xì)說。
Scalable pixels 用于設(shè)置字體,在用戶更改字體大小時候會適配。
澄清了基本概念,我們現(xiàn)在從一個例子開始說明以上單位之間的區(qū)別與聯(lián)系。
布局文件里有個View,長寬都是200px,分別在分辨率為480(寬)x800(高)簡稱A設(shè)備、1080(寬)x1920(高)簡稱B設(shè)備,效果如下:
左邊是A設(shè)備,右邊是B設(shè)備。問題出來了,同樣長寬都是200px,為啥A設(shè)備顯示很大,B設(shè)備顯示很小呢?你可能會說B設(shè)備的橫向分辨率1080比A設(shè)備的480大,所以在B設(shè)備上看起來比較小。來看看A、B設(shè)備橫向到底是多少英寸,怎么來計算呢?這時候就需要用到ppi了,既然知道橫向的像素點個數(shù),也知道每英寸能容納的像素點,當(dāng)然可以得知橫向的尺寸了。
其中一種方式獲取DisplayMetrics對象:
A設(shè)備寬度尺寸:480(px)/240(ppi)=2inch
B設(shè)備寬度尺寸:1080(px)/420(ppi)=2.5inch
可以看出,A、B設(shè)備尺寸差別不大。A設(shè)備ppi=240 B設(shè)備ppi=420,明顯地看出B設(shè)備單位長度上比A設(shè)備能夠容納更多的像素,因此同樣的200px,B設(shè)備只需要較小的尺寸就能夠顯示,因此在B設(shè)備上的view看起來比A設(shè)備小很多。
知道了問題的原因,然而顯示的效果卻不能接受。
我們總不能自己判斷每個設(shè)備的ppi,然后計算實際需要多少像素,再動態(tài)設(shè)置view的大小吧,那layout里的靜態(tài)布局大小就無法動態(tài)更改適應(yīng)了。想當(dāng)然的能有一個統(tǒng)一的地方替我們轉(zhuǎn)換,沒錯!Android系統(tǒng)已經(jīng)幫我們實現(xiàn)了轉(zhuǎn)換。接下來就是dpi、dp出場了。
Android系統(tǒng)使用dpi來描述屏幕的密度,使用dp來描述密度與像素的關(guān)系。
A設(shè)備dpi=240
B設(shè)備dpi=420
Android系統(tǒng)最終識別的單位是px,怎么將dpi和px關(guān)聯(lián)起來呢?,答案是dp。
Android規(guī)定當(dāng)dpi=160時,1dp=1px,當(dāng)dpi=240時,1dp=1.5px,依此類推,并且給各個范圍的dpi取了簡易的名字加以直觀的識別,如120dpi=160,稱作為mdpi,120dpi=240 稱作hdpi,最終形成如下規(guī)則:
現(xiàn)在知道了dp能夠在不同dpi設(shè)備上對應(yīng)不同px,相當(dāng)于中間轉(zhuǎn)換層,我們只需要將view長寬單位設(shè)置為合適的dp,就無需關(guān)注設(shè)備之間密度差異,系統(tǒng)會幫我們完成dp-px轉(zhuǎn)換。將我們之前的例子稍微更改,再看看效果驗證一下:
通過上面對dp的了解,我們知道在設(shè)定view大小、間距時使用dp能最大限度地屏蔽設(shè)備密度之間的差異??赡苣憔蜁柫?,那bitmap展示的時候如何適配不同密度的設(shè)備呢?
自定義view從磁盤上加載一張圖片,并將之顯示在view上,view的大小決定于bitmap大小。依舊以上述A、B設(shè)備為例,展示結(jié)果如下:
左邊是A設(shè)備,右邊是B設(shè)備。
明顯地看出,在A設(shè)備顯示比B設(shè)備大很多,實際上和我們之前用px來描述view的大小原理是一樣的,bitmap的寬、高都是px在描述,而bitmap決定了view的寬、高,最終導(dǎo)致A設(shè)備和B設(shè)備上的view大?。▽?、高像素)是一樣的,而它們屏幕密度又不相同,因此產(chǎn)生了差異。
那不會每次都需要我們自己根據(jù)屏幕密度來轉(zhuǎn)換bitmap大小吧?幸運的是,Android已經(jīng)為我們考慮到了。
生成不同密度的目錄有什么作用?
A設(shè)備dpi=240,根據(jù)dpi范圍,屬于hdpi
B設(shè)備dpi=420,根據(jù)dpi范圍,屬于xxhdpi
圖片原始尺寸:photo1.jpg(寬高 172px-172px)
當(dāng)我們想要在不同密度設(shè)備上顯示同一張圖片并且想要“看起來一樣大時”。假設(shè)設(shè)計的時候以hdpi為準(zhǔn),放置photo1.jpg為172*172,那么根據(jù)計算規(guī)則在xxhdpi上需要設(shè)置photo1.jpg為:
現(xiàn)在hdpi和xxhdpi目錄下分別存放了同名圖片:photo1.jpg,只是大小不同。當(dāng)程序運行的時候:
來看看效果:
左邊A設(shè)備,右邊B設(shè)備
針對不同的密度設(shè)計不同的圖片大小,最大限度保證了同一圖片在不同密度設(shè)備上表現(xiàn)“看起來差不多大”。
來看看A、B設(shè)備上圖片占內(nèi)存大小:
說明在B設(shè)備上顯示photo1.jpg需要更多的內(nèi)存。
上邊只是列舉了hdpi、xxhdipi,同理對于mdpi、xhdpi、xxxhdpi根據(jù)規(guī)則放入相應(yīng)大小的圖片,程序會根據(jù)不同的設(shè)備密度從對應(yīng)的mipmap文件夾下加載資源。如此一來,我們無需關(guān)注bitmap在不同密度設(shè)備上顯示問題了。
在mipmap各個文件夾下都放置同一套資源的不同尺寸文件似乎有點太占apk大小,能否只放某個密度下圖片,其余的靠系統(tǒng)自己適配呢?
現(xiàn)在只保留hdpi下的photo1.jpg圖片,看看在A、B設(shè)備上運行情況如何:
看起來和上張圖差不多,說明系統(tǒng)會幫我們適配B設(shè)備上的圖片。
再來看看A、B設(shè)備上圖片占內(nèi)存大小:
先看A設(shè)備:
對比photo1.jpg 分別放在hdpi、xxhdpi和只放在hdpi下可以看出:B設(shè)備上圖片所占內(nèi)存變小了。為什么呢?接下來從源碼里尋找答案。
A、B設(shè)備同樣加載hdpi/photo1.jpg,返回的bitmap大小不相同,我們從這方法開始一探究竟。
上面涉及到的關(guān)鍵點是density,分別是TypedValue的density和Options的density。
先來看看TypedValue density:
再來看看Options density
現(xiàn)在分析B設(shè)備加載hdpi/photo1.jpg如何做的:
和我們之前調(diào)試的結(jié)果一致。
B設(shè)備是怎么決定使用hdpi下的圖片資源呢?
根據(jù)實驗(嘗試找了源碼,沒怎么看懂,因此只是做了實驗,可能在不同密度設(shè)備上找尋規(guī)則不一樣):B設(shè)備先找屬于自己密度范圍文件夾下的圖片,B設(shè)備屬于xxhdpi,先查看xxhdpi有沒有photo1.jpg,如果沒有則往更高的密度找,比它高的密度是xxxhdpi,還是沒有,則往低密度找,找xhdpi,沒有再找hdpi,找到了則返回構(gòu)造好的TypedValue,剩下的就是我們前面分析的。
既然我們只想放某個密度下的一份切圖,該放哪個密度下呢?從系統(tǒng)尋找規(guī)則看,更推薦放置在更高密度下的,因為如果放在低密度下,那么當(dāng)運行在高密度設(shè)備上時,圖片會進行放大,可能導(dǎo)致不清晰。我一般習(xí)慣放在xxhdpi下。
Android Studio默認(rèn)創(chuàng)建了不同密度的mipmap文件夾,默認(rèn)放置了ic_launcher.png。我們普通的切圖該放drawable還是mipmap下呢?對于這個問題網(wǎng)上也是眾說紛紜,實際上對于我們來說,關(guān)注的重點是圖片放在drawable或者mipmap,加載出來bitmap是否有差異,如果沒有差異放在哪就看習(xí)慣了。通過實踐,普通的切圖放drawable和mipmap下加載出來的bitmap是沒有差異的,只不過用drawable的話需要自己創(chuàng)建不同密度的文件夾。我習(xí)慣于放在drawable下(啟動圖標(biāo)logo還是放在mipmap下)。
前邊 [注1] 留了個問題,我們使用dp來表示view的大小了,為啥兩個看起來還是有些差距?下面我們更加直觀地看一個例子。
A設(shè)備dpi=240 密度1.5 分辨率(寬高px):480 * 800
B設(shè)備dpi=420 密度2.625 分辨率(寬高px):1080 * 1794
換算成dp
A設(shè)備分辨率:320dp * 533dp
B設(shè)備分辨率:411dp * 683dp
依舊是上邊的例子:
將view寬高分別設(shè)置為320dp,看看效果:
左邊A設(shè)備,右邊B設(shè)備
可以看出同樣的320dp大小,A設(shè)備鋪滿了屏幕,而B設(shè)備沒有。這效果顯然是不能接受的,Android考慮到不同設(shè)備寬高不同,推出了"寬高限定符"。以A、B設(shè)備為例:
在res文件夾下創(chuàng)建文件夾:
假設(shè)設(shè)計師出圖是按照800x480,那么我們創(chuàng)建dimen文件的時候
該文件放在values-800x480文件夾下。
根據(jù)分辨率比例算出1794x1080的dimen值
這樣子,A、B設(shè)備加載資源的時候使用對應(yīng)分辨率限定符下的px,如果找不到再找默認(rèn)值,可以在一定程度上解決屏幕寬高碎片化適配問題。
但是這樣子的限定比較嚴(yán)格,需要測試各種分辨率,后來Android又推出了"smallest-width"簡稱最小寬度限制。
A設(shè)備寬320dp
B設(shè)備寬411dp
假設(shè)設(shè)計師切圖標(biāo)準(zhǔn)屏幕寬是320dp(A設(shè)備),那么可以定義如下dimen.xml文件
該文件放在values-sw320dp文件夾下
根據(jù)規(guī)則,計算B設(shè)備dimen.xml
現(xiàn)在我們繼續(xù)來看之前的view
通過對dimen引用,A設(shè)備尋找和自己寬度一樣的dimen文件,找到values-sw320dp,dp320=320dp。B設(shè)備尋找和自己寬度一樣的dimen文件,找到values-sw411dp,dp320=410dp。這樣子同樣的dp320,得出不同的值,就適配了屏幕寬度不同的問題。
看看效果:
這次B設(shè)備也鋪滿了屏寬。
綜上,為了適配不同屏幕大小,推薦使用dp+smallest-width。
獲取設(shè)備dpi最終都是從這方法獲取的,實際上就是讀取系統(tǒng)的配置文件。因此我們也可以通過adb shell 獲取:
可以看出dpi是系統(tǒng)配置好的,當(dāng)然有些手機是可以設(shè)置分辨率的,設(shè)置之后我們查看分辨率:
分辨率變低了,dpi也變小了。
1: dp: android 尺寸的基本單位。 在不同的分辨率的手機里面,1dp對應(yīng)著不同數(shù)量的px, 這樣就實現(xiàn)了dp定義一個控件大小的時候,在不同分辨率手機里表現(xiàn)出相應(yīng)大小的像素值。
2: 屏幕分辨率: 1080下160, 表示寬度有1080個像素點而高度有2160個像素點。常見的分辨率有320x480, 480x800, 720x1280, 1080x1920等。
3: 屏幕尺寸: 以寸為單位, Android設(shè)備對角線的長度
4: 像素密度: 每英寸的像素點
5: 屏幕尺寸, 分辨率,像素密度 三者之間的關(guān)系:
密度(dpi)= √(寬2 + 高2)/屏幕尺寸
6: px:像素,是屏幕上顯示數(shù)據(jù)的最基本的點
7: dpi:屏幕像素密度,每英寸上的像素點數(shù)
8: sp:與dp類似,通常用于指定字體的大小,當(dāng)用戶修改手機顯示的字體時,字體大小會隨之改變。
1: dp適配方案: Android自帶的原始的適配方案, 在不同的分辨率手機里面表現(xiàn)出相應(yīng)大小的像素點。
缺點: Android的碎片化嚴(yán)重, 如果生產(chǎn)廠家沒有根據(jù)屏幕尺寸、分辨率和像素密度的關(guān)系來規(guī)則定義, 或者出一些亂七八糟的屏幕大小,這樣的適配方案就不在適合了。
2: 寬高限定符:枚舉所有的屏幕寬高像素值,根據(jù)等比縮放去適配。如果沒有找到對應(yīng)的屏幕, 則取默認(rèn)的。 目前這種方案已經(jīng)被棄用。
缺點:
1: 占用資源大,會增加APK的體積。
2: 容錯機制大需要精準(zhǔn)命中資源文件才能適配,比如1920x1080的手機就一定要找到1920x1080的限定符,否則就只能用統(tǒng)一的默認(rèn)的dimens文件了。而使用默認(rèn)的尺寸的話,UI就很可能變形。
3:AndroidAutoLayout適配方案(停止維護)
4: SW限定符適配方案:(smallestWidth最小寬度適配)
Android 會去識別屏幕可用高度或者寬度的最小尺寸的dp值。然后根據(jù)識別到的結(jié)果去對應(yīng)的資源文件里面去找尋相應(yīng)的結(jié)果。
如何生成:ScreenMatch插件
此方案跟寬高限定的適配方案相比,有很好的容錯機制, 如果沒有找到對應(yīng)的適配寬度, 那么會在vlues文件里面去找跟他最接近的寬度。
5:今日頭條適配方案:
1: px 轉(zhuǎn) dp 的公式 dp = px / density.不管我們設(shè)定的單位是什么, 最終我們都會將這些單位長度轉(zhuǎn)化為px的。density就是他們的轉(zhuǎn)化比, 所以,動態(tài)改變這個轉(zhuǎn)化比也是可以達到我們適配屏幕的目的的。
2: 通過修改density值,強行把所有不同尺寸分辨率的手機的寬度dp值改成一個統(tǒng)一的值(在清單文件中定義),這樣就解決了所有的適配問題。
3: Density = 當(dāng)前設(shè)備屏幕總寬度(單位為像素)/ 設(shè)計圖總寬度(單位為 dp) ;
4:引入了AndroidAutoSize屏幕適配框架:
最后, 最重要的................
點贊 點贊 點贊, 不重要的事情也就說3遍......