原本想分析AppsCustomizePagedView類,不過今天忽然接到一個臨時任務。客戶反饋說機器界面的圖標很難點擊啓動程序,常常點擊了沒有反應,Boss說要優先解決這問題。沒辦法,只能看看是怎麼回事。今天分析一下Launcher啓動APP的過程。從用戶點擊到程序啓動的流程,下面針對WorkSpace上的快捷圖標點擊啓動流程進行分析。(若是分不清WorkSpace是什麼或者不知道快捷方式和其餘圖標區別,請看我前面的Launcher分析文章)html
PS:新建的QQ羣,有興趣能夠加入一塊兒討論:Android羣:322599434java
下面咱們先看看Launcher啓動APP的大概流程:app
(鑑於不少轉載文章的人把做者信息都刪除了,只能在圖片上加入水印,不會給你們閱讀形成影響)函數
上面就是手指觸摸屏幕開始,到點擊響應的流程。Launcher裏面由於有滑動、拖曳、點擊等手勢操做,因此區分了不少流程判斷。最後調用Launcher.java裏面的onClick()方法響應點擊,啓動程序。下面咱們針對關鍵流程作分析。佈局
一、WorkSpace觸摸post
前面咱們分析Launcher的配置文件時就說過,Launcher外面的界面主要就是經過WorkSpace來顯示的。它是一個ViewGroup的自定義類。下面咱們先看看WorkSpace的onInterceptTouchEvent作了什麼。this
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onInterceptTouchEvent(MotionEvent ev) { if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent enter");
//對ACTION_DOWN和ACTION_UP作一些標記處理。 switch (ev.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mXDown = ev.getX(); mYDown = ev.getY(); break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: if (mTouchState == TOUCH_STATE_REST) { final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); if (!currentPage.lastDownOnOccupiedCell()) { onWallpaperTap(ev); } } } if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent call super InterceptTouch"); //調用父類的onInterceptTouchEvent,這裏是調用了SmoothPagedView return super.onInterceptTouchEvent(ev); }
在WorkSpace裏面並無攔截消息,主要是調用父類的方法,也就是PagedView的onInterceptTouchEvent()方法。由於WorkSpace的直接父類SmoothPagedView也是繼承了PagedView類,有關PagedView的onInterceptTouchEvent()方法,我在前面的文章已經分析過。這裏不作多說,不瞭解的朋友能夠看看(點這裏)。url
二、CellLayout的onInterceptTouchEvent()方法spa
CellLayout也是一個繼承了ViewGroup的類,主要用來顯示桌面控件。剛開始分析Launcher的時候,咱們分析配置文件的時候也說過WorkSpace就是由5個CellLayout組成的。所以咱們點擊WorkSpace裏面圖標的時候,天然會調用CellLayout裏面的東西。CellLayout裏面的onInterceptTouchEvent()作的事情並很少:code
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onInterceptTouchEvent(MotionEvent ev) { if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent enter"); final int action = ev.getAction(); if (action == MotionEvent.ACTION_DOWN) {
//清除全部觸摸標記 clearTagCellInfo(); } //mInterceptTouchListener是WorkSpace的onTouch方法回調,下面會分析
//掌握這點很重要,由於onInterceptTouchEvent的返回值直接決定了觸摸事件的傳遞方向 mythou if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) { //截斷Touch傳輸,直接處理 ,返回true會直接調用onTouchEvent處理。 mythou if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent return true Intercept msg"); return true; } if (action == MotionEvent.ACTION_DOWN) { setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY()); } if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent return false");
//注意這裏返回的是false ,onInterceptTouchEvent的返回值決定了觸摸事件的傳遞方式。mythou return false; }
這裏須要注意的是onInterceptTouchEvent()的返回值,若是是返回true,觸摸消息會直接被CellLayout的onTouchEvent()處理,通常點擊啓動程序返回的都是false,由於消息最後是TextView處理(workspace上的快捷方式都是TextView的子類)。Android的觸摸消息傳遞機制和消息攔截機制,須要好好理解好才能明白Launcher的觸摸事件處理。由於我的以爲Launcher裏面事件處理層次仍是比較多,若是對Android的事件傳遞機制理解不深,就很難理解Launcher的事件處理。若是對這方面不瞭解的朋友,能夠查查網上相關資料。後面有空我也會寫一篇深刻分析Android觸摸事件處理的文章。
三、WorkSpace的onTouch()事件
WorkSpace裏面還處理了onTouch事件,這裏的onTouch事件是由於WorkSpace使用了View.OnTouchListener接口,因此實現了onTouch事件的回調。
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onTouch(View v, MotionEvent event)
{
return (isSmall() || !isFinishedSwitchingState());
}
onTouch裏面其實沒作什麼事情,就是根據兩個方法返回值,判斷onTouch是返回false仍是返回true,返回值是什麼決定了觸摸事件的傳遞方向,上面已經說過了。這裏的onTouch是給WorkSpace裏面的裝載的View使用的,也就是CellLayout。咱們能夠看看每次調用WorkSpace的onChildViewAdded()方法,會設置CellLayout的onTouch監聽器。
//Edited by mythou
//http://www.cnblogs.com/mythou/
public void onChildViewAdded(View parent, View child) { if (!(child instanceof CellLayout))
{
throw new IllegalArgumentException("A Workspace can only have CellLayout children."); }
CellLayout cl = ((CellLayout) child);
//設置onTouch的監聽器,CellLayout的onInterceptTouchEvent()方法會根據onTouch監聽器判斷是否須要攔截onTouch事件。 cl.setOnInterceptTouchListener(this); cl.setClickable(true); cl.setContentDescription(getContext().getString( R.string.workspace_description_format, getChildCount())); }
上面第二點CellLayout的onInterceptTouchEvent()方法的分析裏面,return ture的時候,判斷的依據mInterceptTouchListener和mInterceptTouchListener.onTouch()方法的返回值就是從這裏設置的,他們也就是調用了WorkSpace的onTouch方法。
四、BubbleTextView
BubbleTextView是繼承了TextView的子類,在Launcher裏面全部的WorkSpace的快捷方式都是使用BubbleTextView繪畫的,它的做用至關於一個按鈕。在分析BubbleTextView的觸摸響應前,咱們先看看Launcher裏面如何建立快捷方式。前面的文章我分析過Launcher加載界面數據的流程,不過沒有仔細分析如何建立相關對象,下面咱們先看看WorkSpace的快捷方式如何建立:
//Edited by mythou
//http://www.cnblogs.com/mythou/
View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info)
{
BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
favorite.applyFromShortcutInfo(info, mIconCache);
//注意這裏設置了onClickListener OWL favorite.setOnClickListener(this); return favorite; }
上面就是建立快捷方式的方法,這裏面咱們須要注意的是,BubbleTextView調用了SetOnClickListener()方法,設置點擊監聽器。監聽器的回調函數就是Launcher.java類裏面的onCLick方法。由於Launcher類繼承了View.OnClickListener接口,固然Launcher裏面還繼承了其餘的接口,例如觸摸、長按的Listener。下面咱們先看看BUbbleTextView的onTouchEvent方法:
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onTouchEvent(MotionEvent event)
{ // 調用TextView的onToucEvent方法,主要是獲取返回值,這個返回值做用很大,會響應點擊事件的回調。 // 能夠把這返回值打印出來看看。當點擊啓動程序時,這裏確定會返回true。
//緣由上面已經說了不少次,這裏再也不囉嗦 OWL
boolean result = super.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //下面這裏主要是根據觸摸狀態值,作快捷方式的狀態顯示,例如是否須要顯示按下狀態。 if (mPressedOrFocusedBackground == null) { mPressedOrFocusedBackground = createGlowingOutline( mTempCanvas, mPressedGlowColor, mPressedOutlineColor); } if (isPressed())
{ mDidInvalidateForPressedState = true; setCellLayoutPressedOrFocusedIcon(); } else { mDidInvalidateForPressedState = false; } mLongPressHelper.postCheckForLongPress(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!isPressed()) { mPressedOrFocusedBackground = null; } //這裏根據狀態判斷是否取消長按的觸摸 mLongPressHelper.cancelLongPress(); break; }
//返回值十分重要,直接影響是否會掉用onCLick方法。 return result; }
上面就是BubbleTextView的onTouchEvent的方法,上面強調不少次返回值,這裏若是是點擊流程,會返回true。而後執行onClick方法。
五、onClick()方法
最後點擊會調用Launcher裏面的onClick方法,無論你是點擊快捷方式、文件夾、仍是AllAPP的按鈕,都是從這裏響應回調。
//Edited by mythou //http://www.cnblogs.com/mythou/ public void onClick(View v) { //.......... Object tag = v.getTag(); if (tag instanceof ShortcutInfo) { //這裏點擊打開咱們上面分析的快捷方式圖標。打開的方式也是調用startActivity,
// 只是這裏調用的startActivitySafely對startActivity進行了封裝 mythou
final Intent intent = ((ShortcutInfo) tag).intent; int[] pos = new int[2]; v.getLocationOnScreen(pos); intent.setSourceBounds(new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight())); boolean success = startActivitySafely(v, intent, tag); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; mWaitingForResume.setStayPressed(true); }
//文件夾點擊響應 } else if (tag instanceof FolderInfo) { if (v instanceof FolderIcon) { FolderIcon fi = (FolderIcon) v; handleFolderClick(fi); }
//AllAPP按鈕點擊響應 } else if (v == mAllAppsButton) { if (isAllAppsVisible()) { showWorkspace(true); } else { onClickAllAppsButton(v); } } }
上面給出了最後點擊啓動快捷方式圖標的方式,最後調用的也是startActivity,不過Launcher對它進行了封裝,裏面加入異常處理,包括是否啓動成功等操做。並且裏面分開了startActivity和startActivityForResult兩種方式。可是最後啓動的時候,仍是調用了咱們平時使用的啓動Activity的方法。下面給出我加入打印消息後的代碼流程跟蹤截圖。
六、總結
Launcher裏面有關點擊啓動程序的方法流程就是上述所說的那樣,固然,裏面有些方法判斷執行了不少方法,這裏不作詳細分析,又興趣的朋友能夠跟我同樣加入一些打印消息,跟蹤Log分析代碼流程。裏面還有一個至關重要的部分就是如何區分是頁面滑動仍是按鈕點擊。文章開頭說的問題就是由於把點擊事件判斷爲頁面滑動,致使常常點擊按鈕的時候執行了滑動頁面的操做,致使用戶以爲點擊按鈕都沒反應。我最後的修改方法是修改了判斷是否進行頁面滑動的方法,解決了該問題。
今天就說到這裏,這個流程分析我的以爲仍是有點複雜,由於涉及了Android觸摸事件的傳遞方式,若是不瞭解這個,沒法理解Launcher是如何處理這些事件的。最後歡迎各位轉載Launcher的分析文章,但請不要修改文章內容,並附上原文連接和做者信息。
Launcher分析系列文章:
Edited by mythou
原創博文,轉載請標明出處:http://www.cnblogs.com/mythou/p/3187881.html