妄想プログラマのらくがき帳 : 8月 2013

2013年8月26日月曜日

[Android]ポップアップメニューを表示する。

ポップアップメニューは関連付けたViewのすぐ近くにポップアップ表示されるメニューです。その関連付けたView固有の操作を提供するのに使用します。

今回はListViewの項目を長押ししたときに項目別のポップアップメニューを表示してみます。
まず事前に項目ごとのメニューリソースを作成しておきます。

・listview_item1_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/listViewItem1Menu"
        android:title="Item1Menu"/>
</menu>

・listview_item2_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/listViewItem2Menu"
        android:title="Item2Menu"/>
</menu>

次にListViewのOnItemLongClickListenerにリスナを設定します。リスナはActivity.onCreate()の中などで設定します。
※ListViewのOnLongClickListenerでないことに注意。この違いに気付かず1時間ぐらいハマってました(´・ω・`)
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        // 長押しされた項目でMenuInflater.inflate()に指定するメニューリソースを切り替える
        PopupMenu popup = new PopupMenu(getApplicationContext(), view);
        MenuInflater inflater = popup.getMenuInflater();
        if (position == 0) {
            // 1番目の項目を長押しされたときはlistview_item1_menuのメニューを表示
            inflater.inflate(R.menu.listview_item1_menu, popup.getMenu());
        }
        else if (position == 1) {
            // 2番目の項目を長押しされたときはlistview_item2_menuのメニューを表示
            inflater.inflate(R.menu.listview_item2_menu, popup.getMenu());
        }
        popup.show();
        return true;
    }
}
PopupMenuクラスのコンストラクタには、ポップアップメニューを関連付けるViewを指定します。その後、PopupMenu.getMenuInflater()でMenuInflaterを取得、MenuInflater.inflate()にメニューリソースを指定してメニューを作成します。この流れは他のメニューと同じですね。

上記コードを実際に動作させたときのキャプチャがこちら。

1番目の項目を長押したときは1番目の項目すぐ下にlistview_item1_menuが表示され、2番目の項目を長押ししたときは2番目の項目のすぐ下にlistview_item2_menuが表示されました。

2013年8月14日水曜日

[Android]contextual action modeを使う。

Android3.0以上のバージョンでは、コンテキストメニューの代わりにcontextual action modeが使用できます。

contextual action mode時は以下のような画面になります。

コンテキストメニューはポップアップ形式でメニュー項目が表示されますが、contextual action modeではアクションバーに項目が表示されます。また、コンテキストメニュー表示中はメニュー以外のUI要素は操作不可ですが、contextual action modeならメニュー項目表示中でもUI要素を操作可能です。

以下がcontextual action modeを使用するサンプルコードです。
チェックボックス付きリストビューの項目を長押ししたときにaction modeに移行し、action modeのメニュー1選択時に現在チェックされている項目一覧をToastで表示しています( 1個も選択されていない時のメッセージが変ですが、サンプルなので大目に見てください(^^;) )
public class CAMSampleActivity extends ListActivity {

    private final CAMSampleActivity activity = this;
    private final String[] m_listViewItem = { "ListItem1", "ListItem2", "ListItem3", "ListItem4", "ListItem5" };

    private ActionMode m_actionMode = null;
    private final ActionMode.Callback actionModeCallback = new
        ActionMode.Callback() {

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                // action modeの表示直前に呼ばれる
                return true;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                // action modeが終了し破棄されるときに呼ばれる
                m_actionMode = null;
            }

            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                // action modeが生成される時に呼ばれる。
                // ここでメニューリソースからメニューを作成。
                MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.camsample, menu);
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                // action modeで項目が選択されたときに呼ばれる
                switch (item.getItemId()) {
                    case R.id.menu1:
                        // チェックボックスがチェック状態の項目を取得し、Toastを表示
                        SparseBooleanArray checkedItemPos = activity.getListView().getCheckedItemPositions();
                        StringBuilder builder = new StringBuilder();
                        for (int i = 0; i < checkedItemPos.size(); i++) {
                            if (checkedItemPos.valueAt(i)) {
                                builder.append(checkedItemPos.keyAt(i) + 1);
                                builder.append(',');
                            }
                        }
                        builder.append("行目の項目がチェックされています。");
                        Toast.makeText(activity, builder.toString(), Toast.LENGTH_LONG).show();

                        // ActionMode.finish()でaction modeを終了させる
                        mode.finish();
                        return true;
                    default:
                        return false;
                }
            }
        };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // チェックボックス付きのリストビューを作成
        ArrayAdapter adapter =
            new ArrayAdapter(this, android.R.layout.simple_list_item_multiple_choice, m_listViewItem);
        this.setListAdapter(adapter);
        this.getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

        // リストビューの項目を長押ししたときにaction modeに移行するようにリスナを設定
        this.getListView().setOnItemLongClickListener(new OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
                // action mode作成済みの場合は何もしない
                if (m_actionMode != null) {
                    return false;
                }

                // Activity.startActionMode()でaction modeに移行
                m_actionMode = activity.startActionMode(actionModeCallback);
                return true;
            }
        });
    }
}
contextual action modeにするには、Activity.startActionMode()にActionMode.Callbackを渡すだけです。contextual action modeの動作はActionMode.Callbackのオーバーライドメソッドで定義します。

上記のサンプルを実際に動かすと以下のような動作になります。
1. リストビューの項目を長押ししてcontextual action modeに移行した画面


2. 1の状態でチェックボックスをチェックし、メニュー1(アイコン)を押下したときの画面


このようにcontextual action modeなら複数のUI要素に対し、一括処理が可能です。

2013年8月5日月曜日

[Android]コンテキストメニューを表示する。

コンテキストメニューとはViewを長押ししたときに表示されるフローティングメニューです。

コンテキストメニューを表示するには、まずメニューを定義します。
メニュー定義の方法はこちら→「オプションメニューを表示する。その1。」

今回はcontext_menu_sample.xmlというファイル名で以下のようなメニューを定義しました。
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/menu1" android:title="menu1" ></item>
    <item android:id="@+id/menu2" android:title="menu2" ></item>
    <item android:id="@+id/menu3" android:title="menu3" ></item>
    <item android:id="@+id/menu4" android:title="menu4" ></item>
</menu>

次にコード上でコンテキストメニューの表示とメニュー項目選択時の処理を記述します。
おおまかな処理の流れは、
1. Activity.registerForContextMenu()でコンテキストメニューを表示するViewを登録
2. Activity.onCreateContextMenu()でメニューを作成
3. Activity.onContextItemSelected()でメニュー項目が選択されたときの処理を行う
です。
public class ContextMenuSampleActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_context_menu_sample);

        // registerForContextMenu()でコンテキストメニューを表示するViewを登録
        TextView textView = (TextView) findViewById(R.id.editText);
        registerForContextMenu(textView);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        // registerForContextMenu()で登録したViewが長押しされると、
        // onCreateContextMenu()が呼ばれる。ここでメニューを作成する。
        super.onCreateContextMenu(menu, v, menuInfo);
        getMenuInflater().inflate(R.menu.context_menu_sample, menu);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        // コンテキストメニューで項目が選択されるとonContextItemSelected()が呼ばれる。
        switch (item.getItemId()) {
            case R.id.menu1:
                Toast.makeText(this, "menu1 selected.", Toast.LENGTH_LONG).show();
                return true; // 処理に成功したらtrueを返す
            default:
                return super.onContextItemSelected(item);
        }
    }
}
このサンプルではテキストビューにコンテキストメニューを設定しています。

実際に動かしてみます。テキストビューを長押しすると、以下のようなメニューが表示されます。