How to handle <OnClick> and onClickListener in MotionLayout similar to Youtube

本秂侑毒 提交于 2021-02-07 22:48:31

问题


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 the touchRegionId 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.

  1. MotionLayout: MotionScene OnClick overrides setOnClickListener
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!