Java7 Goldの勉強してて、並行処理の分野で「Fork/Join Framework」を試したくなったのでちょっと実装
お題はフィボナッチ数列を逐次/並行で計算すると、どのくらい差が出るのか
javaで書くから逐次と平行じゃ結構差が出るのでは。と予想
2015年12月12日土曜日
2015年11月15日日曜日
C++の数値入力で無限ループにはまった件
ちょっとC++の勉強をしてる時に入力処理で無限ループにはまる事態に陥ったのでその対応方法を書いとく
問題の処理:1~9までの数字を入力する際、不正入力されたとき再入力をさせる処理
問題の処理:1~9までの数字を入力する際、不正入力されたとき再入力をさせる処理
// C++の再入力処理. #include<iostream> using namespace std; int main() { int num; // 1~9が入力されていなければ再入力 cout << "Please enter the numbers(1-9) --> "; while(1) { cin >> num; if(num > 0 && num < 10)break; cout << "Please enter it again.(1-9) -->"; } return 0; }このとき、半角英数字「a」を入力すると.......
2015年11月4日水曜日
3次元CG作成ソフトを使ってみた
月末に「GCクリエータ」と「CGエンジニア」の資格試験を受けるので、GC作成ソフトを使ってみた
実践から3DCGの基礎を勉強していく方針。ソフトは「POV-Ray」を使う
ちなみに、こんな感じなのが作れるようになる模様
実践から3DCGの基礎を勉強していく方針。ソフトは「POV-Ray」を使う
ちなみに、こんな感じなのが作れるようになる模様
2015年10月24日土曜日
VirtualBox仮想Ubuntuに「Tera Term」で接続
Tera Termで仮想環境にアクセスできるようしておとくと、windowsでコピペしたコマンドを張れたりして楽(^q^)
今回はUbuntu用(Debian系)の設定。RedHat系はインストールコマンドとか別のはずなので注意
今回はUbuntu用(Debian系)の設定。RedHat系はインストールコマンドとか別のはずなので注意
2015年10月22日木曜日
Android6.0をフルビルドする方法
まだ発売されていないAndroid6.0のOSを使ってみようと思ったので、Android6.0のソースを落としてフルビルドしてみた。
一応今回はエミュレータで立ち上げるまで。実機に焼いてみるのはまた次回(まだ焼ける用実機持ってない)
一応今回はエミュレータで立ち上げるまで。実機に焼いてみるのはまた次回(まだ焼ける用実機持ってない)
2015年10月14日水曜日
Win8.1に64bitの仮想環境を作る方法
win8.1にLinux(64bit)の仮想環境を建てようとしたが、
仮想マシンのの作成でバージョンの一覧には64bitがない。。
当然だが、32bitを選んで64bitのOS突っ込んでも、インストール画面でエラー
最近はLinuxも64bitが普通なので、この忌々しき事態をどうにかしないといけない。。
仮想マシンのの作成でバージョンの一覧には64bitがない。。
当然だが、32bitを選んで64bitのOS突っ込んでも、インストール画面でエラー
最近はLinuxも64bitが普通なので、この忌々しき事態をどうにかしないといけない。。
2015年10月7日水曜日
VM VirtualBoxにUbuntuを入れてみた
以前MacでVirtualBoxにvagrantを使ってCentOSを入れてるブログ書いた
今回はWin8.1でVirtualBoxにUbuntuを普通に入れる方法書いとく
2015年9月23日水曜日
スマホでホログラムを演出する方法
今月の頭くらいにツイッターで話題になっていた
CDケースを使ったスマホで疑似ホログラムを実現させる方法を試してみた
割と仕掛けは簡単だったので、作り方、仕組み、実物の人間サイズを表示する方法について考察してみた。
CDケースを使ったスマホで疑似ホログラムを実現させる方法を試してみた
割と仕掛けは簡単だったので、作り方、仕組み、実物の人間サイズを表示する方法について考察してみた。
2015年9月21日月曜日
AndroidStudioの開発環境構築
Androidアプリを作る際、Googleさんが推奨している環境は「Android Studio」!!
以前は Eclipse にバンドルされたADTが一般的だったけど(仕事ではまだEclipseが現役ww)
Android Studio 1.00がやっとリリースされ、Googleさんがこっちメインでやるよーって言ってるので、移行していく
中2心をくすぐられるかっこいいUI!!
以前は Eclipse にバンドルされたADTが一般的だったけど(仕事ではまだEclipseが現役ww)
Android Studio 1.00がやっとリリースされ、Googleさんがこっちメインでやるよーって言ってるので、移行していく
2015年9月13日日曜日
空中に文字を書いてみた
最近、資格試験や、コミトレ出展作品作りとかで全然更新できてなかったの久々!
今回は同人誌即売会での値札のポップ作り(即売会には間に合わなかった模様。。)
残光現象を利用して、空中に本の値段を書いている
今回は同人誌即売会での値札のポップ作り(即売会には間に合わなかった模様。。)
残光現象を利用して、空中に本の値段を書いている
2015年6月30日火曜日
確率とモンテカルロシミュレーション
問題
Aさんには三人の子供がいる
「日曜日生まれの男の子はいるか?」と訊くと「いる」と答えた
Aさんの子供が三人とも男の子である確率は?
最近2ちゃんまとめで上の問題やってみて、Aさんには三人の子供がいる
「日曜日生まれの男の子はいるか?」と訊くと「いる」と答えた
Aさんの子供が三人とも男の子である確率は?
これをモンテカルロでやってみたら結果は理論解に収束するのかな?
って思ったのでやってみた
モンテカルロシミュレーションとは
シミュレーションや数値計算を乱数を用いて行う手法の総称。元々は、中性子が物質中を動き回る様子を探るために考案された。
強みは基礎的な理論さえ把握していれば、一定のアプローチに従って幅広い問題を解決できるという点
まぁ簡単にいえば、乱数つかって事象を表現して、それをループ文で何回も試し大数の法則でまとめ上げる手法強みは基礎的な理論さえ把握していれば、一定のアプローチに従って幅広い問題を解決できるという点
by Wiki
例:コインを投げた時の裏表の確率
表=1,裏=0として、0か1の乱数を生成。これを10万回(試行回数)繰り返し、1(表)が出たときの数をカウントしておく
コインが表になる確率は、「カウント数 / 10万(試行回数)」で求まる。
表=1,裏=0として、0か1の乱数を生成。これを10万回(試行回数)繰り返し、1(表)が出たときの数をカウントしておく
コインが表になる確率は、「カウント数 / 10万(試行回数)」で求まる。
理論解(ベイズの定理)
初見で1/4って思って恥じ書いたww三人のうち、一人が男確定だから、残り二人が男の確立Pは下の通り
\[
P=\frac{1}{2}\cdot\frac{1}{2}
=\frac{1}{4}
\]
( ̄ー ̄) ドヤッ!はい。日曜日を軽視してましたーww
これはどう見ても事後確率だから、ベイズの定理を使って解いてくのが定石
事象$A$:3人とも男が生まれる
事象$B$:少なくとも1人は日曜日に生まれの男がいる
とおくと、求める確率$P(A|B)$はベイズの定理より \[ P(A|B)=\frac{P(A \land B)}{P(B)} \]
$P(B)$は「3人とも日曜生まれの男でない」場合の余事象なので \[ P(B)=1-\frac{ 13^3}{14^3}=\frac{547}{2744} \]
$P(A \land B)$は3人とも男で、うち少なくとも一人は日曜日に生まれてる \[ P(A \land B)= _3C_0\left(\frac{1}{14}\right)^3\left(\frac{6}{14}\right)^0+ _3C_1\left(\frac{1}{14}\right)^2\left(\frac{6}{14}\right)^1+ _3C_2\left(\frac{1}{14}\right)^1\left(\frac{6}{14}\right)^2\\ =\frac{127}{2744} \] よって \[ P(A|B)=\frac{P(A \land B)}{P(B)}=\frac{127}{547} \]
事象$B$:少なくとも1人は日曜日に生まれの男がいる
とおくと、求める確率$P(A|B)$はベイズの定理より \[ P(A|B)=\frac{P(A \land B)}{P(B)} \]
$P(B)$は「3人とも日曜生まれの男でない」場合の余事象なので \[ P(B)=1-\frac{ 13^3}{14^3}=\frac{547}{2744} \]
$P(A \land B)$は3人とも男で、うち少なくとも一人は日曜日に生まれてる \[ P(A \land B)= _3C_0\left(\frac{1}{14}\right)^3\left(\frac{6}{14}\right)^0+ _3C_1\left(\frac{1}{14}\right)^2\left(\frac{6}{14}\right)^1+ _3C_2\left(\frac{1}{14}\right)^1\left(\frac{6}{14}\right)^2\\ =\frac{127}{2744} \] よって \[ P(A|B)=\frac{P(A \land B)}{P(B)}=\frac{127}{547} \]
数値解(モンテカルロ法)
今回はCで書いてみた誕生曜日、性別を持つ構造体をつくり、事象Bを満たす三兄弟を乱数使って生成
三兄弟が全員男である場合をカウントしていく。
最後に、試行回数(ループ数)とカウンターの比率がP(A|B)の確率となる
#include<stdio.h> #include<math.h> #include <time.h> #define SEX_BOY 0 #define SEX_GIRL 1 #define WEEK_SUNDAY 0 #define WEEK_MONDAY 1 #define WEEK_TUESDAY 2 #define WEEK_WEDNESDAY 3 #define WEEK_THURSDAY 4 #define WEEK_FRIDAY 5 #define WEEK_SATURDAY 6 #define LOOP_MAX 10000000 typedef struct { int sex; int week; } human_t; main() { srand((unsigned)time(NULL)); long unsigned loop_count, check_count = 0; const human_t target_child = {SEX_BOY, WEEK_SUNDAY}; human_t children[3]; for (loop_count = 0; loop_count < LOOP_MAX; loop_count++) { // 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる int check_flg = -1; while(check_flg != 0) { int i; for (i = 0; i < 3; i++) { human_t child = {rand() % 2, rand() % 7}; children[i] = child; if (memcmp(&target_child, &child, sizeof(human_t)) == 0) { check_flg = 0; } } } // 事象A:全員男の場合を調べる if( (children[0].sex == SEX_BOY) &&(children[1].sex == SEX_BOY) &&(children[2].sex == SEX_BOY)) { check_count++; } } double reslt = check_count/(double)LOOP_MAX; printf("reslt = %f",reslt); }結果は
reslt = 0.232030
理論解を小数点表記にすると、 127/547 = 0.232175503
よって、誤差は0.0006
いい感じ(^q^)
★ちなみに★
事象Bのロジックについて。。。。ソースの31~42行目で生成してる三兄弟は
三兄弟を乱数で生成 → その中に日曜生まれ男がいるかチェック → いれば三兄弟を指標として採用。いなければ作り直し
// 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる int check_flg = -1; while(check_flg != 0) { int i; for (i = 0; i < 3; i++) { human_t child = {rand() % 2, rand() % 7}; children[i] = child; if (memcmp(&target_child, &child, sizeof(human_t)) == 0) { check_flg = 0; } } }となってる。
これが面倒で
三兄弟を乱数で生成、その後、兄弟の誰かを日曜生まれ男に差し替える
// 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる human_t children[3] = { {rand()%2, rand()%7}, {rand()%2, rand()%7}, {rand()%2, rand()%7} }; // 三人のうち誰かを日曜生まれ男に差し替え children[rand()%3] = target_child;とか
長男を日曜生まれ男で固定し、残り二人を乱数で生成
// 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる human_t children[3] = { {SEX_BOY, WEEK_SUNDAY}, // 日曜男 {rand()%2, rand()%7}, {rand()%2, rand()%7} };などをすると、数値解析結果は
- 三兄弟を乱数で生成、その後、兄弟の誰かを日曜生まれ男に差し替える
- 長男を日曜生まれ男で固定し、残り二人を乱数で生成
僕が理論解でミスった値、1/4に収束していることがわかる
これは事後確率であることがプログラム上で示せていないため
つまり、ちょっと難しいが、モンテカルロシミュレーションでも事後確率と普通の確率は区別して求めることができる!
まとめ
モンテカルロシミュレーションで事後確率を示すには、前提条件の定義さえしっかりプログラムできていれば求めることができる!
2015年5月31日日曜日
積分方法の比較
FORTRANの環境作ったし、実際に計算させてみた
やっぱJavaより早い気がするw
今回は積分のプログラミング方法3パターンの比較を行って、収束具合、正確性を見ていく
でも、実装が一番楽な長方形近似で分割数を膨大に増やして計算するのが、実装ミス、手間が省けて効率いいかも思った
以上
やっぱJavaより早い気がするw
今回は積分のプログラミング方法3パターンの比較を行って、収束具合、正確性を見ていく
積分の計算種類
\[
\begin{eqnarray}
\int_a^b f(x) dx
\end{eqnarray}
=\lim_{n \to \infty} \sum^n_{i=0}f(x)\Delta x
\]
基本的に積分は上記式のように区分求積法で計算していくわけだけど、「$n \to \infty$」はプログラムでは実現できない。なので、様々な計算法で近似して数値解を求める必要がある。
- 長方形近似 一番基本形。積分する領域を長方形に切り刻んで全部足す方法
- 台形近似 長方形近似の上位互換。積分する領域を台形に刻むことで、勾配を配慮した形
- シンプソンの公式 f(x) を二次関数 P(x) で近似する数値解析方法
・数式 \[ \begin{eqnarray} \int_a^b f(x) dx \end{eqnarray} =\sum^n_{i=a}f(i)\Delta x \]
$x$座標の始点、終点、ループ回数を引数にした関数だとこんな感じ
・数式 \[ \begin{eqnarray} \int_a^b f(x) dx \end{eqnarray} =\sum^n_{i=a}\frac{f(i)+f(i+\Delta x)}{2}\Delta x \]
$x$座標の始点、終点、ループ回数を引数にした関数だとこんな感じ
・数式 \[ \begin{eqnarray} \int_a^b f(x) dx \end{eqnarray} =\frac{h}{3}\Bigl\{f(a)+4\sum^m_{i=1}f(x_{2i-1})+2\sum^{m-1}_{i=1}f(x_{2i})+f(b)\Bigr\} \]
$x$座標の始点、終点、ループ回数を引数にした関数だとこんな感じ
積分の比較
次に3つの積分方法の正確性、収束性、計算スピードについてみていく- 正確性 下記式の左辺の数値解をそれぞれを求め、$\pi$にどれだけ近づくか見てみる \[ \begin{eqnarray} \int_0^1 \frac{4}{1+x^2} dx \end{eqnarray} =\pi \]
- 収束性 解の収束度合はどうだろうか。
- 計算スピード 最後に計算速度の差について
計算方法 | 解析解 | 正確性[%] |
---|---|---|
長方形近似 | 3.17157602 | 100.9544002 |
台形近似 | 3.16147637 | 100.6329184 |
シンプソンの公式 | 3.15489316 | 100.4233683 |
x軸に刻み回数、y軸に数値解とし、刻み回数と解の収束をグラフ化した
sekibun1→長方形近似、sekibun2→台形近似、sekibun3→シンプソンの公式、pi→$\pi$ 収束度合の数値化がちょっとできてないけど、シンプソンの公式がかなり早い段階で収束することがわかる。
この程度なら差を感じないけど、計算に一週間かかる場合とかだと重要になってくる。それぞれのアルゴリズムの計算回数からだいたいの計算時間のオーダーを求める
$f(x)$の計算ステップが$F$、分割数$N$とすると
計算方法 | オーダー |
---|---|
長方形近似 | $N(4+F)$ |
台形近似 | $N(6+2F)$ |
シンプソンの公式 | $N(6+F)$ |
つまり、長方形近似>シンプソンの公式>台形近似の順で早いことになる。
特に$f(x)$の計算ステップが異常にかかる系だと、台形近似が大変
結論
シンプソンの公式が収束率、計算速度が優れていてよさげでも、実装が一番楽な長方形近似で分割数を膨大に増やして計算するのが、実装ミス、手間が省けて効率いいかも思った
以上
2015年5月24日日曜日
WINDOWS7でFORTRAN77の開発環境を作る
やっぱ数式とかプログラムしていく上で必要になってきたので、
FORTRAN77の開発環境を作っていく。
「Notepad++ 5.8.5」が簡単だった
インストールは下記のURL
http://filehippo.com/jp/download_notepad/8806/
完成!!
FORTRAN77の開発環境を作っていく。
FORTRAN77とは
科学技術計算に向いた手続き型プログラミング言語。その長い歴史の間に開発された非常に多くの数学関数やサブルーチンを数値解析ソフトウェアの形で持っている
まぁ簡単言えば計算特化型のプログラム言語。むしろ計算しかできない
by Wiki
環境構築
- ダウンロード http://kkourakis.tripod.com/g77.htm
- g77exe.zip
- g77lib.zip
- 環境変数の設定 ダウンロードしたzipを解凍する。
ココから下記をダウンロード
「g77exe.zip」の中に「bin」ファイル
「g77lib.zip」の中に「lib」ファイル
があるので、それぞれのパスを環境変数に登録
環境変数の登録はwin7の場合
「スタートボタン」⇒「コンピュータ」を右クリック「プロパティ」⇒「システムの詳細設定」⇒「環境変数」⇒「システムの環境変数」をスクロールして、「Path」編集
最後尾にbinとlibのパスを追記。
例)
Cドライブ直下に「g77」フォルダを作り、その中に「bin」フォルダ、「lib」フォルダを格納した場合
を環境変数の最後に追記する。
「;」はパスの切れ目を表すので、書き忘れ注意
環境変数の画面を全部「OK」終わらせると、設定が反映される
コンパイルと実行
サンプルで下記のファイル「sample.f」を作る
コマンドプロンプトで下記を実行して実行できる確認
手軽なエディタ
FORTRANのIF文とかに色がついてくれる便利なエディタがあれば開発が楽になるので紹介「Notepad++ 5.8.5」が簡単だった
インストールは下記のURL
http://filehippo.com/jp/download_notepad/8806/
- 開発言語をFORTRANに設定する タブの「言語」⇒「F」⇒「Frotran」
- 日本語記述で文字化けしない様にする 「フォーマット」⇒「UTF-8(BOMなし)エンコード」
完成!!
2015年4月29日水曜日
VNCでラズパイを操作する
ラズパイのディスプレイ操作が必要なとき、いちいちHDMIにつなげないといけないのが面倒だったので、外部からラズパイへリモートアクセスする方法探してみた。
VNCという便利なものがあったので導入手順書いとく
完成!
次回からのアクセス
ラズパイをシャットダウンさせるとVNCも落ちるので、再起動時はVNC起動コマンドが必要
VNCという便利なものがあったので導入手順書いとく
VNCとは
-
VNC (Virtual Network Computing)とは、リモートマシンのデスクトップを手元のPCから使えるようにするやつ。つまり、テラタームみたいなリモートでコマンドしか使えないのではなく、リモートでGUIを使えるツール
ラズパイにVNCサーバを導入
- インストール ラズパイにVNCをインストールする
- パスワード設定 VNCを起動の初回はパスワードの設定が行われる
8文字以内で短すぎないのを設定しないと怒られる
pi@raspberrypi ~ $ tightvncserver # VNC起動コマンド You will require a password to access your desktops. Password: Password too short # 設定したパスワードが短すぎた場合 pi@raspberrypi ~ $ tightvncserver You will require a password to access your desktops. Password: Warning: password truncated to the length of 8. # 設定したパスワードが長すぎた場合切り捨てられる? Verify: # パスワード再入力 Would you like to enter a view-only password (y/n)?y New 'X' desktop is raspberrypi:1 # この数字が大事 Creating default startup script /home/pi/.vnc/xstartup Starting applications specified in /home/pi/.vnc/xstartup Log file is /home/pi/.vnc/raspberrypi:1.log17行目の数字はディスプレイ番号と呼ばれるもの
後で使うので覚えておく
当然ながら、パスワードも後で使うので覚えておく
PC側からVNCを見れるようにする次にPC側の設定
- インストール Viewerをインストールする。
- セットアップ exe起動させてセットアップしていく
- ログイン ラズパイで設定したパスワード入力してログイン
http://www.realvnc.com/download/viewer/
家のPCはwin7 64bitだったのでコレ
ラズパイのIPアドレス、ラズパイでパスワード設定したときのディスプレイ番号を入力する
「IPアドレス:ディスプレイ番号」の形式で入力して、「Connect」を押す
警告ポイの出るけど、気にしない。
チェックボックスにチェック付けると、二度とこの画面でなくなるので、警告ウィンドウがうざいならチェック付けとく
完成!
消し方と次回からのアクセス
- 消し方 消すときはViewerの右上の×ボタンでOK
このときまだプロセス(?)は生きているので、再びWindowsのVNC Viewerから接続を行うと消す前と同じ画面になる
完全に消すにはテラタームで
$ vncserver -kill :X # X はディスプレイ番号または、ラズパイをシャットダウンさせる
$ tightvncserver New 'X' desktop is raspberrypi:1 # ディスプレイ番号 Starting applications specified in /home/pi/.vnc/xstartup Log file is /home/pi/.vnc/raspberrypi:1.logまたディスプレイ番号が付与されるので、その番号を元にViewerで接続する
2015年3月22日日曜日
ラズパイでカメラを使う方法
ラズパイでカメラモジュールを使ってみた
使用したのは「Raspberry Pi カメラモジュール」
ラズパイ用のカメラモジュールのため、かなり簡単に作れた
まぁ全然カメラ起動しなくて苦戦したけどw
奥で観にくいけど、白い部分を持ち上げて、カメラモジュールのピラピラを差し込む。
カメラのピラピラは金の線が入ってる方を手前にする必要があるので注意
画像を見る方法はコレ ※「photo.jpg」は表示したい画像の名前なので任意
できた!!!
使用したのは「Raspberry Pi カメラモジュール」
ラズパイ用のカメラモジュールのため、かなり簡単に作れた
まぁ全然カメラ起動しなくて苦戦したけどw
カメラの取り付け方下画像の赤い枠に注目
奥で観にくいけど、白い部分を持ち上げて、カメラモジュールのピラピラを差し込む。
カメラのピラピラは金の線が入ってる方を手前にする必要があるので注意
カメラモジュールの設定ラズパイの電源を入れてカメラの設定していく。普段SSHで作業してる人はHDMI端子を用意する必要がある。ラズパイの設定画面を操作するので画面出力するから
- 設定画面を開く
- カメラモジュール有効可 下の画像みたいなの出るから、「Enable Camera」を選択。「Enable」を選らんで「Enterキー」押す
下の画像に戻るので、「Escキー」を押す。←これ気づかずに「Finish」押して再起動してたらカメラ有効にならなくてめっちゃ迷ったww
写真の撮り方と確認方法撮り方はこのコマンド ※「photo.jpg」は保存する写真の名前なので任意
画像を見る方法はコレ ※「photo.jpg」は表示したい画像の名前なので任意
できた!!!
Canvasクラスを制すものは描画を制す
AndroidはViewを描写をするときonDraw(Canvas canvas)メソッドで行っている
この引数のCanvasクラスは描画するための最小単位のクラスで、こいつでテキストだしたり、画像出したりしてる。android1.0から存在する大先輩クラス
今ではほとんどCanvasを意識せずViewの組み合わせによりUI構築を行うが、結局は各ViewのonDraw()で描写してるだけに過ぎない。ゆえにImageView、TextView、ListViewなどのViewを継承してるクラスのonDraw()をオーバーライドさせてやれば何でも描画できる!
てことでonDraw時のCanvasを制御する方法をまとめる
Canvas# rotate()メソッドに回転方向、回転軸x、yを引数にして回転させる
回転軸は設定しないと、Viewの左上が回転軸になる。Viewの中心で回転させるので、横幅、縦幅の半分が回転軸
そうすると、X軸に対し、線対称に表示される
Handlerでスレッド止めたりするのキューたまりすぎないか心配なので、Threadとかで時間管理した方がいい気がする
文字反転と同じようにCanvasをView中心で回転させて、onDraw()を何度も呼ぶ。
この引数のCanvasクラスは描画するための最小単位のクラスで、こいつでテキストだしたり、画像出したりしてる。android1.0から存在する大先輩クラス
今ではほとんどCanvasを意識せずViewの組み合わせによりUI構築を行うが、結局は各ViewのonDraw()で描写してるだけに過ぎない。ゆえにImageView、TextView、ListViewなどのViewを継承してるクラスのonDraw()をオーバーライドさせてやれば何でも描画できる!
てことでonDraw時のCanvasを制御する方法をまとめる
文字の逆さ表示(TextViewのOverride)やり方はCanvasを回転させて逆さにする。
Canvas# rotate()メソッドに回転方向、回転軸x、yを引数にして回転させる
回転軸は設定しないと、Viewの左上が回転軸になる。Viewの中心で回転させるので、横幅、縦幅の半分が回転軸
/** 逆向き表示TextView */ TextView textView1 = new TextView(this) { @Override public void onDraw(Canvas canvas) { canvas.save(); canvas.rotate(180, getWidth() / 2, getHeight() / 2); super.onDraw(canvas); canvas.restore(); } };
文字の反転(TextViewのOverride)やり方はCanvasの倍率を変更するメソッドで倍率をx軸を-1倍にする。
そうすると、X軸に対し、線対称に表示される
/** 反転表示TextView */ TextView textView4 = new TextView(this) { @Override public void onDraw(Canvas canvas) { canvas.save(); canvas.scale(-1, 1); canvas.translate(-getWidth(), 0); super.onDraw(canvas); canvas.restore(); } };
影付き文字(TextViewのOverride)影付けるにはCanvasの文字を描写するメソッドを使い、少しずれた位置に表示文字色より、薄い色で文字を描く
/** 影付きTextView */ TextView textView2 = new TextView(this) { Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); @Override public void onDraw(Canvas canvas) { canvas.save(); // 影の部分を先に描いてその上にほんとの文字を描く // 色の設定。本物の色を取ってきて、RGBに加算して色を足す p.setColor(getTextColors().getDefaultColor() | 0x00AFAFAF); // 文字のサイズを合わせる p.setTextSize(getTextSize()); // 表示文字を合わせる String text = getText().toString(); // 文字の表示位置調整。文字の高さ、マージンとか計算 FontMetrics fm = p.getFontMetrics(); float size = (fm.descent - fm.ascent); float margin = (getHeight() - size) / 2; canvas.drawText(text, 0, margin + getTextSize(), p); // 表示位置のずれ具合調整。だいたい5とか10くらい int dx = 5; canvas.drawText(text, 0 + dx, margin + getTextSize() + dx, p); canvas.restore(); // 通常描写 super.onDraw(canvas); } };
スクロールアニメーション(TextViewのOverride)Handler使って描画時間を調整しつつ、Canvasを平行移動させていくことで文字をずらしていく。
Handlerでスレッド止めたりするのキューたまりすぎないか心配なので、Threadとかで時間管理した方がいい気がする
/** スクロールアニメーションTextView. */ TextView textView3 = new TextView(this) { Point point = new Point(); Handler handler = new Handler(); Runnable runnable = new Runnable() { public void run() { // 10m秒このスレッドの時間を止める。その後強制描写メソッドで、onDraw()を呼ぶ try {Thread.sleep(10);} catch (InterruptedException e) {} invalidate();} }; @Override public void onDraw(Canvas canvas) { canvas.save(); // 結構ややこしい計算だけど、幅の2倍移動するので、移動式がこんな感じ canvas.translate(point.x + getWidth(), 0); super.onDraw(canvas); canvas.restore(); // 座標を3ずらして表示。表示幅の2倍より大きくなったらリセット point.x = (point.x -3) % (getWidth() * 2); handler.post(runnable); } };
角度を変えた画像の重ね合わせ(ImageViewのOverride)
この画像を回転させながら重ね合わせて、扇風機の羽を作る |
/** 重ね合わせImageView. */ ImageView imageView = new ImageView(this) { @Override public void onDraw(Canvas canvas) { canvas.save(); // 回転軸の決定 PointF axis = new PointF(getWidth() / 2, getHeight() / 2); super.onDraw(canvas); canvas.rotate(90, axis.x, axis.y); super.onDraw(canvas); canvas.rotate(180, axis.x, axis.y); super.onDraw(canvas); canvas.rotate(270, axis.x, axis.y); super.onDraw(canvas); canvas.restore(); } };こんな感じで簡単にViewの編集が行える
2015年3月10日火曜日
ListViewをカスタマイズする方法
「アニメ番組表 API使った通信Androidアプリを作ってみた」でアニメのタイトル一覧をListViewで表示した。
でもタイトルだけ表示しても面白くないので、ListViewをカスタマイズする
ListViewを制する者は、AndroidのView表示を制す!
というだけあっていろいろなテクニックが必要だった
表示させたいデータをAdapterクラスに入れて、Apapterに設定してあるレイアウトを基にViewを組立て、ListViewに表示させる
既存のAdapterでリストをつくるとこうなる
ArrayAdapterのコードを見てみると、レイアウト内のTextViewに文字列を入れる仕組みになってた
オリジナルのListViewを作るにはAdapterのレイアウトを組み立てる仕組みを作ってやればいい
今回は曜日、開始時間、放送局、タイトルをTextViewで表示する
こんな感じで設定
TextViewは列をはみ出そうとすると自動で改行を入れてくれる。しかし、ListViewでこれをやられると各セルの高さがばらばらになる
個人的にセルの高さは統一したいので、TextViewは一行で表示、はみだした分を…となるように設定する
色つけてる箇所がポイントで、
singLineを"true"で一行表記設定
ellipsize="end"で…の位置を設定
ちなみにellipsize属性に設定できる他のフラグはこんな感じ
データクラスとしてAnime.javaを作っとく。
とりあえずAnimeAPIから得られる情報一式をメンバ変数にする
この辺はだいたいテンプレ。特殊なことしないならこれでOK
でもタイトルだけ表示しても面白くないので、ListViewをカスタマイズする
ListViewを制する者は、AndroidのView表示を制す!
というだけあっていろいろなテクニックが必要だった
ListViewの作り
ListViewに文字を表示させるには表示させたいデータをAdapterクラスに入れて、Apapterに設定してあるレイアウトを基にViewを組立て、ListViewに表示させる
既存のAdapterでリストをつくるとこうなる
// Viewの生成に必要 Context context = this.getApplicationContext(); // Adapterの設定 Adapterのコンストラクタ(Context,LayoutのID,リストID, リストデータ) String[] list = { "りんご", "ゴリラ", "ラッパ", "パイナップル" }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_expandable_list_item_1, list); // AdapterをListViewにセット ListView listView = new ListView(context); listView.setAdapter(adapter);「android.R.layout.simple_expandable_list_item_1」は既存のセル用レイアウトでTextViewが1個あるだけ
ArrayAdapterのコードを見てみると、レイアウト内のTextViewに文字列を入れる仕組みになってた
オリジナルのListViewを作るにはAdapterのレイアウトを組み立てる仕組みを作ってやればいい
Layoutの定義
表示するセルのレイアウトを作ってくプロジェクトフォルダ/res/layout/
定義は右や上のフォルダの位置にxmlファイルつくって描く ソースコード上でも描けるけど、この方が見やすいからねw 名前は何でもいいので、「list_aitem.xml」としておく。この名前はレイアウトのIDとして後々使用するので適当すぎると困る |
こんな感じで設定
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/week_view" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="center" android:layout_margin="10dip" android:textColor="#000000" android:textSize="50sp" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="10dip" android:background="#0FFFF0" android:orientation="horizontal" > <TextView android:id="@+id/time_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20sp" /> <TextView android:id="@+id/tv_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:textColor="#000000" android:textSize="20sp" /> </LinearLayout> <TextView android:id="@+id/title_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:singleLine="true" android:ellipsize="end" android:textColor="#000000" android:textSize="30sp" /> </LinearLayout> </LinearLayout>アニメタイトルのTextViewが問題
TextViewは列をはみ出そうとすると自動で改行を入れてくれる。しかし、ListViewでこれをやられると各セルの高さがばらばらになる
個人的にセルの高さは統一したいので、TextViewは一行で表示、はみだした分を…となるように設定する
色つけてる箇所がポイントで、
singLineを"true"で一行表記設定
ellipsize="end"で…の位置を設定
ちなみにellipsize属性に設定できる他のフラグはこんな感じ
フラグ | 効果 | 例 |
---|---|---|
end | 文末が… | アニメタイ… |
start | 文頭が… | …メタイトル |
middle | 文の途中が… | アニメ…トル |
none | …をしない | アニメタイト |
Adapter作り
上記コードでは既存のAdapterを使っているが、今回は自作したレイアウトにデータを詰め込む実装がいるのでBaseAdapterを継承してオリジナルAdapterを作っていくデータクラスとしてAnime.javaを作っとく。
とりあえずAnimeAPIから得られる情報一式をメンバ変数にする
class Anime { public String title, url, time, station, state, next, episode, cable, today, week; }BaseAdapterは抽象クラスで実装しないといけないクラスがいくつかある
この辺はだいたいテンプレ。特殊なことしないならこれでOK
/** * アニメ表示用Adapter. * */ public class AnimeAdapter extends BaseAdapter { private static final int LAYOUT_ID = R.layout.list_aitem; // 上記で定義したレイアウトのID private Anime[] list; // データリスト private Context mContext; // レイアウトIDからLayoutインスタンス取得に必要 /** コンストラクタ. */ public AnimeAdapter(final Context context, Anime[] animes) { this.mContext = context; this.list = animes; } @Override /** リストの数を取得 */ public int getCount() { return list.length; } @Override /** リストから特定のAnimeを取得.戻り値Anime型にしとくのがミソ */ public Anime getItem(int position) { return list[position]; } @Override /** リストのID取得(まぁリストの順番でOK) */ public long getItemId(int position) { return position; } @Override /** レイアウトにデータを設定する(一番したい処理) */ public View getView(int position, View convertView, ViewGroup parent) { //TODO 色々テクニックいるので下で書く return null; } }次に一番やりたかった処理。レイアウトにデータを詰めて表示する処理
- セル用レイアウトにコンバート セル用レイアウトにデータを突っ込む処理
- 高速処理 上記の方法でも処理できるが、毎回findViewById()をするとViewの呼び出し時間がかかってしまう難点がある
ListViewは生成されたとき全てのセルを作ってるわけではない。セルのViewは画面に出たときに生成されてる
そして、スクロールして新しいセルが表示されたとき、このメソッド(# getView())は呼ばれViewを生成する
引数は
・position | ⇒ | 表示すべきリストのポジション |
・convertView | ⇒ | ListViewで表示するセル用レイアウトのインスタンス |
・parent | ⇒ | よくわからん(今回使わんので無視) |
そこで必要なのがレイアウトのインスタンスを取得するLayoutInflaterクラス。システムからLayoutInflaterのインスタンスを呼び出し、# inflateメソッドでレイアウトを指定してレイアウトのViewのインスタンスを取得する
LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.list_aitem, null);そのレイアウトから各Viewのインスタンスを取得するには#findViewByIdメソッドをつかう
XMLで定義したIDを指定してViewのインスタンスを得て設定する
TextView textView = (TextView) convertView.findViewById(R.id.title_view);以上を行い、レイアウトを設定して戻り値に修正後のレイアウトを指定すればOK
@Override /** レイアウトにデータを設定する(一番したい処理) */ public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = (LayoutInflater) this.mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.list_aitem, null); } Anime anime = getItem(position); ((TextView) convertView.findViewById(R.id.title_view)).setText(anime.title); ((TextView) convertView.findViewById(R.id.time_view)).setText(anime.time); ((TextView) convertView.findViewById(R.id.week_view)).setText(anime.week.substring(0, 1)); ((TextView) convertView.findViewById(R.id.tv_view)).setText(anime.station); return convertView; }
それを回避するためにViewHolderという考え方を使う。レイアウトを使いまわす手法
セル内のそれぞれのViewの参照値あらかじめ持たせておき、そこのインスタンスにデータを突っ込む
@Override /** レイアウトにデータを設定する(一番したい処理) */ public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; // レイアウト ViewHolder holder; // ホルダー if (v == null) { LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = inflater.inflate(R.layout.list_aitem, null); // ホルダーを設定する holder = new ViewHolder(); holder.titleView = (TextView)v.findViewById(R.id.title_view); holder.timeView = (TextView)v.findViewById(R.id.time_view); holder.weekView = (TextView)v.findViewById(R.id.week_view); holder.tvView = (TextView)v.findViewById(R.id.tv_view); v.setTag(holder); }else{ // レイアウトからViewフォルダーを取得 holder = (ViewHolder)v.getTag(); } Anime anime = getItem(position); // Viewフォルダに情報詰め込む holder.titleView.setText(anime.title); holder.timeView.setText(anime.time); holder.weekView.setText(anime.week.substring(0,1)); holder.weekView.setBackgroundColor(color.get(anime.week)); holder.tvView.setText(anime.station); return v; } // ホルダークラス class ViewHolder { TextView titleView, timeView, weekView, tvView; }これでOK
2015年3月2日月曜日
ラズパイで液晶ディスプレイに時間を表示させてみた(作成中)
I2C通信の勉強でラズパイに液晶ディスプレイに時間を表示させてみた。
めっちゃ疲れたーーーーwwww
配線間違えて電源入いらんようになったり、パーツ足らずに代用ためしたり、配線の理屈わからなかったり。。。 電子工作険しいww
とりあえず、まとめる
具体的にはラズパイがマスタ、周辺機器(温度計、加速度センサ、気圧)がスレーブとなって、データのやり取り(温度取得、加速度取得、気圧取得)ができるようになる
しかも、通信するための線が下の2本だけでできるってのも魅力
モジュールのインストール
インストール
普通は100kHzが標準。でも今回使う液晶が50kHzでないと作動しないのでコレで設定
上で、「i2c-dev」と「i2c-bcm2708」を読みこんだけど、「i2c-bcm2708」は電源入れ直したらまた読み込みし直しになるので、自動で起動するようにするPythonで制御するライブラリをインストール
めっちゃ疲れたーーーーwwww
配線間違えて電源入いらんようになったり、パーツ足らずに代用ためしたり、配線の理屈わからなかったり。。。 電子工作険しいww
とりあえず、まとめる
I2C通信とは
I2C通信は周辺機器とシリアル通信する仕組み具体的にはラズパイがマスタ、周辺機器(温度計、加速度センサ、気圧)がスレーブとなって、データのやり取り(温度取得、加速度取得、気圧取得)ができるようになる
しかも、通信するための線が下の2本だけでできるってのも魅力
- データのやり取りする線(SDA)
- タイミング調整する線(SCL)
お買い物リスト
ラズパイでI2C通信をする為の準備
- モジュールを使えるようにする 読み込みできるように設定。管理者権限がいるので注意
$ sudo vi /etc/modules i2c-dev # これを追記しとくデフォルトで読み込み拒否設定ファイルにi2cがいるので、コメントアウトしとく
$ sudo vi /etc/modprobe.d/raspi-blacklist.conf # blacklist i2c-bcm2708 # 行頭に#つけてコメントアウト
$ sudo apt-get install i2c-toolsモジュール読み込み。ここでI2Cの動作クロック周波数を50kHzにする
普通は100kHzが標準。でも今回使う液晶が50kHzでないと作動しないのでコレで設定
$ sudo modprobe i2c-dev $ sudo modprobe i2c-bcm2708 baudrate=50000次回起動時にモジュールが動くように設定
上で、「i2c-dev」と「i2c-bcm2708」を読みこんだけど、「i2c-bcm2708」は電源入れ直したらまた読み込みし直しになるので、自動で起動するようにする
$ sudo vi /etc/modprobe.d/i2c.conf # ←このパスに「i2c.conf 」ファイル作って、下記を書いとけばよい options i2c_bcm2708 baudrate=50000
$ sudo apt-get install python-smbus準備完了!
配線
作成中実装
作成中2015年3月1日日曜日
javaで画像処理する方法
画像処理は計算に時間かかるから普通はCとかでするんだけど、javaであえて書いてみる!
対象は「人によって青&黒または白&金に見える」って話題になったこのドレスの画像で
画像の情報を見るとき、下のかけ算をよくみると思うけどコレがこの画像を表示するための点の数
上記画像は、横方向に288個、縦方向に437個。つまり、125856個の点で構成されているってこと
この点を「ピクセル」といい、色の情報を持ってる
一般的にはピクセルは4Byteで表されてるんだけど、振り分けはこんな感じ
透過率はまぁいいとして、
それぞれ赤、青、緑を色深度を0x00〜0xFF(16進表記)で表してる
色深度は数値が低いほど暗い。例として、下記に示す※透過はFFとしておく
こんな感じで、すべての色を赤青緑でつくってることになる
ポイントは色ついてるとこ(32~34行目)。それぞれの色を半分にすることで、暗くできる
赤のみ抽出
出力結果
ビット反転すると写真のネガみたいになる
不思議なことにビット反転すると白&金になるんだよねw
他にもモザイクをかけたり、色のヒストグラムとったりなどいろいろできるけど、今回はこれまでで
対象は「人によって青&黒または白&金に見える」って話題になったこのドレスの画像で
画像の仕組み
画像とは大量の点からできている画像の情報を見るとき、下のかけ算をよくみると思うけどコレがこの画像を表示するための点の数
上記画像は、横方向に288個、縦方向に437個。つまり、125856個の点で構成されているってこと
この点を「ピクセル」といい、色の情報を持ってる
一般的にはピクセルは4Byteで表されてるんだけど、振り分けはこんな感じ
ビット | 00000000 | 00000000 | 00000000 | 00000000 |
---|---|---|---|---|
意味 |
色深度は数値が低いほど暗い。例として、下記に示す※透過はFFとしておく
色の情報 | 表示される色 | 表示される色 |
---|---|---|
0xFFFF0000 | 赤(純粋) | |
0xFF000100 | 青(めっちゃ暗い) | |
0xFFFF00FF | マゼンダ(赤と緑を混ぜた色) |
実装
javaには最初から画像処理するためのクラスがあるので、ダウンロードとかいらない。インポートさえすれば使える 一例に、画像を少し暗くする/** ImageTest.java 画像を少し暗くする処理 */ import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.ImageIO; public class ImageTest { final static String INPUT_IMAGE = "image.jpg";// 読み込み画像(このjavaファイルと同じフォルダに入れとく) final static String OUTPUT_IMAGE = "reslt.jpg";// 出力する画像(このjavaファイルと同じフォルダに出力される) public static void main(String[] args) throws IOException { // 画像読み込み File in_file = new File(INPUT_IMAGE); BufferedImage read = ImageIO.read(in_file); // 情報取得 w, hは縦、横のピクセル数。writeは画像の1ピクセルごとにアクセスできるようにしてる int w = read.getWidth(),h=read.getHeight(); BufferedImage write = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); // 格ピクセルに処理していく for(int y=0; y < h; y++) { for(int x = 0; x < w; x++) { // 色抽出 int color = read.getRGB(x, y); // 座標の色を取得 int red = (color & 0xff0000) >> 16; // 赤抽出 int green = (color & 0x00ff00) >> 8; // 緑抽出 int blue = (color & 0x0000ff) >> 0; // 青抽出 // 色の情報変換 red = red / 2; // 赤色の色深度を半分にする green = green / 2; // 緑色の色深度を半分にする blue = blue / 2; // 青色の色深度を半分にする // 出力用にフォーマット red =(red << 16) & 0xff0000; green=(green << 8) & 0x00ff00; blue =(blue << 0) & 0x0000ff; color = 0xff000000 | red | green | blue; write.setRGB(x, y, color); // 情報変換した色をもとの座標に戻す } } // 画像の出力 File out_file = new File(OUTPUT_IMAGE); ImageIO.write(write, "jpg", out_file); } }結果はこんな感じ
ポイントは色ついてるとこ(32~34行目)。それぞれの色を半分にすることで、暗くできる
応用
ポイント部分を変えることで、いろんなことができる赤のみ抽出
// 色の情報変換 red = red; // 赤はそのまま green = 0; // 緑は0にする blue = 0; // 青は0にする青のみ抽出
// 色の情報変換 red = 0; // 赤は0にする green = 0; // 緑は0にする blue = blue;// 青はそのまま緑のみ抽出
// 色の情報変換 red = 0; // 赤は0にする green = green; // 緑はそのまま blue = 0; // 青は0にするビット反転
// 色の情報変換 red = ~red; // ビット反転 green = ~green; // ビット反転 blue = ~blue; // ビット反転
出力結果
赤抽出 | 青抽出 | 緑抽出 | ビット反転 |
---|---|---|---|
不思議なことにビット反転すると白&金になるんだよねw
他にもモザイクをかけたり、色のヒストグラムとったりなどいろいろできるけど、今回はこれまでで
2015年2月19日木曜日
Androidの通信処理はAsyncTaskで処理すると楽
前回アニメ番組表APIの通信処理(http://tommyproguram.blogspot.jp/2015/02/apiandroid.html)で、Thread, Hadlerを使った処理を行ったが、UIスレッド、通信スレッドの行き来を意識するのが面倒だった。
なので、今回はそれを簡単に実装できるAsyncTaskクラスを使って通信処理を書き直してみる。
UIが絡むとそれだけコストがかかる。このクラスは必ずUIスレッドを経由するため、UIスレッドを絡めない処理は無難にThreadクラスで処理した方が良い
UI関係の起こりやすく、発見しにくかったバグを2つ紹介する
AsyncTask<Params、Progress、Result>で表される。ジェネリクスはそれぞれ下記の意味
今回の場合はこれなので
なので、今回はそれを簡単に実装できるAsyncTaskクラスを使って通信処理を書き直してみる。
AsyncTaskとは
「エイシンクタスク」って読むらしい。 このクラスはexecute()メソッドを実行することで、下記の順でメソッドが呼ばれる。メソッドごとに実行スレッドが違うのが特徴メソッド名 | 意味 | 使用例 |
---|---|---|
onPreExecute | 非同期処理する前に何かする場合に使用 | ダイアログで通信中を表示 |
doInBackground | 非同期処理 | 通信処理、DB処理、データ読み込み |
onProgressUpdate | 非同期処理中にUI更新する時使用 publishProgress()でこの処理を実行 | %とかバーで処理状態表示 |
onPostExecute | 非同期処理で得られた結果を元にUI更新 | ダイアログ消去 通信処理結果表示 |
使用上の注意
めっちゃ便利そうだけど、何でもかんでもAsyncTaskで平行処理するのはよくないUIが絡むとそれだけコストがかかる。このクラスは必ずUIスレッドを経由するため、UIスレッドを絡めない処理は無難にThreadクラスで処理した方が良い
UI関係の起こりやすく、発見しにくかったバグを2つ紹介する
- AsyncTaskを複数使用するときの処理
- Android2.3以前 execute()メソッドを実行する度に並列処理されている
- Android3.0以降 execute()メソッドを実行すると現在実行中のexecute()が終了してから処理が実行される
- 処理をキャンセルするときの処理 通信中画面遷移したりすると、通信キャンセルせざるおえない場合がある
- Android2.3以前 すぐAsyncTask #onCancelled()が呼ばれる
- Android3.0以降 doInBackground()が終了してからAsyncTask #onCancelled()が呼ばれる
キャンセル処理をAsyncTask #onCancelled()にオーバライドしておき、
AsyncTask #cansel()を実行することで、上記メソッドが呼ばれキャンセルできる仕組みだが、 非同期処理中 ( doInBackground() ) にキャンセルすると下記の2パターンに分岐する
画面遷移のため非同期処理をキャンセルする場合は、onProgressUpdate()にも注意。doInBackground()は途中で終わらないので、画面遷移後の存在しないView更新でヌルポしたりして痛い目にあうw
対処方はdoInBackground()処理にisCancelled()メソッドでこまめに今キャンセル中か確認すること!
実装方法
AsyncTask<Params、Progress、Result>で表される。ジェネリクスはそれぞれ下記の意味
型 | 意味 | 使用箇所 |
---|---|---|
Params | 非同期通信の引数になる。 | execute()の引数で渡す → doInBackground()の引数 |
Progress | 非同期処理中UIスレッド更新に渡す引数 | publishProgress()の引数で渡す → onProgressUpdate()の引数 |
Result | 非同期処理結果のクラス | doInBackground()の戻り値 → onPostExecute()の引数 |
- Params : リクエストするJSONのURL → String
- Progress : 使用しない → Void
- Result : レスポンスのJSONを文字列にしたもの → String
AsyncTask<String, Void, String> task = new AsyncTask<String, Void, String>() { @Override protected String doInBackground(String... params) { // TODO !非同期処理はアブストラクトメソッドなので必須! return null; } };で、あとは非同期処理中に通信処理。通信処理後にListViewの更新かける処理記述
AsyncTask<String, Void, String> task = new AsyncTask<String, Void, String>() { /**通信スレッド処理. * 引数のURLからリクエストし、レスポンスをJSONデータ文字列に変換 * @param url リスエスト先 * @return JSONデータの文字列 */ @Override protected String doInBackground(String... url) { return getAnimeInfo(url[0]); } /**UIスレッドの処理. * 引数のJSON文字列からタイトル配列を取得し、ListViewに表示 * @param JSON文字列 */ @Override protected void onPostExecute(String json) { mListView.setAdapter(new ArrayAdapter<String>( MainActivity.this, android.R.layout.simple_expandable_list_item_1, getTitleForJSON(json))); } };最後に、Activityでexecute()を呼べば完成!※行削減の為コメ消去
public class MainActivity extends Activity{ ListView mListView = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(mListView = new ListView(this)); task.execute("http://animemap.net/api/table/osaka.json"); } AsyncTask<String, Void, String> task = new AsyncTask<String, Void, String>() { @Override protected String doInBackground(String... url) { return getAnimeInfo(url[0]); } @Override protected void onPostExecute(String json) { mListView.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_expandable_list_item_1,getTitleForJSON(json))); } }; public static String getAnimeInfo(String url) { AndroidHttpClient client = AndroidHttpClient.newInstance(null); HttpGet get = new HttpGet(url); StringBuilder sb = new StringBuilder(); try { HttpResponse response = client.execute(get); BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); String line = null; while ((line = br.readLine()) != null) sb.append(line); } catch (IOException e) { e.printStackTrace(); } finally { client.close(); } return sb.toString(); } public static String[] getTitleForJSON(String jsonInfo) { final ArrayList<String> tmp = new ArrayList<String>(); try { JSONArray json = new JSONObject(jsonInfo) .getJSONObject("response") .getJSONArray("item"); for (int i = 0; i < json.length(); i++) tmp.add(((JSONObject) json.get(i)).getString("title")); } catch (JSONException e){ e.printStackTrace(); } return tmp.toArray(new String[0]); } }あ、マニフェストの書き忘れ注意!
2015年2月8日日曜日
アニメ番組表 API使った通信Androidアプリを作ってみた
WebAPIをまとめてるサイト(http://www.find-job.net/startup/api-2013)みてたら面白そうなのがあったので使ってみた
完成!
最終的なコードはこんだけ。行数短くしたかったからコメント無、可読性落とした
WebAPIとは
Webサイトに外部のサイトの提供する機能や情報を組み込んだり、アプリケーションソフトからWeb上で公開されている機能や情報を利用できるサービス- メインスレッド(UIスレッド)で通信処理ができない
- UIスレッドでないとViewの編集ができない
- マニフェストに定義しないと通信処理できない
- JSONの使い方
アニメマップAPIの使い方
- WebAPIの基本的な動き 通信は基本的にXMLかJSONの2通りのやり方がある。
- アニメマップAPIへのリクエストとレスポンス アニメマップのAPIのサイト(http://animemap.net/pages/api/)を見ると地域ごとに取得できるみたい
- JSONを解析 JSONは{}で囲まれた領域に"変数名":"value"見たいな感じで定義される。
違いは取得するデータの形。試しに今週のアニメ情報で比較
XML | : | http://animemap.net/api/table/osaka.xml |
JSON | : | http://animemap.net/api/table/osaka.json |
大阪の番組表が欲しいので、リクエスト(サーバへの要求)はコレ(http://animemap.net/api/table/osaka.json)
実際にブラウザでこのURLに行くとJSON型がどんなデータが返ってくるのか確認できる
Androidで書くとこんな感じでリクエストして、戻り値で JSON 型をStringにする。
/** * アニメマップに大阪のテレビ情報をリクエスト. * * @return JSON型のレスポンスをStringで返す */ public static String getAnimeInfo() { // リクエスト AndroidHttpClient client = AndroidHttpClient.newInstance(null); HttpGet get = new HttpGet("http://animemap.net/api/table/osaka.json"); StringBuilder sb = new StringBuilder(); try { // リクエスト要求 ⇒ レスポンス HttpResponse response = client.execute(get); // レスポンスをStringにして返値にする BufferedReader br = new BufferedReader(new InputStreamReader( response.getEntity().getContent())); String line = null; while ((line = br.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { client.close(); } return sb.toString(); }
ほんとの定義はこの中にもInt型とかいろいろあるけど省略
レスポンスのJSONを改行や字下げとかで解析してみると、
{ "request":{ "type":"json", "url":"地域ごとの一覧URL", "updated":"更新時間" }, "response":{ "item":[ { "title":"アニメのタイトル", "url":"アニメの放送局一覧URL", "time":"放送時間", "station":"放送局", "state":"よくわらん", "next":"第X話", "episode":"総話数", "cable":"よくわからん", "today":"よくわからん", "week":"放送曜日" }, { "title":"アニメのタイトル", ………省略……… } ] } }このJSONは"request","response"の2つの連想配列を持ち、
"response"にはitemって配列([]←この記号は配列)に1アニメずつ連想配列があることがわかる
アニメ連想配列ごとに放送時間、放送局など色々な情報が含まれてるけど、とりあえずアニメのタイトル取得してリスト表示させようと思う
さっきのJSONをStringにしたヤツからアニメタイトル一覧を取得する関数を作る
/** * JSONからアニメのタイトルを抜き出す. * * @param str_json * @return */ public static String[] getTitleListForJSON(String jsonInfo) { final ArrayList<String> tmp = new ArrayList<String>(); try { // String型をJSON型に変換 JSONObject json = new JSONObject(jsonInfo); // "response"連想配列を取得 JSONObject responce = json.getJSONObject("response"); // "item"配列を配列型で取得 final JSONArray animes = responce.getJSONArray("item"); // "item"配列から1コずつ連想配列を取り出し、タイトルを出力用リストに入れてく for (int i = 0; i < animes.length(); i++) { JSONObject o = (JSONObject) animes.get(i); tmp.add(o.getString("title")); } } catch (JSONException e) { e.printStackTrace(); } return tmp.toArray(new String[0]); }
Androidの実装
次にAndroidでの描画を実装。これがまた面倒。。 最初に述べたようにAndroidはUIスレッドでは通信できない。ので、別スレッドでデータの取得を行う。でも、取得したデータは通信のスレッドではViewに入れることができない。よって、元のUIスレッドでViewに表示させる処理がいる、、 なに言ってるのかわからなくなるので、一個ずつ処理していく- ベース作り まず下準備。大本のマニフェストやレイアウトの設定
- Manifest.xml これ入れないと通信系動かないので注意!!
- MainActivity.java
- 通信用スレッド立てる もはやAndroid以前にjavaの問題なんだけど、別スレッドを立てるにはThreadクラスのインスタンスがいる。 Thread#start()メソッドを実行することで、Runnable#run()メソッドが別スレッドで実行される。
- Handler使ってUIスレッドで描画 現在通信スレッド上ににアニメタイトル一覧があるので、ここからViewにデータをセットしたい。そこで直面するのが「通信のスレッドではViewに入れることができない」問題。解決策はHandlerクラス
アニメのタイトル一覧をListViewで表示させる
<uses-sdk/>タグの後らへんに追記
<uses-permission android:name="android.permission.INTERNET" />
public class MainActivity extends Activity { ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mListView = new ListView(this); mListView.setBackgroundColor(Color.BLACK); setContentView(mListView); } // 記載済みの為省略 public static String getAnimeInfo() {……} public static String[] getTitleForJSON(String jsonInfo) {……} }
ActivityにRunnableインターフェースをくっつけて、runを実装
public class MainActivity extends Activity implements Runnable { ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mListView = new ListView(this); mListView.setBackgroundColor(Color.BLACK); setContentView(mListView); // スレッド起動!通信開始!! new Thread(this).start(); } // 記載済みの為省略 public static String getAnimeInfo() {……} public static String[] getTitleForJSON(String jsonInfo) {……} @Override public void run() { String jsonInfo = getAnimeInfo(); final String[] titles = getTitleForJSON(jsonInfo); for(String str:titles)Log.d("TEST","title = "+str); } }ログにアニメタイトル一覧が出ればOK!
HandlerとはHandlerのインスタンスを生成したスレッドで自信の処理を行う事ができる
つまり、UIスレッド(Mainスレッド)でHandlerを生成しといて、別スレッドでこのHandlerを使えばUIスレッドで処理してることになる。run()をちょっと修正
// UIスレッド上にHandler生成 Handler mHandler =new Handler(); //Activityのメンバ変数 @Override public void run() { String jsonInfo = getAnimeInfo(); final String[] titles = getTitleForJSON(jsonInfo); for(String str:titles)Log.d("TEST","title = "+str); // Handlerはpostメソッドで引数のRunnable#run()メソッドを自信の生成スレッド上で実行させる mHandler.post(new Runnable() { @Override public void run() { // ここの処理がUIスレッドで実行される mListView.setAdapter(new ArrayAdapter<String>(MainActivity.this .getApplicationContext(), android.R.layout.simple_expandable_list_item_1, titles)); } }); }
完成!
最終的なコードはこんだけ。行数短くしたかったからコメント無、可読性落とした
public class MainActivity extends Activity implements Runnable{ ListView mListView = null; Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(mListView = new ListView(this)); new Thread(this).start(); } public static String getAnimeInfo() { AndroidHttpClient client = AndroidHttpClient.newInstance(null); HttpGet get = new HttpGet("http://animemap.net/api/table/osaka.json"); StringBuilder sb = new StringBuilder(); try { HttpResponse response = client.execute(get); BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); String line = null; while ((line = br.readLine()) != null) sb.append(line); } catch (IOException e) { e.printStackTrace(); } finally { client.close(); } return sb.toString(); } public static String[] getTitleForJSON(String jsonInfo) { final ArrayList<String> tmp = new ArrayList<String>(); try { JSONArray json = new JSONObject(jsonInfo) .getJSONObject("response") .getJSONArray("item"); for (int i = 0; i < json.length(); i++) tmp.add(((JSONObject) json.get(i)).getString("title")); } catch (JSONException e){ e.printStackTrace(); } return tmp.toArray(new String[0]); } @Override public void run() { final String[] titles = getTitleForJSON(getAnimeInfo()); mHandler.post(new Runnable(){ @Override public void run(){ mListView.setAdapter(new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_expandable_list_item_1,titles)); } }); } }※マニフェストの追記は忘れない様に!
2015年1月28日水曜日
CentOS6.4にRedmineを入れてみた
Redmine構築するたびにコマンド調べるの面倒だったので手順まとめてく
今回は仮想Linux(CentOS6.4)にRedmine2.3.0stableを入れる
Redmineとは進捗管理するためのWEBアプリツール
仕組みを図で書くとこんな感じ
手順としては下から順に作ってってHTTPより上はネットワークでつながってるならどのPCでもブラウザからみれる感じ
じゃー作ってこ!
RedmineサーバのIPアドレスは構築環境で下のコマンドで確認できる
今回は仮想Linux(CentOS6.4)にRedmine2.3.0stableを入れる
Redmineとは進捗管理するためのWEBアプリツール
仕組みを図で書くとこんな感じ
手順としては下から順に作ってってHTTPより上はネットワークでつながってるならどのPCでもブラウザからみれる感じ
じゃー作ってこ!
環境準備
- OSの用意 仮想はvagrant使ってく。じゃないとCentOS6.4はなかなか用意できないw
- アップデート インストールしたてだと不便なので、yumをアップデート
- セキュリティを無効に SELinuxを切ってセキュリティをガバガバにしておいた方が作りやすいので切っておく
手順は「Linux仮想環境構築(ssh接続)」参照
$ sudo yum update
vagrantで作成したら最初から切れてるっぽい?下のコマンドでDisabledがかえってきたら切れてる
$ getenforce Disabled
-
もし、Disabledでなければ
vi /etc/sysconfig/selinux # SELINUX=enforcing # 先頭に#付けてコメントアウトする SELINUX=disabled # 追記文:SELINUXを切るできたら再起動(これCent0S6.5でやったらbootでエラー出るえげつないバグでたw回避方法は覚えてたら書く)
$ reboot
インストールしてく
- yum強化 epel導入してyumをさらに強化する https://dl.fedoraproject.org/pub/epel/6/x86_64/
- いろいろインストール
- ルビーインストール RedmineはRubyで動いてるのでインストール
- Redmineが使用するGemを一括インストールするツールをインストール bundlerっていうツール。コレをインストールするにはrootユーザである必要があるので、rootユーザでコマンドを行う
上記URLから「epel-release- (略」で始まる新しそうなパッケージのリンクアドレスを取得。
今回の場合は「epel-release-6-8.noarch.rpm」のリンクアドレス使用
$ sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
# 開発ツール(Cコンパイラ等)のインストール $ sudo yum groupinstall "Development Tools" # RubyとPassengerのビルドに必要なヘッダファイルなどのインストール $ sudo yum -y install openssl-devel readline-devel zlib-devel curl-devel libyaml-devel # MySQLとヘッダファイルのインストール $ sudo yum -y install mysql-server mysql-devel # Apacheとヘッダファイルのインストール $ sudo yum -y install httpd httpd-devel # ImageMagickとヘッダファイル・日本語フォントのインストール $ sudo yum -y install ImageMagick ImageMagick-devel ipa-pgothic-fonts
RedmineはRuby2.1以降はだめなので、2.0.0らへんを入れる
$ curl -O http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p451.tar.gz $ tar zxvf ruby-2.0.0-p451.tar.gz $ cd ruby-2.0.0-p451 $ ./configure --disable-install-doc $ make $ sudo make install
$ su - passwd: # vagrantでつくるとrootユーザのパスもvagrant $ gem install bundler --no-rdoc --no-ri $ exit # rootユーザをログアウト
MySQLの設定
- 文字をutf8に設定 RedmineではUTF-8でDBとやり取りするので、文字をUTF-8に設定する
- MySQLの初期化
- Redmineで使用するDB作成
$ cat /etc/my.cnf [mysqld] datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock user=mysql # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 character-set-server=utf8 [mysqld_safe] log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid [mysql] default-character-set=utf8MySQLを起動させる
$ sudo service mysqld start $ sudo chkconfig mysqld on文字がUTF-8になってるかチェック
$ mysql -u root mysql> show variables like 'character_set%'; # 文字コードをみる +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec) # ↑こんな感じ担ってればOK mysql> exit # MySQL終了
$ mysql_secure_installation # あとは言われるがまま設定をしていけばOKここで設定するパスワードは今後rootユーザでMySQLを起動する度に必要になるので忘れないように
$ mysql -u root -p # パスワード聞かれるのでさっき設定したパスワード入力 mysql> create database redmine_db default character set utf8; # redmine用のユーザ作成 # by ' 'のところは好きなパスワード設定。redmine側の設定するときに必要になる mysql> grant all on redmine_db.* to redmine_user@localhost identified by 'redmine'; mysql> flush privileges; mysql> exit;
Redmineの設定
- Redmineをダウンロード 下のアドレスから欲しいRedmineバージョンを選択
- RedmineのDB接続設定 接続設定はRedmineディレクトリのなかのconfigの中に「database.yml」作ってそこで設定する
- Gemパッケージのインストール bundlerを使つかってRedmineで使用するGemをインストール
http://www.redmine.org/releases/
.tar.gzの拡張子のリンクアドレスコピー。
# 2.3.0を入れるので redmine-2.3.0.tar.gz のリンクアドレス $ curl -O http://www.redmine.org/releases/redmine-2.3.0.tar.gz # 解凍 $ tar xvf redmine-2.3.0.tar.gz # アパッチで公開する所に解凍したファイルを移動 $ sudo mv redmine-2.3.0 /var/lib/redmine
# redmineディレクトリに移動 $ cd /var/lib/redmine $ cd config # DB接続設定のテンプレートが用意されているのでコピー $ cp database.yml.example database.yml $ vi database.ymldatabase.ymlの編集箇所
production: adapter: mysql2 database: redmine_db host: localhost username: redmine_user password: "redmine" encoding: utf8
ついでにRedmineの初期設定とデータベースのテーブル作成
Redmineディレクトリで実行
$ cd /var/lib/redmine $ bundle install --without development test $ bundle exec rake generate_secret_token $ RAILS_ENV=production bundle exec rake db:migrate
Apatcheとかwebサーバ設定
このへんからrootユーザでないとできないコマンドがあるのでユーザ切り替え$ su - Password: # vagrantで環境設定してたらパスワードは「vagrant」 $ id # 今のユーザを調べるコマンド↓この表示我出ればrootユーザ状態 uid=0(root) gid=0(root) groups=0(root)
- Passengerインストール Redmineは「Ruby on Rails」というフレームワークで動いてるので、このフレームワークを動かすためのPassengerをインストール
- Passengerの設定をApatheに追加 /etc/httpd/conf.dにpassenger.confをつくる
- Apathe再起動
- WebサーバをRedmine専用機に編集 Apacheを実行するユーザー・グループで読み書きできるよう、オーナー変更
$ gem install passenger --no-rdoc --no-ri $ passenger-install-apache2-module # apache用モジュールインストール
$ passenger-install-apache2-module --snippet > /etc/httpd/conf.d/passenger.conf # ちょっと修正 $ vi /etc/httpd/conf.d/passenger.conf #のタグと字下げを消す。 # その他は環境依存の設定値なのでいじらないように LoadModule passenger_module /usr/local/lib/ruby/gems/2.0.0/gems/passenger-4.0.58/buildout/apache2/mod_passenger.so PassengerRoot /usr/local/lib/ruby/gems/2.0.0/gems/passenger-4.0.58 PassengerDefaultRuby /usr/local/bin/ruby
service httpd start chkconfig httpd on # Linux再起動時もApathe onにする
chown -R apache:apache /var/lib/redminehttpd.conf を編集
$ vi /etc/httpd/conf/httpd.conf # DocumentRoot "/var/www/html" # コレをコメントアウトして下の設定にする DocumentRoot "/var/lib/redmine/public"Apatcheの再起動
/etc/init.d/httpd configtest /etc/init.d/httpd graceful
起動して確認してみる
ブラウザ開いて、URLにIPアドレスを入力RedmineサーバのIPアドレスは構築環境で下のコマンドで確認できる
$ ifconfig # この辺に書いてる「inet addr:」のところ↓ eth1 Link encap:Ethernet HWaddr 08:00:27:39:EB:4B inet addr:192.168.33.10 Bcast:192.168.33.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe39:eb4b/64 Scope:Linkでた!! Redmineの初期状態のログインIDは
ID | admin |
---|---|
PASS | admin |
登録:
投稿 (Atom)