AutoLayout源码解析(1)
鴻神的AutoLayout堪稱適配終結(jié)者,雖然項(xiàng)目已經(jīng)不再維護(hù),對(duì)于一些好的框架和實(shí)現(xiàn)原理我們還是有學(xué)習(xí)意義的!現(xiàn)在讓我們一起來(lái)學(xué)習(xí)AutoLayout的源碼吧!
首先我們應(yīng)該思考這樣一個(gè)問(wèn)題:對(duì)于一個(gè)框架如何去學(xué)習(xí)呢?
難道是下載源碼后,一個(gè)類一個(gè)類的去看嗎? 這肯定不行,這樣會(huì)浪費(fèi)大量的時(shí)間,而且還不一定能很好的理解他
正確的流程應(yīng)該是從如何去使用入手,然后根據(jù)調(diào)用流程一步一步的去理解,這樣就能很好的理解框架的整個(gè)流程了!
現(xiàn)在讓我們來(lái)看一下AutoLayout的使用流程:
1、將Autolayout添加到我們的項(xiàng)目依賴中
dependencies {// 其他依賴// ...implementation 'com.zhy:autolayout:1.4.5' }2、在AndroidManifest.xml標(biāo)明設(shè)計(jì)稿尺寸
<!--ui設(shè)計(jì)稿提供的手機(jī)尺寸--> <meta-data android:name="design_width" android:value="768"/> <meta-data android:name="design_height" android:value="1280"/>3、在Application中進(jìn)行初始化
AutoLayoutConifg.getInstance().useDeviceSize().init(this);4、讓你的Activity繼承AutoLayoutActivity或者直接在xml使用AutoLinearLayout、AutoRelativeLayout、AutoFrameLayout
首先出現(xiàn)在我們眼前的有AutoLayoutConifg、AutoLayoutActivity、AutoLinearLayout、AutoRelativeLayout、AutoFrameLayout這些類
那我們就按順序分別去看這些類做了那些事情
1、AutoLayoutConifg
首先這是一個(gè)單例類
private static AutoLayoutConifg sIntance = new AutoLayoutConifg();private AutoLayoutConifg() { }public static AutoLayoutConifg getInstance() {return sIntance; }它有這些屬性
// 屏幕的寬度 private int mScreenWidth; // 屏幕的高度 private int mScreenHeight; // 設(shè)計(jì)圖的寬度 private int mDesignWidth; // 設(shè)計(jì)圖的高度 private int mDesignHeight; // 暫時(shí)不知道這個(gè)參數(shù)是啥,字面意思是使用設(shè)備大小 private boolean useDeviceSize;接下來(lái)我們來(lái)看init里面做了啥
public void init(Context context) {// 通過(guò)方法名我們猜想應(yīng)該是從AndroidManifest讀取MetaData里面的設(shè)計(jì)尺寸getMetaData(context);// 這里是獲取實(shí)際屏幕的大小,傳入了useDeviceSize屬性???int[] screenSize = ScreenUtils.getScreenSize(context, useDeviceSize);// 給mScreenWidth賦值mScreenWidth = screenSize[0];// 給mScreenHeight賦值mScreenHeight = screenSize[1];L.e(" screenWidth =" + mScreenWidth + " ,screenHeight = " + mScreenHeight); }接下來(lái)我們來(lái)看getMetaData方法里的代碼
private void getMetaData(Context context) {PackageManager packageManager = context.getPackageManager();ApplicationInfo applicationInfo;try{applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);if (applicationInfo != null && applicationInfo.metaData != null){// 給mDesignWidth賦值mDesignWidth = (int) applicationInfo.metaData.get(KEY_DESIGN_WIDTH);// 給mDesignHeight賦值mDesignHeight = (int) applicationInfo.metaData.get(KEY_DESIGN_HEIGHT);}} catch (PackageManager.NameNotFoundException e){throw new RuntimeException("you must set " + KEY_DESIGN_WIDTH + " and " + KEY_DESIGN_HEIGHT + " in your manifest file.", e);}L.e(" designWidth =" + mDesignWidth + " , designHeight = " + mDesignHeight); }這里印證了我們的猜想:這個(gè)方法就是從AndroidManifest讀取MetaData里面的設(shè)計(jì)尺寸,然后賦值給屬性[mDesignWidth、mDesignHeight]
接下來(lái)我們看 ScreenUtils.getScreenSize(context, useDeviceSize)方法是如何獲取真實(shí)的屏幕大小,順便確定useDeviceSize屬性的意思
??? ????? int[] size = new int[2];// 這部分的代碼是獲取屏幕寬高的代碼,有多種寫法,寫法也比較固定WindowManager w = (WindowManager) ????????context.getSystemService(Context.WINDOW_SERVICE);Display d = w.getDefaultDisplay();DisplayMetrics metrics = new DisplayMetrics();d.getMetrics(metrics);int widthPixels = metrics.widthPixels;int heightPixels = metrics.heightPixels;// since SDK_INT = 1;if (!useDeviceSize){size[0] = widthPixels;size[1] = heightPixels - getStatusBarHeight(context);return size;}我們看紅色注釋部分,可以看到從SDK1開始,只有useDeviceSize為false才會(huì)走進(jìn)來(lái)
?????? ??// includes window decorations (statusbar bar/menu bar)if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17)try{widthPixels = (Integer) Display.class.getMethod("getRawWidth").invoke(d);heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(d);} catch (Exception ignored){}// includes window decorations (statusbar bar/menu bar)if (Build.VERSION.SDK_INT >= 17)try{Point realSize = new Point();Display.class.getMethod("getRealSize", Point.class).invoke(d,realSize);widthPixels = realSize.x;heightPixels = realSize.y;} catch (Exception ignored){}size[0] = widthPixels;size[1] = heightPixels;return size;可以看到接下來(lái)部分的代碼都不同版本的適配代碼,所以我們可以這樣理解useDeviceSize:根據(jù)版本來(lái)適配獲取屏幕真實(shí)寬高的,所以當(dāng)我們項(xiàng)目的最低版本大于等于14時(shí),應(yīng)該在Application里面去將useDeviceSize配置為true。因?yàn)楝F(xiàn)在構(gòu)建工程的最低版本基本都是19或21了,所以應(yīng)該將useDeviceSize設(shè)置為true。
那么如果我們不設(shè)置會(huì)出現(xiàn)什么問(wèn)題呢?
通過(guò)DisplayMetrics獲取到的heightPixels的值在高版本中是已經(jīng)減去了狀態(tài)欄高度的,而代碼里面又減了一次狀態(tài)欄高度,會(huì)導(dǎo)致減去了2次狀態(tài)欄的高度,從而導(dǎo)致適配的縮放比例存在誤差
其實(shí)看完這個(gè)類如果我們善于思考的話,已經(jīng)能夠猜到它的大致原理了
比如設(shè)計(jì)圖上有一個(gè)384px*640px的View,設(shè)計(jì)圖是768x1280,我們的手機(jī)分辨率是1440x3168,所以:
mScreenWidth = 1440; mScreenHeight = 3168; mDesignWidth = 768; mDesignHeight = 1280;可以看到設(shè)計(jì)圖上這個(gè)View占用了屏幕的4分之1,我們要在1440x3168的屏幕上也顯示4分之1,怎么辦呢?
我們用1440*384/760 = 720,3168*640/1280 = 1584,所以我們想要在1440x3168的屏幕上顯示相同的效果這個(gè)控件的大小應(yīng)該是720px*1584px,目前大致能想到是就這么多了,接下來(lái)我們繼續(xù)看源碼是如何實(shí)現(xiàn)的!
總結(jié)
以上是生活随笔為你收集整理的AutoLayout源码解析(1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Visual Studio的调试技巧
- 下一篇: 《过早退出是一切失败的根源》读后感