这份源码是基于2.1的launcher2,以后版本虽有变化,但大概的原理一直还是保留了。
一、主要文件和类
1.Launcher.java:launcher中主要的activity。
2.DragLayer.java:launcher layout的rootview。DragLayer实际上也是一个抽象的界面,用来处理拖动和对事件进行初步处理然后按情况分发下去,角色是一个controller。它首先用onInterceptTouchEvent(MotionEvent)来拦截所有的touch事件,如果是长按item拖动的话不把事件传下去,直接交由onTouchEvent()处理,这样就可以实现item的移动了,如果不是拖动item的话就把事件传到目标view,交有目标view的事件处理函数做相应处理。如过有要对事件的特殊需求的话可以修改onInterceptTouchEvent(MotionEvent)来实现所需要的功能。
3. DragController.java:为Drag定义的一个接口。包含一个接口,两个方法和两个静态常量。接口为DragListener(包含onDragStart(),onDragEnd()两个函数),onDragStart()是在刚开始拖动的时候被调用,onDragEnd()是在拖动完成时被调用。在launcher中典型的应用是DeleteZone,在长按拖动item时调用onDragStart()显示,在拖动结束的时候onDragEnd()隐藏。两个函数包括startDrag()和setDragItemInfo().startDrag()用于在拖动是传递要拖动的item的信息以及拖动的方式,setDragItemInfo()用于传递item的参数信息(包括位置以及大小)。两个常量为DRAG_ACTION_MOVE,DRAG_ACTION_COPY来标识拖动的方式,DRAG_ACTION_MOVE为移动,表示在拖动的时候需要删除原来的item,DRAG_ACTION_COPY为复制型的拖动,表示保留被拖动的item。
4.LauncherModel.java:辅助的文件。里面有许多封装的对数据库的操作。包含几个线程,其中最主要的是ApplicationsLoader和DesktopItemsLoader。ApplicationsLoader在加载所有应用程序时使用,DesktopItemsLoader在加载workspace的时候使用。其他的函数就是对数据库的封装,比如在删除,替换,添加程序的时候做更新数据库和UI的工作。
5.Workspace.java:抽象的桌面。由N个celllaout组成,从cellLayout更高一级的层面上对事件的处理。
6.LauncherProvider.java:launcher的数据库,里面存储了桌面的item的信息。在创建数据库的时候会loadFavorites(db)方法,loadFavorites()会解析xml目录下的default_workspace.xml文件,把其中的内容读出来写到数据库中,这样就做到了桌面的预制。
7.CellLayout.java:组成workspace的view,继承自viewgroup,既是一个dragSource,又是一个dropTarget,可以将它里面的item拖出去,也可以容纳拖动过来的item。在workspace_screen里面定了一些它的view参数。
8.ItemInfo.java:对item的抽象,所有类型item的父类,item包含的属性有id(标识item的id),cellX(在横向位置上的位置,从0开始),cellY(在纵向位置上的位置,从0开始) ,spanX(在横向位置上所占的单位格),spanY(在纵向位置上所占的单位格),screen(在workspace的第几屏,从0开始),itemType(item的类型,有widget,search,application等),container(item所在的)。
9.UserFolder.java: 用户创建的文件夹。可以将item拖进文件夹,单击时打开文件夹,长按文件夹上面标题处可以重命名文件夹。
10.LiveFolder.java:系统自带的文件夹。从系统中创建出的如联系人的文件夹等。
11.DeleteZone:删除框。在平时是出于隐藏状态,在将item长按拖动的时候会显示出来,如果将item拖动到删除框位置时会删除item。DeleteZone实现了DropTarget和DragListener两个接口。
12.LauncherSettings.java:字符串的定义。数据库项的字符串定义,另外在这里定义了container的类型,还有itemType的定义,除此还有一些特殊的widget(如search,clock的定义等)的类型定义。
补充Launcher工程中的类:
简要补充
二、主要模块
1.界面模型:
Launcher的界面的rootview是DragLayer,它是一个FrameLayout,在它上面workspace(应该说是celllayout)占了绝大部分的空间,celllayout的参数文件是workspace_screen.xml。workspace既是一个DropTarget又是一个DragSource,可以从AllAppGridView中拖出应用程序放在它上面,也可以把它里面的item拖走删除或者拖到bottomabr里面去。
(对于想修改launcher的同学,可以自定义DragLay.java,比如改为AbsoluteLayout等,再修改launcher.xml布局文件,就可以实现各种样式的launcher几面。)
2.Drop& Drag模型:
2.1 DragSource:可以拖动的对象来源的容器,在launcher中主要有AllAppGridView,workspace等。
void onDropCompleted(View target, boolean success,int x,int y);
2.2 DropTarget:可以放置被拖动的对象的容器。在launcher中有folder,workspace,bottombar等,一个View既可以是Dragsource也可以是DropTarget。主要包含以下几个接口:
1) boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
acceptDrop 函数用来判断dropTarget是否可以接受item放置在自己里面。
2) void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragEnter是item被拖动进入到一个dropTarget的时候的回调。
3) void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragOver是item在上一次位置和这一次位置所处的dropTarget相同的时候的回调。
4) void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragExit是item被拖出dropTarget时的回调。
5) boolean onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDrop是item被放置到dropTarget时的回调。
函数的调用模式为:
DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
if (dropTarget != null) {
/**
* 当这一次的 target 跟上一次相同时,根据坐标来移动item
*/
if (mLastDropTarget == dropTarget) {
dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
} else {
/**
* 当上一次的位置跟这一次不同而且上一次的位置不为空,说明item移 *动出了,将上次的 View 根据上次的坐标重新排列,并根据当前坐标重排*当前的*/
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
}
dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
}
} else {//如果这一次为 null ,上一次不为 null ,那么把上一次坐标位置的 cell 去掉
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
}
}
//记录上次的droptarget
mLastDropTarget = dropTarget;
3.Touch event总结:
由于launcher的事件比较多比较复杂,所以在事件处理的时候一般采用rootview先用onInterceptTouchEvent(MotionEvent)拦截所有的touch事件,经过判断后分发给childview。
判断的规则如下:
a.down事件首先会传递到onInterceptTouchEvent()方法
b.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
c.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
d.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
e.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
三、几种问题的解决方式
1.将所有的应用都排列在桌面上
将所有的应用都排列在桌面是通过首先创建一个三维的boolean型全局数组来记录item的排列情况,第一维是屏数,第二维是纵向上的排列情况,第三维是横向的排列情况,如果那个位置被item所占用就标记为1,否则标记为0.在启动时把全局数组初始化为0,然后在添加的时候把相应的位置置1.凡是涉及到workspace上item的变化,比如移动、添加、删除操作时都需要维护数组,保持数组的正确性,因为在安装新程序时依据数组的状态去判断把item加到什么位置。
2.动态增加屏幕
动态增加屏幕是通过worksapce .addchild(view)的方式实现。基本思路是:首先预先规定所允许的最大的屏幕数,然后在需要增加屏幕而且当前屏幕数没有超过最大屏幕数的时候通过(CellLayout)mInflater.inflate(R.layout.workspace_screen,null)创建一个celllayout实例出来,然后通过addchild把它加入进去。在屏幕上的item被删除时通过从最后一屏起判断屏幕上是否有item,如果有的话保留,没有的话则删除最后一屏,以此类推。
3.预制桌面
a.添加普通的应用程序快捷方式:
在../res/xml下的default_workspace.xml文件中加入默认要放置的普通的应用程序。加入的格式为:
<favorite
launcher:packageName="... " //应用的packageName
launcher:className="... " //应用启动时的第一个activity
launcher:screen="..." //放置在第几屏(放在workspace的时候需要,从0开始,0为第一屏,1为第二屏,以此类推...)
launcher:x="..." //放置x方向的位置(在列中的位置)
launcher:y="..." /> //放置y方向的位置(在行中的位置)
packageName和className可以通过点击程序,然后在打印出的log中找到comp={...},例如如下信息:
comp={com.estrongs.android.taskmanager/com.estrongs.android.taskmanager.TaskManager}。其中com.estrongs.android.taskmanager为packageName, com.estrongs.android.taskmanager.TaskManager为className。
workspace的布局如下:
(0,0) |
(1,0) |
(2,0) |
(3,0) |
(4,0) |
(0,1) |
(1,1) |
(2,1) |
(3,1) |
(4,1) |
(0,2) |
(1,2) |
(2,2) |
(3,2) |
(4,2) |
b.添加widget:
在../package/apps/VLauncher/res/xml下的default_workspace.xml文件中加入默认要放置的普通的应用程序。加入的格式为:
<widget
launcher:packageName="..." //widget的packageName
launcher:className=" ..." //实现 widget的 receiver 类的名称.
launcher:container="..." //放置的位置(只能为desktop)
launcher:screen="..." //放置在第几屏上
launcher:x="..." //放置的x位置
launcher:y="..." //放置的y位置
launcher:spanx="..." //在x方向上所占格数
launcher:spany="..."/> //在y方向上所占格数
例如,要在第3屏的第一行第二列放置开始放置一个x方向上占两个单位格,y方向上占两个单位格的时钟,可以加入以下代码:
<appwidget
launcher:packageName="com.android.alarmclock" launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
launcher:container="desktop"
launcher:screen="2"
launcher:x="1"
launcher:y="0"
launcher:spanx="2"
launcher:spany="2"/>
4.改变主界面的排列方式
要修改桌面的排列方式,如下,先根据横竖屏设置修改workspace_screen.xml里shortAxisCells和longAxisCells的参数,然后在Launcher.java中修改NUMBER_CELLS_X和NUMBER_CELLS_Y的值,在2.3版本中刚开始往数据库中添加item的时候会去判断,如果不修改NUMBER_CELLS_X和NUMBER_CELLS_Y的话会导致一部分的item显示不出来,导致预制apk的失败。
5.增加worksapce上的屏数
要增加屏数,首先在根据横竖屏在launcher.xml中的<com.android.launcher.Workspace 中删除或增加 <include android:id="@+id/cellN" layout="@layout/workspace_screen" />,然后在Launcher.java中修改SCREEN_COUNT的值即可。
四、xml文件
1.workspace_screen.xml
launcher:cellWidth="95dip" cell(即item)的宽
launcher:cellHeight="93dip" cell(即item)的宽
launcher:longAxisStartPadding="25dip"
较长(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离起点的像素数
launcher:longAxisEndPadding="55dip"
较长(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离终点的像素数
launcher:shortAxisStartPadding="20dip"
较短(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离起点的像素数
launcher:shortAxisEndPadding="120dip"
较短(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离起点的像素数
launcher:shortAxisCells="3"
较短的方向上可以容纳的cell的数量
launcher:longAxisCells="5"
较长的方向上可以容纳的cell的数量
shortAxisCells和longAxisCells决定一个workspace(即CellLayout)上可以容纳的item的个数为shortAxisCells*longAxisCells.
2. application_boxed.xml
所有应用程序和系统文件夹中item的定义。
3.application.xml
Workspace的item的layout定义。
转载请注明出处:http://blog.csdn.net/fzh0803/archive/2011/03/26/6279995.aspx
【转】Android Launcher研究 (一)的更多相关文章
-
Android Launcher 研究学习
Launcher是系统启动后第一个启动的程序,是其它应用程序的入口,也就是我们的手机程序的桌面程序; 一.Launcher的定义及构成: <1>通过查看官方提供的Launcher源码可以知 ...
-
android 1.6 launcher研究之自定义ViewGroup (转 2011.06.03(二)——— android 1.6 launcher研究之自定义ViewGroup )
2011.06.03(2)——— android 1.6 launcher研究之自定义ViewGroup2011.06.03(2)——— android 1.6 launcher研究之自定义ViewG ...
-
Android Launcher 3 简单分析
最近在学习Android Launcher的相关知识,在github上找到可以在Android studio上编译的Launcher 3代码,地址:https://github.com/rydanli ...
-
Android安全研究经验谈
安全研究做什么 从攻击角度举例,可以是:对某个模块进行漏洞挖掘的方法,对某个漏洞进行利用的技术,通过逆向工程破解程序.解密数据,对系统或应用进行感染.劫持等破坏安全性的攻击技术等. 而防御上则是:查杀 ...
-
Android Launcher分析和修改13——实现Launcher编辑模式(1) 壁纸更换
已经很久没更新Launcher系列文章,今天不分析源码,讲讲如何在Launcher里面添加桌面设置的功能.目前很多第三方Launcher或者定制Rom都有简单易用的桌面设置功能.例如小米MIUI的La ...
-
Android Launcher分析和修改9——Launcher启动APP流程
本来想分析AppsCustomizePagedView类,不过今天突然接到一个临时任务.客户反馈说机器界面的图标很难点击启动程序,经常点击了没有反应,Boss说要优先解决这问题.没办法,只能看看是怎么 ...
-
Android Launcher 详解
(1) Launcher的定义:Android系统启动后,加载的第一个程序,是其他应用程序的入口. (2) Launcher的构成:HomeScreen(1.workspace(A ...
-
Android Launcher分析和修改10——HotSeat深入进阶
前面已经写过Hotseat分析的文章,主要是讲解如何在Launcher里面配置以及修改Hotseat的参数.今天主要是讲解一下如何在Hotseat里面的Item显示名称.这个小问题昨天折腾了半天,最后 ...
-
Android Launcher分析和修改2——Icon修改、界面布局调整、壁纸设置
上一篇文章说了如何修改Android自带Launcher2的默认界面设置(http://www.cnblogs.com/mythou/p/3153880.html). 今天主要是说说Launcher里 ...
随机推荐
-
mysql锁
锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所有数 ...
-
如何使用新浪微博账户进行应用登录验证(基于Windows Azure Mobile Service 集成登录验证)
使用三方账号登录应用应该对大家来说已经不是什么新鲜事儿了,但是今天为什么还要在这里跟大家聊这个话题呢,原因很简单 Windows Azure Mobiles Service Authenticatio ...
-
利用SQL语句查询一个数据库中的所有表
SQL : select * from information_schema.tables ORACLE: select table_name from user_tables ACCESS: s ...
-
Nape实现坐标旋转角度回弹
乒乓球以一个向量运动,碰到障碍后反弹以一个新的向量运动,如下图: 要实现回弹只需要求出向量v1,把向量v0取反,再旋转(a+b)度就可以得到向量v1. 向量取反: var v:vec2 = new V ...
-
写一函数,用来求表达式1+2+3+.....+n的值,并编写主函数
Description 写一函数,用来求表达式1+2+3+.....+n的值,并编写主函数.n由键盘输入. Input 输入一个整数 Output 输出表达式的值 Sample Input 5 Sam ...
-
采坑:python base64
需求: 读取文本内容,对字符串进行base64加密 >>> str = 'aaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbb\nccc ...
-
解决Eclipse启动时报Initializing Java Tooling异常信息
1.启动Eclipse报错:An internal error occurred during: "Initializing Java Tooling".java.lang.Nul ...
-
p1257 平面上最接近点对---(分治法)
首先就是一维最接近点的情况... #include<iostream> #include<cstdio> #include<cstring> #include< ...
-
vue使用vue-video-player在直播中的应用
文档地址:https://github.com/savokiss/vue-videojs-demo live demo地址:https://github.com/savokiss/vue-videoj ...
-
PCA实现教程
数据是机器学习模型的生命燃料.对于特定的问题,总有很多机器学习技术可供选择,但如果没有很多好的数据,问题将不能很好的解决.数据通常是大部分机器学习应用程序中性能提升背后的驱动因素. 有时,数据可能很复 ...