2013年06月22日

Navigation Drawer使用時のアクションバーボタンの三本線について

画面左からニョっと出てくるメニュー画面。最近流行ってますよね、このデザインパターン。(何を今更w)

(Navigation Drawer | Android Developers から引用)

サイドメニューとか勝手に呼んでたんですが、Android的にはNavigation Drawerというのが正式名称のようです。

ActionBarとの組み合わせでお手軽にNavigationDrawerを実現するサポートライブラリも提供されています。

Creating a Navigation Drawer | Android Developers

これはこれで大変便利なのですが、Jota+ではアクションバー毎スライドさせたいので、サポートライブラリは使用せずにAdamrockerさんのSimpleSideDrawerに手を加えて使っています。(大感謝!)

Androidアプリにサイドメニューを簡単に追加できるライブラリ | throw life

 

で、今日の本題。

The New Navigation Drawer Design Pattern | ANDROID // UI PATTERNS

どうやら、Navigation Drawerを使う場合は上の絵のようにアクションバーのアイコンボタンを三本線にするのがガイドラインのようなのです。

実際サポートライブラリ(android.support.v4.app.ActionBarDrawerToggle)を使うとこんな感じになります。

ですが、ActionBarクラスのAPIにはここの絵を変えるというメソッドが存在しません。

サポートライブラリを使わない場合は、こんな風に「<」のままです。(ActionBarの標準動作です)

sidemenu_3

では、どうするか?

ソースを読むしかありません。で、見つけました。


class ActionBarDrawerToggleHoneycomb {
        SetIndicatorInfo(Activity activity) {
            try {
                setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod("setHomeAsUpIndicator",
                        Drawable.class);
                setHomeActionContentDescription = ActionBar.class.getDeclaredMethod(
                        "setHomeActionContentDescription", Integer.TYPE);

                // If we got the method we won't need the stuff below.
                return;
            } catch (NoSuchMethodException e) {
                // Oh well. We'll use the other mechanism below instead.
            }

            final View home = activity.findViewById(android.R.id.home);
            if (home == null) {
                // Action bar doesn't have a known configuration, an OEM messed with things.
                return;
            }

            final ViewGroup parent = (ViewGroup) home.getParent();
            final int childCount = parent.getChildCount();
            if (childCount != 2) {
                // No idea which one will be the right one, an OEM messed with things.
                return;
            }

            final View first = parent.getChildAt(0);
            final View second = parent.getChildAt(1);
            final View up = first.getId() == android.R.id.home ? second : first;

            if (up instanceof ImageView) {
                // Jackpot! (Probably...)
                upIndicatorView = (ImageView) up;
            }
        }


大雑把にまとめると、setHomeAsUpIndicatorというメソッドがあれば、それを呼び出す。(JB-MR2=Android4.3?で追加されるAPIらしい)

なければ、{
android.R.id.home というidのViewがあるはず。
その親Viewを取得。
子Viewが二つあるはず。
そのうち、片方がandroid.R.id.homeのはずなので、もう一方を取得。
そいつがImageViewだったら「多分当たり!」

当たりを引いたら、setImageDrawable()する。てな感じになっています。「はず」じゃなかった場合「OEMが何か壊したんだ」などとコメントに愚痴が入ってるのが楽しいです。

 

で、これを、まんまパクって以下の様なメソッドを作りました。


private void setHomeAsUpIndicator(View view, int resId)
{
    int homeid;
    if ( JotaTextEditor.sHoneycomb ){
        homeid = android.R.id.home;
    }else{
        homeid = R.id.abs__home;
    }

    final View home = view.findViewById(homeid);
    if (home == null) {
        // Action bar doesn't have a known configuration, an OEM messed with things.
        return;
    }

    final ViewGroup parent = (ViewGroup) home.getParent();
    final int childCount = parent.getChildCount();
    if (childCount != 2) {
        // No idea which one will be the right one, an OEM messed with things.
        return;
    }

    final View first = parent.getChildAt(0);
    final View second = parent.getChildAt(1);
    final View up = first.getId() == homeid ? second : first;

    if (up instanceof ImageView) {
        // Jackpot! (Probably...)
        ImageView upView = (ImageView) up;
        upView.setImageResource(resId);
    }
}


で、これを、ActivityでActionBar関連の設定をした後あたりで呼び出します。

setHomeAsUpIndicator(getWindow().getDecorView(), R.drawable.ic_drawer);

「getWindow().getDecorView()」はwindowのroot viewを取得するためです。(contentViewにはActionBarが含まれていないことに注意)

また、ActionBarSherlockではここのアイコンのidはR.id.abs__homeになるので、OSバージョンを見て持ち替えてあげます。

後は、サポートライブラリのリソースから、ic_drawer.pngをコピーしてきます。

sidemenu_1

sidemenu_2

これでうまい具合にアイコン横に三本線が描けました。

posted by Jiro at 02:38 | Android