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

2013年10月29日火曜日

[Android]Spinnerでドロップダウン形式のリストを表示する。

Spinnerを使うとドロップダウン形式のリストを表示できます。

リスト項目の設定方法はいろいろありますが、今回はリソース内で項目を定義する方法で実装してみました。
まずはリソースで項目を定義します。プロジェクトの[res]-[values]-strings.xmlに、resources要素の子要素として以下の要素を追加します。
<string-array name="week_array">
    <item>Sun</item>
    <item>Mon</item>
    <item>Tue</item>
    <item>Wed</item>
    <item>Thu</item>
    <item>Fri</item>
    <item>Sat</item>
</string-array>
次にコード上でSpinnerに定義した項目を設定します。設定はActivity.onCreate()あたりですればOKです。
Spinner spinner = (Spinner) findViewById(R.id.spinner);
// リソースからArrayAdapterを作成し、Spinnerにセット
ArrayAdapter<charsequence> adapter =
    ArrayAdapter.createFromResource(getApplicationContext(), R.array.week_array,
        android.R.layout.simple_spinner_item);
spinner.setAdapter(adapter);

これで以下のようなドロップダウン形式のリストが表示されます。
※デフォルトの色だと見づらかったので背景を黒にしてます。ドロップダウン部分の背景色はpopupBackground属性で設定できます。


次に、項目選択時の処理を実装してみます。
項目選択時のイベントを受け取るにはOnItemSelectedListenerインターフェースを実装する必要があります。
public class MainActivity extends Activity implements OnItemSelectedListener {

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

        initSpinner();
    }

    private void initSpinner() {
        Spinner spinner = (Spinner) findViewById(R.id.spinner);
        // リソースからArrayAdapterを作成し、Spinnerにセット
        ArrayAdapter<CharSequence> adapter =
            ArrayAdapter.createFromResource(getApplicationContext(), R.array.week_array,
                android.R.layout.simple_spinner_item);
        spinner.setAdapter(adapter);
        // Spinnerにリスナをセット
        spinner.setOnItemSelectedListener(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        Spinner spinner = (Spinner) findViewById(R.id.spinner);
        String selected = (String) spinner.getItemAtPosition(pos);

        Toast.makeText(getApplicationContext(), selected + "を選択しました。", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }
}
Spinner.setOnItemSelectedListener()でOnItemSelectedListenerインターフェースを実装したリスナを設定することで、そのSpinnerの項目選択イベントを受け取れるようになります。項目選択時の処理はonItemSelected()内に実装します。上記コードでは、Spinner.getItemAtPosition()を用いて選択されている位置(pos)の項目(文字列)を取得し、Toastで表示しています。

実際に動作させると↓のように選択した項目がToastで表示されます。

2013年9月30日月曜日

[Android]ToggleButton、Switchを使う。

ToggleButton、Switchは、2つの状態を切り替えるのに使います。
※Swithcは、Android4.0(API level 14)以上から使用できます。

今回は状態がONになった時にメッセージを表示するサンプルを作ってみました。
まずはActivityにToggleButton、Switchを配置し、それぞれのonClick属性にオンクリックメソッドを設定します。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ToggleButton
        android:id="@+id/tglOnOff"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="115dp"
        android:text="ToggleButton"
        android:onClick="onToggleButtonClicked" />

    <Switch
        android:id="@+id/swtOnOff"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tglOnOff"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="72dp"
        android:text="On/Off"
        android:onClick="onSwitchClicked" />

</RelativeLayout>

次にActivityでオンクリックメソッドを実装します。
public void onToggleButtonClicked(View view) {
    ToggleButton tglOnOff = (ToggleButton) view;
    if (tglOnOff.isChecked()) { // ON状態になったとき
        Toast.makeText(getApplicationContext(), "ToggleButtonがONになりました。", Toast.LENGTH_SHORT).show();
    }
}

public void onSwitchClicked(View view) {
    Switch swtOnOff = (Switch) view;
    if (swtOnOff.isChecked()) { // ON状態になったとき
        Toast.makeText(getApplicationContext(), "SwitchがONになりました。", Toast.LENGTH_SHORT).show();
    }
}
見てわかる通り、ON状態(isCheckedがtrueを返すとき)になったときにメッセージを表示しています。
ActivityにToggleButtonとSwitchはそれぞれ1つのみなので、getId()による識別処理は行っていません。

実際に動作させたときにキャプチャがこちら。

2013年9月15日日曜日

[Android]ラジオボタンを使う。

ラジオボタンは排他的な選択項目を表します。

基本的な使い方はチェックボックスと同じです。
今回もラジオボタンがチェックされたときにメッセージを表示するサンプルを作ってみました。
まずはActivityにRadioGroupを配置し、子要素であるRadioButtonのonClick属性にオンクリックメソッドを設定します。
※RadioButtonをRadioGroup無しで配置することもできますが、その場合自前で排他処理を実装しなければなりません。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <RadioGroup
        android:id="@+id/rdoGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="136dp" >

        <RadioButton
            android:id="@+id/rdoItem1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:text="Item1"
            android:onClick="onRadioButtonClicked" />

        <RadioButton
            android:id="@+id/rdoItem2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Item2"
            android:onClick="onRadioButtonClicked" />

        <RadioButton
            android:id="@+id/rdoItem3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Item3"
            android:onClick="onRadioButtonClicked" />
    </RadioGroup>

</RelativeLayout>

次にActivityでオンクリックメソッドを実装します。
public void onRadioButtonClicked(View view) {
    // ラジオボタンの選択状態を取得
    RadioButton radioButton = (RadioButton) view;
    // getId()でラジオボタンを識別し、ラジオボタンごとの処理を行う
    boolean checked = radioButton.isChecked();
    switch (radioButton.getId()) {
        case R.id.rdoItem1:
            if (checked) {
                Toast.makeText(getApplicationContext(), "項目1が選択状態になりました。", Toast.LENGTH_SHORT).show();
            }
            break;
        case R.id.rdoItem2:
            if (checked) {
                Toast.makeText(getApplicationContext(), "項目2が選択状態になりました。", Toast.LENGTH_SHORT).show();
            }
            break;
        case R.id.rdoItem3:
            if (checked) {
                Toast.makeText(getApplicationContext(), "項目3が選択状態になりました。", Toast.LENGTH_SHORT).show();
            }
            break;
        default:
            break;
    }
}
こちらもチェックボックスと同じようにして選択状態とIDを取得できます。

実際に動作させたときにキャプチャがこちら。

Item1を選択後、Item3を選択したときのキャプチャですが、Item3を選択したときにItem1の選択状態が自動で非選択になります。

2013年9月3日火曜日

[Android]チェックボックスを使う。

チェックボックスは設定の有効/無効の切り替え等に使われます。

今回はチェックボックスでチェックされたときにメッセージを表示するサンプルを作ってみました。
まずはActivityに2個のチェックボックスを配置し、onClick属性にオンクリックメソッドを設定します。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <CheckBox
        android:id="@+id/chkOption1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="60dp"
        android:text="Option1"
        android:onClick="onCheckboxClicked" />

    <CheckBox
        android:id="@+id/chkOption2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/chkOption1"
        android:layout_below="@+id/chkOption1"
        android:text="Option2"
        android:onClick="onCheckboxClicked" />

</RelativeLayout>

次にActivityでオンクリックメソッドを実装します。
public void onCheckboxClicked(View view) {
    // チェックボックスのチェック状態を取得
    CheckBox checkBox = (CheckBox) view;
    boolean checked = checkBox.isChecked();
    // CheckBox.getId()でチェックボックスを識別し、チェックボックスごとの処理を行う
    switch (checkBox.getId()) {
        case R.id.chkOption1:
            if (checked) {
                Toast.makeText(getApplicationContext(), "Option1を選択状態にしました。", Toast.LENGTH_SHORT).show();
            }
            break;
        case R.id.chkOption2:
            if (checked) {
                Toast.makeText(getApplicationContext(), "Option2を選択状態にしました。", Toast.LENGTH_SHORT).show();
            }
            break;
    }
}
チェックボックスがチェックされているかどうかはCheckBox.isChecked()で取得できます。

実際に動作させたときにキャプチャがこちら。

チェックボックスなので、Option1を選択したままOption2も選択可能です。

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);
        }
    }
}
このサンプルではテキストビューにコンテキストメニューを設定しています。

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

2013年7月28日日曜日

[Android]オプションメニューを表示する。その2。

Android3.0以降ではハードキーの無い機種が存在します。そのような機種の場合、オプションメニューの表示位置は画面下部ではなく、アクションバー右端のアイコン(Action Overflow)押下時のリストになります。

Action Overflowを押下すると、メニューがリストとなって表示されます。

また、メニューxmlのitemタグにshowAsAction属性を指定することで、アクションバーへの表示方法を変更することができます。showAsAction属性の値は以下の通りです。
ifRoomアクションバーに空きがあれば項目をアクションバーに表示する
withTextアクションバーに空きがあればアイコンと共にandroid:titleで指定した文字列を表示する
neverアクションバーに表示しない(常にAction Overflowでの表示とする)
always常にアクションバーに表示する(アクションバーに空きが無い場合、他のUI要素に重なった状態で表示される)
collapseActionViewActionViewを折りたたみ可能にする

例として次のようなメニューを定義します。
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/menu1" android:titleCondensed="m1" android:title="menu1" android:icon="@drawable/menu_icon" android:showAsAction="ifRoom|withText" />
    <item android:id="@+id/menu2" android:title="menu2" android:titleCondensed="m2" />
</menu>
これがどんなふうに表示されるかというと・・・

このようにandroid:showAsAction="ifRoom|withText"を指定したmenu1はアクションバーに表示され、指定していないmenu2はAction Overflowに格納されます。menu1はwithTextを設定していますが、画面幅が小さいためアイコンのみの表示になっています。

2013年7月16日火曜日

[Android]オプションメニューを表示する。その1。

オプションメニューとは、メニューボタンを押下したときに画面下部に出てくるメニューです(※ハードキーのメニューボタンが無い機種の場合、画面下部ではなくアクションバーのAction Overflowによって表示されます)。

メニューを表示するには、まずxmlファイルでメニューを定義します。
Eclipseのメニュー「ファイル」-「新規」-「その他」から「Android XML ファイル」を選択します。


次の画面では、リソースタイプを「Menu」にし、ファイル名を入力します。


完了すると、プロジェクトの「res」-「menu」フォルダにxmlファイルが生成されるので、ここにメニューを定義します。
以下がメニューxmlの例です。
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/menu1" android:titleCondensed="m1" android:title="menu1" android:icon="@drawable/menu_icon">
        <menu>
            <item android:id="@+id/menu1_1" android:titleCondensed="m1_1" android:title="Menu1_1"/>
            <item android:id="@+id/menu1_2" android:titleCondensed="m1_2" android:title="Menu1_2"/>
        </menu>
    </item>
    <item android:id="@+id/menu2" android:title="menu2" android:titleCondensed="m2" android:icon="@drawable/menu_icon">
        <menu>
            <group android:id="@+id/menu2_g1" android:checkableBehavior="all">
                <item android:id="@+id/menu2_g1_1" android:title="Menu2_g1_1"/>
                <item android:id="@+id/menu2_g1_2" android:title="Menu2_g1_2"/>
            </group>
            <group android:id="@+id/menu2_g2">
                <item android:id="@+id/menu2_g2_1" android:title="Menu2_g2_1"/>
            </group>
        </menu>
    </item>
</menu>
menuタグの中にitemタグでメニューの項目を定義します。menuタグは入れ子にすることができ、入れ子にされたmenuはサブメニューになります。また複数の項目をgroupタグで囲うことでグループ化できます。グループ化すると、複数の項目を同時に非表示にする等の操作が簡単になります。

itemタグの主な属性は以下の通りです。
title項目の表示文字列
titleCondensed項目の省略表示文字列。表示する際にtitleに設定した文字列だと長すぎる場合、こちらが表示される。
icon項目のアイコン
showAsActionアクションバーでの表示方法
checkabletrueなら項目にチェックボックスを表示する
orderInCategorygroup内での項目並び順

今回は上記xml例で定義したメニューをAndroid2.3で表示してみます。
まず、オプションメニューを表示するActivityのonCreateOptionsMenu()をオーバーライドします。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.option_menu_sample, menu);
    return true;
}
MenuInflater.inflate()の第1引数に表示するメニューを定義したメニューリソースを指定します(上記コードの場合、option_menu_sample.xmlに定義したメニューを表示します)。

次に、メニュー項目を選択されたときの動作を定義します。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.menu1_1:
            Toast.makeText(this,
                "Menu1_1 selected.", Toast.LENGTH_SHORT).show();
            return true;
        case R.id.menu2_g1_1:
        case R.id.menu2_g1_2:
            // チェックボックスは自前でチェック状態を反転させる必要あり
            if (item.isChecked()) {
                item.setChecked(false);
            }
            else {
                item.setChecked(true);
            }
            return true;
        default:
            // メニュー選択イベントをハンドリングしないときは、
            // superクラスのonOptionsItemSelected()を呼び出す。
            return super.onOptionsItemSelected(item);
    }
}
上記のようにActivityのonOptionsItemSelected()をオーバーライドし、選択項目に応じた処理を記述します。
MenuItem.getItemId()で選択項目のIDが取得できるので、それをswitchのキーにします。
上記の例では、menu1_1を選択されたときにToastを表示し、menu2_g1_1、menu2_g1_2を選択されたときはチェックボックスの状態をトグルしています。また、コメントにも書いていますが、選択イベントをハンドリングしないときはsuper.onOptionsItemSelected()を呼び出すようにします。

上記のオプションメニューを表示させると以下のようになります。

一番左がメニューボタンを押下したときの表示、真ん中がmenu1を選択したときの表示、右がmenu2を選択したときの表示です。このようにサブメニューは親の項目を選択したときにポップアップで表示されます。
ちなみにアイコンは自作の適当アイコンです(´ー`A;)実際のアプリでは項目を表した適切な画像を使いましょう。

2013年5月27日月曜日

[Android]通知を表示する。その4。

通知ではプログレスバーを表示することもできます。

以下のサンプルコードでは、onClickBtnShowNotification()で通知を表示し、
onClickBtnUpdate()でプログレスバーを進め、onClickBtnFinish()でプログレスバーを終了しています。
public class MainActivity extends Activity {

    private final int notifyID = 100;
    private NotificationCompat.Builder m_builder;

    private int m_count = 0;

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public void onClickBtnShowNotification(View view) {
        // 通知を表示する
        m_builder = new NotificationCompat.Builder(this);
        m_builder.setSmallIcon(R.drawable.ic_launcher);
        m_builder.setContentTitle("notification title");
        m_builder.setContentText("notification text.");
        m_builder.setTicker("notification ticker");

        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.notify(notifyID, m_builder.build());
    }

    public void onClickBtnUpdate(View view) {
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        // 第1引数に最大値、第2引数に現在値する。
        // 第3引数は値増加型プログレスバーの場合、falseを設定。
        m_builder.setProgress(100, m_count++, false);
        // マーキースタイルのプログレスバーにするには↓の引数でsetProgress()を呼び出す
        // m_builder.setProgress(0, 0, true);
        
        // 通知を更新する
        manager.notify(notifyID, m_builder.build());
    }

    public void onClickBtnFinish(View view) {
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        // プログレスバーを終了させるには、以下の引数でsetProgress()を呼び出す
        m_builder.setProgress(0, 0, false);
        
        // 通知を更新する
        manager.notify(notifyID, m_builder.build());
    }
}
サンプルなのでボタンクリックでプログレスバーを進めていますが、
実際は別スレッドの処理内でsetProgress()を呼び出すことになると思います。

2013年5月20日月曜日

[Android]通知を表示する。その3。

前回のエントリからの続きで、通知ドロワーから起動するアクティビティにbackスタックを設定する方法です。

まず、マニフェストファイルでアクティビティの階層を定義します。
以下の通りに子アクティビティにmeta-dataを追加します。
<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="com.example.notificationsample.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name="com.example.notificationsample.NotificationActivity"
        android:label="@string/title_activity_notification" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
        </intent-filter>
        <!-- 子アクティビティに以下のような meta-dataを追加する。以下の場合、親アクティビティにMainActivityを設定している。  -->
        <meta-data 
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".MainActivity"/>
    </activity>
</application>

次にTaskStackBuilderでbackスタックを作成し、PendingIntentを取得します。
その取得したPendingIntentをsetContentIntent()の引数に指定して通知を表示します。
// 以下は、通知ドロワークリック時にNotificationActivityを表示する場合のコード

// TaskStackBuilderを作成
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(NotificationActivity.class); // スタックにNotificationActivityの親アクティビティを追加
// TaskStackBuilder.addNextIntent()でNotificationActivityをスタックに追加
Intent intent = new Intent(this, NotificationActivity.class);
stackBuilder.addNextIntent(intent);

// TaskStackBuilder.getPendingIntent()でPendingIntentを取得
PendingIntent pendingIntent =
    stackBuilder.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT);

// 通知を表示
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("notification title");
builder.setContentText("notification text.");
builder.setTicker("notification ticker");
builder.setContentIntent(pendingIntent);

int notifyID = 1;
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(notifyID, builder.build());
上記のコードで表示した通知ドロワーをクリックすると、まずNotificationActivityが表示されます。
そこでbackボタンを押すと、NotificationActivityの親アクティビティであるMainActivityが表示されます。

TaskStackBuilder.addNextIntent()は複数回呼び出すことができ、
最後に渡したIntentのアクティビティが最初に表示されるアクティビティになります。
例えば、.addNextIntent(A)→.addNextIntent(B)→.addNextIntent(C)と呼び出すと、
A、B、Cの順にスタックに追加され、

[ドロワークリック]→C表示→[backボタン押下]→B表示→[backボタン押下]→
A表示→[backボタン押下]→Aの親アクティビティ表示

という動作になります。

2013年5月13日月曜日

[Android]通知を表示する。その2。

通知ドロワーでの通知クリック時に、アクティビティを起動させることができます。
方法は簡単で、PendingIntentを作成しNotificationCompat.Builder.setContentIntent()に渡すだけです。
以下がそのコード。
// まず画面遷移時と同様に、起動するアクティビティのintentを作成。
// putExtra()でデータを渡すことも可能。
Intent intent = new Intent(this, NotificationActivity.class);
intent.putExtra("text", "Notification Activity");

// アクティビティが新しい空のタスクで起動するようにフラグを設定
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

// intentからPendingIntentを作成
PendingIntent pendingIntent =
    PendingIntent.getActivity(this, 0, intent,
                              PendingIntent.FLAG_CANCEL_CURRENT);

// NotificationCompat.Builder.setContentIntent()に作成したPendingIntentを渡し、
// 通知を表示する。
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher); // 通知ドロワーに表示するアイコンを設定
builder.setContentTitle("notification title");
builder.setContentText("notification text.");
builder.setTicker("notification ticker");
builder.setContentIntent(pendingIntent);

int notifyID = 1;
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(notifyID, builder.build());
※putExtra()で渡したデータは、以下の方法でNotificationActivity内で取得できます。
Intent intent = getIntent();
String text = intent.getStringExtra("text");

上記コードによって、通知ドロワーに表示された通知をクリックすると、NotificationActivityが起動します。
ただし、NotificationActivity単独での起動となります。そのため、NotificationActivityでbackボタンを押すと
ホーム画面(または通知ドロワー表示前の画面)に戻ります。

通知の種類によってはこのような動作ではなく、あたかもアプリが起動されていたかのような動作にしたい場合があります(例えばGmailの場合、通知ドロワーをクリックすると受信メッセージが表示されますが、そこでbackボタンを押すと受信トレイが表示されます。この動作はGmailが通常起動されたときと同じ動作ですよね)。

このような場合、backスタックを作成する必要があるのですが、その方法は次回のエントリにて。

2013年5月8日水曜日

[Android]通知を表示する。その1。

通知をを表示するにはNotificationCompat.Builderを使います。
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);  // 左端に表示されるアイコン
builder.setContentTitle("notification title"); // 通知のタイトル
builder.setContentText("notification text.");  // 通知メッセージ
builder.setTicker("notification ticker");      // 通知バーに表示されるティッカー文字列

int notifyID = 1;
// NotificationManager.notify()に、通知IDとNotificationを渡すことで、通知が表示される
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(notifyID, builder.build());
上記コードでは、以下のような通知が表示されます。


場合によっては、通知を通知バーから削除不可にしたい場合があります(プログレスバーの表示中とか)。
そういう場合は、NotificationCompat.Builder.setOngoing()を使います。
// 通知を通知バーから削除できなくする
builder.setOngoing(true);

// 引数falseで呼び出せば削除できるようになる
builder.setOngoing(false);

2013年4月10日水曜日

[Android]Toastを表示する。

Toastとは、一定時間が経過すると自動的に消えるポップアップメッセージです。
メールの問い合わせをしたときに「新着メールがn件あります。」と出てきますが、あれがToastです。
そのToastの表示方法をまとめてみました。

・一般的なToast

よく見る画面下部に表示されるToastです。

Toast.makeText(getApplicationContext(), "toast message.", Toast.LENGTH_LONG).show();
3番目の引数はToastの表示時間を指定します。指定できる値は、Toast.LENGTH_LONGかToast.LENGTH_SHORTの2種類です。

・表示位置を指定したToast

Toastの表示位置を指定することもできます。

Toast toast = Toast.makeText(getApplicationContext(), "toast message.", Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Toast.setGravity()で位置を指定します。
1番目の引数はGravity定数、2、3番目の引数はオフセット値です。
例えば、画面左上ちょっと下に表示したければ setGravity(Gravity.TOP | Gravity.LEFT, 0, 50) とします。

・独自レイアウトのToast

自前で定義したレイアウトのToastです。
ダイアログのときと同様、まずはレイアウトを定義します。


xmlファイルの方はこんな感じです。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/CustomToastRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#60FF6633" >

    <ImageView
        android:id="@+id/ImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/MessageTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/ImageView"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="18dp"
        android:layout_toRightOf="@+id/ImageView"
        android:text="Medium Text"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>

コードも基本的にはダイアログのときと同じです。
// xmlリソースからViewを生成するために、LayoutInflaterを取得
LayoutInflater inflater = getLayoutInflater();
// xmlリソースからViewを生成
View layout = inflater.inflate(R.layout.custom_toast, (ViewGroup) findViewById(R.id.CustomToastRoot));
// CustomToastのTextViewにメッセージを設定
TextView text = (TextView) layout.findViewById(R.id.MessageTextView);
text.setText("custom toast message.");

// setView()でxmlリソースから生成したViewを設定し、Toastを表示
Toast toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.TOP, 0, 180);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();

こんな感じのToastになりました。

2013年4月1日月曜日

[Android]ダイアログを表示する。その3。

「いろんな種類のダイアログの表示方法についてまとめてみました。」のその3です。

・独自レイアウトのダイアログ

Androidでは自前で定義したレイアウトのダイアログも作成できます。
まずは、プロジェクトの[res] - [layout]にxmlファイルを追加し、レイアウトを定義します。

Graphical Layoutタブで、以下のようなレイアウトを定義しました。


xmlファイルの方はこんな感じです。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:measureWithLargestChild="false"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/HeaderText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:text="volume control"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/MinusButton"
            style="?android:attr/buttonStyle"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:minWidth="48dip"
            android:text="-" />

        <SeekBar
            android:id="@+id/VolumeSeekBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="0.93" />

        <Button
            android:id="@+id/PlusButton"
            style="?android:attr/buttonStyle"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:minWidth="48dp"
            android:text="+" />
    </LinearLayout>

</LinearLayout>

上記レイアウトのダイアログを作成するコードは次のようになります。
// ※thisはActivity自身

// xmlリソースからViewを生成するために、LayoutInflaterを取得
LayoutInflater inflater = this.getLayoutInflater();
// xmlリソースからViewを生成
View customDialogView = inflater.inflate(R.layout.custom_dialog, null);

// AlertDialog.Builder.setView()で生成したViewを設定
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(customDialogView)
        .setPositiveButton("Yes", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }

        })
        .setNegativeButton("No", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        });
// ダイアログを表示
builder.show();

作成されたダイアログはこんな感じになります。


今気づいたんですが、このダイアログでYes/Noっておかしいですね。。

2013年3月25日月曜日

[Android]ダイアログを表示する。その2。

「いろんな種類のダイアログの表示方法についてまとめてみました。」のその2です。

・リスト選択のダイアログ

リスト項目から1つを選択するタイプのダイアログです。

// リスト項目を定義
final String[] listItems = new String[] { "small", "medium", "large" };

AlertDialog.Builder builder = new AlertDialog.Builder(this); // ※thisはActivity自身
builder.setTitle("サイズ選択")
        .setItems(listItems, new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("selected size => " + listItems[which]);
            }
        });

builder.show();
AlertDialogと同じくAlertDialog.Builderを使います。
AlertDialog.Builder.setItems()でリスト項目を設定することで、リスト選択のダイアログになります。
リストで何が選択されたかは、onClick()の第2引数whichに格納されているindexで分かります
(上のコードの場合、"small"が選択されたらwhichには0が格納されます)。


・チェックボックスのダイアログ

チェックボックスで複数選択するタイプのダイアログです。

// チェック項目を定義
final String[] checkItems = new String[] { "rock", "pop", "metal", "jazz" };

AlertDialog.Builder builder = new AlertDialog.Builder(this); // ※thisはActivity自身
builder.setTitle("ジャンル選択")
        .setMultiChoiceItems(checkItems, null, new OnMultiChoiceClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                // ここでチェック状態を一時保存しておく
                if (isChecked) {
                    System.out.println("checked => " + checkItems[which]);
                }
                else {
                    System.out.println("unchecked => " + checkItems[which]);
                }
            }
        })
        .setPositiveButton("OK", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // ここで一時保存しておいた選択状態を正式保存する。
            }

        })
        .setNegativeButton("Cancel", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // ダイアログでの変更を破棄するため、何もしない。
            }
        });

builder.show();
これもAlertDialog.Builderを使います。
AlertDialog.Builder.setMultiChoiceItems()で項目を設定することで、チェックボックスのダイアログになります。

OnMultiChoiceClickListener.onClick()はチェックボックスのON/OFFが変更されるたびに呼ばれ、
引数whichには変更された項目のindex、引数isCheckedには変更後チェック状態が格納されています。
onClick()内で一時変数に各項目のチェック状態を保存しておき、「肯定ボタン」が押されたときに一時変数を本体や設定に反映するというのが一般的な使い方だと思います。


・ラジオボタンのダイアログ

ラジオボタンで単数選択するタイプのダイアログです。

// 選択項目を定義
final String[] radioItems = new String[] { "classic", "modern", "blue", "green", "black", "special" };

AlertDialog.Builder builder = new AlertDialog.Builder(this); // ※thisはActivity自身
builder.setTitle("テーマ選択")
        .setSingleChoiceItems(radioItems, 0, new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // ここで選択状態を一時保存しておく
                System.out.println("selected theme => " + radioItems[which]);
            }
        })
        .setPositiveButton("OK", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // ここで一時保存しておいた選択状態を正式保存する。
            }

        })
        .setNegativeButton("Cancel", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // ダイアログでの変更を破棄するため、何もしない。
            }
        });

builder.show();
ほとんどチェックボックスのダイアログと同じですが、AlertDialog.Builder.setSingleChoiceItems()で項目を設定するところが異なります。

2013年3月18日月曜日

[Android]ダイアログを表示する。その1。

いろんな種類のダイアログの表示方法についてまとめてみました。

・AlertDialog

まずは標準的なダイアログであるAlertDialogです。

// タイトル、メッセージ、ボタンを設定
AlertDialog.Builder builder = new AlertDialog.Builder(this); // ※thisはActivity自身
builder.setTitle("Alert Dialog") // タイトル
        .setMessage("message.") // メッセージ
        // 肯定ボタン
        .setPositiveButton("Yes", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("YesButtoon clicked in alert dialog.");
            }

        })
        // 否定ボタン
        .setNegativeButton("No", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("NoButtoon clicked in alert dialog.");
            }
        })
        // 中立ボタン
        .setNeutralButton("Cancel", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("CancelButtoon clicked in alert dialog.");
            }
        });
// ダイアログを表示
builder.show();
AlertDialog.Builderで各設定を行い、AlertDialog.Builder.show()で表示します。
キャプチャ画像を見れば分かりますが、ボタンの並びは左から「否定ボタン」「中立ボタン」「肯定ボタン」になります。
※但し、この並びはAndroidのバージョンで変わる可能性があるので(実際、たしか4.0になった時に否定と肯定の位置が入れ替わってます)、左側ボタンを設定したいからsetNegativeButton()を使うといった実装は止めましょう。


・DatePickerDialog

DatePickerDialogは日付を選択するためのダイアログです。

// 日付が設定されたときのリスナを定義
OnDateSetListener listener = new OnDateSetListener() {
    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        System.out.println("picked date => " + year + "/" + monthOfYear + "/" + dayOfMonth);
    }
};

// 初期表示日付を今日に設定してダイアログを表示
Calendar now = Calendar.getInstance();
new DatePickerDialog(this, // ※thisはActivity自身
                        listener,
                        now.get(Calendar.YEAR),
                        now.get(Calendar.MONTH),
                        now.get(Calendar.DATE)).show();
onDateSet()はダイアログで「設定ボタン」が押されたときに呼ばれます。
ダイアログで設定された日付が引数に格納されているので、ここで必要に応じて保存処理等を行います。


・TimePickerDialog

TimePickerDialogは時刻を選択するためのダイアログです。

// 時間が設定されたときのリスナを定義
OnTimeSetListener listener = new OnTimeSetListener() {
    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        System.out.println("picked time => " + hourOfDay + " : " + minute);
    }
};

// 初期表示時間を現時刻に設定してダイアログを表示
Calendar now = Calendar.getInstance();
new TimePickerDialog(this, // ※thisはActivity自身
                        listener,
                        now.get(Calendar.HOUR_OF_DAY),
                        now.get(Calendar.MINUTE),
                        true).show();
基本的にDatePickerDialogと同じです。OnTimeSetListener()はダイアログで「設定ボタン」が押されたときに呼ばれるので、ここで保存処理等を行います。
コンストラクタの5番目の引数is24HourViewは、24時間表記かどうかを指定します。trueにすると24時間表記、falseにすると12時間表記になります。

2013年3月11日月曜日

[Android]ブロードキャストインテントを受け取る

Androidには明示的/暗黙的インテントの他にブロードキャストインテントという仕組みがあります。
明示的/暗黙的インテントは特定のアクティビティのみが受信するのに対して、
ブロードキャストインテントは複数の受信者(レシーバ)が受信することができます。

ブロードキャストインテントの一例を挙げます。例えば、
・バッテリー残量が残り少なくなった
・スクリーンがONになった
といったイベントが発生したとき、Androidシステムはブロードキャストインテントを送信します。
複数の受信者はこれらのインテントを受け取ることで、イベント発生を知ることができ、各々の適切な処理を行うことができます。

システムから送信されるブロードキャストインテントには以下のようなものがあります。

ACTION_TIME_TICK現在時刻が変わった(分ごとに送信)。
ACTION_BOOT_COMPLETEDシステムの起動が完了した。
ACTION_BATTERY_CHANGEDバッテリーの状態が変化した。

この他にも多くのブロードキャストインテントが定義されています。
(http://developer.android.com/reference/android/content/Intent.htmlを参照)

また、独自のインテントをブロードキャストすることもできます。
// 独自のインテントをブロードキャスト送信
Intent myBroadcast = new Intent("com.example.broadcastsender.BUTTON_CLICKED");
sendBroadcast(myBroadcast);

ブロードキャストインテントはブロードキャストレシーバに対して送信されるので、
受信するにはブロードキャストレシーバを登録する必要があります。

ブロードキャストレシーバを登録する

登録方法は2通りあります。

1.registerReceiver()で動的に登録する
以下のサンプルコードは、ACTION_TIME_TICKを受信したら現在時刻をTextViewに表示するものです。
ACTION_TIME_TICKのレシーバをonCreate()内で動的に登録しています。
public class MainActivity extends Activity {

    private TextView textView;
    BroadcastReceiver receiver;

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

        textView = (TextView) findViewById(R.id.textView);

        // ACTION_TIME_TICKを受信するレシーバを登録する
        IntentFilter filter = new IntentFilter(Intent.ACTION_TIME_TICK);
        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // ACTION_TIME_TICKを受信したら、TextViewに時刻を表示する
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd kk:mm:ss");
                textView.setText(dateFormat.format(new Date()));
            }
        };
        registerReceiver(receiver, filter);
    }

    @Override
    protected void onDestroy() {
        // 登録したレシーバを解除する
        unregisterReceiver(receiver);
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
}

2. マニフェストファイルで静的に登録する
静的に登録するには、マニフェストファイルでレシーバを登録します。
登録するレシーバはBroadcastReceiverを継承して作成しておきます。
public class BatteryLowReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // BATTERY_LOWを受信したらメッセージを表示
        Toast.makeText(context, "Battery low!", Toast.LENGTH_LONG).show();
    }
}
マニフェストファイルでは、受信するインテントとレシーバを設定します。
        <receiver android:name="com.example.broadcastreceivesample.BatteryLowReceiver">
            <intent-filter ><action android:name="android.intent.action.BATTERY_LOW"/></intent-filter>
        </receiver>

2013年2月25日月曜日

[Android]暗黙的インテントを受け取る

前回のエントリでは、暗黙的インテントを使って他アプリを起動する方法について書きました。
今回は暗黙的インテントを受け取る側(起動される側)についてです。

暗黙的インテントを受け取れるようにするには、マニフェストファイルにインテントフィルタを追加します。
インテントフィルタとは、処理できる暗黙的インテントの種類をシステムに伝えるためのものです。
<activity    
    android:name="com.example.allowingotherappstostartsample.MainActivity"
    android:label="@string/app_name" >

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>

    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="http" />
    </intent-filter>
    
</activity>
1個目のインテントフィルタは、EclipseでAndroidプロジェクトを新規作成したときに
自動生成されるマニフェストファイルに記述されているものです。
親タグのactivityがアプリのエントリポイントであることを示すandroid.intent.action.MAINと、
ランチャーから起動可能(であることを示すandroid.intent.category.LAUNCHERが指定されています。

2個目のインテントフィルタが今回追加したもので、下記の条件を満たす暗黙的インテントを受け取れる(処理できる)ことを表しています。

・アクションがIntent.ACTION_VIEW
・データに設定されているURIがhttp
カテゴリに指定されているcategory.DEFAULTは、暗黙的インテントを受け取るには必ず指定する必要があります
(但し、android.intent.action.MAINとandroid.intent.category.LAUNCHERを指定した場合は必須ではない)。

上記のインテントフィルタを設定したアプリを起動する側のコードは以下のようになります。
Intent browserIntent = new Intent(Intent.ACTION_VIEW);
browserIntent.setData(Uri.parse("http://www.example.com"));
startActivity(browserIntent );
このコードを実行すると、Chooserにデフォルトのブラウザに加えて受け取り側アプリが表示され、
暗黙的インテントを受け取れるようになったことが分かります。

2013年2月18日月曜日

[Android]暗黙的インテントで他アプリを起動する

他アプリを起動する方法として明示的インテントと暗黙的インテントがあります。

明示的インテントは、起動するアクティビティを明示的に指定する方法です。
この方法で他アプリを起動するには、起動するアプリのパッケージ名を直接指定する必要があります。

暗黙的インテントは、起動するアクティビティを指定するのではなく、
アクティビティに実行して欲しいアクションを指定する方法です。
この方法では指定したアクションを実行できるアプリがシステムによって選択されます
(該当するアプリが複数ある場合、アプリ選択画面が表示されます)。

指定するアクションには以下のようなものがあります。

ACTION_SENDTOメッセージ送信する。
ACTION_SEND_MULTIPLE複数のデータを送信する。
ACTION_DIAL電話をかける。
ACTION_VIEWデータをユーザに表示する。
ACTION_EDITデータを編集する。

この他にも数多くのアクションが定義されています。
(http://developer.android.com/reference/android/content/Intent.htmlを参照)

また、インテントにはアクションに加えてカテゴリやデータの種類を指定できます。
カテゴリやデータを指定することで、より最適なアプリが起動されるようになります。

暗黙インテントによる他アプリ起動のサンプル

暗黙的インテントのサンプルをいくつか挙げてみます。
Uri number = Uri.parse("tel:0123456789");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

startActivity(callIntent);
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
emailIntent.setData(Uri.parse("mailto:abc@example.com"));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "タイトル");
emailIntent.putExtra(Intent.EXTRA_TEXT, "本文");

startActivity(emailIntent);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

startActivity(cameraIntent);
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

startActivity(mapIntent);
Intent browserIntent = new Intent(Intent.ACTION_VIEW);
browserIntent.setData(Uri.parse("http://www.example.com"));

startActivity(browserIntent);

2013年2月11日月曜日

[Android]SQLデータベースにデータを保存する

データベースにデータを保存するにはSQLiteOpenHelperクラスを使います。

まず最初にSQLiteOpenHelperを継承したクラスを作成します。
public class ClientsDbHelper extends SQLiteOpenHelper {

    /** データベースのバージョン */
    public static final int DB_VERSION = 1;  // データベーススキーマを変更した場合、インクリメントする。

    /** データベースファイル名 */
    public static final String DB_NAME = "clients.db";

    public ClientsDbHelper(Context context) {
        // SQLiteOpenHelperクラスのコンストラクタには
        // データベースのバージョンを渡す。
        // コンストラクタに渡したバージョンと
        // データベースが保持しているバージョンが異なる場合、
        // onUpgrade()、またはonDowngrade()が呼ばれる。

        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // データベースが作成された時に呼び出される。
        // ここでデータベースの初期化(テーブルの作成やテーブルの初期化等)を行う。

        final String createSql = "CREATE TABLE clients("
          + "id INTEGER PRIMARY KEY AUTOINCREMENT, "
          + "name TEXT"
          + ")";
        try {
         db.execSQL(createSql);
        } catch (SQLException e) {
         e.printStackTrace();
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // データベースをアップグレードする必要がある場合に呼び出される。
        // ここでデータベースを新バージョンにアップグレードする処理を行う。
        // 以下は、既存データはすべて破棄しても良い場合の処理例

        db.execSQL("DROP TABLE clients"); // 旧バージョンのテーブルを破棄
        onCreate(db); // 新バージョンでテーブルを作成し直す
    }
}
onDowngrade()の実装は必須ではないので、必要な場合は実装します。

データを挿入する。

データの挿入は、SQLiteDatabase.execSQL()でSQLのINSERT文を実行する方法と
SQLiteDatabase.insert()を使う方法があります。
以下はinsert()を使う場合のコードです。
// ContentValuesに列名と値を設定
ContentValues values = new ContentValues();
values.put("name", "Taro"); 

try {
    // データベースを書き込み用で開く
    ClientsDbHelper dbHelper = new ClientsDbHelper(getApplicationContext());
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    
    db.insert("clients", null, values); // 挿入先テーブルを指定して挿入
    // ↑の場合、valuesが空だとinsert()が例外をスローする。
    // これを防ぐには、次のようにnullColumnHackを指定する。
    // db.insert("clients", "name", values);
} catch (SQLException e) {
    e.printStackTrace();
}
insert()の2つ目の引数nullColumnHackには、ContentValuesが空の場合にNULLを設定する列を指定します。
SQLiteDatabaseはContentValuesが空だとinsert()に失敗(例外スロー)しますが、
nullColumnHackを指定しておくと失敗しないようにできます。

そのため、指定した列はNULLを設定できなければなりません。そうでない場合(intやNOT NULL列の場合)、insert()に失敗します。

通常、ContentValuesが空になることは無いと思うので、nullColumnHackはnullで問題ありません。

データを取得する。

データの取得は、SQLiteDatabase.rawQuery()でSQLのSELECT文を実行する方法と
SQLiteDatabase.query()を使う方法があります。
個人的な好みになりますが、rawQuery()を使った方が分かりやすく簡単だと思います。
以下はrawQuery()を使う場合のコードです。
final String querySql = "SELECT * FROM clients;";

ClientsDbHelper dbHelper = new ClientsDbHelper(getApplicationContext());
Cursor cursor = null;
try {
    // データベースを読み取り専用で開く
    SQLiteDatabase db = dbHelper.getReadableDatabase();
    
    // SELECT文を実行
    cursor = db.rawQuery(querySql, null);

    // Cursorから値を取得するための列indexを取得
    int idColumnIndex = cursor.getColumnIndexOrThrow("id");
    int nameClumnIndex = cursor.getColumnIndexOrThrow("name");
    // Cursorから値を取得
    if (cursor.moveToFirst()) {
        do {
            int id = cursor.getInt(idColumnIndex);
            String name = cursor.getString(nameClumnIndex);

            // do something

        } while (cursor.moveToNext());
    }
} catch (SQLiteException e) {
    e.printStackTrace();
} finally {
    if (cursor != null) {
        cursor.close();
    }
}

データの更新/削除も同様に、SQLiteDatabaseを使って行えます。
SQL文を実行する方法とSQLiteDatabaseのメソッドを使用する方法がありますが、
どちらの方法を使っても大差ないので、使い勝手の良い方を使うのがいいと思います。

2013年1月28日月曜日

[Android]外部ストレージのパブリックディレクトリに読み書きを行う

内部ストレージや外部ストレージのアプリPrivate領域に保存したファイルは、
アプリのアンインストール時に全て削除されます。
しかし、カメラで撮影した写真(画像ファイル)やエディタで編集したテキストファイル等は
アプリをアンインストールしても残しておきたいファイルです。
そんなファイルを保存するためのディレクトリが外部ストレージのパブリックディレクトリです。

外部ストレージのパブリックディレクトリにファイルを作成し、データを保存する

外部ストレージのパブリックディレクトリにファイルを作成し、データを書き込むコードは以下のようになります。
※コードでは前回同様http://developer.android.com/training/basics/data-storage/files.htmlにあるisExternalStorageWritable()とisExternalStorageReadable()を使ってます。
// 外部ストレージに書き込み可能かチェック
if (isExternalStorageWritable()) {
 FileOutputStream outputStream;
 
 // ※以下、例外処理は省略

 // パブリックディレクトリにファイルを作成し、書き込む。 
 File file = new File(
     Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
     "myfile.txt");

 outputStream = new FileOutputStream(file);

 outputStream.write("test".getBytes());
 
 // ちなみにDDMSで確認したところ、確認時の環境下では
 // "/mnt/sdcard/Download/myfile.txt"
 // に書き込まれた。
 
 outputStream.close();
}
アプリPrivate領域に保存する時との違いは、getExternalFilesDir()ではなくEnvironment.getExternalStoragePublicDirectory()を使う点です。
Environment.getExternalStoragePublicDirectory()の引数に指定するディレクトリのタイプは、getExternalFilesDir()と同じです。

外部ストレージのパブリックディレクトリに保存したファイルからデータを読み込む

ファイルからデータを読み込むコードは以下のようになります。
// 外部ストレージから読み込み可能かチェック
if (isExternalStorageReadable()) {
 FileInputStream inputStream;
 byte[] buffer = new byte[256];

 // ※以下、例外処理は省略
 
 File file = new File(
     Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
     "myfile.txt");

 if (file.exists()) {
  inputStream = new FileInputStream(file);
  inputStream.read(buffer);

  String data = new String(buffer, "UTF-8");

  inputStream.close();
 }
}
こちらもgetExternalFilesDir()ではなくEnvironment.getExternalStoragePublicDirectory()を使う点が異なるだけです。

2013年1月21日月曜日

[Android]外部ストレージのファイルに読み書きを行う

Androidアプリのファイル保存先には外部ストレージもあります。
外部ストレージの特徴として以下の3点が挙げられます。
  • デバイスが取り外されていて使用できない場合がある
  • ファイルは外部から読み書き可能
    (内部ストレージに保存するときみたいに、Context.MODE_PRIVATEを指定して外部に非公開にできない)
  • 保存したファイルはアプリのアンインストール時に削除される
外部ストレージに置いたファイルは他アプリからもアクセス可能なので、
他アプリから使用するようなデータは外部ストレージに保存します。

外部ストレージが使用可能かチェックする

外部ストレージは常に使用可能とは限らないので、読み書きする前に使用可能かチェックする必要があります。
読み書き可能かどうかはEnvironment.getExternalStorageState()の戻り値で判断できます。

チェック用のコードですが、http://developer.android.com/training/basics/data-storage/files.htmlにあるisExternalStorageWritable()とisExternalStorageReadable()がそのまま使えるので、これを使うことにします。

外部ストレージにファイルを作成し、データを保存する

外部ストレージにファイルを作成し、データを書き込むコードは以下のようになります。
// 外部ストレージに書き込み可能かチェック
if (isExternalStorageWritable()) {
 FileOutputStream outputStream;
 
 // ※以下、例外処理は省略

 // 外部ストレージにファイルを作成し、書き込む。 
 File file = new File(getExternalFilesDir(null), "myfile.txt");
 outputStream = new FileOutputStream(file);

 outputStream.write("test".getBytes());
 
 // ちなみにDDMSで確認したところ、確認時の環境下では
 // "/mnt/sdcard/Android/data/[パッケージ名]/files/myfile.txt"
 // に書き込まれた。
 
 outputStream.close();
}
getExternalFilesDir()の引数にはディレクトリのタイプを指定します。
Environment.DIRECTORY_MUSIC や Environment.DIRECTORY_PICTURESといった定数を指定でき、
これらを指定することでディレクトリ内のデータがシステムから適切に扱われるようになります。
例えば、Environment.DIRECTORY_RINGTONESを指定したディレクトリにあるオーディオファイルは、着信音として扱われます。
これらの定数ではなくnullを指定すると、アプリ用ディレクトリのルートディレクトリが返されます。

外部ストレージに保存したファイルからデータを読み込む

ファイルからデータを読み込むコードは以下のようになります。
// 外部ストレージから読み込み可能かチェック
if (isExternalStorageReadable()) {
 FileInputStream inputStream;
 byte[] buffer = new byte[256];

 // ※以下、例外処理は省略
 
 File file = new File(getExternalFilesDir(null), "myfile.txt");

 if (file.exists()) {
  inputStream = new FileInputStream(file);
  inputStream.read(buffer);

  String data = new String(buffer, "UTF-8");

  inputStream.close();
 }
}

2013年1月14日月曜日

[Android]キャッシュファイルに読み書きを行う

内部ストレージにはキャッシュファイル専用のディレクトリがあります。
このディレクトリの特徴として以下の3点が挙げられます。
  • アプリのprivate領域にある
  • 内部ストレージが不足状態になると、このフォルダ内のデータはシステムによって削除される可能性がある
  • キャッシュファイルはアプリのアンインストール時に削除される
2点目が大きな特徴です。この特徴があるため、ファイルが必要無くなったら削除を行い、1MB程度の使用量に抑えておく必要があります。

キャッシュディレクトリにファイルを作成し、データを保存する

キャッシュディレクトリにファイルを作成し、データを書き込むコードは以下のようになります。
FileOutputStream outputStream = null;
try {
    // キャッシュ領域にファイルを作成し、書き込む。
    File file = new File(getCacheDir(), "cache.txt");
    file.createNewFile();
    if (file.exists()) {
        outputStream = new FileOutputStream(file);

        outputStream.write("test".getBytes());
        // ちなみにDDMSで確認したところ、確認時の環境下では
        // "/data/data/[パッケージ名]/cache/cache.txt"
        // に書き込まれた。
    }
} catch (IOException e) {
    Log.e("MyApp", "exception", e);
}
finally {
    try {
        if(outputStream != null) { outputStream.close(); }
    }
    catch (IOException e){
        Log.e("MyApp", "exception", e);
    }
}

キャッシュディレクトリに保存したファイルからデータを読み込む

ファイルからデータを読み込むコードは以下のようになります。
FileInputStream inputStream = null;
byte[] buffer = new byte[256];
try {
    File file = new File(getCacheDir(), "cache.txt");
    if (file.exists()) {
        inputStream = new FileInputStream(file);
        inputStream.read(buffer);
        String data = new String(buffer, "UTF-8");
    }
}catch (IOException e) {
    Log.e("MyApp", "exception", e);
}finally {
    try {
        if(inputStream != null) { inputStream.close(); }
    } catch (IOException e) {
        Log.e("MyApp", "exception", e);
    }
}

キャッシュディレクトリに保存したファイルを削除する

ファイルの削除はFileオブジェクトのdelete()を使います。
File file = new File(getCacheDir(), "cache.txt");
file.delete();

※2015/7/5  五嶋さんのコメントを受け、書き込み、読み込みのコードにtry-catch文を追加しました。

2013年1月7日月曜日

[Android]内部ストレージのファイルに読み書きを行う

Androidアプリのファイル保存先の一つに内部ストレージがあります。
内部ストレージの特徴として以下の3点が挙げられます。
  • 常に使用可能
  • ファイルはアプリのprivate領域に保存される
  • 保存したファイルはアプリのアンインストール時に削除される
通常、アプリのみで使用するようなデータ(他のアプリから使用したりしないデータ)は
内部ストレージに保存するようです。

内部ストレージにファイルを作成し、データを保存する

内部ストレージにファイルを作成し、データを書き込むコードは以下のようになります。
FileOutputStream outputStream;

// ※以下、例外処理は省略

// 内部ストレージにファイルを作成し、書き込む。
outputStream = openFileOutput("myfile.txt", Context.MODE_PRIVATE);
outputStream.write("test".getBytes());

// ちなみにDDMSで確認したところ、確認時の環境下では
// "/data/data/[パッケージ名]/files/myfile.txt"
// に書き込まれた。

outputStream.close();
openFileOutput()でoutputStreamを直接取得してデータを書き込む方法の他に、
getFilesDir()を使って内部ストレージのルートディレクトリのFileオブジェクトを取得し、
そのFileオブジェクトを使ってデータを書き込む方法があります。

内部ストレージに保存したファイルからデータを読み込む

ファイルからデータを読み込むコードは以下のようになります。
FileInputStream inputStream;
byte[] buffer = new byte[256];

// ※以下、例外処理は省略

inputStream = openFileInput(FILE_NAME);
inputStream.read(buffer);

String data = new String(buffer, "UTF-8");

inputStream.close();
こちらも同様にgetFilesDir()からFileオブジェクトを取得し、それを利用する方法があります。