概要
環状に展開するメニューを実装できるライブラリ Cleveroad/CycleMenu について説明します。
Cleveroad/CycleMenu
こんな感じで環状に展開するメニューを実装できます。
メニューボタンは View の四隅に置くことができます。メニューアイテムの表示には、内部的に RecyclerView を用いて実装されています。
完成度は高いですが、最終更新が2017年の5月というのが気にかかるところではあります。
できること
一通りメニューとしての役割は果たせます。
- 非展開時に表示するアイコンの変更
- 各○ボタンのアイコンと色の設定
- 各○ボタンのロングタップアクション実装
できないこと
主にデザイン面での制約があります。これらをどうにかするにはフォークする必要があります。
- 白背景を別の色 or 透明にする
- 各○ボタンの見た目を動的に変える(例: タブ一覧のタブ枚数を表示)
- テキストの同時表示
ライセンス
The MIT License でライセンスされています。
導入
依存の追加
app/build.gradle に以下を追加してください。
dependencies {
implementation 'com.cleveroad:cycle-menu:1.0.1'
簡単な使い方
まず、メニューを追加したいレイアウトに CycleMenuWidget を追加してください。
<com.cleveroad.cyclemenuwidget.CycleMenuWidget
android:id="@+id/cycle_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cm_autoMaxRadius="200dp"
app:cm_autoMinRadius="10dp"
app:cm_corner="right_bottom"
app:cm_fixedRadius="200dp"
app:cm_radius_scale_type="auto"
app:cm_scroll_type="endless"
app:cm_item_background_tint="@color/colorPrimary"
app:cm_corner_image_src="@drawable/ic_menu_cycle"
app:cm_ripple_color="@color/filter_white_aa"
/>
Attribute | 説明 | 指定値の例 |
---|---|---|
app:cm_corner | 表示位置、parent の四隅を指定可能 | "right_bottom" |
app:cm_scroll_type | 無限に回るか否か | "endless" |
app:cm_item_background_tint | ○ボタンの背景色 | "@color/colorPrimary" |
app:cm_corner_image_src | メニューのアイコン | "@drawable/ic_menu_cycle" |
app:cm_ripple_color | タップエフェクトの色 | "@color/filter_white_aa" |
MenuItem の追加
メニューの項目を追加します。リソースから読み込む方法と、プログラム内で追加する方法があります。
リソースから読み込む
特に凝ったことをしないのであれば、こちらがお手軽です。
リソースの用意
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/to_top"
android:icon="@drawable/ic_top"
/>
<item
android:id="@+id/to_bottom"
android:icon="@drawable/ic_bottom"
/>
<item
android:id="@+id/back"
android:icon="@drawable/ic_back"
/>
<item
android:id="@+id/forward"
android:icon="@drawable/ic_forward"
/>
<item
android:id="@+id/reload"
android:icon="@drawable/ic_reload"
/>
<item
android:id="@+id/setting"
android:icon="@drawable/ic_settings"
/>
<item
android:id="@+id/exit"
android:icon="@drawable/ic_exit"
/>
</menu>
リソースをセット
cycleMenu?.also {
it.setMenuRes(R.menu.browser_menu)
}
分岐の実装
View の id には menu リソースで指定した id が設定されているので、それを使って when 式で分岐させられます。
応用
ロングタップしたらメニューの説明を表示する
リソースを使う方式だと id と iconId 以外の情報を設定できず、実装が楽ではないので、setMenuItems
を使うやり方で進めます。
まず、新たに MenuItem を生成するためのクラスを用意します。今回は enum class で定義してみました。
internal enum class Menu(
@param:StringRes val titleId: Int,
@param:DrawableRes val iconId: Int
) {
TOP(R.string.title_menu_to_top, R.drawable.ic_top),
BOTTOM(R.string.title_menu_to_bottom, R.drawable.ic_bottom),
BACK(R.string.title_menu_back, R.drawable.ic_back),
FORWARD(R.string.title_menu_forward, R.drawable.ic_forward),
SETTING(R.string.title_settings, R.drawable.ic_settings),
EXIT(R.string.exit, R.drawable.ic_exit)
;
companion object {
fun items(context: Context?): List<CycleMenuItem> {
if (context == null) {
return Collections.emptyList()
}
return values().map { convertMenuItem(context, it) }
}
private fun convertMenuItem(context: Context, menu: Menu) =
CycleMenuItem(menu.ordinal, ContextCompat.getDrawable(context, menu.iconId))
fun showInformation(view: View?) {
if (view == null) {
return
}
val activityContext = view.context
Toaster.snackLong(
view,
values().find { it.ordinal == view.id }?.titleId ?: 0,
R.string.run,
View.OnClickListener { view.performClick() },
PreferenceApplier(activityContext).colorPair()
)
}
}
}
Listを cycleMenu にセットします。
cycleMenu?.also {
it.setMenuItems(Menu.items(context))
it.setOnMenuItemClickListener(object : OnMenuItemClickListener {
override fun onMenuItemLongClick(view: View?, itemPosition: Int) {
Menu.showInformation(view)
}
override fun onMenuItemClick(view: View?, itemPosition: Int) {
onMenuClick(view?.id ?: 0)
}
when (id) {
Menu.BACK.ordinal -> {
back()
}
メニューの開閉を取得する
OnStateChangedListener を実装しセットすることにより、メニューの開閉状態を取得したり処理を差し込んだりすることが可能です。
領域外をタップしたらメニューを閉じる、という実装をしたい時には役立ちます。
private var menuOpen: Boolean = false
it.setStateChangeListener(object : OnStateChangedListener {
override fun onCloseComplete() = Unit
override fun onOpenComplete() = Unit
override fun onStateChanged(state: CycleMenuWidget.STATE?) {
when (state) {
CycleMenuWidget.STATE.OPEN -> menuOpen = true
CycleMenuWidget.STATE.CLOSED -> menuOpen = false
else -> Unit
}
}
})
まとめ
メニューが環状に展開する CycleMenu の使い方について述べました。ユーザに片手で操作させたい時に役立つメニューかと思います。