Activity中View的加载
Activity加载布局时调用setContentView方法来加载布局。看下源码中的代码(android-23,不同版本可能存在差异)
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
找到getWindow方法
public Window getWindow() {
return mWindow;
}
找到mWindow的创建
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);
...
}
那么找到PhoneWindow的setContentView方法
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();//关键代码
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
这里关键是mContentParent创建,把view加载进去,关键代码installDecor方法
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();//关键1
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);//关键2
...
}
...
}
这里主要是mDecor和mContentParent的创建,看注释关键1、关键2代码
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
DecorView是PhoneWindow的内部类,继承FrameLayout
protected ViewGroup generateLayout(DecorView decor) {
...
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = ...
} else if(...) {
layoutResource = ...
} else {
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
到这里实际上就可以看出mDecor实际上是Activity的root view,根据不同的activity window features加载不同的系统layout布局,content view放在id为com.android.internal.R.id.content的FrameLayout中
Activity中的View
View触摸事件分发机制
| 类型 | 相关方法 | Activity | ViewGroup | View |
|---|---|---|---|---|
| 事件分发 | dispatchTouchEvent | ✔ | ✔ | ✔ |
| 事件拦截 | onInterceptTouchEvent | ✖ | ✔ | ✖ |
| 事件消费 | onTouchEvent | ✔ | ✖ | ✔ |
触摸事件首先进入Activity的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
可知先由window的superDispatchTouchEvent方法处理,返回true表示已处理,如果没有处理再由Activity的onTouchEvent方法处理,查看源码梳理大致是如下一个流程
Touch事件大致流程
重点在ViewGroup中的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
...
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//如果没有重写就是false,如果重写返回true,事件就被拦截了
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;
}
...
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
//子view按z轴从小到大排列
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//从最上层view开始遍历
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
...
//会调用child的dispatchTouchEvent方法
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//记录响应处理的child
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
...
}
...
}
...
}
...
}
...
return handled;
}
再结合View的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent event) {
...
boolean result = false;
...
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
//如果设置了mOnTouchListener优先处理
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//再由onTouchEvent处理
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
再看看View的onTouchEvent方法
public boolean onTouchEvent(MotionEvent event) {
...
final int action = event.getAction();
...
//满足任一条件event将被消费掉,返回true
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
...
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
//如果有注册将会执行mOnClickListener的onClick方法
performClick();
}
}
}
...
}
...
break;
...
}
return true;
}
return false;
}
所以综合来看,触摸事件传递有两个过程:
- 由上往下:由顶层
ViewGroup的dispatchTouchEvent往下层ViewGroup或View的dispatchTouchEvent传递,当某一层ViewGroup实现了onInterceptTouchEvent方法并返回true,事件传递终止,如果找到某个底层View执行onTouchEvent方法返回true,事件被消费掉,传递终止。 - 由下往上:底层
View的onTouchEvent方法返回false,就会执行父View的onTouchEvent方法,如果还是false,继续往其父View传递,直到返回true为止。
触摸冲突解决思路
父view A中有一个子viewB,A、B触摸事件有冲突
内部拦截
在B中重写dispatchTouchEvent方法,在需要自己处理的事件中调用getParent().requestDisallowInterceptTouchEvent(true),在自己不处理的事件中调用getParent().requestDisallowInterceptTouchEvent(false),如果DOWN事件需要处理,则A中确保onInterceptTouchEvent也会返回false(需要时修改代码)
外部拦截
确保B需要处理的事件,在A中onInterceptTouchEvent都返回false
参考《Android进阶之光》













网友评论