MotionLayout:如何在特定视图上限制 'OnSwipe' 事件而不是在整个屏幕上限制 'OnSwipe'
MotionLayout: How to Limit the 'OnSwipe' event on a specific view instead of 'OnSwipe' on whole screen
我有一个带有以下内容的动画布局'layoutDescription'(场景文件)
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<OnSwipe
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="left"
motion:dragDirection="dragLeft" />
</Transition>
<ConstraintSet android:id="@+id/start">
<!-- ConstraintSet for starting -->
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<!-- ConstraintSet for end -->
</ConstraintSet>
我在这里的期望是,如果用户将 'button' 向左侧拖动,那么它应该执行过渡,但实际发生的是当用户从屏幕中的任何位置向左拖动(向左滑动)时,这个过渡被处决!
如何限制动画仅在拖动指定视图时起作用?
(我正在使用 "constraintlayout:2.0.0-beta2")
更新的答案:使用 ConstraintLayout 2.0.0 alpha-5,您可以简单地使用
app:touchRegionId
在您的 "OnSwipe" 中有同样的行为。也可以将 'app:targetId' 用于 "OnClick" 用法。
感谢@RuslanLeshchenko
原回答:
我最终通过扩展 MotionLayout 和覆盖 onTouchEvent 将事件转发到所需视图来设法做到这一点。
通过扩展 MotionLayout 创建自定义布局
class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) : MotionLayout(context, attributeSet) {
private var viewToDetectTouch1 :View? = null
private var viewToDetectTouchRes :Int = -1
private val viewRect = Rect()
private var touchStarted = false
init {
setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
}
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
}
override fun onTransitionChange(p0: MotionLayout, p1: Int, p2: Int, p3: Float) {
}
override fun onTransitionCompleted(p0: MotionLayout, p1: Int) {
touchStarted = false
}
})
context.theme.obtainStyledAttributes(
attributeSet,
R.styleable.SingleViewTouchableMotionLayout,
0, 0).apply {
try {
viewToDetectTouchRes = getResourceId(
R.styleable.SingleViewTouchableMotionLayout_viewToDetectTouch, 0)
} finally {
recycle()
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewToDetectTouch1 = (parent as View).findViewById<View>(viewToDetectTouchRes)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
touchStarted = false
return super.onTouchEvent(event)
}
}
if (!touchStarted) {
viewToDetectTouch1?.getHitRect(viewRect)
touchStarted = viewRect.contains(event.x.toInt(), event.y.toInt())
}
return touchStarted && super.onTouchEvent(event)
}
}
并添加自定义属性,该属性将用于声明将接收事件以触发 Transition/animation 的视图。
将这些样式添加到 res 文件夹中的 attrs.xml (res/values/attrs.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SingleViewTouchableMotionLayout">
<attr name="viewToDetectTouch" format="reference" />
</declare-styleable>
</resources>
就是这样!!
这样我们就可以在指定视图上归档 MotionLayout OnSwipe。
布局 XML 文件现在将如下所示:
<your_package.SingleViewTouchableMotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/motion_file"
app:viewToDetectTouch="@+id/triggerView"
>
<!-- Notice that 'app:viewToDetectTouch' hold the id of the view we interested -->
<!-- View we interested -->
<View
android:id="@+id/triggerView"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:background="@color/colorBlack"
/>
</your_package.SingleViewTouchableMotionLayout>
实际上对于 onSwipe
你可以使用 touchRegionId
和 targetId
对于 OnClick
我有一个带有以下内容的动画布局'layoutDescription'(场景文件)
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<OnSwipe
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="left"
motion:dragDirection="dragLeft" />
</Transition>
<ConstraintSet android:id="@+id/start">
<!-- ConstraintSet for starting -->
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<!-- ConstraintSet for end -->
</ConstraintSet>
我在这里的期望是,如果用户将 'button' 向左侧拖动,那么它应该执行过渡,但实际发生的是当用户从屏幕中的任何位置向左拖动(向左滑动)时,这个过渡被处决!
如何限制动画仅在拖动指定视图时起作用?
(我正在使用 "constraintlayout:2.0.0-beta2")
更新的答案:使用 ConstraintLayout 2.0.0 alpha-5,您可以简单地使用
app:touchRegionId
在您的 "OnSwipe" 中有同样的行为。也可以将 'app:targetId' 用于 "OnClick" 用法。
感谢@RuslanLeshchenko
原回答:
我最终通过扩展 MotionLayout 和覆盖 onTouchEvent 将事件转发到所需视图来设法做到这一点。
通过扩展 MotionLayout 创建自定义布局
class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) : MotionLayout(context, attributeSet) {
private var viewToDetectTouch1 :View? = null
private var viewToDetectTouchRes :Int = -1
private val viewRect = Rect()
private var touchStarted = false
init {
setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
}
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
}
override fun onTransitionChange(p0: MotionLayout, p1: Int, p2: Int, p3: Float) {
}
override fun onTransitionCompleted(p0: MotionLayout, p1: Int) {
touchStarted = false
}
})
context.theme.obtainStyledAttributes(
attributeSet,
R.styleable.SingleViewTouchableMotionLayout,
0, 0).apply {
try {
viewToDetectTouchRes = getResourceId(
R.styleable.SingleViewTouchableMotionLayout_viewToDetectTouch, 0)
} finally {
recycle()
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewToDetectTouch1 = (parent as View).findViewById<View>(viewToDetectTouchRes)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
touchStarted = false
return super.onTouchEvent(event)
}
}
if (!touchStarted) {
viewToDetectTouch1?.getHitRect(viewRect)
touchStarted = viewRect.contains(event.x.toInt(), event.y.toInt())
}
return touchStarted && super.onTouchEvent(event)
}
}
并添加自定义属性,该属性将用于声明将接收事件以触发 Transition/animation 的视图。 将这些样式添加到 res 文件夹中的 attrs.xml (res/values/attrs.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SingleViewTouchableMotionLayout">
<attr name="viewToDetectTouch" format="reference" />
</declare-styleable>
</resources>
就是这样!! 这样我们就可以在指定视图上归档 MotionLayout OnSwipe。
布局 XML 文件现在将如下所示:
<your_package.SingleViewTouchableMotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/motion_file"
app:viewToDetectTouch="@+id/triggerView"
>
<!-- Notice that 'app:viewToDetectTouch' hold the id of the view we interested -->
<!-- View we interested -->
<View
android:id="@+id/triggerView"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:background="@color/colorBlack"
/>
</your_package.SingleViewTouchableMotionLayout>
实际上对于 onSwipe
你可以使用 touchRegionId
和 targetId
对于 OnClick