Я нашел классное решение, используя 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](https://i.stack.imgur.com/Z6h21.gif?s=300)