LoginSignup
0
0

More than 5 years have passed since last update.

CycleMenu で環状に展開するメニューを実装する

Posted at

概要

環状に展開するメニューを実装できるライブラリ Cleveroad/CycleMenu について説明します。

Cleveroad/CycleMenu

こんな感じで環状に展開するメニューを実装できます。

5fyxa-jekjb.gif

メニューボタンは View の四隅に置くことができます。メニューアイテムの表示には、内部的に RecyclerView を用いて実装されています。

完成度は高いですが、最終更新が2017年の5月というのが気にかかるところではあります。

できること

一通りメニューとしての役割は果たせます。

  • 非展開時に表示するアイコンの変更
  • 各○ボタンのアイコンと色の設定
  • 各○ボタンのロングタップアクション実装

できないこと

主にデザイン面での制約があります。これらをどうにかするにはフォークする必要があります。

  • 白背景を別の色 or 透明にする
  • 各○ボタンの見た目を動的に変える(例: タブ一覧のタブ枚数を表示)
  • テキストの同時表示

ライセンス

The MIT License でライセンスされています。

導入

依存の追加

app/build.gradle に以下を追加してください。

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 の使い方について述べました。ユーザに片手で操作させたい時に役立つメニューかと思います。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0