I am using a MotionLayout with a scene-xml:
<
To setup onClick action on the view, use:
android:onClick="handleAction"
inside the MotionLayout file, and define "handleAction" in your class.
@muetzenflo's response is the most efficient solution I've seen so far for this problem.
However, only checking the Event.Action
for MotionEvent.ACTION_MOVE
causes the MotionLayout
to respond poorly. It is better to differentiate between movement and a single click by the use of ViewConfiguration.TapTimeout
as the example below demonstrates.
public class MotionSubLayout extends MotionLayout {
private long mStartTime = 0;
public MotionSubLayout(@NonNull Context context) {
super(context);
}
public MotionSubLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MotionSubLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if ( event.getAction() == MotionEvent.ACTION_DOWN ) {
mStartTime = event.getEventTime();
} else if ( event.getAction() == MotionEvent.ACTION_UP ) {
if ( event.getEventTime() - mStartTime <= ViewConfiguration.getTapTimeout() ) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
}
With the help of this great medium article I figured out that MotionLayout is intercepting click events even though the motion scene only contains an OnSwipe transition.
So I wrote a customized MotionLayout to only handle ACTION_MOVE
and pass all other touch events down the View tree. Works like a charm:
/**
* 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
}
}