MotionLayout 阻止所有视图上的 ClickListener

mue*_*flo 4 android onclicklistener android-constraintlayout android-motionlayout

我正在使用带有场景 xml 的 MotionLayout:

<Transition
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    >
    <OnSwipe
        motion:touchAnchorId="@+id/v_top_sheet"
        motion:touchRegionId="@+id/v_top_sheet_touch_region"
        motion:touchAnchorSide="bottom"
        motion:dragDirection="dragDown" />
</Transition>
Run Code Online (Sandbox Code Playgroud)

2ConstraintSets只引用了 2 个视图 ID:v_notifications_containerv_top_sheet

在我的活动中,我想将一个普通的 ClickListener 设置为这个 MotionLayout 中的其他视图之一:

iv_notification_status.setOnClickListener { Timber.d("Hello") }
Run Code Online (Sandbox Code Playgroud)

执行此行,但从未触发 ClickListener。我搜索了其他帖子,但其中大多数都涉及在与motion:touchAnchorId. 这不是这里的情况。ClickListener 设置为在 MotionLayout 设置中未曾提及的视图。如果我删除该app:layoutDescription属性,则单击有效。

我也尝试使用setOnTouchListener,但它也从未被调用过。

如何在 MotionLayout 中设置单击侦听器?

mue*_*flo 7

这篇优秀的中等文章的帮助下,我发现 MotionLayout 正在拦截点击事件,即使运动场景只包含一个 OnSwipe 转换。

所以我写了一个定制的 MotionLayout 来只处理ACTION_MOVE和传递视图树下的所有其他触摸事件。奇迹般有效:

/**
 * MotionLayout will intercept all touch events and take control over them.
 * That means that View on top of MotionLayout (i.e. children of MotionLayout) will not
 * receive touch events.
 *
 * If the motion scene uses only a onSwipe transition, all click events are intercepted nevertheless.
 * This is why we override onInterceptTouchEvent in this class and only let swipe actions be handled
 * by MotionLayout. All other actions are passed down the View tree so that possible ClickListener can
 * receive the touch/click events.
 */
class ClickableMotionLayout: MotionLayout {

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
        if (event?.action == MotionEvent.ACTION_MOVE) {
            return super.onInterceptTouchEvent(event)
        }
        return false
    }

}
Run Code Online (Sandbox Code Playgroud)


小智 1

对 @TimonNetherlands 代码的小修改也适用于 Pixel4

class ClickableMotionLayout: MotionLayout {
    private var mStartTime: Long = 0
    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {

        if ( event?.action == MotionEvent.ACTION_DOWN ) {
            mStartTime = event.eventTime;
        }

        if ((event?.eventTime?.minus(mStartTime)!! >= ViewConfiguration.getTapTimeout()) && event.action == MotionEvent.ACTION_MOVE) {
            return super.onInterceptTouchEvent(event)
        }

        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)