问题
I have been trying to understand how MotionLayout works and after some trying I have a question as to how OnClick works for a View.
I have something that I tried from official examples. I tried something similar to the youtube swiping.
scene_24.xml
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000"
motion:motionInterpolator="linear">
<OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorSide="bottom"
motion:touchRegionId="@+id/top_image_container" />
<ConstraintSet android:id="@id/start">
<Constraint
android:id="@id/top_image_container"
android:layout_width="0dp"
android:layout_height="320dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@id/top_image"
android:layout_width="0dp"
android:layout_height="0dp"
motion:layout_constraintBottom_toBottomOf="@id/top_image_container"
motion:layout_constraintEnd_toEndOf="@id/top_image_container"
motion:layout_constraintStart_toStartOf="@id/top_image_container"
motion:layout_constraintTop_toTopOf="@id/top_image_container" />
<Constraint
android:id="@id/recyclerview_container"
android:layout_width="0dp"
android:layout_height="0dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/top_image_container" />
<Constraint
android:id="@id/recyclerview_front"
android:layout_width="0dp"
android:layout_height="0dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/top_image_container" />
<Constraint android:id="@+id/image_play">
<PropertySet motion:alpha="0" />
</Constraint>
<Constraint android:id="@+id/image_clear">
<PropertySet motion:alpha="0" />
</Constraint>
<Constraint
android:id="@id/bottom_nav"
android:layout_width="0dp"
android:layout_height="wrap_content"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toBottomOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="@id/end">
<Constraint
android:id="@id/top_image_container"
android:layout_width="0dp"
android:layout_height="52dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
motion:layout_constraintBottom_toTopOf="@id/bottom_nav"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent" />
<Constraint
android:id="@id/top_image"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
motion:layout_constraintBottom_toBottomOf="@id/top_image_container"
motion:layout_constraintDimensionRatio="H,1:2.5"
motion:layout_constraintStart_toStartOf="@id/top_image_container"
motion:layout_constraintTop_toTopOf="@id/top_image_container" />
<Constraint
android:id="@id/recyclerview_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
motion:layout_constraintBottom_toTopOf="@id/bottom_nav"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/top_image_container" />
<Constraint
android:id="@id/recyclerview_front"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="@id/top_image_container"
motion:layout_constraintEnd_toEndOf="@id/top_image_container"
motion:layout_constraintStart_toStartOf="@id/top_image_container"
motion:layout_constraintTop_toBottomOf="@id/top_image_container" />
<Constraint android:id="@+id/image_play">
<PropertySet motion:alpha="1" />
</Constraint>
<Constraint android:id="@id/image_clear">
<PropertySet motion:alpha="1" />
</Constraint>
<Constraint
android:id="@id/bottom_nav"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintRight_toRightOf="parent" />
</ConstraintSet>
<KeyFrameSet>
<KeyPosition
motion:curveFit="linear"
motion:framePosition="90"
motion:motionTarget="@id/top_image"
motion:percentWidth="0"
motion:percentX="0" />
<KeyPosition
motion:curveFit="linear"
motion:framePosition="90"
motion:motionTarget="@id/top_image_container"
motion:percentWidth="0" />
<KeyPosition
motion:curveFit="linear"
motion:framePosition="90"
motion:motionTarget="@id/recyclerview_container"
motion:percentWidth="0" />
<KeyAttribute
android:alpha="0"
motion:framePosition="75"
motion:motionTarget="@id/recyclerview_front" />
<KeyAttribute
android:alpha="0.10"
motion:framePosition="90"
motion:motionTarget="@id/image_clear" />
<KeyAttribute
android:alpha="0.10"
motion:framePosition="90"
motion:motionTarget="@id/image_play" />
</KeyFrameSet>
</Transition>
motion_layout_anim
:
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#444"
app:layoutDescription="@xml/scene_24"
tools:ignore="contentDescription"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/top_image_container"
android:layout_width="match_parent"
android:layout_height="320dp"
android:background="?android:attr/windowBackground"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.youtube.player.YouTubePlayerView
android:id="@+id/top_image"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="@id/top_image_container"
app:layout_constraintBottom_toBottomOf="@id/top_image_container"
app:layout_constraintStart_toStartOf="@id/top_image_container"
app:layout_constraintEnd_toEndOf="@id/top_image_container" />
<!-- <ImageView
android:id="@+id/image_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:srcCompat="@drawable/ic_play_arrow_gray_32dp"
android:alpha="0"
app:layout_constraintEnd_toStartOf="@id/image_clear"
app:layout_constraintTop_toTopOf="@id/top_image_container"
app:layout_constraintBottom_toBottomOf="@id/top_image_container"
/>
<ImageView
android:id="@+id/image_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:srcCompat="@drawable/ic_clear_gray_32dp"
android:alpha="0"
app:layout_constraintEnd_toEndOf="@id/top_image_container"
app:layout_constraintBottom_toBottomOf="@id/top_image_container"
app:layout_constraintTop_toTopOf="@id/top_image_container"
/>-->
<FrameLayout
android:id="@+id/recyclerview_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_image_container"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview_front"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_image_container" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu"
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Coming to the point now, I have three questions:
- if I use youtube player and if I place those play icon and close, my player does not work as I get this. How does Youtube work then (provided I took this example from the official documentation)?
W/YouTubeAndroidPlayerAPI: YouTube video playback stopped due to unauthorized overlay on top of player. The YouTubePlayerView is obscured by android.widget.ImageView{3ae28d60 V.ED.... ........ 704,320-704,320 #7f09009f app:id/image_play}. The obscuring view is inside the player view's interior zone. The distance (px) between each edge of the obscuring view and each corresponding interior zone edge is: left: 704, top: 320, right: 64, bottom: 320. .
- My video stops when I minimize i.e, use the
OnSwipe
and the player becomes smaller. I tried to understand this but I just wonder how they play it in the official Youtube app.
W/YouTubeAndroidPlayerAPI: YouTube video playback stopped due to the player's view being too small. The YouTubePlayerView is 384dp wide (minimum is 200dp) and 87dp high (minimum is 110dp).
- Last but not the least and the most important how exactly do I make the Youtube player to pause/play and use any other controls when I use
motion:touchRegionId="@+id/top_image_container"
when I use it either the player becomes unclickable/unfocussable or even after using thetouchRegionId
the player can only be swiped down from the empty place below(why though).
P.S: What I have tested and tried, though doesn't work.
- MotionLayout: MotionScene OnClick overrides setOnClickListener
- Can we use OnSwipe and OnClick in the same <Transition> for Android MotionLayout?
回答1:
Official Youtube application uses ExoPlayer inside it, and as I understand they don't use their Youtube SDK :) So, ExoPlayer hasn't restrictions: width/height/overlays.
I faced the same problem as yours, and I used this library - android-youtube-player for playing a youtube video.
Regarding screen touches, when you touch VideoView(youtube) it intercepts the TouchEvent, so, we need to intercept TouchEvent before VideoView catch it. We need to understand what kind of event it is. If it's Swipe -> we intercept event in parent view. If it Click -> we don't intercept this event.
Example of custom MotionLayout I got from this article. It catches touches only from the selected view. Code sample:
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000"
motion:motionInterpolator="linear">
<OnSwipe
motion:touchAnchorId="@+id/top_image_container"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragUp" />
<ConstraintSet android:id="@id/start">
<Constraint
android:id="@id/top_image_container"
android:layout_width="0dp"
android:layout_height="233dp"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent" />
<Constraint
android:id="@id/top_image"
android:layout_width="0dp"
android:layout_height="0dp"
motion:layout_constraintTop_toTopOf="@id/top_image_container"
motion:layout_constraintBottom_toBottomOf="@id/top_image_container"
motion:layout_constraintStart_toStartOf="@id/top_image_container"
motion:layout_constraintEnd_toEndOf="@id/top_image_container"
/>
<Constraint
android:id="@id/recyclerview_container"
android:layout_width="0dp"
android:layout_height="0dp"
motion:layout_constraintTop_toBottomOf="@id/top_image_container"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent" />
<Constraint
android:id="@id/recyclerview_front"
android:layout_width="0dp"
android:layout_height="0dp"
motion:layout_constraintTop_toBottomOf="@id/top_image_container"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"/>
<Constraint android:id="@id/image_play" >
<PropertySet motion:alpha="0" />
</Constraint>
<Constraint android:id="@id/image_clear" >
<PropertySet motion:alpha="0" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@id/end">
<Constraint
android:id="@id/top_image_container"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_marginBottom="56dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/close"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginRight="16dp"
motion:layout_constraintTop_toTopOf="@+id/playerContainer"
motion:layout_constraintRight_toRightOf="@+id/playerContainer"
motion:layout_constraintBottom_toBottomOf="@+id/playerContainer"/>
<Constraint
android:id="@id/top_image"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
motion:layout_constraintTop_toTopOf="@id/top_image_container"
motion:layout_constraintBottom_toBottomOf="@id/top_image_container"
motion:layout_constraintStart_toStartOf="@id/top_image_container"
motion:layout_constraintDimensionRatio="H,1:1.8"
/>
<Constraint
android:id="@id/recyclerview_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="56dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
motion:layout_constraintTop_toBottomOf="@id/top_image_container"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent" />
<Constraint
android:id="@id/recyclerview_front"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0"
motion:layout_constraintTop_toBottomOf="@id/top_image_container"
motion:layout_constraintBottom_toBottomOf="@id/top_image_container"
motion:layout_constraintEnd_toEndOf="@id/top_image_container"
motion:layout_constraintStart_toStartOf="@id/top_image_container"
/>
<Constraint android:id="@id/image_play" >
<PropertySet motion:alpha="1" />
</Constraint>
<Constraint android:id="@id/image_clear" >
<PropertySet motion:alpha="1" />
</Constraint>
</ConstraintSet>
<KeyFrameSet>
<KeyPosition
motion:motionTarget="@id/top_image"
motion:framePosition="90"
motion:percentWidth="0"
motion:percentX="0"
motion:curveFit="linear"
/>
<KeyPosition
motion:motionTarget="@id/top_image_container"
motion:framePosition="90"
motion:percentWidth="0"
motion:curveFit="linear"
/>
<KeyPosition
motion:motionTarget="@id/recyclerview_container"
motion:framePosition="90"
motion:percentWidth="0"
motion:curveFit="linear"
/>
<KeyAttribute
android:alpha="0"
motion:framePosition="75"
motion:motionTarget="@id/recyclerview_front" />
<KeyAttribute
android:alpha="0.10"
motion:framePosition="90"
motion:motionTarget="@id/image_clear" />
<KeyAttribute
android:alpha="0.10"
motion:framePosition="90"
motion:motionTarget="@id/image_play" />
<KeyAttribute
android:alpha="0"
motion:framePosition="95"
motion:motionTarget="@id/close" />
</KeyFrameSet>
</Transition>
</MotionScene>
SingleViewTouchableMotionLayout.kt
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
import com.example.android.R
class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) : MotionLayout(context, attributeSet) {
private val viewRect = Rect()
private var touchStarted = false
private val viewToDetectTouch by lazy {
findViewById<View>(R.id.top_image_container)
}
private val gestureListener by lazy {
object : GestureDetector.SimpleOnGestureListener() {
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
viewToDetectTouch.getHitRect(viewRect)
return viewRect.contains(e1.x.toInt(), e1.y.toInt())
}
}
}
private val gestureDetector by lazy {
GestureDetector(context, gestureListener)
}
init {
setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
}
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
}
override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
}
override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
touchStarted = false
}
})
}
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
return gestureDetector.onTouchEvent(event)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
touchStarted = false
return super.onTouchEvent(event)
}
}
if (!touchStarted) {
viewToDetectTouch.getHitRect(viewRect)
touchStarted = viewRect.contains(event.x.toInt(), event.y.toInt())
}
return touchStarted && super.onTouchEvent(event)
}
}
fragment_video_new.kt
<?xml version="1.0" encoding="utf-8"?>
<com.example.android.android.features.music.SingleViewTouchableMotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene_24"
tools:ignore="contentDescription">
<FrameLayout
app:layout_constraintTop_toTopOf="@+id/top_image_container"
app:layout_constraintBottom_toBottomOf="@+id/recyclerview_container"
android:id="@+id/playerContainer"
android:layout_width="match_parent"
android:layout_height="0dp"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/top_image_container"
android:layout_width="match_parent"
android:layout_height="320dp"
android:background="?android:attr/windowBackground"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
android:id="@+id/top_image"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="@id/top_image_container"
app:layout_constraintBottom_toBottomOf="@id/top_image_container"
app:layout_constraintStart_toStartOf="@id/top_image_container"
app:layout_constraintEnd_toEndOf="@id/top_image_container" />
<ImageView
android:id="@+id/image_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:alpha="0"
app:layout_constraintEnd_toStartOf="@id/image_clear"
app:layout_constraintTop_toTopOf="@id/top_image_container"
app:layout_constraintBottom_toBottomOf="@id/top_image_container"
/>
<ImageView
android:id="@+id/image_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:alpha="0"
app:layout_constraintEnd_toEndOf="@id/top_image_container"
app:layout_constraintBottom_toBottomOf="@id/top_image_container"
app:layout_constraintTop_toTopOf="@id/top_image_container" />
<FrameLayout
android:id="@+id/recyclerview_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_image_container" />
<ImageView
android:id="@+id/close"
android:layout_width="28dp"
android:layout_height="28dp"
android:alpha="0"
app:layout_constraintTop_toTopOf="@+id/playerContainer"
app:layout_constraintRight_toRightOf="@+id/playerContainer"
app:layout_constraintBottom_toBottomOf="@+id/playerContainer"
app:srcCompat="@drawable/ic_close" />
<com.revolut.rxdiffadapter.AsyncDiffRecyclerView
android:id="@+id/recyclerview_front"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_image_container" />
</com.example.android.android.features.music.SingleViewTouchableMotionLayout>
gif
来源:https://stackoverflow.com/questions/57819969/how-to-handle-onclick-and-onclicklistener-in-motionlayout-similar-to-youtube