Android Launcher 3 简单分析
最近在学习Android Launcher的相关知识,在github上找到可以在Android studio上编译的Launcher 3代码,地址:https://github.com/rydanliu/Launcher3
Launcher 3的界面主要由SearchDropTargetBar、Workspace、CellLayout、PageIndicator、Hotseat组成。如下图:

Launcher 3 最主要的是一个Activity,基本上所有操作都集中在这个Activity上。这个Activity文件为Launcher.java,他的布局文件为launcher.xml。
下面为竖屏的布局文件,路径为res/layout-port/launcher.xml。
<?xml version="1.0" encoding="utf-8"?> <!-- Full screen view projects under the status bar and contains the background -->
<com.android.launcher3.LauncherRootView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"> <com.android.launcher3.DragLayer
android:id="@+id/drag_layer" android:layout_width="match_parent"
android:layout_height="match_parent"> <com.android.launcher3.FocusIndicatorView
android:id="@+id/focus_indicator"
android:layout_width="22dp"
android:layout_height="22dp" /> <!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
launcher:pageIndicator="@+id/page_indicator"></com.android.launcher3.Workspace> <!-- DO NOT CHANGE THE ID -->
<include
android:id="@+id/hotseat"
layout="@layout/hotseat" android:layout_width="match_parent"
android:layout_height="match_parent" /> <include
android:id="@+id/overview_panel"
layout="@layout/overview_panel"
android:visibility="gone" /> <!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
<include
android:id="@+id/page_indicator"
layout="@layout/page_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" /> <include
android:id="@+id/search_drop_target_bar" layout="@layout/search_drop_target_bar" /> <include
android:id="@+id/widgets_view"
layout="@layout/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" /> <include
android:id="@+id/apps_view"
layout="@layout/all_apps"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</com.android.launcher3.DragLayer> <ViewStub
android:id="@+id/launcher_overlay_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inflatedId="@+id/launcher_overlay"
android:layout="@layout/launcher_overlay" />
</com.android.launcher3.LauncherRootView>
SearchDropTargetBar
屏幕最上方有个搜索框,在我们拖动图标的时候,搜索框会替换成“删除“
Workspace
就是屏幕上左右滑的好几屏幕的容器
CellLayout
Workspace里面可以滑动的单独一屏,CellLayout负责图标和小部件的显示和整齐摆放。
PageIndicator
滑动屏幕的时候看见下方的指示器
Hotseat
用来放置比较常用的应用,比如拨号,短信,相机等。
下面介绍几个类
CellLayout 类
mCountX 和 mCountY 分别表示 “x方向icon的个数” 和 “y方向icon的个数”
mOccupied 二维数组用来标记每个cell是否被占用,内部类CellInfo为静态类,其对象用于存储cell的基本信息。
DeviceProfile 类
设置各元素布局的padding 。修改workspace的padding使用getWorkspacePadding方法。
/** Returns the workspace padding in the specified orientation */
Rect getWorkspacePadding(boolean isLayoutRtl) {
Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
Rect padding = new Rect();
if (isLandscape && transposeLayoutWithOrientation) {
// Pad the left and right of the workspace with search/hotseat bar sizes
if (isLayoutRtl) {
padding.set(hotseatBarHeightPx, edgeMarginPx,
searchBarBounds.width(), edgeMarginPx);
} else {
padding.set(searchBarBounds.width(), edgeMarginPx,
hotseatBarHeightPx, edgeMarginPx);
}
} else {
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
float gapScale = 1f + (dragViewScale - 1f) / 2f;
int width = getCurrentWidth();
int height = getCurrentHeight();
int paddingTop = searchBarBounds.bottom;
int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
int availableWidth = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
(inv.numColumns * gapScale * cellWidthPx)));
int availableHeight = Math.max(0, height - paddingTop - paddingBottom
- (int) (2 * inv.numRows * cellHeightPx));
padding.set(availableWidth / 2, paddingTop + availableHeight / 2,
availableWidth / 2, paddingBottom + availableHeight / 2);
} else {
// Pad the top and bottom of the workspace with search/hotseat bar sizes padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
searchBarBounds.bottom,
desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
hotseatBarHeightPx + pageIndicatorHeightPx); }
}
return padding;
}
比如我要将workspace里图标顶部不留空隙,需要设置padding.set的第二个参数为0.
padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
0,//searchBarBounds.bottom,
desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
hotseatBarHeightPx + pageIndicatorHeightPx);
InvariantDeviceProfile类
一些不变的设备相关参数管理类,landscapeProfile 和 portraitProfile为横竖屏模式的DeviceProfile。
getPredefinedDeviceProfiles方法 负责加载在不同设备上不同的布局,和图标大小等。
ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles() {
ArrayList<InvariantDeviceProfile> predefinedDeviceProfiles = new ArrayList<>();
// width, height, #rows, #columns, #folder rows, #folder columns,
// iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId.
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
255, 300, 2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
255, 400, 3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
275, 420, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
255, 450, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
296, 491.33f, 4, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
335, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
406, 694, 5, 5, 4, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5));
// The tablet profile is odd in that the landscape orientation
// also includes the nav bar on the side
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7",
575, 904, 5, 6, 4, 5, 4, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6));
// Larger tablet profiles always have system bars on the top & bottom
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
727, 1207, 5, 6, 4, 5, 4, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
1527, 2527, 7, 7, 6, 6, 4, 100, 20, 7, 72, R.xml.default_workspace_4x4));
return predefinedDeviceProfiles;
}
比如我在上面代码的17行加入下列代码,将Hotseat设置成3格,图标大小为72dp
predefinedDeviceProfiles.add(new InvariantDeviceProfile("MX 4",
359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, , , R.xml.default_workspace_4x4));
由于launcher是有许多自定义控件构成的,这里涉及到onMesure,onLayout,onDraw方法的复写
onMesure方法顾名思义,主要是用来重新测量自定义控件的高度和宽度,就是设置它的dimesion,一般所有自定义VIEW都需要复写这个方法。
onLayout则主要是ViewGroup需要复写这个方法,其作用给这个ViewGroup下子View布局好显示的位置。
onDraw则是需要真真正正画出内容的控件需要复写的方法,比如textview,或者其子类,其最终利用一个很重要的类Canvas的对象来实现一系列的画图,比如canvas.drawcircle,canvas.drawline.
Android Launcher 3 简单分析的更多相关文章
- Android.mk文件简单分析
Android.mk文件简单分析 一个Android.mk文件用来向编译系统描写叙述须要编译的源码.详细来说:该文件是GNUMakefile的一小部分.会被编译系统解析一次或多次. 能够在每个Andr ...
- Android ListView初始化简单分析
下面是分析ListView初始化的源码流程分析,主要是ListVIew.onLayout过程与普通视图的layout过程完全不同,避免流程交代不清楚,以下是一个流程的思维导图. 思维导图是顺序是从左向 ...
- Android Launcher分析和修改13——实现Launcher编辑模式(1) 壁纸更换
已经很久没更新Launcher系列文章,今天不分析源码,讲讲如何在Launcher里面添加桌面设置的功能.目前很多第三方Launcher或者定制Rom都有简单易用的桌面设置功能.例如小米MIUI的La ...
- Android Launcher分析和修改10——HotSeat深入进阶
前面已经写过Hotseat分析的文章,主要是讲解如何在Launcher里面配置以及修改Hotseat的参数.今天主要是讲解一下如何在Hotseat里面的Item显示名称.这个小问题昨天折腾了半天,最后 ...
- Android Launcher分析和修改4——初始化加载数据
上面一篇文章说了Launcher是如何被启动的,Launcher启动的过程主要是加载界面数据然后显示出来, 界面数据都是系统APP有关的数据,都是从Launcher的数据库读取,下面我们详细分析Lau ...
- Android Launcher分析和修改5——HotSeat分析
今天主要是分析一下Launcher里面的快捷方式导航条——HotSeat,一般我们使用手机底下都会有这个导航条,但是如果4.0的Launcher放到平板电脑里面运行,默认是没有HotSeat的,刚好我 ...
- Android Launcher分析和修改7——AllApp全部应用列表(AppsCustomizeTabHost)
今天主要是分析一下Launcher里面的所有应用列表.Android4.0 Launcher的所有应用列表跟2.X比较大的区别就是多了Widget的显示.下面会详细分析Launcher里面有关所有应用 ...
- Android Launcher分析和修改8——AllAPP界面拖拽元素(PagedViewWithDraggableItems)
接着上一篇文章,继续分析AllAPP列表界面.上一篇文章分析了所有应用列表的界面构成以及如何通过配置文件修改属性.今天主要是分析PagedViewWithDraggableItems类,因为在我们分析 ...
- Android Launcher分析和修改11——自定义分页指示器(paged_view_indicator)
Android4.0的Launcher自带了一个简单的分页指示器,就是Hotseat上面那个线段,这个本质上是一个ImageView利用.9.png图片做,效果实在是不太美观,用测试人员的话,太丑了. ...
随机推荐
- 我如何调优SQL Server查询
我是个懒人,我只想干尽可能少的活.当我干活的时候我不想太多.是,你没看错,这看起来很糟糕,作为一个DBA这很不合格.但在今天的文章里,我想给你展示下,当你想对特定查询创建索引设计时,你如何把你的工作和 ...
- 实现iOS图片等资源文件的热更新化(零): 序
必要的序 以后在写系列文章,准备把基本的规划和动机等,单独作为一个小的序言部分给独立出来.序言部分,可以较为完整地交待系列文章的写作动机,所展示的编码技术可能的应用场景等.个人,我还是比较看重文章或者 ...
- Android SDK Android NDK Android Studio 官方下载地址
2016.12 Android Studio Windows Includes Android SDK https://dl.google.com/dl/android/studio/install/ ...
- RDLC报表打印
如果你的报表能正常显示,会看到RDLC工具条下的打印图标: 如果在客户端,第一次去点击此铵钮,如果当前操作者没有管理权限,会提示: Unable to load client print contro ...
- Document
每个载入浏览器的 HTML 文档都会成为 Document 对象.Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问.提示:Document 对象是 Window 对象的一 ...
- ActionSupport与action区别
action是接口,只有一个execute方法需要实现.ActionSupport是action接口的一个实现类.这个类除了实现action接口还实现了Validateable(用于验证)等接口,开发 ...
- java servlet调用带有多个返回结果集的存储过程
一.mysql存储过程 这里我先说下我这个功能实现的逻辑及途中遇到的一些问题.这个存储过程一共带两个输入参数,一共关联到两张表的查询,每个参数都对应查询表中的一个判断,所以一共返回了两个结果集(当然要 ...
- 2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)
原题链接 http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1809 Description Bobo has a balanced parenthes ...
- 泛函编程(31)-泛函IO:Free Monad-Running free
在上节我们介绍了Free Monad的基本情况.可以说Free Monad又是一个以数据结构替换程序堆栈的实例.实际上Free Monad的功能绝对不止如此,以heap换stack必须成为Free M ...
- NOI 2015 荷马史诗【BZOJ 4198】k叉Huffman树
抱歉因为NOIP集训,好长时间没再写题解了. NOI 2015也就只有这道题一看就能懂了-- 4198: [Noi2015]荷马史诗 Time Limit: 10 Sec Memory Limit: ...