轉載請註明出處:http://blog.csdn.net/fzh0803/archive/2011/03/26/6279995.aspxjava
去年作了launcher相關的工做,看了很長時間。不少人都在修改launcher,但尚未詳細的文檔,把本身積累的東西分享出來,你們一塊兒積累。這份源碼是基於2.1的launcher2,之後版本雖有變化,但大概的原理一直仍是保留了。android
1、主要文件和類數據庫
1.Launcher.java:launcher中主要的activity。數組
2.DragLayer.java:launcher layout的rootview。DragLayer實際上也是一個抽象的界面,用來處理拖動和對事件進行初步處理而後按狀況分發下去,角色是一個controller。它首先用onInterceptTouchEvent(MotionEvent)來攔截全部的touch事件,若是是長按item拖動的話不把事件傳下去,直接交由onTouchEvent()處理,這樣就能夠實現item的移動了,若是不是拖動item的話就把事件傳到目標view,交有目標view的事件處理函數作相應處理。如過有要對事件的特殊需求的話能夠修改onInterceptTouchEvent(MotionEvent)來實現所須要的功能。app
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。ide
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文件,把其中的內容讀出來寫到數據庫中,這樣就作到了桌面的預製。spa
7.CellLayout.java:組成workspace的view,繼承自viewgroup,既是一個dragSource,又是一個dropTarget,能夠將它裏面的item拖出去,也能夠容納拖動過來的item。在workspace_screen裏面定了一些它的view參數。.net
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的定義等)的類型定義。
2、主要模塊
1.界面模型:
|
Bottombar |
Deletezone or Handview |
CellLayout(由N個cellLayout組成workspace) |
statusbar |
<!--[if !vml]-->
<!--[endif]-->
Launcher的界面的rootview是DragLayer,它是一個FrameLayout,在它上面workspace(應該說是celllayout)佔了絕大部分的空間,celllayout的參數文件是workspace_screen.xml。workspace既是一個DropTarget又是一個DragSource,能夠從AllAppGridView中拖出應用程序放在它上面,也能夠把它裏面的item拖走刪除或者拖到bottomabr裏面去。
2.Drop& Drag模型:
DragSource:能夠拖動的對象來源的容器,在launcher中主要有AllAppGridView,workspace等。
void onDropCompleted(View target, boolean success,int x,int y);
DropTarget:能夠放置被拖動的對象的容器。在launcher中有folder,workspace,bottombar等,一個View既能夠是Dragsource也能夠是DropTarget。主要包含如下幾個接口:
boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
acceptDrop函數用來判斷dropTarget是否能夠接受item放置在本身裏面。
void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragEnter是item被拖動進入到一個dropTarget的時候的回調。
void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragOver是item在上一次位置和這一次位置所處的dropTarget相同的時候的回調。
void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragExit是item被拖出dropTarget時的回調。
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()處理。
3、幾種問題的解決方式
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的值便可。
4、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定義。