Развернуть / свернуть панель инструментов, потянув за вид - PullRequest
4 голосов
/ 03 июля 2019

Я пытаюсь создать следующее поведение, используя CollapsingToolbarLayout и другое представление внизу AppBarLayout, но полоса не сворачивается / не расширяется, когда я прокручиваю / тяну на PullView, это означает, что нетневозможно открыть панель, когда она свернута или закрыта с помощью представления.

Я уже пытался использовать NestedScrollView в корне PullView, но безуспешно

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

    <FrameLayout
        android:id="@+id/frmContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:expanded="true">

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

                <FrameLayout
                    android:id="@+id/frmMenu"
                    android:layout_width="match_parent"
                    android:layout_height="300dp"
                    android:clickable="true"
                    android:focusable="true"/>

            </com.google.android.material.appbar.CollapsingToolbarLayout>

        </com.google.android.material.appbar.AppBarLayout>

        <com.xpto.customview.PullView
            android:id="@+id/pullDownView"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            app:layout_anchor="@+id/appBarLayout"
            app:layout_anchorGravity="bottom|center_horizontal"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>


    </androidx.coordinatorlayout.widget.CoordinatorLayout>

</FrameLayout>

enter image description here enter image description here

1 Ответ

2 голосов
/ 07 июля 2019

Я нашел классное решение, используя MotionLayout.

Прежде всего, я расширяю MotionLayout, чтобы немного изменить поведение, вместо перетаскивания всего MotionLayout Я толькочтобы можно было перетащить индикатор и меню.

TLDR: Пример на Github

Так что мой MotionLayout переопределяет OnTouchEvent и проверяет, касаемся ли мы одного из непосредственных потомков.

class TouchableMotionLayout @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0)
    : MotionLayout(context, attrs, defStyleAttr), MotionLayout.TransitionListener {

    private val viewRect = Rect()
    private var touchStarted = false

    init {
        initListener()
    }

    private fun initListener() {
        setTransitionListener(this)
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.actionMasked) {
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                touchStarted = false
                return super.onTouchEvent(event)
            }
        }

        if (!touchStarted) {
            touchStarted = verifyIfChildHasTouched(event)
        }

        return touchStarted && super.onTouchEvent(event)
    }

    /**
     * Verify if touching one fo the children.
     *
     * @param event The motion event.
     * @return True if touch its in one of the children.
     */
    private fun verifyIfChildHasTouched(event: MotionEvent): Boolean {
        for (index in 0 until childCount) {
            val view = getChildAt(index)
            view.getHitRect(viewRect)
            if (viewRect.contains(event.x.toInt(), event.y.toInt())) {
                return true
            }
        }

        return false
    }

    override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
        touchStarted = false
    }

    override fun allowsTransition(p0: MotionScene.Transition?) = true

    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) {}
}

В этом макете у меня есть пейджер, а затем пользовательский MotionLayout, можно только открывать / закрывать пролистывание менюв прямых потомках MotionLayout то есть menuIndicator и menu.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/baseViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.extmkv.example.TouchableMotionLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        app:layoutDescription="@xml/menu_scene">

        <FrameLayout
            android:id="@+id/menuIndicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:paddingBottom="8dp"
            android:paddingEnd="16dp"
            android:paddingStart="16dp"
            android:paddingTop="48dp"
            android:translationZ="1dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/menu">

            <FrameLayout
                android:layout_width="@dimen/menu_indicator_outside_width"
                android:layout_height="@dimen/menu_indicator_outside_height"
                android:layout_gravity="center"
                android:background="#F00"
                tools:ignore="UselessParent" />

        </FrameLayout>

        <LinearLayout
            android:id="@+id/menu"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center|top"
            android:background="#FFF"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent">

            <LinearLayout
                android:id="@+id/lnrMenuOptionsContainer"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:orientation="horizontal" />

        </LinearLayout>

    </com.extmkv.example.TouchableMotionLayout>

</FrameLayout>

А теперь сцена: @xml/menu_scene.xml Вы можете отрегулировать перетаскивание и изменить attrs в конце.

<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <ConstraintSet android:id="@+id/start">
        <Constraint android:id="@id/menu">
            <Layout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                motion:layout_constraintBottom_toTopOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintStart_toStartOf="parent" />
        </Constraint>

        <Constraint android:id="@id/menuIndicator">
            <Layout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toBottomOf="@id/menu" />
        </Constraint>

    </ConstraintSet>

    <ConstraintSet
        android:id="@+id/end"
        motion:deriveConstraintsFrom="@id/start">
        <Constraint android:id="@id/menu">
            <Layout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </Constraint>

        <Constraint android:id="@id/menuIndicator">
            <Layout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                motion:layout_constraintBottom_toBottomOf="@id/menu"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintStart_toStartOf="parent" />
        </Constraint>

    </ConstraintSet>

    <!-- All the animations values are hardcoded for now. -->
    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="400">

        <OnSwipe
            motion:dragDirection="dragDown"
            motion:dragScale="0.5"
            motion:maxAcceleration="10"
            motion:maxVelocity="10.0" />

    </Transition>

</MotionScene>

А теперь окончательный результат:

Menu Example

...