转载请注明原地址:https://www.jianshu.com/p/484c1c5a1795

本文对Launcher的布局做一个整体性的描述。我们先看一下布局文件launcher.xml

                                                                                                

在常规状态下,几个主要的区域如下图所示。Workspace(@+id/workspace)是显示图标和widget的主体;PageIndicator(@+id/page_indicator,代码中是一个具体的对象WorkspacePageIndicator)是页面指示器,用于指示滑动和表明当前页面;HotSeat(@+id/hotseat)是常驻底部的图标栏;DragLayer是包在这些View的外层的一个ViewGroup,Launcher的拖拽操作需要依赖其内部实现,以后再详细说明。

然后还有常规情况下隐藏起来的一些重要区域,首先是@+id/overview_panel_container。我们知道在Android 9.0上,Recent界面不像以前一样是进入到一个新的页面,而是与Launcher的内容结合在一起。这个ViewGroup就是显示Recent内容的控件了。

然后还有@+id/drop_target_bar,当拖动图标或widget的内容时,出现在Launcher上方用于执行特定操作的控件。

Untitled.png

最后是@+id/apps_view,这就是应用抽屉了,容纳了所有应用图标。

另外还有容纳所有Widget的页面,Launcher设置页面,这里不详说。

那Launcher一页一页的视图结构是如何做到的呢?简单画了一个示意图,真正呈现到用户面前的就是这样一个结构。用户可见的部分为红框(DragLayer区域),Workspace真正的大小比可见区域要大很多。桌面上的每一页是一个CellLayout(也是一个自定义的ViewGroup),当有多页时,就一个一个横向排布在Workspace中。当触摸滑动桌面时,通过scrollTo来改变workspace内部子View的位置,就可以使用户看到不同页的内容了。

对于图标的排布,CellLayout还不是真正容纳图标的ViewGroup,每个CellLayout会包含一个ShortcutAndWidgetContainer,这才是真正容纳图标和Widget的ViewGroup。

介绍完布局结构,接下来看看一些布局数据的初始化过程。从上面已经可以看出,Launcher有一个相对复杂的视图结构,那么如何让这个视图在各种不同分辨率下都能良好的适配呢?继续分析源码。

在前文的Launcher启动流程(点此跳转)中提到过LauncherAppState这个类,在它初始化时有这样一句mInvariantDeviceProfile = new InvariantDeviceProfile(mContext),这里就已经开始有针对不同设备的处理逻辑了。我们看InvariantDeviceProfile的构造函数,重点在于findClosestDeviceProfiles这一句。

public InvariantDeviceProfile(Context context) {        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 closestProfiles = findClosestDeviceProfiles(                minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));        InvariantDeviceProfile interpolatedDeviceProfileOut =                invDistWeightedInterpolate(minWidthDps,  minHeightDps, closestProfiles);                ...    }        /**     * Returns the closest device profiles ordered by closeness to the specified width and height     */    // Package private visibility for testing.    ArrayList findClosestDeviceProfiles(            final float width, final float height, ArrayList points) {        // Sort the profiles by their closeness to the dimensions        ArrayList pointsByNearness = points;        Collections.sort(pointsByNearness, new Comparator() {            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;    }

执行这个函数时,传入了根据当前设备DisplayMetrics计算得到的一个宽高值,另外还传入了一个ArrayList。

这里岔开一下,我们知道不同的设备屏幕宽高不同,那么桌面上放多少行多少列也应该要动态可调。我们当然可以在代码里写一个动态计算的规则,让其自动去适配不同的宽高,但对于定制ROM的厂家来说,同样的宽高下可能你想要5行,我只想要4行,所以这个计算规则如果要调整就得修改代码了。那么Launcher是如何去做这件事的呢?它定义了一个device_profile.xml,里面的内容我列出了其中两个。可以看到,Launcher的处理方式是在xml中由开发者自行定义对于一个设备的所有layout配置信息,包括多少行、多少列、图标大小、文件夹行列、HotSeat列数、默认图标排布的配置文件等。这样既不用改到代码,又可以直观且灵活地进行配置。

            

了解了上面的信息,我们回到findClosestDeviceProfiles,传入的ArrayList就是解析这个xml之后传入的一个对象数组。然后根据宽高找到最接近的一个设备,然后Launcher就知道了在这个设备上的想要的各个基本数据了,将其储存在成员变量中。后面使用这些数据的地方就比较分散了,比如某个ViewGroup onLayout时、文件夹初始化时等,这里就不一一列出了。大家只要知道这些宽高、行列、padding等数据的来源都是此时决定好了的就可以了。

更多相关文章

  1. Android(安卓)Toolbar
  2. 常用的android弹出对话框
  3. Android(安卓)实现 选择文件对话框
  4. android 修改statusbar(二)
  5. android Wifi自动连接
  6. Android多分辨率适配实践【1】使用字体图标(内含两枚神器)
  7. Android自定义进度条
  8. )Android之getSystemService
  9. Android之getSystemService

随机推荐

  1. Android(安卓)permission 权限类及中英文
  2. android 获取手机屏幕分辨率
  3. android联系人中英文混合排序
  4. android 本地存储数据
  5. android XML解析
  6. android 的几个黄色警告解决办法
  7. SHA 的实现
  8. Android(安卓)上层实现IPC通讯
  9. android 4.0 com.android.ddmlib.Install
  10. Android(安卓)Media Recorder录音播放源