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

USBシリアル通信ケーブル届いた

先日注文したUSB接続のシリアル通信ケーブル届いた。


先日は260円だったけど値上がりしてるね。でも安い。


早速メインPC(Arch Linux)に接続してみたが、ただ接続するだけで、
ls /dev
/dev/ttyUSB0 が出現!
基板はケーブル内内蔵のコンパクトで接続用のpin用ソケットが剥き出しのケーブル。
安いし接続も簡単でいいですねー。

ケーブルの機器側はpin用ソケットが剥き出しだが、赤が5Vで黒がGNDなのはわかるが、
説明書とかついてないけど、白と緑は何?
って感じだが、Amazonのレビューページに書いてくれてる人がいました。
緑: TxD
白: RxD
とのこと。

以前使ってた無線LANルーター(
Corega WLR-300GNH)に接続したかったわけで、ルーター内部にシリアルと思われる4pinが出てるが、片側のピン側に●印がついてる。
他の機器のピンアサインを参考に、
5V(●印)
TxD
RxD
GND
の順とよんで、
ケーブルの赤は接続せずに、緑、白、黒の順で繋いだ。(TxDRxDは自動?後で繋ぎ直してみたが、どっちでも通信できるぽい)

そして、
sudo screen /dev/ttyUSB0 115200
とコンソール出してルーターの電源入れ直してみたら、何か出てくるんだが文字化け・・・
接続自体はうまく行っているようだが・・・

最初、文字コード?と思ったが、ルーターの起動出力なんか全部半角文字だろうし、全文字文字化けしてるってことは違うね。
で、シリアル通信の文字化けについて調べてみたが、通信速度が合ってない場合に正しく通信できず文字化けするみたい。
シリアル通信で一般的な他の通信速度試してみたが、
sudo screen /dev/ttyUSB0 57600
で正常に受信できた!


のだが、
1.1.3-WLR300NHS2-32bit-V1.2.5.2 (Jul 8 2009 - 15:3
Board: CG-WLR300NH
DRAM: 32 MB
** Warning - bad CRC, using default ==========================
Ralink UBoot Version: 3--------------------------------
ASIC 3052_MP2 (MAC to VITESSE Mode)
DRAM COMPONENT: 128Mbits
US: 32BIT
memory: 32 MByte09 Time:15:37:36
================================
icache: sets:25s:4, linesz:32 ,total:32768
dcache: set ways:2 ,total:16384

Pl the operation:
1:RAM via TFTP.
2: Load system code then write to Flash via TFTP. commania TFTP.

You choosed 3
0

3: System Boot system codFlash.
## Be at bf080000 ...
Image Name: Linux Kernel Image
ted: 2011-06-4:08:07 UTC

System Controus = 0x20440000
Image Type: MIPS Linux Kernel (lzma compressed)
Data Size: 4320205 Bytes = 4.1 MB
Load Address: 8000000Point: 802e4000
Verifying Checksum ressinel Image ... Jtag in GPIO Modl ...


LINUX started...

IS ASIC
ersion 2.6.21 (josn@dni(gcc versionce set to 384 MHz
CPU revisi000000 @ 00000000 (usable)
B28
Ke: console=ttyS1,5 root=/dev/mtdblock5
P tagginesiz
Primary data cache 16kB, 4-way, linesize 32 refill hand0 instructions).
Synthesizetructions).
Synthesizeer fastpath (32 instructions).
Synth (31uctions).
Cache parity protection disabled
cause = 800000, status = 1100ff00
PID hash table entries: 128 (order: 7, 512 bytes)
calculating r4koff... 00177000(1536000)
CPU frequency 384.00 MHz
Using 192.000 MHz high precision timer.
Console: colour dummy device 80x25
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 29292k/32768k available (2442k kernel code, 3476k reserved, 513k data, 120k init, 0k highmem)
Mount-cache hash table entries: 512
NET: Registered protocol family 16
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
NET: Registered protocol family 2
Time: MIPS clocksource has been installed.
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
TCP reno registered
squashfs: version 3.2-r2 (2007/01/15) Phillip Lougher
squashfs: LZMA suppport for slax.org by jro
NTFS driver 2.1.28 [Flags: R/O].
fuse init (API version 7.8)
io
Ralink SoC physically mapped flash: Found 1 x16 devices at 0x0 in 16-bit bank
Amd/Fujitsu Extended Query Table at 0x0040
number of CFI chips: 1
cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.
Creating 6 MTD partitions on "Ralink SoC physically mapped flash":
0x00000000-0x00030000 : "Bootloader"
0x00030000-0x00040000 : "Config"
0x00040000-0x00050000 : "Factory"
0x00050000-0x00080000 : "Ecos"
0x00080000-0x00180000 : "Kernel"
0x00180000-0x00800000 : "RootFS"
block2mtd: version $Revisiotered new iface driver usbhid
drivers/usb/input/hid-core.c: v2.6:USB HID core driver
ip_conntrack version 2.4 (256 buckets, 2048 max) - 236 bytes per conntrack
ip_conntrack_rtsp v0.6.21 loading
ip_nat_rtsp v0.6.21 loading
ip_conntrack_pp.8 Ben Greear <greearb@candelatechiller <davem@redhat.com>
VFS: Mounted root (squashfs filesystem) readonly.
Freeing unused kernel memory: 120k freed
init started: BusyBox v1.11.2 (2011-06-03 11:20:28 CST)
Algorithmics/MIPS FPU Emulator v1.5


BusyBox v1.11.2 (2011-06-03 11:20:28 CST) built-in shell (ash)
Enter 'help' for a list oh2.2 entered promiscuous mode
br0: port 3(eth2.2) entering learning state
br0: topology change detected, propagating111111111
br0: port 3(eth2.2) entering forwarding state
: killall: crond: no process killed

: Timeout waiting for PADO packets

: Unable to complete PPPoE Discovery

: Timeout waiting for PADO packets

: Unable to complete PPPoE Discovery
ルーター起動後の出力はこんな感じ。
Linuxが起動しているが、ログインプロンプトが表示されないんで中に入れないぽい。
ブートローダのメニューにはTFTP経由でブートできそうな項目があるが、待ち時間無しでROMブートしてしまうんで何もできない。

シリアル通信でいじるの無理ぽい・・・
他にファームウェアを改造してルーターの機能でアップデートさせるという手があるが、ブートローダからのROM焼きは無理ぽいから一発で成功しなきゃ再更新はできなくなる。
ルーター改造は諦めかな・・・

アプリ開発時のテスト用にAndroid-x86

引き続きAndroidのアプリ開発を勉強しながらやってるんだが、HTTP通信でHTML取得する処理のあるアプリ作ってて、
InputStreamで1byteずつ取得して1行ずつString化、charset指定のMETAタグがあったらそれ以降指定charsetでString化。
んで、内部から文字列抽出してSQLiteにデータ保存。

って感じの処理を作ったんだが、糞遅い。
最初SQLiteのBEGIN忘れてたんだが、BEGINするように修正しても、やっぱ遅い。
処理に問題があるのかと思ってコード見なおしたりしてたが、エミュレータが遅い?

初めてAVDを使った時は糞遅くて使い物にならないと思ってたが、最近使って開発のテスト用には使える速度だと思ってたんだが、
とりあえずARMエミュレータ以外の方法について調べたり考えたりしてみた。

AVDにはx86仮想デバイス用のHAXMアクセラレーターってのがあって、それ入れるとx86の仮想デバイスがKVM使って動作させられるらしいんだが、SDKから入れようとしてもLinuxは対象外となっている。
Linuxでも手動で入れる方法はあるみたいなんだが、ちょっと手順が複雑でやりたくない感じ。

ADBで操作できるならAVDでなくてもいいから、
以前ちょっとVirtualboxで使ったことあるが、Android-x86を使って母艦からADB接続できないか?
っての調べてみたが、簡単にできた。

Android-x86は最初からVirtualboxに入れてあったんだが、最新版が今年の1月リリースの4.4-r2だが、インストール済みのはもっと古そうなんでインストールしなおした。
Android-x86のisoは普通のOSインストール用のCDと変わらないんで、Android-x86のisoでCDブートすればインストーラーが起動する。
項目も少ないのでインストールは簡単だが、GRUBのインストールが選択式で初期状態がインストールしない方になってるが、Virtualboxで使うならGRUB入れる方と思う。
パーティションは最大サイズでext3一個だけ作ってGRUB入れるだけ。

入れたらHDDからリブートだが、Virtualboxのマウス統合が使えないので、マウス統合を無効化しないとカーソルが見えない。
初回起動後は言語を選択して、Wifi設定をスキップ。ネットワークはVirtualboxがエミュレートした有線LANなのでWifi不要で最初からネットワーク使える。

で、Virtualbox上のAndroid-x86へ母艦からADBで接続する方法だが、Android-x86起動中でも停止中でもどっちでもできるが、
初期状態ではVirtualboxのネットワーク設定でアダプター1がNATで作成されていると思うが、下の方にある「ポートフォワーディング」を押してルールを一個追加する。
Virtualboxのポートフォワーディング機能は、ホストのportを一個奪ってゲストに転送する機能。
ADBのportは5555の様なのでゲストのportは5555なのだが、ホストのportは5555だとAVDと被るようなので5556にした。

ポートフォワーディングを設定したら、
adb connect localhost:5556
と、adbのconnect機能でlocalhost:5556に接続すると、
adb devices
でVirtualbox上のAndroid-x86が確認でき、普通にADBで操作できる。
adb connectのport番号は省略すると5555になるが、AVDを起動しているとそっちにportが奪われちゃう。


実際にAndroid-x86にADBで制作中のアプリをインストールして試してみたが、
処理自体は速い感じなんだが、マウス統合が無くてGUIがスムーズに利用できない感じ。
AVDのARMエミュレータで遅かった処理が速度違うんで、やはりAVDのARMエミュレータが遅かったようなんだが、
Android-x86がマウス統合ができないから、どっちもどっちだなあ・・・
Android-x86にゲストモジュール入れられないかな・・・

アプリ名の設定と文字列の別ファイル化

引き続きAndroidアプリ開発関連。

アプリ名を設定しようとしたんだが、

AndroidManifest.xml内のandroid:label="@string/app_name"の値を変更すれば設定した文字列がアプリ名となる。
が、@string/app_nameはres/values/string.xmlで定義された文字列で、このxmlファイルを編集することでも設定できる。
res/values/string.xmlは、res/values-en/string.xmlの様にフォルダを言語別に分けることでアプリの他言語化が可能みたい。
AndroidManifest.xmlの方で固定値を設定してしまうよりも、後から多言語かとかもできるしres/values/string.xmlの方で設定したほうが良さそう。

res/values/string.xmlにはアプリ名以外にも自由に文字列を設定できる。

res/values/string.xml内で

<string name="_sample">サンプル</string>
の様に定義しておき、
Resources res=getResources();
String str=res.getString(R.string._sample);
の様な感じで取得できる。
後から多言語化する可能性も考えて、アプリで表示する文字列はres/values/string.xml内に記載するようにしたほうが良いね。
android.content.res.Resourcesのimportが必要。
APIが細かく分かれてるから、importの数が多くなっちゃうね。

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

引き続き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全体に設定してクリックされたものを取得する感じでやるみたい。

openOrCreateDatabase

引き続きAndroidアプリの開発について勉強中で、SQLite関連をやってるが、わかりにくい・・・


android.database.sqliteの下にSQLiteOpenHelperというクラスがあって、このクラスを使って初期化するのが一般的らしいんだが、
オーバーライドしたクラスを作成する必要があるので、アクティビティが複数あるならSQLiteOpenHelperもpublicなクラスで用意しないといけない。
つまり、ソースファイルを分離しなきゃダメぽい。


他にSQLiteDatabaseクラスにopenOrCreateDatabaseというメソッドがあって、メソッド一発で必要なら作成してopenできる。
こっちの方が簡単そうなんだが、引数はDBファイル名じゃなくてpathを渡す必要があり、ファイル名だけを指定するとアプリ実行時にエラーになる。


更に他に、android.content.ContextWrapperクラスの下に同じ名前のopenOrCreateDatabaseメソッドがある。
こちらはpathではなくファイル名を指定して、2番目の引数にファイルのモードを指定する。
ファイルのモードはint型だが、android.contentで定数が定義されていて、
定数名

パーミッション
MODE_PRIVATE0
660
MODE_WORLD_READABLE1
664
MODE_WORLD_WRITEABLE2
666
(パーミッションはMODE_PRIVATEしか確認してないが、意味を考えるとそうなると思う)

android.content.ContextWrapperはandroid.app.Activityが継承しているので、Activityを継承したクラスで使用可。


openOrCreateDatabaseが2種類あって凄くわかりにくいが、アプリ用のDBを作成(open)するにはandroid.content.ContextWrapperの方のメソッドを使えば良い感じ。
android.content.ContextWrapperはActivityが継承しているので別途import不要だが、DBの操作自体はandroid.database.sqlite.SQLiteDatabase等で行うので、操作するのにimportは必要。


てな感じと思ったんだが、
DB作成時にtable作成とかもしなきゃいけないし、SQLiteOpenHelperならonCreateメソッドで作成時処理を書けるから、
やっぱSQLiteOpenHelper使ったほうが良さそうかもしれない・・・
もうちょい勉強の必要あるな。

複数アクティビティを持つアプリ

Androidのアプリでは画面を再描画する時に、現在のアクティビティを初期化して再描画するのではなく、画面ごとにアクティビティを用意するのが基本ぽい。
だから戻るボタンで前の画面に戻れるんだね。

というわけで、Androidアプリを作るには、複数アクティビティの使い方を知る必要があるようなので、
ボタンをクリックすると別のアクティビティに移動するアプリを作ってみた。

MainActivity.java
package com.example.hello;

import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.view.View;
import android.widget.Button;

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);
        Button bt=new Button(this);
        bt.setText("Click");
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
            }
        });
        setContentView(bt);
    }
}

前回の"Hello World"を表示するだけのコードからの変更点だけ赤文字にしてる。
最初に表示されるMainActivityでは、今度はTextViewの代わりにButtonウィジェットを使う。
android.widget.Buttonはandroid.widget.TextViewを継承しているので、文字列の設定とウィジェットの設置は同じなので赤文字にしてない。
前回と違うのは、クリックイベントの設定関連だけ。

setOnClickListenerでイベントの登録のようだが、これはandroid.widget.TextViewが継承しているandroid.view.Viewのメソッドなんで、ボタンじゃなくてTextViewにクリックイベントを設定することも出来そう?
プロジェクト作成時のコードにも「@Override」があるが、これは、既存メソッドを上書きしていることを明示しているということらしい。(書いておくとデバックがしやすいとか・・・)

setOnClickListenerの引数はOnClickListenerで、OnClickListenerのonClickメソッドにクリック時の処理を書く。
なんか気持ち悪い書き方だね・・・
覚えにくい・・・

今回はクリックされたら2つ目のアクティビティを起動するので、startActivityを使うが、startActivityの引数にはIntentを渡す。
Intentの作成方法は複数あるようだが、別のクラスのアクティビティを起動するには、ContextとClassの2つを渡してIntentを作成する。
Contextはwidget作成時にも使って、前回の時に単にthisじゃ問題起きる場面に遭遇しそうな気がしたが、さっそく遭遇した。
単にthisだとダメな場合は、「クラス名.this」で良いみたい。
Classの渡し方は、「クラス名.class」ぽい。


SecondActivity.java
package com.example.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class SecondActivity extends Activity{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tv=new TextView(this);
        tv.setText("Hello, World");
        setContentView(tv);
    }
}

こちらがクリックされたら起動する2番目のアクティビティのソースコード。
最初、1つのソースコードで出来るんじゃ?と思ったんだが、publicじゃないと起動できず、Javaではpublicなclassはソースファイルの名前と同一じゃないとダメぽい。
というわけで、アクティビティが複数ある場合は、アクティビティの数だけソースファイルを分ける必要があるね。
めんどい・・・
こちらのコードは、前回のMainActivityと名前が違うだけで同じ。


AndroidManifest.xml(の一部)

<activity android:name="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="SecondActivity" android:label="@string/app_name"></activity>

更に、AndroidManifest.xmlを修正してアクティビティを登録する必要がある。
プロジェクト作成時点で<activity>が1個あるが、それをコピーして名前を変更。
起動時に開始するわけではないので、<intent-filter>は不要って感じかな。


これで、エミュレーターにインストールして、予定通りボタンクリックで"Hello World"が表示されるアプリができた。

ウィジェットの使い方と複数アクティビティの使い方はわかったから、後は、
データ保存に、android.database.sqlite
HTTP通信に、android.net.http
HTTP以外の通信するなら、android.net
文字列操作するなら、android.text.TextUtils
HTMLアプリにするなら、android.webkit
辺りを調べれば良さそうかな。
AndroidのAPI覚えるのもめんどいし、android.webkitでできるようなことは webkit使っちゃった方が良いんじゃないかと思う。
そうすればAndroid以外に移植するのも簡単と思うし。

thisってなんだ?

Androidで"Hello World"アプリを作ってみた。

package com.example.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

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);
        TextView tv = new TextView(this);
        tv.setText("Hello, World");
        setContentView(tv);
    }
}

赤文字以外の部分は前回の方法でプロジェクトを作成すると自動的に書かれるので、赤文字部分だけ追加。
文字を表示するだけの部品はTextViewウィジェットで、描画系部品はwidgetクラス以下にまとまっているようですね。

公式のAPIリファレンスを読めばわかる感じだが、APIの規模がでかい上に英語だから入門にはちょいきつい。
今回はまず"Hello World"ググッていろんなサイト参考にしたんだが、どのサイトも「new TextView(this);」のthisについて記載してないんだよね。
説明省略しちゃイカンところでしょ・・・

このthisは何?
ってことでわからないから調べたが、公式リファレンスには"TextView(Context context)"と記載されている。

今度はContextって何?
って感じだが、widget作成時はアクティビティの状態を渡す必要があり、渡すものがContext。
Contextを渡すのに省略してthisと書ける。
って感じかね。
Javaはよくわからないが、この場合のthisはMainActivityのことなのか?onCreateのことなのか?

習慣的にthisで開発してると問題に遭遇することもありそうな気がするが、
渡すべきものはContextって覚えとけば平気かな・・・


残りの2行は、
TextViewに文字列を設定。
アクティビティにTextViewを設置。
って感じで、リファレンス読むまでもなく雰囲気でわかるね。


コードはonCreateのブロック内に書いているが、名前がonから始まるからイベント的なものだろうという雰囲気があるが、
onCreateはアクティビティ初期化時に実行される。
他にonStart、onResume、onPause、onStop、onDestroyがあり、
開始時は、onCreate → onStart → onResume の順で処理される。
Androidのアプリは非表示にしてもスリープになるだけだが、
スリープ状態になる時は、onPause → onStop の順。
スリープした際にonStopが実行されるが、Stopする前にPauseでキャンセルされる場合があり、その場合はPauseに戻る。
Stopから復帰した場合はStartに戻る。
Destroyは正常終了だが、メモリ不足に陥るとPause又はStop状態でkillされる可能性がある。

スリープからの復帰時に何も変更しないなら、開始時の処理はonCreateだけで良さそう。
終了時に保存等の処理をする場合は、killされることも考えて、Pauseでやればいいのかな?