Android_launcher的源码详细分析
这份源码是基于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工程中的类: ;
简要补充 AddAdapter: 维护了 live fold , widget , shortcut , wallpaper 4 个
ListItem , 长按桌面会显示该列表AllAppsGridView :显示 APP 的网格ApplicationInfo
:一个可启动的应用ApplicationsAdapter : gridview 的 adapterBubbleTextView: 一个定制了的
textviewCellLayout: 屏幕网格化DeleteZone : UI 的一部分DragController ,
dragscroller, dragsource, droptarget: 支持拖拽操作DragLayer :内部支持拖拽的
viewgroupFastBitmapDrawable :工具Folder : Icons 的集合FolderIcon: 出现在
workspace 的 icon 代表了一个 folderFolderInfo: ItemInfo 子类HandleView :一个
imageview .InstallShortcutReceiver , UninstallShortcutReceiver :一个
broadcastrecierItemInfo: 代表 Launcher 中一个 Item (例如 folder )Launcher:
Launcher 程序的主窗口LauncherApplication :在 VM 中设置参数LauncherAppWidgetHost ,
LauncherAppWidgetHostView ,: Widget 相关LauncherModel : MVC 中的
MLauncherProvider :一个 contentprovider ,为 Launcher 存储信息LauncherSettings:
设置相关的工具LiveFolder , LiveFolderAdapter , LiveFolderIcon , LiveFolderInfo
: livefolder 相关Search : 搜索UserFolder , UserFolderInfo :文件夹包含
applications ,shortcutsUtilities: 小工具WallpaperChooser :选择 wallpaper 的
activityWorkspace: 屏幕上的一块区域widget : 代表启动的 widget 实例,例如搜索 二,主要模块 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 ;
; ;