Android子view的pressed状态受父view的clickable影响分析
android从sdk16开始,修改了onTouchEvent的ACTION_DOWN代码(Android: Child elements sharing pressed state with their parent even when duplicateParentState specified),如下:
sdk16以前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
java case MotionEvent.ACTION_DOWN: mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true);
checkForLongClick(0);
}
break;
1
| |
java
public void setPressed(boolean pressed) {
final boolean needsRefresh = pressed != ((mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED);
if (pressed) {
mPrivateFlags |= PFLAG_PRESSED;
} else {
mPrivateFlags &= ~PFLAG_PRESSED;
}
if (needsRefresh) {
refreshDrawableState();
}
dispatchSetPressed(pressed);
}
//viewgroup的实现
protected void dispatchSetPressed(boolean pressed) {
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = 0; i < count; i++) {
final View child = children[i];
// Children that are clickable on their own should not
// show a pressed state when their parent view does.
// Clearing a pressed state always propagates.
if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
child.setPressed(pressed);
}
}
}
“`
可以看出sdk16以后如果子view是!isClickable,父view是isClickable(父view如果是!isClickable都不会处理ACTION_DOWN事件),则即使在子view区域外的点击也会触发子view的pressed行为。这样改的原因不得而知,但个人觉得这不是正常期望的,UI表现上来看,父view点击任何区域子view都会触发pressed。
这种情景是存在的,比如子view是不可点,但又要设置父view可点以防点击父view会触发父父view事件。如果要处理这种情况,父view可以覆盖dispatchSetPressed方法进行重写,或者直接父view设置setEnable(false)。