裏返しアニメーションを付けてついに完成!Androidアプリで2Dグラフィックスを扱う練習をしようと思う。簡単なものという事で、オセロ(リバーシ)ゲームを作りたい。Viewを使って描画するか、SurfaceViewを使って描画するかを決めないと行けない。GLSurfaceViewというのもあるけれどもこちらは今やろうとしている事にはオーバースペックなので置いておく。愚鈍人KENSINのホームページ-Androidで星が流れるプログラムを作成してみるViewを使った場合は大体4fpsぐらいしか出ないが、SurfaceViewだと30fpsぐらいは出るみたいだ。「チェスなどのボードゲームならViewで充分」という記述もよく見かけるので、今回はとりあえずViewで行こうと思う。しても必要なら後からSurfaceViewに変更するという事で。こんな感じで初期状態を描画してみた。(笑)横向きの場合はこんな感じになる。カスタムビューによるグラフィック表示Activityクラスではカスタムビューをセットするだけ。ReversiViewというカスタムビューを作ってこの中でオセロの表示処理を全て行う。Paint();としているのはパフォーマンス上は良くないらしい。後でちゃんと直そうと思う。色の指定なども本当はXMLファイルに出してしまいたいところ。あと、円を描く前にアンチエイリアスを指定しないとギザギザになってしまうので注意。モデルとビューの分離オセロの盤面の状態を保持するのと、ゲームのロジックを記述するために2つのクラスを作成しておこう。盤面全体を表すBoardクラスと、個々のマスを表すCellクラスがあれば良いと思う。leftプロパティと、Cellクラスのオブジェクトを格納する2次元配列を用意しておく。leftプロパティと、そのマスがどういう状態にあるかを示す変数を作成しておこう。各マスは、「何もない」「黒」「白」の3つの状態を持つので、それらを表すEnum型も定義する。Boardクラスのソースコードはこちら。Cellクラスのソースコードはこちら。ここまでここまでで、画面をタッチすればそこに黒または白の石を置く事が出来る様になった。一応、既に石が置かれているマスをタッチしても反応しない様になっている。ゲームのロジックと言えるものは何も書いていないので、オセロのルールに関係無く自由に石を置けてしまうし、挟んでも挟まれても何も起こらない。これだけでも「一から自分で作っている」という感じがたまらない。(笑)プロジェクト全体のダウンロードはこちらから。時間のかかる処理を別スレッドで実行するHTTP通信を別スレッド化するAndroidアプリケーションでスレッドを使う方法はいろいろあるみたいだが、今回は以下の手順で実装した。ActivityクラスでRunnableインターフェースをインプリメントする。android.os.HandlerクラスのインスタンスをActivityクラスで保持する。Runnableのrunメソッドをオーバーライドする。別スレッドでの処理を開始する。ActivityクラスでRunnableインターフェースをインプリメントする。Activityでインプリメントせずに、単にその都度Runnableのインスタンスを生成しても多分大丈夫。こちらの方が多少はメモリの節約になるらしい。(という記述をどこかで読んだ気がする。android.os.HandlerクラスのインスタンスをActivityクラスで保持する。別スレッドでの処理が完了した後にUIスレッド側で実行する処理を受け取る為に必要になる。RunnableのrunメソッドをOverrideする。別スレッドでの処理が完了した後、mHandlerのpost()メソッドを呼んでUIスレッド側で実行したい処理を渡す。変数urlとtitleはfinalで宣言しないとハンドラ内から参照する事が出来ないので注意。逆に言うとfinalで宣言したローカル変数がそのまま参照出来るので、Bundleに詰め込んでパラメータとして渡すなどという方法と比べるとこの書き方はかなり便利だと思う。//処理完了後、ハンドラにUIスレッド側で実行する処理を渡す。//データベースを更新。別スレッドでの処理を開始する。Thread(this).start();実行時のイメージはこんな感じになった。処理中はくるくる回るアイコンを表示しておくと結構いい感じ。クラスの中でtitleがセットされているかどうかで判断して行っている。ネットワーク接続が可能かどうか事前に調べるネットワークに接続されていない場合はタイトルの取得を行わずにURLだけを一覧に追加する様にした。接続されているかどうかの判断は下の関数で可能だ。(著)よりブラウザを自動的に開くかどうかを設定で選べる様にする設定画面にチェックボタンを追加して、インテントで受け取ったURLを自動的にブラウザで開くかどうか設定出来る様にした。ONの場合とOFFの場合で試してみた所、自動的にブラウザを開かずに単に一覧リストを表示するだけの方が使いやすい様な気がする。自動的にブラウザを開く場合にWebサーバーからリダイレクトのレスポンスが返って来ると、標準ブラウザはリダイレクト先のURLを開く為にまたインテントを発行する様になっている。そのインテントを再度このアプリが受け取ると、結局一覧リスト上はURLが微妙に違うだけで内容は同じページ、という行が複数追加されてしまう事になる。http://d.hatena.ne.jp/hogehoge/というサイトを開いて、http://d.hatena.ne.jp/hogehoge/touchにリダイレクトされた場合にこの現象が起きる。「インテントを受け取った時にそれがリダイレクトされて来たものなのかどうか判定してそうであれば無視する。」という動作が出来ればいいのだが、その判定方法が見つからないのでこの方法は今の所実現出来ていない。「時間制限を設けて、前のインテント処理から一定の時間内(20秒とか)は同じタイトルの行を追加しない様にする。」というやり方が考えられる。ややこしくなりそうなのでそこまで実装するのは今回は見送り。今回の全ソースはこちらからダウンロード可能になっている。GitHub他にも「設定画面で標準以外のブラウザを選べる様にする」とか、「ListViewにチェックボックスを付けて処理対象を選択出来るようにする」とか、検討した機能はあった。「メールを送る代わりにどこかのサーバーにデータを送信する」というのも面白そうだ。DocumentsのSpreadsheetに書き込めば意外と使い途があるかも知れない。このアプリを変更するのは今回で一旦ストップしておこうと思う。Readerの「スターを付ける」機能を使えばほぼ達成出来る事に気付いてしまったからだ。ライフハッカー[日本版]情報の収集、閲覧、集約は全てGoogleリーダーに任せることにした。Readerの管理外のURLをPCに送りたい時だけになってしまった。それでも完全に用が無くなった訳ではないのでよしとしよう。それに、他にもっと面白そうな事がたくさんある。次からはSurfaceViewを使ったグラフィック処理や、SoundPoolを使って音を出す事などをやってみようかなと思っている。あと、OpenGLにも興味を惹かれる。何か子供向けの教育アプリでも作れないものかなとこんな本を読んでみたりもしている。(著)簡単なものから始めて少しずつ機能を追加して行く構成になっていて、とても分りやすい。2.3.3)への公式アップデートが可能になったので、早速やって見た。Androidのバージョンがほぼ最新に近い2.3.3になるという事で、事前の情報だとこんな感じで色々と変わるみたいだった。UI(以前と同じバージョン)のままなのでほとんど違いに気付かない。これには正直がっかり。UIが悪い訳ではないけれど、やっぱり新しいもの好きなので少しは見た目も変わって欲しかった。別のHomeアプリを入れればいくらでも変えられるので問題は無いけど。Androidのバージョンはこの通り、確かに2.3.3に変わっている。個人的には一番目立った変更点はこれだった。バッテリーの使用状況が分かり易くなったのでこれは確かに有り難い。確かに個々の動作が前よりもキビキビとしている様に感じる。すると気のせいかも知れないというレベルだけれども、なんとなく以前よりも動作が軽くなった様に思える。実は今日まで普通に使っている分にはこれぐらいしか違いに気付かなかった。以下、上のリンクの記事などを見ながら細かく変更点をチェックした。calling」というオプションが追加されている。ここでWifi接続時にSIP通話機能を使うかどうかを設定出来る(中央の画像)。その上の「Accounts」を開くと、SIPアカウントを複数追加する事が出来る様になっている(右の画像)。試しに以前に構築したテスト用のAsteriskサーバーに接続して発信した所、問題無く使えた。これは社内にSIPのVoIP環境がある所ではものすごく有用な機能ではないだろうか。Android携帯さえあれば内線の受信・発信も出来る様になるからだ。今までもSIP電話アプリを入れれば出来た事だが、OSの標準機能として統合されているという事で、さらに安心して使える様になりそうだ。自宅のWifi接続を使って会社のLANにVPN接続してからSIPで発信しようとすると出来なかった。VPN経由はダメみたいだ。UIの関係か標準とは違う見た目になっている。以前より多少使い易くはなったかも知れない。ペーストする時に勝手に両端に空白を入れられてしまうのはどうも変な感じ。日本語の文字列を選択状態にすると「コピー」の選択肢が出ずに「ペースト」しか選べなくなる場合があった。これはどう見てもバグっぽい。すると使っているIMEとの相性もあるのかも知れないが、よく分からない。電源ボタン長押しで再起動が可能にほとんど使う事はないけども、これ(Restart)も確か以前は無かったような気がする。そんなこんなで、Gingerbreadにアップデートはされたものの、日常的な使用感という意味ではこれまでとほとんど違いが無い。バッテリーの持ちが特に良くなったという感じも無い。大きな不具合が無さそうだと言う事だけでも有難い、...とポジティブに受け取っておこうと思う。EvoのGingerbreadへのアップデート開始へSprintから公式アナウンスがあった。UpdateGingerbreadが公開されたのが2010年の12月6日との事なので、ちょうどその半年後にEvoに反映される事になる。よし、いよいよ来週からGingerbreadだー。2.3)の違いを予習しておこう。(Gingerbread)う~ん、NFC非搭載の端末では正直言ってあんまり劇的に感動させられる様な違いは無いのかも知れない。文章に表せない点で使い勝手が良くなっている事に期待したい。あとはVoIPに標準対応という辺りが面白そうかな。時間のかかる処理を別スレッドで実行する前回まででSQLiteデータベースに履歴一覧を保存出来る様になり、少しは本格的(?)にアプリらしくなって来た。今回はURLからWebページのタイトルを取得して表示する様に変更したい。スレッドを使って非同期にHTTP通信を行ない、処理結果をListViewに反映すると言う、このブログの流れからすると今までで最高レベルの難易度(笑)になる内容だ。実際、作っていて「あ、そうだったのか」と気付かされる事が多くとても勉強になった。プログラミングは実際に手を動かして試行錯誤しながら習得するに限ると改めて感じた。今回の画面URLだけでなくページのタイトルも表示すると一気に実用的(な感じ)になった。ついでにListViewの各行の背景にグラデーションを使ってみた。URLからHTMLを取得するまずはHTTP通信でURLが示すWebページにアクセスしてHTML文字列を取得する部分だ。HttpUtilというクラスをプロジェクトに追加して、そこに色々書いて行く事にした。そのHttpUtilクラスに作ったgetHtmlメソッドはこんな感じになった。}予想もしなかった部分でつまずいた。文字エンコーディングの判別だった。SDK標準のDefaultHttpClientクラスが上手く処理してくれるのかと思っていたらそうではなく、取得したバイト配列を文字列に変換する部分は自分でやらないと行けない様だ。最初は自前でResponseヘッダのContent-Typeを見たりmetaタグのcharsetの値を見たりして、なんとか上手く動くようにはなったのだが、それでもたまに表示したタイトルが文字化けしている事があった。該当のWebページのソースを表示して見ると、metaタグでの指定はEUCなのに実際にはUTF-8でエンコーディングされている、などという様にResponseでの指定内容と実際のエンコーディングが異なっているケースがある事が分かった。結局、Responseに含まれているエンコーディング指定は完全には信用出来ないのだ。!それで色々調べた挙句、「juniversalchardet.jar」というライブラリを利用させてもらう事にした。Javaについてこのライブラリを使っている部分はこんな感じ。追記:下のページではjuniversalchardetについて「デコード時の文字コードがEUC-JPだった場合に、判定に失敗する可能性が非常に高い。」と書かれているので、要注意。grush-blogHTMLからタイトルを抜き出すHTMLが取得出来たら、次は<title>タグの始まりと終わりを探してその間の文字列を抜き出せばOKだ。正規表現を使えば楽勝だ、と思っていたら、これも意外と手こずってしまった。id='aaa'>タイトルの文字列</title>などの様に開始タグに属性が付いている事もある。それに、<title>(改行)タイトルの(改行)文字列(改行)</title>みたいにtitleタグの中に改行が入っているケースもある。正規表現は行単位に処理される(と思う)ので、開始タグと終了タグの間に改行が入ってしまうと途端に話がややこしくなる。)改行については最初に除去してから処理すればいいかと思ってやってみたけれども、そうすると文字列が長い場合に大量にCPUを使って戻って来なくなったりするので簡単には行かなそうだった。駆使(?)して自前で抜き出す事にした。本当はDOMを操作するライブラリなどを使えばこの辺を簡単にしてくれるメソッドが用意されているのかも知れない。実際のタイトル抽出部分のコードがどうなったかは、こちらから参照可能だ。javaの達人であればもっとキレイに書けると思うので、ちょっと恥ずかしい。「〜」)が含まれている場合や、実体参照(例:&gt;→「>」)が含まれている場合もあったので、タイトルを抜き出した後それぞれ変換処理をかませるようにしておいた。HTMLからタイトルを抜き出すだけの処理なのに、結構色々あるものだ。とここまで書いて今また一つ気付いてしまった。コメントアウトされたtitleタグへの対応だ。<title>コメントアウトされたタイトル</title>--><title>抽出して欲しいタイトル</title>というパターンになっていると今回のコードだとコメントアウトされた方のタイトルを抜き出してしまう。言うケースにも対応しようと思うと、やはり何らかのDOMライブラリを使う方がいいのかも知れない。今回のソースでHTTP通信の処理は別スレッドで行う様になっている。Androidプログラミングでは必須とも言えるマルチスレッド化については、長くなりそうなので別のエントリで書こうと思う。ダウンロードはこちらから。共有EclipseでAndroidのプログラムを実行しようとして変なxml.outファイルが出来てしまう場合EclipseでAndroidの開発をしている時にすごく気になる事があった。CTRL+F11で「実行」のはずが、何も起こらずに、代わりによく見ると変なファイルが出来ているのだ。エディタでXMLファイルを開いている場合に発生する。javaのソースを開いている場合は問題ないので、CTRL+F6を押してjavaのソースファイルを開いた状態にしてから実行する様に気を付けていたのだが、急いでいる時はついつい忘れてしまって、不便な事この上ない。その解決方法が今日やっと見つかった。実行したことになるため。以下の設定を変更すればOK。section."のラジオボタンを選択する。