android_launcher的源码详细分析和壁纸修改 .
一、主要文件和類
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的定義等)的類型定義。
?
二、主要模塊
1.界面模型:
| ? | |
| ? | ? |
?
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()處理。
?
?
三、幾種問題的解決方式
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定義。
解決壁紙顯示不完整的問題:
問題在于Launcher.java里面的setWallpaperDimension方法,它檢查橫豎屏時將寬和高搞反了,所以造成設置壁紙尺寸時
wpm.suggestDesiredDimensions總是顯示不正常。
壁紙分為動態和靜態兩種:
如果只需要修改默認靜態壁紙,替換frameworks/base/core/res/res/drawable/default_wallpaper.jpg即可,或者在源碼中修改對應default_wallpaper地址.
修改動態壁紙:在frameworks/base/core/res/res/values目錄找到Config.xml文件,最后一行有定義:
<string name="default_wallpaper_component">null</string>?
將其修改為對應的live wallpaper的啟動component即可。
靜態的圖片壁紙和live wallpaper是兩個系統,加載的時候是完全兩套機制,靜態圖片通過launcher里面的一個xml文件配置來管理系統默認靜態壁紙,live wallpaper則是通過intent機制向系統搜尋所有匹配Activity,所以,這里的默認值為null,而不是那個系統默認靜態壁紙的地址,系統是讀config.xml文件的設置,若為null才繼續查找靜態壁紙配置內容.
總結
以上是生活随笔為你收集整理的android_launcher的源码详细分析和壁纸修改 .的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android编译系统学习 .
- 下一篇: rk4.0系统鼠标图标更换