ジャンル不定の日記です。

アクティビティ間でのデータ受け渡し

引き続きAndroidのアプリ開発。
今回はアクティビティ間でのデータ受け渡し。

ググったら、android.app.Applicationはアプリ終了まで維持されるので、このクラスを拡張してデータの受け渡しに使うという例が多いようなんだが、
これもサブクラスを作らなければならない方法と思うんで、共感できないやり方。


他の方法として、別のアクティビティを開始する際は
startActivity(Intent);
と、Intentを起動させる感じになるが、
Intentには
intent.putExtra("key",value);
の様な感じで追加データを記録できる。
わざわざ受け渡し用の機能を用意してくれてるんだから、これ使ってデータの受け渡しをするのが基本でしょ。
valueには様々な型を記録できるように用意されており、
"key"とvalueの組み合わせのMapとして扱えるぽい。

Intent intent=new Intent(MainActivity.this,SubActivity.class);
intent.putExtra("key",value);
startActivity(intent);
こんな感じで別のアクティビティを起動する。

SubActivity側でのデータ受け取りは、
Intent intent=getIntent();
でIntentが取得できる。

putExtraで記録したデータは型によって取得用のメソッドが異なるが、
intent.getIntExtra("key",0);
Int型ならgetIntExtra()で取得できる。
getIntExtraの1番目の引数はMapのキーだが、
2番目の引数は取得できない場合?のデフォルト値。


受け渡し方法自体は難しくないんだが、別のアクティビティの起動はイベント経由になると思うが、
元のアクティビティの処理をonCreateに書くと思うから、イベントリスナと別メソッドになるので、
別のアクティビティに渡す以前に、元のアクティビティ内でローカル変数のスコープが面倒な感じ。

しかし、ググッて出てくる解説サイトが糞な書き方してる人ばっかで困る。
ソースファイル数やコード量が多くなる書き方してる説明が多すぎ。

Androidアプリ開発 Toast

引き続きAndroidアプリ開発について。
メモも兼ねて記事書いてる感じで数が多くなりそうなんで、そのうち別サイトに情報まとめようと思う。
APIが複雑すぎて、同じことするにも複数の記法があるので公式リファレンス見ながら開発だと難しい。
自分なりの使い方だけまとめといた方が良さそう。

今回はandroid.widget.Toastの使い方。
アプリ画面内に一時的に文字が浮かび上がる通知用ウィジェット。
ユーザーへの通知に使うウィジェットだが、設置しないで画面に表示できるので、JavaScriptのalertの様に開発時のテスト用に使える感じ。


まず
import android.widget.Toast;


Toastはnewで作成することもできるが、
Toast.makeText()メソッドで表示する文字列と表示時間を設定して作成できる。
Toast.makeText(this,"表示する文字列",Toast.LENGTH_SHORT).show();
さらにそのまま.show()で表示しちゃえば1行でかけて簡単。
1番目の引数は毎度おなじみのContext。
3番目の引数は表示時間ぽいが、「Toast.LENGTH_SHORT」「Toast.LENGTH_LONG」の2種類が利用できる。

SimpleAdapterでListView

引き続き、Androidアプリ開発の勉強。

前回はArrayAdapterでのListView作成をやってみたが、ArrayAdapterだと1行に2カラム以上あるリストが作成できないね。
あと、ArrayAdapter.add()で1行ずつ追加したが、クリック等のイベントを設定する場合、リスト中のクリックされた行の行番号しか取れなそうなんで、
アイテムに対応した値を取得しようと思ったら別途多次元配列のようなものを用意しておかないと無理だね。
すごく不便。

というわけで、ArrayAdapterと別のSimpleAdapterを試してみた。
こちらはArrayAdapter以上にわかりにくかったが、まあArrayAdapterである程度わかったから、ハマりはしなかった。
ArrayAdapterは空で作成してaddで1行ずつ追加できたが、SimpleAdapterはオブジェクト作成時に一括でデータを渡す以外に方法はないみたい。

SimpleAdapter作成時に渡すデータは、MapのListという形式。

Mapは、
HashMap map=new HashMap();
map.put("key1","value1-"+i);
map.put("key2","value2-"+i);
って感じで、keyとvalueの値を複数登録できる型。
Perlで言うところのハッシュだね。
使うにはjava.util.HashMapをimportする必要がある。

Listは、
ArrayList list=new ArrayList();
data.add(data);
これはPerlで言うところの普通の配列だね。
Javaの配列は宣言時に容量が決まるので後から拡張ができない?
Listは容量不定で作成でき、addで拡張しつつ追加できる。
ってことぽい。
使うにはjava.util.ArrayListをimportする必要がある。

今回SimpleAdapterに渡すデータはMapのListなので、
        ArrayList data=new ArrayList();
        for(int i=0;i<10;i++){
            HashMap map=new HashMap();
            map.put("key1","value1-"+i);
            map.put("key2","value2-"+i);
            data.add(map);
        }
こんな感じで作った。

SimpleAdapterの作成は、
SimpleAdapter sa=new SimpleAdapter(this,data,android.R.layout.simple_list_item_2,new String[]{"key1","key2"},new int[]{android.R.id.text1,android.R.id.text2});
こんな感じ。
1番目の引数はContext。
2番目の引数はデータ(上で作成した、MapのList)。
3番目の引数はレイアウト。
4番目の引数はStringの配列。2番目の引数のデータ内のMapから、ここで指定した文字列に対応した値がリスト表示内容となる。
5番目の引数はカラムに適用するViewのID?の配列。これはレイアウトが対応したものでないと表示されないぽい。

最終的に、
package com.example.list;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import java.util.ArrayList;
import java.util.HashMap;

public class MainActivity extends Activity{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ArrayList data=new ArrayList();
        for(int i=0;i<10;i++){
            HashMap map=new HashMap();
            map.put("key1","value1-"+i);
            map.put("key2","value2-"+i);
            data.add(map);
        }
        ListView lv=new ListView(this);
        SimpleAdapter sa=new SimpleAdapter(this,data,android.R.layout.simple_list_item_2,new String[]{"key1","key2"},new int[]{android.R.id.text1,android.R.id.text2});
        lv.setAdapter(sa);
        setContentView(lv);
    }
}
こんな感じのコードになったんだが、


↑こんな表示。
前回のArrayAdapterを試した時はレイアウトにandroid.R.layout.simple_list_item_1を使用したが、android.R.layout.simple_list_item_1は1行にTextViewが一つしか設置できない。
今回のandroid.R.layout.simple_list_item_2は上段にandroid.R.id.text1を表示して、下段にandroid.R.id.text2を表示する2段式。
だが、これ2つしかデータ表示できないし、用意されたレイアウトだと横並べとかできないぽい。
いくつもファイル小分けにしたくないからJavaのソースファイル一つでカスタマイズしたかったが、レイアウトファイル使わないとダメぽいね・・・


ArrayAdapterにはaddメソッドがあるが。SimpleAdapterには後から追加したりするメソッドはないので困りそうだが、
ListViewが継承しているAbsListViewクラスにinvalidateViews()メソッドがあって、これ使うとListViewが再構築される。
SimpleAdapter作成後にListを更新して、ListView.invalidateViews()で再構築をしてみたが、反映した。
というわけで、addがないのは問題無さそう。

表示内容が1行に1個ならArrayAdapterでもいいんだが、
ArrayAdapterを使う場合でもクリック等のイベントで取得できるのが行番号だけとなると、表示内容以外に行番号に対応したIDを持った多重配列を用意しないと使いにくいと思うのし、Javaの配列は容量が固定ぽいからListの方が使いやすそうだし、
ArrayAdapterを使う場合とSimpleAdapterを使う場合で、コード量の差は1,2行しか変わらなそう。
それなら、常にSimpleAdapterを使ってコードを統一しちゃったほうが良さそう。

ListViewもむずい・・・

引き続きAndroidのアプリ開発について勉強中だが、widgetについては全て最初にやったTextViewと似たような使い方だと思ったんだが、
リスト表示する時に使うListViewを使ってみたら、凄く難解だった・・・
APIがJavaでできてて継承が大量にあって目的のメソッドがどこにあるのか探すのが大変だからと思う。
クラスが細かく分かれてて、GUIアプリはGTKくらいしか触ったことはないが、桁違いにメソッド数多い気がする。


今回ハマったのはListViewだが、その前に前回のSQLite関連で、
前回はandroid.content.ContextWrapperのopenOrCreateDatabaseでDB作成をしたが、初期化の処理を考えるとやっぱSQLiteOpenHelperを使ったほうが良い?
ってことになったが、
SQLiteOpenHelperも試してみたが、やはりpublicなclassを用意しないと無理な気がして難しい。
SQLiteDatabaseにgetVersion()とsetVersion()があって、setVersionでint型のバージョン値を設定、getVersionで取得ってのができることを知って、DB作成直後でバージョンを設定していない場合は0になる。
ifでこれの値を確認して処理を分ければSQLiteOpenHelper使う必要は全く無いね。
PerlではちょくちょくSQLite使うし、MySQLともほぼ変わらんし、Androidでもクエリ投げるだけぽいから、DB関連は問題無さそう。


で、今回のListViewだが、
ListViewにaddFooterViewってメソッドがあって、単にListViewを作成してaddFooterViewでアイテム追加できるもんだと思ったんだが、
どうも、ListViewにsetAdapterでListAdapterを設定しないとリストが使えないみたい。
ListAdapterをListViewと別に作るようなんだが、作るためのクラスが多数あって、どれ使ったら良いんだかわかりにくい。
最終的にArrayAdapterというのを使ってうまく行ったんだが、
ArrayAdapterの使い方をググると、xmlでデータを用意したり、配列からAdapterを作成したりとかで、1個ずつ追加する方法がなかなかわからんかった。

最終的には、
        ListView lv=new ListView(this);
        ArrayAdapter aa=new ArrayAdapter(this,android.R.layout.simple_list_item_1);
        aa.add("Hello World");
        lv.setAdapter(aa);
        setContentView(lv);
こんな感じで、さほど長くないコードで表示できた。
ArrayAdapterの2番目の引数はレイアウトファイルのIDらしいんだが、Rから始まる値で定数がいくつか用意されてる。

今回の実験コードでは黒背景になったけど、ちゃんとアプリ作るなら白背景の仕方とかも知らないとダメだな。

今回のやり方だとリストのアイテムに対してクリックイベントとか設定することは出来ないぽいんだが、
クリックイベントの設定は、ListView全体に設定してクリックされたものを取得する感じでやるみたい。