リストアイテムを使用したコンテキストアクションは、Androidアプリケーションで広く使用されています。 リストの複数の要素またはすべての要素を選択し、選択したすべての要素にアクションを一度に適用すると非常に便利です。 たとえば、削除します。
Androidアプリケーションでは、これにActionMode
を使用できます。これにより、選択した要素で利用可能なアクションをToolbar
上に表示できます。 そこで、現在選択されている要素の数やその他の有用な情報をユーザーに表示できます。 それは便利で見栄えが良いですが、場合によってはToolbar
自体に表示される情報が重要であり、非表示にしたくないでしょう。 たとえば、ユーザーの名前と写真、リストに表示されるメッセージのリストがあります。 一部のメッセージを強調表示する場合、これらのメッセージの宛先となるユーザーの名前を確認すると便利です。
この場合、 Toolbar
をブロックすることなく、リスト自体の上にリスト項目を含むコンテキストアクションパネルを表示できます。 この記事では、このようなコンテキストアクションパネルの作成について説明します。
CustomView- FloatingActionMode
または単にFAM
と呼ばれるコンテキストアクションのパネルを開発しました。
実行中のFloatingActionMode
(下からロック)
ビデオ-FloatingActionModeの使用例(以下に修正)
コメントでは、ユーザーが画面の周りにパネルをドラッグすることはあまり便利ではない可能性があることが指摘されたため、上のスクリーンショットとビデオに示すように、画面の下部に固定できます。 (これを行うには、 android:layout_gravity="bottom"
およびapp:fam_can_drag="false"
の属性を指定します)。
同時に、次のスクリーンショットとビデオに示すように、ユーザーが画面上でFAM
を移動できるようにすることができます。
実行中のFloatingActionMode
ビデオ-FloatingActionMode(ドラッグアンドドロップ)の使用例
デフォルトでは、 FAM
はbackground
がないため、好きなものを使用できます。 また、 android:translationZ="8dp"
属性android:translationZ="8dp"
を使用して、API> = 21のデバイスでシャドウを作成できます。
XML属性
マークアップファイルを使用してFAM
を構成するために、いくつかの特別な属性が定義されていますが、プログラムで変更することもできます。
fam_opened
、作成時にFAM
を開くかどうかを決定します。 (デフォルトではfalse
)
fam_content_res
は、FAM
コンテンツ(たとえば、いくつかのボタン)を表すLayoutRes
です。fam_content_res
から作成されたView
は、View
子としてFAM
追加されView
。 コンテンツは、アプリケーションの実行中にプログラムで変更できるため、FAM
属性を指定できますandroid:animateLayoutChanges="true"
コンテンツのアニメーションの変更。 (デフォルトではコンテンツなし)
fam_can_close
は、FAM
に閉じるボタンがあるかどうかを決定します。 (デフォルトでtrue
)
fam_close_icon
は、fam_close_icon
閉じるボタンです。 (デフォルト値はクロスです)
fam_can_drag
は、FAM
にドラッグアンドドロップするボタンがあるかどうかを決定します。 (デフォルトでtrue
)
fam_drag_icon
はfam_drag_icon
ドラッグボタンです。 (デフォルト値があります)
fam_can_dismiss
は、ユーザーが水平方向に十分にドラッグした場合にFAM
閉じるかどうかを決定します(デフォルトではtrue
)
fam_dismiss_threshold
は、ユーザーfam_drag_button
ときにFAM
が閉じる水平シフトしきい値fam_drag_button
。 つまり、(getTranslationX
/getWidth
)>dismissThreshold
場合、FAM
は閉じられます。 (デフォルトでは0.4f
)
fam_minimize_direction
は、FAM
が最小化されたときに移動する方向を決定します。 この属性には、次の値を指定できます(デフォルトでnearest
値)。
-
top
-FAM
は、折り畳み中に親の上の境界(パディングを除く)に移動します -
bottom
-FAM
は折り畳み中に親の下部の境界(インデントを除く)に移動します -
nearest
-FAM
は折り畳み中に親の最も近い(上または下の)境界(インデントを除く)に移動します
-
-
fam_animation_duration
は、アニメーションの最小化/展開の期間を決定します。 (デフォルトでは400
ミリ秒)
FAM
にはOnCloseListener
もあります。これにより、ユーザーOnCloseListener
FAM
OnCloseListener
ときに特定のアクションを実行できます(たとえば、リストから項目を選択解除します)。
主なアクション
FAM
の主なアクションは、開く/閉じる、折り畳む/展開することです。 開くと表示されて展開し、閉じると折りたたまれて消えます。
FAM
の展開にFAM
アニメーションFAM
伴います。その間に、親ViewGroup
の上端または下端(この端はfam_minimize_direction
属性によって設定されます)からマークアップファイルで指定された位置に移動します。 アニメーションは次のように定義されます。
animate() .scaleY(1f) .scaleX(1f) .translationY(calculateArrangeTranslationY()) .alpha(1f)
最小化すると、アニメーションは「反対方向に」実行されます。
animate() .scaleY(0.5f) .scaleX(0.5f) .translationY(calculateMinimizeTranslationY()) .alpha(0.5f)
メソッドcalculateArrangeTranslationY()
およびcalculateMinimizeTranslationY()
使用すると、ユーザーがFAM
、 fam_minimize_direction
属性fam_minimize_direction
および以下で説明するインデントをドラッグした場所を考慮して、それぞれ展開状態および折りたたみ状態のtranslationY
を計算できます。
閉じてドラッグ
正確で美しい作業のために、 FAM
はボタン( ImageView
)があり、ユーザーは状況依存アクションモードを閉じるか、画面の別の部分に垂直にドラッグできます(目的のリストアイテムがブロックされている場合)。 FAM
は、水平方向にドラッグして閉じることもできます(スワイプして閉じます)。
FAM
はLinearLayout
で、作成中にボタンを追加( fam_drag_button
)およびドラッグ( fam_close_button
)します。 FAM
を閉じる/ドラッグする機能は、アプリケーションの実行中にオン/オフを切り替えることができるため、これらのボタンを含むLinearLayout
は、 android:animateLayoutChanges="true"
属性android:animateLayoutChanges="true"
ます。
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="wrap_content" android:layout_height="?attr/actionBarSize" android:animateLayoutChanges="true" android:layout_gravity="center_vertical"> <ImageView android:id="@+id/fam_close_button" android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" android:layout_gravity="center_vertical" android:background="@drawable/fam_image_button_background" android:scaleType="center" android:src="@drawable/fam_ic_close_white_24dp"/> <ImageView android:id="@+id/fam_drag_button" android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" android:layout_gravity="center_vertical" android:background="@drawable/fam_image_button_background" android:scaleType="center" android:src="@drawable/fam_ic_drag_white_24dp"/> </LinearLayout> </merge>
ドラッグメカニズムはOnTouchListener
を使用して実装されますOnTouchListener
は、タッチの開始点を記憶し、移動するとtranslationX
とtranslationY
それぞれタッチに設定します。 ユーザーがドラッグボタン( fam_drag_button
)をfam_drag_button
と、 FAM
は元の水平位置に戻り、ユーザーがFAM
十分に水平方向にドラッグすると、 this@FloatingActionMode.close()
メソッドがthis@FloatingActionMode.close()
ます。
fam_drag_button.setOnTouchListener(object : OnTouchListener { var prevTransitionY = 0f var startRawX = 0f var startRawY = 0f override fun onTouch(v: View, event: MotionEvent): Boolean { if (!this@FloatingActionMode.canDrag) { return false } val fractionX = Math.abs(event.rawX - startRawX) / this@FloatingActionMode.width when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { this@FloatingActionMode.fam_drag_button.isPressed = true startRawX = event.rawX startRawY = event.rawY prevTransitionY = this@FloatingActionMode.translationY } MotionEvent.ACTION_MOVE -> { this@FloatingActionMode.maximizeTranslationY = prevTransitionY + event.rawY - startRawY translationX = event.rawX - startRawX if (canDismiss) { val alpha = if (fractionX < dismissThreshold) 1.0f else Math.pow(1.0 - (fractionX - dismissThreshold) / (1 - dismissThreshold), 4.0).toFloat() this@FloatingActionMode.alpha = alpha } } MotionEvent.ACTION_UP -> { fam_drag_button.isPressed = false this@FloatingActionMode.animate().translationX(0f) .duration = animationDuration if (canDismiss && fractionX > dismissThreshold) { this@FloatingActionMode.close() } } } return true } })
CoordinatorLayout
使用
calculateMinimizeTranslationY()
メソッドとcalculateMinimizeTranslationY()
メソッドは、上下のインデントを考慮して正しいFAM
位置を決定すると以前に言われました。 これらのパディングは、 CoordinatorLayout.Behavior
拡張であるFloatingActionModeBehavior
を使用して計算されます。これは、上部パディングをAppBarLayout
の高さにAppBarLayout
し、下部パディングをAppBarLayout
の表示部分の高さに設定しSnackbar.SnackbarLayout
。
また、 FloatingActionModeBehavior
使用すると、 FAM
スクロールFAM
応答し、下にスクロールすると折り畳み、上にスクロールすると展開します(クイックリターンパターン)。
open class FloatingActionModeBehavior @JvmOverloads constructor(context: Context? = null, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<FloatingActionMode>(context, attrs) { override fun layoutDependsOn(parent: CoordinatorLayout?, child: FloatingActionMode?, dependency: View?): Boolean { return dependency is AppBarLayout || dependency is Snackbar.SnackbarLayout } override fun onDependentViewChanged(parent: CoordinatorLayout, child: FloatingActionMode, dependency: View): Boolean { when (dependency) { is AppBarLayout -> child.topOffset = dependency.bottom is Snackbar.SnackbarLayout -> child.bottomOffset = dependency.height - dependency.translationY.toInt() } return false } override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout?, child: FloatingActionMode?, directTargetChild: View?, target: View?, nestedScrollAxes: Int): Boolean { return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL } override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionMode, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int) { super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed) // FAM View. var parent = target.parent while (parent != coordinatorLayout) { if (parent == child) { return } parent = parent.parent } if (dyConsumed > 0) { child.minimize(true) } else if (dyConsumed < 0) { child.maximize(true) } } }
これは、マークアップファイルでFAM
がどのように見えるかです。
<android.support.design.widget.CoordinatorLayout> <android.support.design.widget.AppBarLayout> ... </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView app:layout_behavior="@string/appbar_scrolling_view_behavior"/> <com.qwert2603.floating_action_mode.FloatingActionMode android:id="@+id/floating_action_mode" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/action_mode_margin" android:animateLayoutChanges="true" android:background="@drawable/action_mode_background" android:translationZ="8dp" app:fam_animation_duration="@integer/action_mode_animation_duration" app:fam_can_dismiss="true" app:fam_can_drag="true" app:fam_content_res="@layout/user_list_action_mode_2" app:fam_dismiss_threshold="0.35" app:fam_drag_icon="@drawable/ic_drag_white_24dp" app:fam_minimize_direction="nearest"/> </android.support.design.widget.CoordinatorLayout>
ソースコード
FloatingActionMode
のソースコードは、 GitHub ( ライブラリディレクトリ)で入手できます。 FAM
( appディレクトリ)を使用したデモアプリケーションもあります。
FloatingActionMode
自体とFloatingActionModeBehavior
open
クラスとして定義されてopen
ため、必要に応じてアップグレードできます。 主要なFloatingActionMode
メソッドもopen
として定義されていopen
。
ご清聴ありがとうございました。 ハッピーコーディング!