編輯:關於Android編程
在ViewRootImpl的setView方法中,用戶的觸摸按鍵消息是體現在窗體上的,而windowManagerService則是管理這些窗口,它一旦接收到用戶對窗體的一些觸摸按鍵消息,會進行相應的動作,這種動作是需要體現在具體的view上面,在Android中,一個具體的界面是由一個Activity呈現的,而Activity中則包含了一個window,此window中又包含了一個phoneWindow,這個phoneWindow才是真正意義上的窗口,它把一個框架布局進行了一定的包裝,並提供了具體的窗口操作接口,phoneWindow中包含了一個DecorView,這個view才是包含整個Activity的ui,它將被attach到Activity主窗口中。所以說用戶觸摸按鍵的消息是由windowManagerService捕捉到然後交給phoneWindow中的DecorView進行相應的處理,而連接兩者的橋梁則是一個ViewRoot類,ViewRoot類由windowManagerService創建,其內部有一個W類,這個W類是一個binder,負責WindowManagerService的ipc調用,W接收到windowManagerService發送過來的消息後,把消息傳遞給ViewRoot,進而傳遞給ActivityThread解析做出處理,
1、在ViewRootImpl.java類的setView方法中:
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mInputChannel);在這裡把mWindow傳遞給了window,這個mWindow就是W類的一個實例 2、當具體的觸摸按鍵消息發生後,會由ViewRootImpl類中WindowInputEventReceiver這個類的onInputEvent方法負責接收,其實也就是個回調。
public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true); }
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); // Always enqueue the input event in order, regardless of its time stamp. // We do this because the application or the IME may inject key events // in response to touch events and we want to ensure that the injected keys // are processed in the order they were received and we cannot trust that // the time stamp of injected events are monotonic. QueuedInputEvent last = mFirstPendingInputEvent; if (last == null) { mFirstPendingInputEvent = q; } else { while (last.mNext != null) { last = last.mNext; } last.mNext = q; } if (processImmediately) { // 立即處理事件 doProcessInputEvents(); } else { // 將事件放到隊列的最後 scheduleProcessInputEvents(); } }
private void deliverInputEvent(QueuedInputEvent q) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, deliverInputEvent); try { if (q.mEvent instanceof KeyEvent) { // 如果是按鍵事件,也就是back、home等按鍵 deliverKeyEvent(q); } else { // touch事件 final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { deliverPointerEvent(q); } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { deliverTrackballEvent(q); } else { deliverGenericMotionEvent(q); } } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }在deliverPointerEvent方法中,如果view對象不存在,或者沒有被添加,則這個event不會被處理,直接finishInputEvent。接著判斷action是否是MotionEvent.ACTION_DOWN,如果是,則表示觸摸方式改變了,需要告訴windowManager在本地進行處理,因為每一次的按下操作都代表了一個新的event事件的到來。然後記錄觸摸的位置,這個位置就代表了應該是哪一個view來接收這個事件,然後直接調用mView.dispatchPointerEvent(event)來分發這個事件,如果這個事件被分發下去了,則結束事件。
private void deliverPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; final boolean isTouchEvent = event.isTouchEvent(); if (mInputEventConsistencyVerifier != null) { if (isTouchEvent) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } else { mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); } } // If there is no view, then the event will not be handled. if (mView == null || !mAdded) { // view對象為空,或者沒有被添加,這個事件就不會被處理 finishInputEvent(q, false); return; } // Translate the pointer event for compatibility, if needed. if (mTranslator != null) { mTranslator.translateEventInScreenToAppWindow(event); } // Enter touch mode on down or scroll. final int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { // 如果是MotionEvent.ACTION_DOWN // 如果是如果觸摸方式改變,告訴wm,在本地進行處理 // 每一次的按下操作就是一個觸摸事件的改變 ensureTouchMode(true); } // Offset the scroll position. if (mCurScrollY != 0) { event.offsetLocation(0, mCurScrollY); } if (MEASURE_LATENCY) { lt.sample(A Dispatching PointerEvents, System.nanoTime() - event.getEventTimeNano()); } // Remember the touch position for possible drag-initiation. // 有可能拖拽開始,記錄觸摸的位置 if (isTouchEvent) { mLastTouchPoint.x = event.getRawX(); mLastTouchPoint.y = event.getRawY(); } // Dispatch touch to view hierarchy. // 給view的層級上view分發事件 // DecorView繼承FrameLayout也就間接繼承了ViewGroup,View // DoverView---->Activity-->PhoneWindow--->DocerView---->ViewGroup boolean handled = mView.dispatchPointerEvent(event); if (MEASURE_LATENCY) { lt.sample(B Dispatched PointerEvents , System.nanoTime() - event.getEventTimeNano()); } if (handled) { // 結束事件 finishInputEvent(q, true); return; } // Pointer event was unhandled. // 暗示事件已經被處理 finishInputEvent(q, false);在DecorView中沒有dispatchPointerEvent方法,所以調用的是View.java的dispatchPointerEvent方法中判斷具體的是哪一類的事件,然後又調用了自身的dispatchTouchEvent。
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { // 如果是觸摸事件 return dispatchTouchEvent(event); } else { // 如果是一般的移動事件 return dispatchGenericMotionEvent(event); } }
@Override public boolean dispatchTouchEvent(MotionEvent ev) { // callback就是Activity本身 final Callback cb = getCallback(); // 如果Activity不為空,並且沒有被銷毀,則調用Activity的dispatchTouchEvent // 否則調用父類的dispatchTouchEvent return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }在Activity的dispatchTouchEvent方法中,調用的是PhoneWindow的superDispatchTouchEvent的方法去分發事件,如果到最後Activity中的所有的view都不去處理這個事件時,就有Activity的OnTouchEvent來處理。
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { // 在dispatch之前做一些操作,其實什麼也沒做 onUserInteraction(); } // 調用PhoneWindow中的superDispatchTouchEvent // PhoneWindow中superDispatchTouchEvent 直接調用了mDecor的superDispatchTouchEvent // mDecore的superDispatchTouchEvent方法中直接調用super.dispatchOnTouchEvent // 也就是開始進入了viewGroup中的dispatchOnTouchEvent方法 if (getWindow().superDispatchTouchEvent(ev)) { return true; } // 當Activity中所有的View都不處理Event的時候,就用由Activity的onTouchEvent()來處理 // 通知window關閉這個touch事件 return onTouchEvent(ev); }
public boolean onTouchEvent(MotionEvent event) { if (mWindow.shouldCloseOnTouch(this, event)) { finish(); return true; } return false; }在PhoneWindow的superDispatchTouchEvent中,調用了DecorView的superDispatchTouchEvent方法,進而調用了super.dispatchTouchEvent(event)方法,也就是進入了viewGroup中開始事件的分發。
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); }ViewGroup的dispatchTouchEvent中,處理如下,如果action是MotionEvent.ACTION_DOWN,則需要重置觸摸的狀態。繼而判斷是否攔截此事件,然後遍歷所有的孩子以便找到一個可以接收此事件的孩子,如果child不存在TouchTarget中,則把事件分發給子view,這個尋找是根據view的區域來尋找的。如果childView沒有消費掉此事件,則 自己處理的事件,如果自己也沒有處理,回溯至父view處理,否則viewGroup把事件一級一級的遞歸傳遞,如果child是一個viewGroup,則重復上述的步驟,如果是view,直接調用dispatchTouchEvent方法。
public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. // ACTION_DOWN意味著touch事件的改變,所以需要把之前的TouchTargets和TouchState都clear掉,mFirstTouchTarget = null cancelAndClearTouchTargets(ev); // 重置觸摸的狀態 resetTouchState(); } // Check for interception. final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { // 攔截事件,默認返回false,表示不攔截,如果攔截了就不繼續往下面執行了 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; } // Check for cancelation. final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int actionIndex = ev.getActionIndex(); // always 0 for down final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); final int childrenCount = mChildrenCount; if (childrenCount != 0) { // Find a child that can receive the event. // Scan children from front to back. // 遍歷所有的孩子,以便找到一個可以接收這個事件的孩子 // 某個區域內的孩子 final View[] children = mChildren; final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); final boolean customOrder = isChildrenDrawingOrderEnabled(); for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = children[childIndex]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; } // 判斷child是否在TouchTarget中 newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // 存在 // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); // child不存在TouchTarget中,則調用dispatchTransformedTouchEvent // 把event分發給子view,這裡並不是做處理,應該就是找到touch區域 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } } if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } } // Dispatch to touch targets. if (mFirstTouchTarget == null) { // mFirstTouchTarget 為空,表示childview沒有將此事件消費掉,則自己處理這個event // 如果viewGroup自己也沒有處理,則回溯到父view進行處理 // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; // viewGroup把事件遞歸傳遞,如果child是一個gourp,則重復上述步驟 // 如果是view,則直接調用dispatchTouchEvent方法 if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // Update list of touch targets for pointer up or cancel, if needed. if (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { resetTouchState(); } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { final int actionIndex = ev.getActionIndex(); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; }在View.java中的 dispatchTouchEvent方法中,如果已經注冊了listener監聽器並且是enable的,並且監聽器的onTouch返回true,則onTouchEvent不會被調用。
public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { // 要想執行onTouchEvent方法上述三個條件只要一個不滿足就可以了 // return true; } if (onTouchEvent(event)) { return true; } // 如果所有的View都不處理TouchEvent,最後由Activity來處理 } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; }
public boolean onTouchEvent(MotionEvent event) { if (mWindow.shouldCloseOnTouch(this, event)) { finish(); return true; } return false; }
手機上輸入中文相當容易,但是在靠譜助手這個電腦上的安卓模擬器要怎麼輸入中文呢?電腦上直接輸入中文根本沒反應啊?靠譜助手模擬器輸入中文需要安裝拼音輸入法。經小
從2012年自學Android開始,到現在第4個年頭了,期間一直沒接觸正規的Android項目,加上這幾年一直忙.NET項目,導致去年有兩單Android的私活沒底氣接,
通過本次小Demo我學到了:1、ListView的小小的一個分頁功能2、加深了對自定義控件的理解3、對ListView的優化4、對BaseAdapter的使用5、自定義A
下面是效果圖 目錄工程如下: 具體實現以及寫的過程中遇到的問題第一步:建立數據庫,像這種比較繁多的數據,可以用execl表格來做,然後Na