launcher修改–launcher架構解析

來源:互聯網
上載者:User

        要想做好launcher,對其基本的架構瞭解是必須的,在這篇文章裡,簡單的介紹下launcher中類的構成和架構,首先,這篇文章中很多資源均從互連網中獲得,感謝網友們的無私奉獻,大家都共用,進步才會更快。

第一步,先看兩張從網上找來的launcher的架構圖。

第二張:

通過這兩張圖,簡單的說下:

整個launcher,準確來說應該是homescreen更為合適,是一個包含三個child view的FrameLayout(com.android.launcher.DragLayer)。

第一個child就是案頭com.android.launcher.Workspace。這個案頭又包含5個child。每個child就對應一個桌 面。這就是你在Android上看到的五個案頭。每個案頭上可以放置下列對象:應用捷徑,appwidget和folder。(第一張圖應該是以前版本的,在2.2以前的版本應該是只有三個案頭,在2.3中,有五個案頭)

第二個child是一個SlidingDrawer控制項,這個控制項由兩個子控制群組成。一個是 com.android.launcher.HandleView,就是Android案頭下方的把手,當點擊這個把手時,另一個子控 件,com.android.launcher.AllAppsGridView就會彈出,這個子控制項列出系統中當前安裝的所有類型為 category.launcher的Activity。

第三個child是com.android.launcher.DeleteZone。當使用者在案頭上長按一個widget時,把手位置就會出現一個垃圾桶形狀的控制項,就是這個控制項。(其實在2.2以後的版本中,在左下方和右下方添加了頁面標記,來告訴使用者當前在哪個案頭,其代碼在launcher.xml代碼中,

<ImageView        android:id="@+id/previous_screen"        android:layout_width="93dip"        android:layout_height="@dimen/button_bar_height"        android:layout_gravity="bottom|left"        android:layout_marginLeft="6dip"        android:scaleType="center"        android:src="@drawable/home_arrows_left"                android:onClick="previousScreen"        android:focusable="true"        android:clickable="true" />    <ImageView        android:id="@+id/next_screen"        android:layout_width="93dip"        android:layout_height="@dimen/button_bar_height"        android:layout_gravity="bottom|right"        android:layout_marginRight="6dip"        android:scaleType="center"        android:src="@drawable/home_arrows_right"        android:onClick="nextScreen"        android:focusable="true"        android:clickable="true" />

下面就是launcher中主要類的簡介:
AddAdapter:添加案頭元素的適配器,維護了live fold  , widget , shortcut , wallpaper 4個ListItem , 長按案頭會顯示該列表
AllAppsGridView:Icon列表的的主介面,繼承gridView。

ApplicationInfo:一個可開機應用。

ApplicationsAdapter:gridview的adapter。

BubbleTextView:一個定製了的textview,主要用於顯示應用表徵圖。

DeleteZone:luancher的刪除地區,繼承ImageView。在平時是出於隱藏狀態,在將item長按拖動的時候會顯示出來,如果將item拖動到刪除框位置時會刪除item。   DeleteZone實現了DropTarget和DragListener兩個介面。

DragController:拖動控制介面。為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。

DragLayer:整個launcher的父節點,繼承FrameLayout,實現介面DrayController,是內部支援拖拽的viewgroup。DragLayer實際上也是一個抽象的介面,用來處理拖動和對事件進行初步處理然後按情況分發下去,角色是一個controller。它首先用onInterceptTouchEvent(MotionEvent)來攔截所有的touch事件,如果是長按item拖動的話不把事件傳下去,直接交由onTouchEvent()處理,這樣就可以實現item的移動了,如果不是拖動item的話就把事件傳到目標view,交有目標view的事件處理函數做相應處理。如過有要對事件的特殊需求的話可以修改onInterceptTouchEvent(MotionEvent)來實現所需要的功能。

DragSource:拖動源介面,定義了void onDropCompleted(View target, boolean success)。

DropTarget:拖動目標,定義很多拖動過程需要的方法:onDrop,onDragEnter,onDragOver,onDragExit,acceptDrop。

FastBitmapDrawable:工具

Folder:Icons的集合

FolderIcon:出現在workspace的icon 代表了一個folder

FolderInfo: ItemInfo子類

HandleView:launcher抽屜的開關,不過在android2.2已經沒用抽屜了。

InstallShortcutReceiver,UninstallShortcutReceiver:一個broadcastrecier

ItemInfo:代表Launcher中一個Item(例如folder)對item的抽象,所有類型item的父類,item包含的屬性有id(標識item的id),cellX(在橫向位置上的位置,從0開始),cellY(在縱向位置上的位置,從0開始) ,spanX(在橫向位置上所佔的單位格),spanY(在縱向位置上所佔的單位格),screen(在workspace的第幾屏,從0開始),itemType(item的類型,有widget,search,application等),container(item所在的)。

Launcher:整個launcher的程式的入口,代碼量最大的一個檔案。

LauncherApplication:在VM中設定參數

LauncherAppWidgetHost,LauncherAppWidgetHostView,:Widget相關

LauncherModel: MVC中的M,裡面有許多封裝的對資料庫的操作。包含幾個線程,其中最主要的是ApplicationsLoader和DesktopItemsLoader。ApplicationsLoader在載入所有應用程式時使用,DesktopItemsLoader在載入workspace的時候使用。其他的函數就是對資料庫的封裝,比如在刪除,替換,添加程式的時候做更新資料庫和UI的工作。

LauncherProvider:launcher的資料庫,一個contentprovider裡面儲存了案頭的item的資訊。在建立資料庫的時候會loadFavorites(db)方法,loadFavorites()會解析xml目錄下的default_workspace.xml檔案,把其中的內容讀出來寫到資料庫中,這樣就做到了案頭的預製。

LauncherSettings:設定相關的工具,資料庫項的字串定義,另外在這裡定義了container的類型,還有itemType的定義,除此還有一些特殊的widget(如search,clock的定義等)的類型定義。

LiveFolder,LiveFolderAdapter,LiveFolderIcon,LiveFolderInfo: livefolder相關

Search: 搜尋

UserFolder,UserFolderInfo:檔案夾包含applications,shortcuts

Utilities:小工具

WallpaperChooser:選擇wallpaper的activity

Workspace:整個介面layout,幾個視窗就是他下面的子節點。

widget : 代表啟動的widget執行個體,例如搜尋

在案頭中,有一下四種類型的對象:
1. ITEM_SHORTCUT,應用捷徑,對應實現布局檔案R.layout.application
2. ITEM_APPWIDGET,app widget  案頭組件
3. ITEM_LIVE_FOLDER,檔案夾

     --UserFolderInfo 對應實現布局檔案R.layout.folder_icon
     --LiveFolderInfo 對應實現布局檔案R.layout.live_folder_icon
4. ITEM_WALLPAPER,牆紙。

下面,我們詳細的來說一下launcher裡面的詳細功能:

1.DragLayer--DragLayer繼承FrameLayout,並在此基礎上組合了DragController實現拖放功能,DragLayer主要監聽下面兩個使用者事件onInterceptTouchEvent和onTouchEvent
並交給DragController進行處理,DragController根據是否在拖放中等資訊控制控制項拖放過程處理。DragLayer 是Launcher這個activity的頂層view,其實在Launcher2這個應用只有Laucher.java這麼一個activity。

 

2.DeleteZone--

<com.android.launcher2.DeleteZone        android:id="@+id/delete_zone"        android:layout_width="@dimen/delete_zone_size"        android:layout_height="@dimen/delete_zone_size"        android:paddingTop="@dimen/delete_zone_padding"        android:layout_gravity="bottom|center_horizontal"        android:scaleType="center"        android:src="@drawable/delete_zone_selector"        android:visibility="invisible"        launcher:direction="horizontal"        />

在launcher.xml中,可以發現,DeleteZone預設是不顯示的android:visibility="invisible",但是我們每次開始拖放表徵圖的時候DeleteZone就顯示了,這個功能是如何?的呢?在代碼中可以發現DeleteZone實現了DragController.DragListener介面,

public class DeleteZone extends ImageView implements DropTarget, DragController.DragListener 

DragListener提供兩個介面方法,
onDragStart:隱藏把手,顯示DeleteZone
onDragEnd:顯示把手,隱藏DeleteZone

在DeleteZone中,看一下代碼:

public void onDragStart(DragSource source, Object info, int dragAction) {        final ItemInfo item = (ItemInfo) info;        if (item != null) {            mTrashMode = true;            createAnimations();            final int[] location = mLocation;            getLocationOnScreen(location);            mRegion.set(location[0], location[1], location[0] + mRight - mLeft,                    location[1] + mBottom - mTop);            mDragController.setDeleteRegion(mRegion);            mTransition.resetTransition();            startAnimation(mInAnimation);            mHandle.startAnimation(mHandleOutAnimation);            setVisibility(VISIBLE);        }    }    public void onDragEnd() {        if (mTrashMode) {            mTrashMode = false;            mDragController.setDeleteRegion(null);            startAnimation(mOutAnimation);            mHandle.startAnimation(mHandleInAnimation);            setVisibility(GONE);        }    }

分別在開始DragController開始拖放和結束拖放的時候被調用.
另外DeleteZone實現了DropTarget介面的onDrop方法

public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,            DragView dragView, Object dragInfo) {        final ItemInfo item = (ItemInfo) dragInfo;        if (item.container == -1) return;        if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {            if (item instanceof LauncherAppWidgetInfo) {                mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);            }        } else {            if (source instanceof UserFolder) {                final UserFolder userFolder = (UserFolder) source;                final UserFolderInfo userFolderInfo = (UserFolderInfo) userFolder.getInfo();                // Item must be a ShortcutInfo otherwise it couldn't have been in the folder                // in the first place.                userFolderInfo.remove((ShortcutInfo)item);            }        }        if (item instanceof UserFolderInfo) {            final UserFolderInfo userFolderInfo = (UserFolderInfo)item;            LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);            mLauncher.removeFolder(userFolderInfo);        } else if (item instanceof LauncherAppWidgetInfo) {            final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;            final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();            if (appWidgetHost != null) {                final int appWidgetId = launcherAppWidgetInfo.appWidgetId;                // Deleting an app widget ID is a void call but writes to disk before returning                // to the caller...                new Thread("deleteAppWidgetId") {                    public void run() {                        appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);                    }                }.start();            }        }        LauncherModel.deleteItemFromDatabase(mLauncher, item);    }

當把表徵圖拖放到DeleteZone,就會調用DeleteZone,實現的onDrop方法對應用表徵圖進行刪除處理。

 

3.螢幕左右移動按鈕,就是使用的ImageView,

<ImageView        android:id="@+id/previous_screen"        android:layout_width="93dip"        android:layout_height="@dimen/button_bar_height"        android:layout_gravity="bottom|left"        android:layout_marginLeft="6dip"        android:scaleType="center"        android:src="@drawable/home_arrows_left"                android:onClick="previousScreen"        android:focusable="true"        android:clickable="true" />    <ImageView        android:id="@+id/next_screen"        android:layout_width="93dip"        android:layout_height="@dimen/button_bar_height"        android:layout_gravity="bottom|right"        android:layout_marginRight="6dip"        android:scaleType="center"        android:src="@drawable/home_arrows_right"        android:onClick="nextScreen"        android:focusable="true"        android:clickable="true" />

注意三點,

--1.案頭左右移動時Drawable的變換,變換表徵圖列表可查看home_arrows_right.xml
,ImageView通過把drawable傳遞給worksapce,當案頭切換時通過調用Drawable.setLevel()方法實現不同表徵圖顯示。
--2.當點擊實現左右案頭切換,查看上面的布局檔案中android:onClick="previousScreen",該屬性定義了一個 onClick事件響應函數,在Launcher.java中的788行。

@SuppressWarnings({"UnusedDeclaration"})    publicvoid previousScreen(View v) {        if(!isAllAppsVisible()) {            mWorkspace.scrollLeft();        }    }

--3.在(Launcher.setupViews)中添加了長按事件OnLongClickListener有當長按會執行launcher.onlongclick方法,方法執行顯示5個案頭的預覽微縮圖顯示。

 

4.RelativeLayout--android:id="@+id/all_apps_button_cluster",如前面右邊灰色豎狀條,它是一個相對布局對象,上面承載了三個view
中間是一個HandleView,是一個進入allappview的按鈕,HandleView的左面是撥號,右面是瀏覽器兩個ImageView。
        --HandleView
                --1.點擊事件 傳遞給Launcher.onClick進行處理 顯示應用菜單view
                --2.長按事件 傳遞給Launcher.onLongClick進行處理,方法執行顯示5個案頭的預覽微縮圖顯示
        --撥號或者瀏覽器
                --onClick響應:android:onClick="launchHotSeat"

5.Workspace--使用者案頭包括5個workspace_screen,launcher:defaultScreen="2"在前面已經說過,表示預設案頭是第三個。

workspace繼承了viewgroup,5個workspace_screen作為它的child,值得注意它只接收CellLayout類型的child,workspace重寫了addview函數,添加了非CellLayout的child將拋異常
--Workspace長按事件由launcher.onLongClick來監聽
--Workspace實現了DropTarget, DragSource兩個介面,意味著Workspace既是拖放源,又是拖放目的地
--Workspace實現DragScroller介面,DragScroller介面提供兩個方法
    void scrollLeft()和void scrollRight()在拖放過程被DragController調用實現案頭的左右滾動。
  --CellLayout Workspace下的一個案頭布局,CellLayout也是ViewGroup的子類,

  Workspace下有5個CellLayout順序排列,Workspace下布局檔案:android:scrollbars="horizontal"決定了5個CellLayout排列是橫向還是縱向的

<com.android.launcher2.CellLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:hapticFeedbackEnabled="false"    launcher:cellHeight="@dimen/workspace_cell_height"//每個cell的高度    launcher:longAxisStartPadding="8dip"//cell距離父view CellLayout左邊距    launcher:longAxisEndPadding="78dip"//cell距離父view CellLayout右邊距    launcher:shortAxisStartPadding="0dip"//cell距離父view CellLayout上邊距    launcher:shortAxisEndPadding="0dip"//cell距離父view CellLayout下邊距    launcher:shortAxisCells="4"//CellLayout cells行數    launcher:longAxisCells="4"//CellLayout cells列數 />

當縱向的控制項不夠cells排列時,cell將產生重疊,橫向不產生重疊,橫向每個cell間隔至少為0     

  --CellLayout覆蓋重新實現了onMeasure方法,和onlayout方法,它限定了child view 使用的布局參數類型為CellLayout.LayoutParams因此企圖通過修改

        workspace_screen.xml來改變它的案頭布局是不會得以成功的,你必須修改CellLayout類
        --CellLayout.LayoutParams說明,CellLayout.LayoutParams下有幾個成員需要說明一下
                --cellX:該child view佔用的第幾列的cell(若橫向佔用多個cell,表示最左邊的cellx)
                --cellY: 該child view佔用的第幾行的cell(若縱向佔用多個cell,表示最上邊的celly)
                --cellHSpan:橫向跨越的列數
                --cellVSpan: 縱向跨越行數
                --isDragging:該child是否正在被拖動
                --regenerateId:是否重建view id

最後以網上淘來的launcher類的關係來結束這篇文章:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.