【原始碼解析】Launcher 8.0原始碼(6)---Launcher的原始碼啟動過程第一步之InvariantDeviceProfile獲取硬...
獲取硬體引數是在InvariantDeviceProfile中進行的,確定佈局引數是在DeviceProfile中進行的,本篇文章就對這個至關重要的一步進行詳細分析
InvariantDeviceProfile構造方法
我們來看InvariantDeviceProfile的構造方法,構造方法一開始是獲取是獲取獲取硬體引數
首先需要獲取系統元件windowmanager、Display 、DisplayMetrics 。而後獲取長和寬的px值通過螢幕密度切換成螢幕的實際物理尺寸。最終得到minWidthDps 和minHeightDps 這兩個手機的實際長和寬。
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); display.getMetrics(dm); Point smallestSize = new Point(); Point largestSize = new Point(); display.getCurrentSizeRange(smallestSize, largestSize); // This guarantees that width < height minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm); minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
獲取到手機的實際長寬以後就走到最關鍵的兩個方法
ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles( minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context)); InvariantDeviceProfile interpolatedDeviceProfileOut = invDistWeightedInterpolate(minWidthDps,minHeightDps, closestProfiles);
這兩個方法的引數都傳入了手機的實際寬高minWidthDps,minHeightDps,不同的是在findClosestDeviceProfiles方法中呼叫了getPredefinedDeviceProfiles(context)這個方法。
其實這個方法主要做的事情就是解析xml佈局R.xml.device_profiles返回一個型別為InvariantDeviceProfile的ArrayList集合。具體的程式碼比較多,就不貼了。
device_profiles的xml檔案
對於這個xml檔案是由很多profile檔案組成,其中一個.
<profile launcher:name="Short Stubby" launcher:minWidthDps="275" launcher:minHeightDps="420" launcher:numRows="3" launcher:numColumns="4" launcher:numFolderRows="3" launcher:numFolderColumns="4" launcher:minAllAppsPredictionColumns="4" launcher:iconSize="48" launcher:iconTextSize="11" launcher:numHotseatIcons="5" launcher:defaultLayoutId="@xml/default_workspace_4x4" />
在這個profile中有很多的屬性,包括最小的寬高minWidthDps和minHeightDps,佈局的行列數,資料夾的行列數,allapp的行列數,圖示大小,圖示名字代銷,快捷欄圖示大小,快捷欄圖示數,以及預設佈局檔案。這些引數都需要在後面獲取到。
在這11個profile中從
launcher:minWidthDps="255" launcher:minHeightDps="300"
到
launcher:minWidthDps="1527"launcher:minHeightDps="2527"
所有的裝置都包含了。
findClosestDeviceProfiles()
接著我們來回過頭來看一些findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context))這個方法。
ArrayList<InvariantDeviceProfile> findClosestDeviceProfiles( final float width, final float height, ArrayList<InvariantDeviceProfile> points) { // Sort the profiles by their closeness to the dimensions ArrayList<InvariantDeviceProfile> pointsByNearness = points; Collections.sort(pointsByNearness, new Comparator<InvariantDeviceProfile>() { public int compare(InvariantDeviceProfile a, InvariantDeviceProfile b) { return Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps), dist(width, height, b.minWidthDps, b.minHeightDps)); } }); return pointsByNearness; }
points就是我們通過解析R.xml.device_profiles這個佈局來獲得的型別為InvariantDeviceProfile的ArrayList,賦值給pointsByNearness ,然後進行排序,排序方式是呼叫Collections.sort的第二種方式自定義比較器來實現的ofollow,noindex">關於Collections.sort的講解友情連結 比較的。在自定義比較器的方法中採用浮點型別的比較方法Float.compare(f1,f2),此方法返回值0,則f1在數值上等於f2; 返回值小於0,f1在數值上比f2小; 返回值大於0,f1在數值上比f2大.而真正的演算法是在dist方法中。
@Thunk float dist(float x0, float y0, float x1, float y1) { return (float) Math.hypot(x1 - x0, y1 - y0); }
我們可以看到使用了Math.hypot,此方法返回的結果是sqrt(x2 +y2) ,也就是直角三角形的斜邊長,其實就是手機螢幕的對角線的長度即手機尺寸。
dist方法得到的結果是讀取的profile的斜邊長度與手機螢幕的對角線的長度的一個差值。最後進行compare,差值小的在前面,差值大的在後面。之後將新的排序返回。這就是findClosestDeviceProfiles方法做的事情。
總結一下findClosestDeviceProfiles:通過將手機實際尺寸和理論的11個模板的理論尺寸進行比較,得到與目標的匹配度,匹配度越高,在closestProfiles的集合裡面越靠前。
講解完InvariantDeviceProfile構造方法中的findClosestDeviceProfiles方法,我們接著來看第二個精華的地方,那就是 invDistWeightedInterpolate(minWidthDps,minHeightDps, closestProfiles);這個方法
invDistWeightedInterpolate()
呼叫這個方法時傳進去的引數是當前手機真實的寬和高,以及經過排序後得到的與目標匹配度由高到低的profiles集合。
InvariantDeviceProfile invDistWeightedInterpolate(float width, float height, ArrayList<InvariantDeviceProfile> points) { float weights = 0; //獲取集合中的第一個InvariantDeviceProfile,也就是匹配度最高的那一個 InvariantDeviceProfile p = points.get(0); //判斷如果當前手機與profile的差值等於0,則直接返回這個InvariantDeviceProfile。 if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) { return p; } InvariantDeviceProfile out = new InvariantDeviceProfile(); for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) { p = new InvariantDeviceProfile(points.get(i)); float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER); weights += w; out.add(p.multiply(w)); } return out.multiply(1.0f/weights); }