父类的View selector 影响到子view的 selector

当View点击时

微博上@弹棉花的孩子 这位同学,发表了如下微博:

那么在我印象里,给View设置selector,并且没有设置onClickListener时,父容器的点击会触发selector效果,我利用这个特性还做过不少效果,因此我认定,po主说的这个情况与MIUI无关。 为了证明MIUI是清白的,我决定简单的分析以下View点击的流程。

相关的代码肯定与View,ViewGroup的触摸事件相关,那么我们首先从ViewonTouchEvent入手:

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
28
29
30
31
32
33
/**
     * Implement this method to handle touch screen motion events.
     *
     * @param event The motion event.
     * @return True if the event was handled, false otherwise.
     */
    public boolean onTouchEvent(MotionEvent event) {
    ...
                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;
...

我们注意到,当ACTION_DOWN发生时,setPressed()会被调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Sets the pressed state for this view.
 *
 * @see #isClickable()
 * @see #setClickable(boolean)
 *
 * @param pressed Pass true to set the View's internal state to "pressed", or false to reverts
 *        the View's internal state from a previously set "pressed" state.
 */
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);
}

setPressed()方法中,refreshDrawableState()会被调用,这个方法的作用是刷新Viewselector状态,也就是显示出被点击的Drawable State,同时我们注意到,方法的最末尾调用了dispatchSetPressed(),从命名可以看出这个方法会对pressed状态做分发。

1
2
3
4
5
6
7
8
9
/**
 * Dispatch setPressed to all of this View's children.
 *
 * @see #setPressed(boolean)
 *
 * @param pressed The new pressed state
 */
protected void dispatchSetPressed(boolean pressed) {
}

View中该方法没有具体实现,但注释已经说明此时此刻,答案离我们很近了。

以下是ViewGroup对该方法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
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);
        }
    }
}

So, the answer is clear. 总的说来就是在点击了父容器之后,父容器会遍历每一个子View并设置其点击状态。

如何避免? 通过代码可以判断,如果子View本身是可以点击的,将不会受到父容器的pressedState影响,所以代码可以这么写

1
child.setClickable(true);


  

当然在xml中设置也是可以的。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值