屏幕適配
屏幕適配的概念
碎片化既是 Android 的優(yōu)勢和弱點(diǎn),也是開發(fā)者們頭疼的問題,同時(shí)也為 Android 的全球影響力提供了基礎(chǔ)。Android 設(shè)備的外形和尺寸各不相同可柿,性能水平和屏幕尺寸也都大不相同。此外丙者,有許多不同版本的 Android 在任何時(shí)候都同時(shí)處于活動狀態(tài)复斥,增加了另一層的碎片化。這意味著械媒,開發(fā)各種Android設(shè)備的應(yīng)用程序?qū)⑹菢O具挑戰(zhàn)性和耗時(shí)的目锭。
今年,我們看到了之前報(bào)告中出現(xiàn)的趨勢的延續(xù);更多的設(shè)備和更多的設(shè)備之間的區(qū)別滥沫。今年我們注意到的一件大事是品牌的碎片化侣集,今年有超過1000個(gè)品牌被發(fā)現(xiàn),我們在2012年第一次發(fā)布這個(gè)報(bào)告時(shí)沒有看到兰绣。三星仍然是市場領(lǐng)頭羊的世分,在全球安卓設(shè)備市場的份額從43%降至37.8%(盡管市場仍在繼續(xù)增長)。從消費(fèi)者的角度來看缀辩,Android 生態(tài)系統(tǒng)的強(qiáng)大之處一直是能夠挑選出一款適合你的規(guī)格的設(shè)備臭埋,因?yàn)橛心敲炊嗟脑O(shè)備可以選擇。這種趨勢只會持續(xù)下去臀玄,今年的Android設(shè)備和品牌比以往任何時(shí)候都多瓢阴。
從以上兩段話能看出:
- Android碎片化不能避免
- Android碎片化只會越來越嚴(yán)重
資料來源:opensignal.com
屏幕適配的重要性
作為一名 Android 開發(fā)者,有一個(gè)逾越不過的問題健无,就是屏幕適配荣恐。談屏幕適配其實(shí)是一件非常讓人蛋疼的事情,究其原因就是Android 的開放性。由于 Google 這種搞一個(gè)東西就開放源代碼的“習(xí)慣”造成了 Android 系統(tǒng)的開放性叠穆,以至于隨隨便便一個(gè)廠商就可以隨心所欲定制自家系統(tǒng)少漆,任意修改成他們想要的樣子。這使整個(gè) Android 的碎片化非常嚴(yán)重硼被。
嚴(yán)重到什么程度呢示损?下面給出幾張圖,大家感受一下嚷硫。
1检访、設(shè)備的平臺
這是對過去幾個(gè)月下載OpenSignal應(yīng)用程序的Android設(shè)備進(jìn)行可視化的最佳方式。這張圖表顯示了Android開發(fā)者面臨的挑戰(zhàn);超過24000個(gè)不同的設(shè)備可以立即使用他們的應(yīng)用仔掸,這使得優(yōu)化成為一個(gè)真正的挑戰(zhàn)脆贵。在兩年內(nèi),Android的碎片化比我們在2013年看到的11868年翻了一倍多嘉汰。
2丹禀、品牌的碎片化
與第一個(gè)圖表類似,這表明市場在制造商方面存在分歧鞋怀,三星再次證明了自己的主導(dǎo)地位双泪。2012年,我們看到三星擁有47.5%的市場份額密似,而這一差距可以通過競爭對手的崛起來解釋焙矛,上面的圖表顯示了1294個(gè)不同的品牌。
3残腌、Android 操作系統(tǒng)碎片
設(shè)備碎片化并不是開發(fā)者在為 Android 開發(fā)時(shí)面臨的唯一挑戰(zhàn);操作系統(tǒng)本身非常分散村斟。然而,在過去的一年里抛猫,我們看到了碎片化的輕微減少蟆盹,在過去的12個(gè)月里,占主導(dǎo)地位的API版本(在這個(gè)例子中是KitKat)的市場份額有所上升闺金。
4逾滥、Android與iOS系統(tǒng)版本分布比對
與iOS相比,所有類型的安卓系統(tǒng)都有不同的表現(xiàn)败匹。這兩個(gè)餅圖清楚地顯示了兩個(gè)競爭操作系統(tǒng)之間的API碎片的差異寨昙。
5、屏幕大小碎片
Android
任何應(yīng)用程序成功的關(guān)鍵都是讓UI正確掀亩,而Android在這方面給開發(fā)者帶來了兩個(gè)特別的挑戰(zhàn)舔哪。首先,品牌傾向于在系統(tǒng)UI上產(chǎn)生自己的變體(三星的Touchwhizz和HTC的感覺是兩個(gè)這樣的例子)槽棍,這可以改變各種默認(rèn)元素的外觀捉蚤。其次抬驴,沒有其他的智能手機(jī)/平板電腦平臺擁有如此大的屏幕尺寸。今年的一個(gè)重大變化是包含了巨大的Slate 21——這絕對讓我們今年看到的所有其他Android設(shè)備相形見絀(以及所有的iOS設(shè)備)缆巧。
要想知道在過去的幾年里怎爵,平均屏幕尺寸(以及CPU、RAM和NFC流行率)發(fā)生了怎樣的變化盅蝗,請參閱本文附帶的博客文章。
iOS
設(shè)計(jì)和編碼布局在所有這些屏幕上都能很好地工作姆蘸,對于任何開發(fā)人員來說都是極具挑戰(zhàn)性的墩莫。下圖中所示的iOS生態(tài)系統(tǒng)與Android有很好的對比,因?yàn)樵谙喈?dāng)小的維度上設(shè)計(jì)要容易得多逞敷。
6狂秦、品牌擴(kuò)散
這張圖表顯示了過去幾年設(shè)備品牌數(shù)量的巨大增長,有1294個(gè)不同的品牌對我們調(diào)查的682000臺設(shè)備負(fù)責(zé)推捐。自2012年以來裂问,我們的樣品中所看到的品牌數(shù)量幾乎達(dá)到了六倍。
7牛柒、Android 全球市場分布
這張地圖顯示了我們擁有重要數(shù)據(jù)的每個(gè)國家的領(lǐng)先制造商堪簿,并基于2015年下載OpenSignal應(yīng)用的所有設(shè)備。正如預(yù)期的那樣皮壁,三星是世界上許多國家的主要制造商椭更,在墨西哥、印度和中國也有明顯的例外蛾魄。
調(diào)查數(shù)據(jù)只截止到2015年:
- 2012年虑瀑,支持Android的設(shè)備共有3997種。
- 2013年滴须,支持Android的設(shè)備共有11868種舌狗。
- 2014年,支持Android的設(shè)備共有18796種扔水。
雖然數(shù)據(jù)只顯示到2015年痛侍,但是這些數(shù)據(jù)已經(jīng)足夠表明屏幕適配的重要性了。
針對這么多的手機(jī)铭污,我們不可能做到面面俱到恋日,當(dāng)今市場上最流行哪些呢?根據(jù)我在 screensiz.es 上看到的數(shù)據(jù)(截止到2017年7月19日)嘹狞,當(dāng)前全球手機(jī)屏幕尺寸岂膳、操作系統(tǒng)、品牌以及市場占有率的調(diào)查數(shù)據(jù)如下:
根據(jù)【友盟+】2016年手機(jī)生態(tài)發(fā)展報(bào)告H1 數(shù)據(jù)來看:
國產(chǎn)手機(jī)品牌比例分布
國內(nèi)Android設(shè)備屏幕分辨率分布
國內(nèi)Android手機(jī)系統(tǒng)版本分布
現(xiàn)在應(yīng)該很清楚為什么我們要對 Android 的屏幕進(jìn)行適配了吧磅网?這么多的屏幕尺寸谈截,怎么能保證我們自己開發(fā)的程序能美觀的顯示到不同尺寸、不同分辨率的設(shè)備上呢?顯而易見簸喂,這肯定需要我們自己去處理毙死,而如何處理這些問題,這就涉及到屏幕適配喻鳄,也就是今天要講的扼倘。
從上圖可以看出,主流的分辨率是前六種:1280×720除呵、1920×1080再菊、854×480、960×540颜曾、800×480纠拔、1184×720。而真實(shí)開發(fā)中泛豪,我們要做的事就是適配當(dāng)前市場上絕大多數(shù)的 Android 屏幕就可以了稠诲。
知道了屏幕適配的重要性之后,接下來開始進(jìn)入正題诡曙。
必須要了解的幾個(gè)概念
- 屏幕尺寸臀叙、屏幕分辨率、屏幕像素密度
- 屏幕尺寸:屏幕對角線長度岗仑,單位是英寸匹耕,我們常說的多少多少寸,比如4.7存手機(jī)荠雕、5.7存手機(jī)稳其,指的就是這個(gè)。
- 屏幕分辨率:如 1920×1080炸卑,是指在手機(jī)屏幕的像素點(diǎn)的個(gè)數(shù)既鞠,單位是px,1px = 1 像素點(diǎn)盖文,一般是縱向像素 × 橫向像素嘱蛋,意味著高有 1920 個(gè)像素點(diǎn),寬有 1080 個(gè)像素點(diǎn)五续。
- 屏幕像素密度:是指每英寸上的像素點(diǎn)數(shù)洒敏,單位是 dpi(dotper inch)。像素密度和屏幕尺寸和屏幕分辨率有關(guān)疙驾,它是由對角線的像素點(diǎn)數(shù)除以屏幕的大小得到的凶伙,關(guān)系如下:
單一變化條件下,屏幕尺寸越小它碎、分辨率越高函荣,像素密度越大显押,反之越小。
- dp傻挂、dip乘碑、dpi、sp金拒、px
- dp:是Android 特有的兽肤,意為密度無關(guān)像素,Google 發(fā)布的 BASELINE(基準(zhǔn)線)為 160绪抛,以此為基準(zhǔn)轿衔。
- dip:Density Independent Pixels,同dp一個(gè)意思睦疫,目前廢棄了,一般都寫dp鞭呕。
- dpi:即為屏幕像素密度的單位
- sp:Scale-IndependentPixels的縮寫蛤育,可以根據(jù)文字大小首選項(xiàng)自動進(jìn)行縮放。Google推薦我們使用12sp以上的大小葫松,通惩吒猓可以使用12sp,14sp腋么,18sp咕娄,22sp,為避免精度損失珊擂,建議最好不要使用奇數(shù)和小數(shù)圣勒。
- px:就是我們常說的像素
- mdpi、hdpi摧扇、xhdpi圣贸、xxhdpi、xxxhdpi
了解了以上這些“紙上談兵”的概念之后扛稽,接下來進(jìn)入真實(shí)解決問題的環(huán)節(jié)吁峻。但請記住,只有了解了這些概念在张,你才能去解決這些問題用含,所以不要輕視這些概念性的東西。
一般都是采用以下幾種解決方案:
布局適配
這種適配方案基本上不怎么使用了帮匾,因?yàn)閷?shí)在是太耗費(fèi)資源啄骇,試想一下。多寫一套布局辟狈,而大部分的代碼都是相同的肠缔,純粹是為了適配而做的這些事情夏跷,無疑增加了開發(fā)者的負(fù)擔(dān)以及使程序變得更冗余和龐大,我個(gè)人認(rèn)為明未,實(shí)在是得不償失匣缘。
權(quán)重適配
這是 LinearLayout 的特有屬性:weight,意味權(quán)重家夺,我們可以讓界面布局按照我們設(shè)定的比例來顯示疤坝。比如,現(xiàn)在有個(gè)需求披摄,要求界面上有兩個(gè)控件亲雪,一個(gè)占屏幕寬的1/3,另一個(gè)占屏幕寬的2/3疚膊。
- 文本1的weight=1义辕,寬度為0dp
- 文本2的weight=2,寬度為0dp
而顯示效果就是文本1的寬度占了1/(1+2)寓盗,文本2的寬度占了1/(1+2)灌砖,如上圖所示,這樣就很好的完成了需求傀蚌。
但我希望你能明白權(quán)重的計(jì)算規(guī)則基显,權(quán)重的意思是控件的大小等于自身大小加上占剩余空間的比例。注意是剩余空間的比例善炫,什么意思呢撩幽?
先看另外一種效果再來講講原因:
與之前相比,我只改了一個(gè)屬性箩艺,就是把文本1和文本2這兩個(gè)TextView的寬度改成了match_parent窜醉。
然而結(jié)果卻變了個(gè)樣,本該顯示1/3的文本1卻占了2/3艺谆,而文本2卻變成了1/3酱虎。
再來看一下剛才那句話,什么叫剩余空間呢擂涛?
當(dāng)前的LinearLayout的orientatioin為橫向读串,兩個(gè)TextView的weight分別是1和2。
先分析第一種情況撒妈,就是當(dāng)寬度為0dp的時(shí)候恢暖。
假設(shè)說屏幕的寬度為L,現(xiàn)在該屏幕下有兩個(gè)控件狰右,兩個(gè)控件的寬度為0杰捂,那剩余空間就等于L-(0+0)=L。
文本1的weight=1棋蚌,那么它的寬度:
自身寬度 + 所占剩余空間的比例 = 真正的寬度
0 + L * 1/(1+2) = 1/3 L
同理嫁佳,文本2的寬度為:
自身寬度 + 所占剩余空間的比例 = 真正的寬度 0 + L * 2/(1+2) = 2/3 L
因此當(dāng)寬度設(shè)置為0dp的時(shí)候挨队,顯示效果就是1:2的關(guān)系。
再來討論當(dāng)寬度設(shè)置為match_parent的時(shí)候的情況蒿往,就是當(dāng)控件的寬度為 L 的時(shí)候盛垦。
現(xiàn)在屏幕的寬度為L,現(xiàn)在這兩個(gè)控件的寬度都為L瓤漏,那剩余空間就等于L-(L+L) = -L腾夯。
文本1的weight=1,那么它的寬度:
自身寬度 + 所占剩余空間的比例 = 真正的寬度
L + -L * 1/(1+2) = 2/3 L
同理蔬充,文本2的寬度為:
自身寬度 + 所占剩余空間的比例 = 真正的寬度 L + -L * 2/(1+2) = 1/3 L
因此當(dāng)寬度設(shè)置為0dp的時(shí)候蝶俱,顯示效果就是2:1的關(guān)系。
這就是權(quán)重的計(jì)算規(guī)則饥漫,很多人面試都遇到過榨呆。
這是橫向的只測試了寬的權(quán)重,其實(shí)高也是一樣庸队,這里不做講解愕提。
注意:權(quán)重只有LinearLayout才有,RelativeLayout沒有這個(gè)屬性皿哨,雖然 Google 推薦使用RelativeLayout而不是LinearLayout。
這里多提一句為什么Google推薦使用RelativeLayout而不是LinearLayout纽谒。
在 Android 中证膨,系統(tǒng)對View進(jìn)行測量、布局和繪制時(shí)鼓黔,都是通過對 View樹的遍歷來進(jìn)行操作的央勒。如果一個(gè)View 樹的高度太高,就會嚴(yán)重影響測量澳化、布局和繪制的速度崔步,因此我們?nèi)绻幌氡挥绊懙脑挘谝粋€(gè)方法就是要降低View樹的高度缎谷,Google也在其API文檔中建議View樹的高度不宜超過10層井濒。
真實(shí)項(xiàng)目中,大部分的根布局幾乎都是RelativeLayout列林,因?yàn)橐獙?shí)現(xiàn)相對位置的控制瑞你,所以比較方便一些。
而最初的時(shí)候創(chuàng)建一個(gè) xml 布局希痴,Google是用 LinearLayout作為默認(rèn)根布局的者甲,而現(xiàn)在已經(jīng)使用 RelativeLayout 作為 xml 文件默認(rèn)的根布局了,原因就是希望通過扁平的 RelativeLayout來降低通過 LinearLayout 嵌套所產(chǎn)生布局樹的高度砌创,從而提高 UI 渲染的效率虏缸。
代碼適配
有一些情況下鲫懒,我們需要去動態(tài)的設(shè)置控件的大小或者是控件的位置,比如dialog或者popupwindow的偏移量或者是顯示的位置等等刽辙,這個(gè)時(shí)候在xml布局里就顯得有點(diǎn)乏力窥岩,我們可以根據(jù)當(dāng)前屏幕的大小屬性來設(shè)置合適的數(shù)值。
比如
//獲取屏幕高寬
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int windowsHeight= metric.heightPixels;
int windowsWight= metric.widthPixels;
//動態(tài)改變布局
LinearLayout production_factory = (LinearLayout)findViewById(R.id.production_factory);
ViewGroup.LayoutParams params = production_factory.getLayoutParams();
params.height= windowsHeight / 2;
production_factory.setLayoutParams(params);
圖片適配 + 多套切圖的解決辦法
1扫倡、圖片適配
圖片適配什么意思谦秧?為什么要有圖片適配? 切多套圖撵溃,正確的圖片放入正確的文件夾下面
首先看一張圖
在默認(rèn)的res文件夾下面疚鲤,發(fā)現(xiàn)有這么多mipmap文件夾,現(xiàn)在看到里面只是放了一個(gè)ic_launcher圖片缘挑,就是應(yīng)用的啟動圖標(biāo)集歇,為什么要有這么多張?
點(diǎn)開之后發(fā)現(xiàn)每張圖片的分辨率和所占用的大小都不一致
mipmap-mdpi 48x48 2.21k
mipmap-hdpi 72x72 3.42k
mipmap-xhdpi 96x96 4.84k
mipmap-xxhdpi 144x144 7.72k
mipmap-xxxhdpi 192x192 10.49k
這是因?yàn)槊總€(gè)手機(jī)的屏幕密度都不一樣语淘,當(dāng)程序運(yùn)行到手機(jī)上時(shí)诲宇,系統(tǒng)會根據(jù)當(dāng)前手機(jī)所對應(yīng)的屏幕密度去找相應(yīng)文件夾下面的圖片。比如當(dāng)我們啟動一個(gè)屏幕密度為mdpi的手機(jī)時(shí)惶翻,加載的其實(shí)就是分辨率48x48大小為2.21k的ic_launcher圖片姑蓝,其他以此類推。我們需要按照規(guī)則將不同的圖片放到相對應(yīng)的文件夾下面吕粗,這樣做的好處就是節(jié)省內(nèi)存纺荧。下面做一個(gè)實(shí)驗(yàn)來驗(yàn)證這一結(jié)論。
圖片地址
這是一張1080x1920分辨率颅筋、1.4MB的圖片宙暇,我們把它放到mipmap-xxhdpi的文件夾下面,接下來啟動一個(gè)1920x1080分辨率的模擬器
然后xml布局里將其設(shè)置為背景议泵,然后運(yùn)行占贫。
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/img_welcome"/>
</FrameLayout>
打開Android Studio的內(nèi)存檢測,檢查耗費(fèi)的內(nèi)存先口,如圖
可以看到型奥,這個(gè)1.4MB的圖片運(yùn)行到這個(gè)手機(jī)上所消耗的內(nèi)存是8.95MB。
接下來碉京,咱們把這個(gè)圖片移動到mipmap-xhdpi文件夾下面桩引。運(yùn)行后,監(jiān)測內(nèi)存情況如下:
可以看到收夸,這個(gè)1.4MB的圖片運(yùn)行到這個(gè)手機(jī)上所消耗的內(nèi)存是18.83MB坑匠。
繼續(xù)放進(jìn)mipmap-hdpi文件夾下面時(shí),情況如下:
可以看到卧惜,這個(gè)1.4MB的圖片運(yùn)行到這個(gè)手機(jī)上所消耗的內(nèi)存是32.67MB厘灼。
繼續(xù)放進(jìn)mipmap-mdpi文件夾下面時(shí)夹纫,情況如下:
可以看到,這個(gè)1.4MB的圖片運(yùn)行到這個(gè)手機(jī)上所消耗的內(nèi)存是72.23MB设凹。
為什么都是同一張圖片舰讹,什么都沒改,只是換了個(gè)文件夾而已闪朱,它就有這么大的區(qū)別呢月匣?
這張圖片是1.4Mb,我們啟動的模擬器參數(shù)如下:
屏幕尺寸:4.95"
分辨率:1920*1080
系統(tǒng)版本:Android6.0
然后我有用了Android5.1和Android7.1的系統(tǒng)分別做了測試:
測試統(tǒng)計(jì)結(jié)果如下:
根據(jù)前面所講的屏幕像素密度計(jì)算公式可以得出該模擬器的屏幕像素密度是445奋姿,這就意味著它對應(yīng)的xxhdpi锄开,它會優(yōu)先去加載此文件夾下面的圖片。
因此當(dāng)我們把圖片放到xxhdpi文件夾下面時(shí)称诗,系統(tǒng)第一優(yōu)先找的是這個(gè)文件夾萍悴,這是有好處的,好處就是節(jié)省內(nèi)存寓免,從上圖也可以看出癣诱,的確如此。這就是為什么我們要在對應(yīng)的文件夾下面放對應(yīng)分辨率圖片的好處袜香,就是占用內(nèi)存小撕予。這就是好處,至于為什么會出現(xiàn)這么大的差別蜈首,我們這樣來分析实抡。
還記得前面講過,mdpi疾就、hdpi、xhdpi艺蝴、xxhdpi猬腰、xxxhdpi的比例是2:3:4:6:8的概念嗎?
當(dāng)前圖片1.4Mb猜敢,放到相應(yīng)文件夾下面時(shí)占用內(nèi)存不同
首先這張圖片理應(yīng)消耗的內(nèi)存是8.97Mb-1.4Mb=7.6Mb (在手機(jī)里占用的內(nèi)存-圖片自身的大泄煤伞)
得出該圖片大約為8Mb,分析其他幾種情況:
當(dāng)放到mdpi下時(shí)缩擂,按照2:3:4:6:8的比例鼠冕,mdpi跟xxhdpi的比例是2:6,我們的模擬器是xxhdpi的胯盯,在去mdpi文件夾里找圖片時(shí)會自動轉(zhuǎn)換懈费,轉(zhuǎn)換的比例就是2:6。也就是1:3的關(guān)系博脑,既然如此憎乙,寬是3倍票罐,高是3倍,自然一張圖就變成了9倍泞边。
xhdpi是4:6,也就是1.5x1.5=2.25 所以8Mb x 2.25 = 18Mb左右该押,差不多也跟上面一樣。
再分析hdpi的情況阵谚,跟xxhdpi的比例是3:6蚕礼,也就是1:2,2 x 2=4,也就是擴(kuò)大了4倍梢什。 所以8Mb x 4=32Mb奠蹬,也符合上面的情況。
雖然在Android7.+系統(tǒng)下每個(gè)階段差不多比Android5.+和6.+多出了4M左右的內(nèi)存绳矩,但是不影響結(jié)果罩润。
總結(jié):由此可見,一張圖片放到正確的文件夾里面是多么的重要翼馆,由此你是否明白Google為什么造了那么多套ic_launcher的圖片了吧割以。
那我們開發(fā)的圖片到底是放在mipmap目錄還是放在drawable目錄下呢?
官方介紹:
Mipmapping for drawables
Using a mipmap as the source for your bitmap or drawable is a simple way to provide a quality image and various image scales, which can be particularly useful if you expect your image to be scaled during an animation.
Android 4.2 (API level 17) added support for mipmaps in the Bitmap class—Android swaps the mip images in your Bitmap when you've supplied a mipmap source and have enabled setHasMipMap(). Now in Android 4.3, you can enable mipmaps for a BitmapDrawable object as well, by providing a mipmap asset and setting the android:mipMap attribute in a bitmap resource file or by calling hasMipMap().
應(yīng)用場景:
If you know that you are going to draw this bitmap at less than 50% of its original size, you may be able to obtain a higher quality by turning this property on. Note that if the renderer respects this hint it might have to allocate extra memory to hold the mipmap levels for this bitmap.
一個(gè)應(yīng)用實(shí)例:
Nexus 6
ScreenThe Nexus 6 boasts an impressive 5.96” Quad HD screen display at a resolution of 2560 x 1440 (493 ppi). This translates to ~ 730 x 410 dp (density independent pixels).
Check your assets
It has a quantized density of 560 dpi, which falls in between the xxhdpi and xxxhdpi primary density buckets. For the Nexus 6, the platform will scale down xxxhdpi assets, but if those aren’t available, then it will scale up xxhdpi assets.
Provide at least an xxxhdpi app icon because devices can display large app icons on the launcher. It’s best practice to place your app icons in mipmap- folders (not the drawable- folders) because they are used at resolutions different from the device’s current density. For example, an xxxhdpi app icon can be used on the launcher for an xxhdpi device.
res/
mipmap-mdpi/
ic_launcher.png
mipmap-hdpi/
ic_launcher.png
mipmap-xhdpi/
ic_launcher.png
mipmap-xxhdpi/
ic_launcher.png
mipmap-xxxhdpi/
ic_launcher.png # App icon used on Nexus 6 device launcher
Choosing to add xxxhdpi versions for the rest of your assets will provide a sharper visual experience on the Nexus 6, but does increase apk size, so you should make an appropriate decision for your app.
res/
drawable-mdpi/
ic_sunny.png
drawable-hdpi/
ic_sunny.png
drawable-xhdpi/
ic_sunny.png
drawable-xxhdpi/ # Fall back to these if xxxhdpi versions aren’t available
ic_sunny.png
drawable-xxxhdpi/ # Higher resolution assets for Nexus 6
ic_sunny.png
總結(jié): mipmap 只是放 ic_launcher 应媚,其他的還是應(yīng)該像以前一樣放在 drawable Stack Overflow鏈接
但是用mipmap系統(tǒng)會在縮放上提供一定的性能優(yōu)化严沥。
2、 使用.9圖
Android里有9patch圖的概念中姜,也就是.9圖消玄,能夠自動拉伸你指定的地方,這其實(shí)是一種格式特殊的png文件丢胚,能指明可以拉伸以及不可拉伸的區(qū)域翩瓜,同時(shí)還可以把顯示內(nèi)容區(qū)域的位置標(biāo)示清楚。
.9圖的有兩條線必須畫携龟,就是左邊和上邊兔跌,否則會報(bào)錯。
上圖的紅色區(qū)域就是圖片會被拉伸的區(qū)域峡蟋,其他地方不會發(fā)生任何變化坟桅,這個(gè)時(shí)候,一個(gè)簡單的.9圖就制作完成了蕊蝗。
我們控制這張圖片哪些區(qū)域可以縮放仅乓,縮放的區(qū)域就是兩條黑邊的交集,即中間位置的紅框蓬戚。
3夸楣、使用 Vector Asset
Android Support 25中BottomNavigationView與ViewPager結(jié)合實(shí)現(xiàn)material Tab標(biāo)準(zhǔn)效果
多套切圖的解決辦法
我們需要提供備用位圖(符合屏幕尺寸的圖片資源)
由于 Android 可在各種屏幕密度的設(shè)備上運(yùn)行,因此我們提供的位圖資源應(yīng)該始終可以滿足各類密度的要求:
密度類型 代表的分辨率(px) 系統(tǒng)密度(dpi)
低密度(ldpi) 240x320 120
中密度(mdpi) 320x480 160
高密度(hdpi) 480x800 240
超高密度(xhdpi) 720x1280 320
超超高密度(xxhdpi) 1080x1920 480
根據(jù)以下尺寸范圍針對各密度生成相應(yīng)的圖片。
比如說裕偿,如果我們?yōu)?xhdpi 設(shè)備生成了 200x200 px尺寸的圖片洞慎,就應(yīng)該按照相應(yīng)比例地為 hdpi、mdpi 和 ldpi 設(shè)備分別生成 150x150嘿棘、100x100 和 75x75 尺寸的圖片
即一套分辨率=一套位圖資源
接下來將生成的圖片文件放在 res/ 下的相應(yīng)子目錄中(mdpi劲腿、hdpi、xhdpi鸟妙、xxhdpi)焦人,系統(tǒng)就會根據(jù)運(yùn)行您應(yīng)用的設(shè)備的屏幕密度自動選擇合適的圖片
最后通過引用 @mipmap/id,系統(tǒng)都能根據(jù)相應(yīng)屏幕的 屏幕密度(dpi)自動選取合適的位圖重父。
注意:
如果是.9圖或者是不需要多個(gè)分辨率的圖片花椭,放在drawable文件夾即可對應(yīng)分辨率的圖片要正確的放在合適的文件夾,否則會造成圖片拉伸等問題房午。
更好地方案解決“圖片資源”適配問題
上述方案是常見的一種方案矿辽,這固然是一種解決辦法,但缺點(diǎn)很明顯:
1.每套分辨率出一套圖郭厌,為美工或者設(shè)計(jì)增加了許多工作量
2.對Android工程文件的apk包變的很大
那么袋倔,有沒有一種方法:
保證屏幕密度適配
可以最小占用設(shè)計(jì)資源
使得apk包不變大(只使用一套分辨率的圖片資源)
下面我們就來介紹這個(gè)方法:
只需選擇唯一一套分辨率規(guī)格的圖片資源
Google官方給出的高清設(shè)計(jì)圖尺寸有兩種方案,一種是以mdpi設(shè)計(jì)折柠,然后對應(yīng)放大得到更高分辨率的圖片宾娜,另外一種則是以高分辨率作為設(shè)計(jì)大小,然后按照倍數(shù)對應(yīng)縮小到小分辨率的圖片扇售。
推薦使用第二種方法前塔,因?yàn)樾》直媛试谏筛叻直媛蕡D片的時(shí)候,會出現(xiàn)像素丟失承冰。
而分辨率可以以1280*720或者是1920*1080作為主要分辨率進(jìn)行設(shè)計(jì)华弓。
首先來理解下Android 加載資源過程
Android SDK會根據(jù)屏幕密度自動選擇對應(yīng)的資源文件進(jìn)行渲染加載(自動渲染)
比如說,SDK檢測到你手機(jī)的分辨率是320x480(dpi=160)困乒,會優(yōu)先到mipmap-mdpi文件夾下找對應(yīng)的圖片資源寂屏;
但假設(shè)你只在xhdpi文件夾下有對應(yīng)的圖片資源文件(mdpi文件夾是空的),那么SDK會去xhdpi文件夾找到相應(yīng)的圖片資源文件顶燕,然后將原有大像素的圖片自動縮放成小像素的圖片凑保,于是大像素的圖片照樣可以在小像素分辨率的手機(jī)上正常顯示冈爹。
所以理論上來說只需要提供一種分辨率規(guī)格的圖片資源就可以了涌攻。
那么應(yīng)該提供哪種分辨率規(guī)格呢?
如果只提供ldpi規(guī)格的圖片频伤,對于大分辨率(xdpi恳谎、xxdpi)的手機(jī)如果把圖片放大就會不清晰
所以需要提供一套你需要支持的最大dpi分辨率規(guī)格的圖片資源,這樣即使用戶的手機(jī)分辨率很小,這樣圖片縮小依然很清晰因痛。
xhdpi應(yīng)該是首選
原因如下:
xhdpi分辨率以內(nèi)的手機(jī)需求量最旺盛
目前市面上最普遍的手機(jī)的分辨率還多集中在720x1080范圍內(nèi)(xhdpi)婚苹,所以目前來看xhdpi規(guī)格的圖片資源成為了首選。
而且很多公司為了保持App不同版本的體驗(yàn)交互一致鸵膏,可能會以iPhone手機(jī)為基礎(chǔ)進(jìn)行設(shè)計(jì)膊升,包括后期的切圖之類的。
iPhone主流的屏幕dpi約等于320, 剛好屬于xhdpi谭企,所以選擇xhdpi作為唯一一套dpi圖片資源廓译,可以讓設(shè)計(jì)師不用專門為Android端切圖,直接把iPhone的那一套切好的圖片資源放入mipmap-xhdpi文件夾里就好债查,這樣大大減少的設(shè)計(jì)師的工作量非区!
dp + dimens 適配
我們總是在xml布局里寫dp,dp到底是什么意思呢盹廷?
這是Android開發(fā)中特有的一種度量征绸,稱作屏幕無關(guān)像素,它不表示任何具體的長度或者像素點(diǎn)俄占,這個(gè)值只有在具體屏幕密度的手機(jī)上管怠,才會被轉(zhuǎn)換為具體的像素值。
它跟px不一樣颠放,咱們在xml里面寫上px排惨,那無論運(yùn)行到任何設(shè)備上,就是固定的px碰凶,不會發(fā)生什么變化暮芭,但是dp就不一樣了。
比如拿以下幾種來說明一下欲低。
分辨率(px) 系統(tǒng)密度(dpi)
240x320 120
320x480 160
480x800 240
720x1280 320
1080x1920 480
1dp轉(zhuǎn)換的px是多少呢辕宏?
其實(shí)就跟dpi有關(guān),而基準(zhǔn)線是160dpi砾莱,這就意味著1dp在320x480這款手機(jī)上就是1px瑞筐,在480x800是1.5px,720x1280上是2px腊瑟,1080x1920上是3px聚假。
這就是dp。
比如現(xiàn)在有這么個(gè)需求闰非,要求一個(gè)控件的寬度占屏幕的一半膘格,我們用dp怎么來實(shí)現(xiàn)呢?
首先進(jìn)行計(jì)算财松,比如320x480瘪贱,它的寬是320個(gè)px纱控,那我們就要寫160px,用dp來表示就是160dp菜秦。所以我們可以在xml布局里寫160dp就可以了,同樣的方式試一試其他的手機(jī):
比如240x320球昨,你寫160dp在這種手機(jī)上轉(zhuǎn)換的px就是 160dp*120/160 = 120px尔店,所以在這種手機(jī)上160dp代表的就是120px,剛好是240的一半主慰,也能適配闹获。
再來試一試480x800,想在這種手機(jī)上也占一半河哑,必須是240px避诽,而咱們寫的是160dp,它轉(zhuǎn)換的px就是 160dp*240/160=240px璃谨,剛好也能符合需求沙庐。
其實(shí)Google出dp這個(gè)東西本身就能達(dá)到適配的概念,你看佳吞,咱們寫了一個(gè)160dp拱雏,跑到這幾種手機(jī)上都能達(dá)到需求,這不就是適配了嗎底扳?
但是我之所以提出來了铸抑,就說明dp并不能保證完美適配,比如咱們試試720x1280衷模。
720的一半是360px鹊汛,而咱們寫的160dp,轉(zhuǎn)換的px是160dp*320/160=320px阱冶,明顯就不是一半刁憋,差了40px。也就是說咱們的代碼運(yùn)行到這種手機(jī)上的時(shí)候就沒有適配好木蹬,沒有達(dá)到需求至耻。
再試試1080x1920的,同樣的計(jì)算镊叁,160dp*480/160=480尘颓,而1080的一半是540,480跟540差了60px晦譬,也不行疤苹,沒有達(dá)到需求。
由此可見蛔添,當(dāng)在240x320痰催,320x480,480x800上面的時(shí)候?qū)?60dp是正常的迎瞧,可以滿足需求夸溶。而在720x1280,1080x1920上面卻不能完美適配凶硅。
問題拋出來了缝裁,該怎么解決這個(gè)問題呢?
比如720p和1080p足绅,我們通過計(jì)算捷绑,720/2=360,而根據(jù)dpi的比例320/160=2氢妈,所以我們要想再720p的手機(jī)上占寬度的一半粹污,dp值應(yīng)該是360px/2=180dp。
反過來算一下首量,如果我們寫180dp壮吩,那在720p上轉(zhuǎn)換的像素是180dpx320/160=360px,是720的一半加缘,換到1080p上鸭叙,180dpx480/160=540px,剛好是1080的一半拣宏,那也就是說沈贝,咱們寫 180dp就可以實(shí)現(xiàn)了需求,那咱們就可以在xml布局里這么寫:
<TextView
android:layout_width="180dp"
android:layout_height="wrap_content" />
可是勋乾,這么一寫又有問題出現(xiàn)了宋下,在240x320,320x480辑莫,480x800上面又壞了杨凑,這可怎么辦呢?
千呼萬喚始出來摆昧,dimens+dp
Android 的values文件夾下面可以創(chuàng)建一個(gè)dimens的文件撩满。這個(gè)dimens的作用就跟mipmap和drawable的引用方式一樣,可以通過@dimens/xxx的方式來引用绅你。我們的思路就是創(chuàng)建多套dimens伺帘,然后xml布局引用dimens,當(dāng)運(yùn)行到不同的手機(jī)上時(shí)忌锯,系統(tǒng)會自動去尋找相應(yīng)的dimens伪嫁,去加載我們提前準(zhǔn)備好的dp值,圖文教程:
首先偶垮,系統(tǒng)默認(rèn)的values下面就有個(gè)dimens文件,點(diǎn)擊values下面的dimens张咳,然后創(chuàng)建一個(gè)dimen帝洪,如下圖所示,我在此創(chuàng)建了一個(gè)名為item_width 的dimen脚猾,值為160dp葱峡,他能適配 240x320,320x480龙助,480x800 三種分辨率:
同時(shí)再打開values-1280x720文件夾下面的dimens砰奕,創(chuàng)建一個(gè)相同的名為 item_width,但是值為180dp提鸟,如圖:
同樣的步驟军援,在values-1920x1080文件夾下面的dimens里也創(chuàng)建一個(gè)同樣的180dp的dimen:
做好了這些之后,接下來我們需要在xml布局里引用称勋,還記得之前的TextView的width寫的多少吧胸哥?
之前寫的是160dp,是一個(gè)固定值赡鲜,而現(xiàn)在就不能這么寫了烘嘱,我們需要寫成“@dimen/item_width”,這個(gè)什么意思呢?其實(shí)這里只是一個(gè)引用蝗蛙,當(dāng)我們點(diǎn)擊想看看具體是多少的值的時(shí)候蝇庭,會發(fā)現(xiàn)這樣的情況,如圖:
這個(gè)意思就是捡硅,當(dāng)前我們寫的“@dimen/item_width”引用了三個(gè)地方哮内,問我們想去查看哪一個(gè)文件夾下面的dimens。
做好了這一切之后壮韭,這個(gè)TextView的值就是一個(gè)變的北发,下次再運(yùn)行到手機(jī)上的時(shí)候,720p手機(jī)上運(yùn)行時(shí)TextView的寬度就是180dp喷屋,1080p手機(jī)上也是180dp琳拨,其他的手機(jī)就是160dp(因?yàn)槲覀儧]有創(chuàng)建其他分辨率的文件夾,所以會走默認(rèn)的values)屯曹。
這就達(dá)到了指定機(jī)型的屏幕適配狱庇。
百分比適配 張鴻洋
雖然上述的dimens+dp能達(dá)到適配的目的,但是項(xiàng)目中如果要適配市場上常見的機(jī)型時(shí)恶耽,我們只能一個(gè)個(gè)的去計(jì)算密任,然后寫上我們計(jì)算好的dp值,而且ui妹子在效果圖上標(biāo)記的都是px偷俭,我們還要根據(jù)這個(gè)再計(jì)算轉(zhuǎn)換浪讳,很麻煩,有沒有那種一勞永逸的涌萤?
參考了hongyang的博客之后淹遵,有這么一套適配方案口猜,如下:
思路:把任何設(shè)備的手機(jī)寬度像素均分為320份,高度像素均分為480份透揣,使用我們寫好的程序自動生成資源values-***×***文件夾济炎,里面包含lay_x.xml和lay_y.xml,分別對應(yīng)寬度和高度的像素淌实。
ay_x.xml(寬):
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">1.0px</dimen>
<dimen name="x2">2.0px</dimen>
<dimen name="x3">3.0px</dimen>
<dimen name="x4">4.0px</dimen>
...
<dimen name="x318">318.0px</dimen>
<dimen name="x319">319.0px</dimen>
<dimen name="x320">320px</dimen>
</resources>
然后lay_y.xml(高):
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="y1">1.0px</dimen>
<dimen name="y2">2.0px</dimen>
<dimen name="y3">3.0px</dimen>
<dimen name="y4">4.0px</dimen>
...
<dimen name="y480">480px</dimen>
</resources>
我們直接在dimens里面寫上具體的px值,而不是dp值猖腕,這樣給定一個(gè)寫死的值拆祈,在手機(jī)上運(yùn)行的時(shí)候就是我們所寫上的px值√雀校可能到這里各位又有疑問了放坏,這樣的話怎么做到適配的呢?別著急老玛,我們假設(shè)手機(jī)屏幕的寬度都是320某單位淤年,那么我們將一個(gè)屏幕寬度的總像素?cái)?shù)平均分成320份,每一份對應(yīng)具體的像素就可以了蜡豹。
我們可以創(chuàng)建多個(gè)values文件夾麸粮,多到覆蓋市面上絕大多數(shù)流行的手機(jī)機(jī)型,并且用相應(yīng)的比例計(jì)算好px值镜廉,由于現(xiàn)在是以320x480為基準(zhǔn)的弄诲,所以如果我們要編寫其他的values,要進(jìn)行如下計(jì)算娇唯,比如我們以1920x1080為例:
由于基準(zhǔn)是320x480齐遵,所以1080/320=3.375px,1920/480=4px塔插,所以相應(yīng)文件應(yīng)該是這樣的
lay_x.xml
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">3.375px</dimen>
<dimen name="x2">6.65px</dimen>
<dimen name="x3">10.125px</dimen>
...
<dimen name="x320">1080px</dimen>
</resources>
lay_y.xml
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="y1">4px</dimen>
<dimen name="y2">8px</dimen>
<dimen name="y3">12px</dimen>
<dimen name="y4">16px</dimen>
...
<dimen name="y480">1920px</dimen>
</resources>
我們可以按照這種計(jì)算的方式補(bǔ)全當(dāng)今市面上流行的機(jī)型梗摇。
但是我們怎么可能會去做這么蠢的事呢?肯定有工具類之類的東西想许!在此伶授,再次感謝hongyang提供的工具類。
代碼應(yīng)該很好懂流纹,我們將一個(gè)屏幕寬度分為320份谎砾,高度480份,然后按照實(shí)際像素對每一個(gè)單位進(jìn)行復(fù)制捧颅,放在對應(yīng)values-widthxheight文件夾下面的lax.xml和lay.xml里面嗦玖,這樣就可以統(tǒng)一所有你想要的分辨率的單位了,無論在什么分辨率下次氨,x320都是代表屏幕寬度,y480都是代表屏幕高度亮蒋。
當(dāng)執(zhí)行了上述代碼之后,會在本地生成很多個(gè)values文件夾妆毕,我們把這些values拷貝到項(xiàng)目的res下面慎玖,如下所示:
注意:
分辨率為480x320的資源文件應(yīng)放在res/values-480x320文件夾中;同理分辨率為1920x1080的資源文件應(yīng)放在res/values-1920x1080文件夾中笛粘。(其中values-480x320是分辨率限定符)
必須在默認(rèn)values里面也創(chuàng)建對應(yīng)默認(rèn) lay_ x.xml 和 lay_y.xml 文件趁怔,如下
lay_x.xml
<?xml version="1.0" encoding="utf-8">
<resources>
<dimen name="x1">1.0dp</dimen>
<dimen name="x2">2.0dp</dimen>
...
</resources>
因?yàn)閷τ跊]有生成對應(yīng)分辨率文件的手機(jī),會使用默認(rèn)values文件夾薪前,如果默認(rèn)values文件夾沒有(即沒有對應(yīng)的分辨率润努、沒有對應(yīng)dimen)就會報(bào)錯,從而無法進(jìn)行屏幕適配示括。
(注意對應(yīng)單位改為dp铺浇,而不同于上面的px。因?yàn)椴恢罊C(jī)型的分辨率垛膝,所以默認(rèn)分辨率文件只好默認(rèn)為x1=1dp以保證盡量兼容(又回到dp老方法了)鳍侣,這也是這個(gè)解決方案的一個(gè)弊端)
而我們怎么用呢?
工作中只需要根據(jù)UI妹子給出的某一個(gè)分辨率設(shè)計(jì)圖的尺寸吼拥,然后找到對應(yīng)像素?cái)?shù)的單位倚聚,然后設(shè)置給控件就可以了,如下:
<TextView
android:text="@string/hello_world"
android:layout_width="@dimen/x160"
android:layout_height="@dimen/y160"/>
最后:本文非原創(chuàng)凿可,大部分資料來源于大神的博客秉沼,這里只是作為一個(gè)歸納,希望更多的同學(xué)能夠?qū)W習(xí)到屏幕適配的知識矿酵,也算是自己學(xué)習(xí)的一個(gè)筆記記錄唬复。感謝下面的大神: