Fragment
内にRecyclerView
をホストするMotionLayout
があります。リサイクラービューに加えて、折りたたんだり展開したりするビューをすべてモーションシーンで実行しています。これはクリックと、リサイクラービューのドラッグへの応答によってトリガーされます。これまでのところ、これはすべて意図したとおりに機能します。
要点は次のとおりです。アプリ内の一部の状態について、折りたたみビューを完全に非表示にしたいと思います。しかし、ビューの可視性を設定したり、高さを変更したりしても、recyclerviewをドラッグすると、ビューに戻ります。
したがって、再び有効にするまで、MotionLayoutを完全に無効にする(コンストレイントをロックする)ことは可能です。ドラッグレコグナイザーを無効にすることも、実行可能なソリューションになります。
次に、状況を説明するための簡単なXMLをいくつか示します。
ヘッダービューとリサイクラービューを含むモーションレイアウトを含むレイアウト
<?xml version="1.0" encoding="utf-8"?>
<Android.support.constraint.motion.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/favouritesMotionLayout"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
app:layoutDescription="@xml/favourites_motion_scene"
Android:animateLayoutChanges="true">
<ImageView
Android:id="@+id/headerView"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
...
/>
<Android.support.v4.widget.SwipeRefreshLayout
Android:id="@+id/swipeRefreshLayout"
Android:layout_width="match_parent"
Android:layout_height="0dp"
...
>
<pinch.nrcnext.view.scroll.NRCRecyclerView
Android:id="@+id/favoritesList"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
</Android.support.v4.widget.SwipeRefreshLayout>
</Android.support.constraint.motion.MotionLayout>
対応するモーションシーン:
<?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:constraintSetStart="@id/expanded"
motion:constraintSetEnd="@id/collapsed"
motion:duration="300">
<OnSwipe
motion:touchAnchorId="@id/swipeRefreshLayout"
motion:touchAnchorSide="top"
motion:dragDirection="dragUp" />
</Transition>
<ConstraintSet Android:id="@+id/expanded">
... Constraints for expanded header ...
</ConstraintSet>
<ConstraintSet Android:id="@+id/collapsed">
... ... Constraints for collapsed header ...
</ConstraintSet>
</MotionScene>
制約レイアウトバージョン「2.0.0-alpha3」の使用
implementation "com.Android.support.constraint:constraint-layout:2.0.0-alpha3"
まとめ:モーションレイアウトを無効にして、ヘッダーを拡張せずにrecyclerviewをスクロールできるようにしてから、再度有効にするように指示します。残念ながら、それはfavouritesMotionLayout.isEnabled = false
ほど簡単ではありませんが、それが私が考えていた方法です。
最新のベータ版(beta2)では、移行を無効にすることが可能であることに注意してください。 https://androidstudio.googleblog.com/2019/06/constraintlayout-200-beta-2.html
Kotlinでの使用例:
(view as? MotionLayout)?.getTransition(R.id.swipe_transition)?.setEnable(false)
実際、シーンの開始状態を開始と終了の両方に設定することで、なんとか機能させることができました。コトリンでは:
motionLayout.setTransition(R.id.start, R.id.start)
レイアウトを有効にする必要がある場合:
motionLayout.setTransition(R.id.start, R.id.end)
シーンxmlを変更する必要はなく、alpha-03で完璧に動作しました
私は_androidx.constraintlayout:constraintlayout:2.0.0-beta4
_とmotionLayout.getTransition(R.id.yourTransition).setEnable(false)
を使用していますが、モーションレイアウトを完全に無効にするには、たとえば、私の場合、特別な場合にビューを非表示にする必要がありますが、このビューはアニメーション。したがって、cardMotionLayout.loadLayoutDescription(0)
はMotionLayoutを完全に無効にし、再度有効にするにはcardMotionLayout.loadLayoutDescription(R.xml.fragment_buy_card_step_one_scene)
を使用しています
LPirro による承認された回答が機能しませんでした。現在、beta3を使用しています。むしろ私は私の要件に従ってlayoutDescriptionを変更しました:
public void enableTransition(boolean enable) {
if (enable) loadLayoutDescription(R.xml.scene);
else loadLayoutDescription(0);
}
わたしにはできる:
if (list.isNullOrEmpty()) {
//disable
motionLayout.apply {
setTransition(R.id.expanded, R.id.expanded)
setTransitionDuration(0)
}
} else {
//enable
motionLayout.apply {
setTransition(R.id.expanded, R.id.collapsed)
setTransitionDuration(200)
}
}
p.s.: 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
ユーザーアクションが完了したら、トランジションを無効にする場合。 motionLayout.transitionToState(0)と設定するだけです。移行完了コールバックでこれを行うことができます。
以下のコードが機能するかどうかを確認してください。
motionLayout.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) {
(motionLayout as MotionLayout).transitionToState(0)
}
})
したがって、状態をハッキングしてモーション遷移を無効にする方法があるようです。上記の移行後にxmlファイルに追加する必要があります。同じ開始状態と終了状態を持つ別の遷移を追加しました。あなたの場合:
<Transition
motion:constraintSetStart="@id/collapsed"
motion:constraintSetEnd="@id/collapsed"
motion:duration="0">
そしてJava/Kotlinで
motion_layout.setTransition(R.id.collapsed, R.id.collapsed)
さて私はこれをしなければならず、私はベータ1とベータ2の両方のライブラリを試しましたが、motionlayoutを無効にすることができませんでした。
[〜#〜]ソリューション[〜#〜]
// this fix the transition but other parts of layout will still able to trigger transition somehow to R.id.end!!
motionLayout.setTransition(R.id.start, R.id.start);
// disable all other views touch handling except view clicks.
motionLayout.findViewById(R.id.view_ids).setOnTouchListener(
(v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
motionLayout.findViewById(R.id.view_ids).setOnTouchListener(
(v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
.... disable all the views touch handling cause they can trigger the motion layout animation!
// And then finally has to disable touch handling of motionlayout itself to avoid the space exposed by motionlayout due to padding or margin which can trigger the transition!
motionLayout.setOnTouchListener(
(v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
つまり、開始と終了の両方の開始IDとしてsetTransition
を使用する必要があります。しかし、これでは不十分であることに気付きました。どういうわけかmotionlayout
はrecylerview
のタッチ処理を無効にしますが、motionlayout
の他のすべての部分は遷移をトリガーできるため、他のすべてのビューのタッチ処理を無効にしてそれらのみを許可する必要がありますクリックイベントを処理し、最後にmotionlayout
についても同じことを行う必要があります。 motionlayoutによって処理されるビューの間にスペースを生じさせるため、motionlayoutが存在する部分に触れると、遷移がトリガーされます。したがって、そのタッチ処理も無効にする必要があります。
そして最後に、このソリューションでうまくいきました!この回答が誰かを助けてくれることを願っています!:)お楽しみください!
現時点では、MotionLayoutでモーションを無効にすることはできないようです。これが将来のバージョンで追加されることを願っています。私のユースケースを解決するために、ちょっと汚いハックを使用しました。ヘッダーを構成するビューを削除しました。
favouritesMotionLayout.removeView(headerView)
レイアウトはこれで問題なく、ビューが消え、モーションはトリガーされませんでした。ビューを元に戻すには、レイアウト全体を再度インフレートしました。 (別の方法としては、ビューとその制約をプログラムで再度追加することです)。
これは最もきれいな解決策ではないことを認識しており、モーションレイアウトなしでユースケース全体を解決する方法を現在検討しています。これは、私のユースケースの最大90%で非常に役立ちました。