2015年12月12日土曜日

フィボナッチ数列を逐次、並行処理してみた

Java7 Goldの勉強してて、並行処理の分野で「Fork/Join Framework」を試したくなったのでちょっと実装
お題はフィボナッチ数列を逐次/並行で計算すると、どのくらい差が出るのか
javaで書くから逐次と平行じゃ結構差が出るのでは。と予想

2015年11月15日日曜日

C++の数値入力で無限ループにはまった件

ちょっとC++の勉強をしてる時に入力処理で無限ループにはまる事態に陥ったのでその対応方法を書いとく

問題の処理: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」を使う
ちなみに、こんな感じなのが作れるようになる模様

2015年10月24日土曜日

VirtualBox仮想Ubuntuに「Tera Term」で接続

Tera Termで仮想環境にアクセスできるようしておとくと、windowsでコピペしたコマンドを張れたりして楽(^q^)
今回はUbuntu用(Debian系)の設定。RedHat系はインストールコマンドとか別のはずなので注意
Tera Termとは?
Windows向けターミナルソフト。ssh使って、リモートで別のコンピュータを操作できる。ただし全部CUI
こんな感じ↓で仮想Linuxから「端末」だけを抜き出してWindowsで操作するイメージ

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が普通なので、この忌々しき事態をどうにかしないといけない。。

2015年10月7日水曜日

VM VirtualBoxにUbuntuを入れてみた

以前MacでVirtualBoxにvagrantを使ってCentOSを入れてるブログ書いた
今回はWin8.1でVirtualBoxにUbuntuを普通に入れる方法書いとく

2015年9月23日水曜日

スマホでホログラムを演出する方法

今月の頭くらいにツイッターで話題になっていた
CDケースを使ったスマホで疑似ホログラムを実現させる方法を試してみた
割と仕掛けは簡単だったので、作り方、仕組み、実物の人間サイズを表示する方法について考察してみた。

2015年9月21日月曜日

AndroidStudioの開発環境構築

Androidアプリを作る際、Googleさんが推奨している環境は「Android Studio」!!
以前は Eclipse にバンドルされたADTが一般的だったけど(仕事ではまだEclipseが現役ww)
Android Studio 1.00がやっとリリースされ、Googleさんがこっちメインでやるよーって言ってるので、移行していく


中2心をくすぐられるかっこいいUI!!

2015年9月13日日曜日

空中に文字を書いてみた

最近、資格試験や、コミトレ出展作品作りとかで全然更新できてなかったの久々!
今回は同人誌即売会での値札のポップ作り(即売会には間に合わなかった模様。。)
残光現象を利用して、空中に本の値段を書いている


2015年6月30日火曜日

確率とモンテカルロシミュレーション

問題
Aさんには三人の子供がいる
「日曜日生まれの男の子はいるか?」と訊くと「いる」と答えた
Aさんの子供が三人とも男の子である確率は?
最近2ちゃんまとめで上の問題やってみて、
これをモンテカルロでやってみたら結果は理論解に収束するのかな?
って思ったのでやってみた

 モンテカルロシミュレーションとは
シミュレーションや数値計算を乱数を用いて行う手法の総称。元々は、中性子が物質中を動き回る様子を探るために考案された。
強みは基礎的な理論さえ把握していれば、一定のアプローチに従って幅広い問題を解決できるという点
by Wiki
まぁ簡単にいえば、乱数つかって事象を表現して、それをループ文で何回も試し大数の法則でまとめ上げる手法

例:コインを投げた時の裏表の確率
表=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} \]
 数値解(モンテカルロ法)
今回は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}
};
などをすると、数値解析結果は
  • 三兄弟を乱数で生成、その後、兄弟の誰かを日曜生まれ男に差し替える
  • reslt = 0.249997
  • 長男を日曜生まれ男で固定し、残り二人を乱数で生成
  • reslt = 0.249940

僕が理論解でミスった値、1/4に収束していることがわかる
これは事後確率であることがプログラム上で示せていないため
つまり、ちょっと難しいが、モンテカルロシミュレーションでも事後確率と普通の確率は区別して求めることができる!

 まとめ
モンテカルロシミュレーションで事後確率を示すには、
前提条件の定義さえしっかりプログラムできていれば求めることができる!

2015年5月31日日曜日

積分方法の比較

FORTRANの環境作ったし、実際に計算させてみた
やっぱ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$」はプログラムでは実現できない。なので、様々な計算法で近似して数値解を求める必要がある。
  1. 長方形近似
  2. 一番基本形。積分する領域を長方形に切り刻んで全部足す方法
    ・数式 \[ \begin{eqnarray} \int_a^b f(x) dx \end{eqnarray} =\sum^n_{i=a}f(i)\Delta x \]
    刻み幅$\Delta x$はループ数$N$を使い、$\Delta x = (b - a) / N$となる

    $x$座標の始点、終点、ループ回数を引数にした関数だとこんな感じ
  3. 台形近似
  4. 長方形近似の上位互換。積分する領域を台形に刻むことで、勾配を配慮した形
    ・数式 \[ \begin{eqnarray} \int_a^b f(x) dx \end{eqnarray} =\sum^n_{i=a}\frac{f(i)+f(i+\Delta x)}{2}\Delta x \]
    刻み幅$\Delta x$はループ数$N$を使い、$\Delta x = (b - a) / N$となる

    $x$座標の始点、終点、ループ回数を引数にした関数だとこんな感じ
  5. シンプソンの公式
  6. f(x) を二次関数 P(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\} \]
    $h=(b-a)/(2m)$ $x_i=a+ih$ $(i=1,2,3...2m-1)$

    $x$座標の始点、終点、ループ回数を引数にした関数だとこんな感じ
 積分の比較
次に3つの積分方法の正確性、収束性、計算スピードについてみていく
  1. 正確性
  2. 下記式の左辺の数値解をそれぞれを求め、$\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 
    刻み回数100回の場合の積分結果
    どの結果も同じオーダーなので、違いが分かりにくい。。。

  3. 収束性
  4. 解の収束度合はどうだろうか。
    x軸に刻み回数、y軸に数値解とし、刻み回数と解の収束をグラフ化した
    sekibun1→長方形近似、sekibun2→台形近似、sekibun3→シンプソンの公式、pi→$\pi$
    収束度合の数値化がちょっとできてないけど、シンプソンの公式がかなり早い段階で収束することがわかる。

  5. 計算スピード
  6. 最後に計算速度の差について
    この程度なら差を感じないけど、計算に一週間かかる場合とかだと重要になってくる。それぞれのアルゴリズムの計算回数からだいたいの計算時間のオーダーを求める
    $f(x)$の計算ステップが$F$、分割数$N$とすると
    計算方法オーダー
    長方形近似$N(4+F)$
    台形近似$N(6+2F)$
    シンプソンの公式$N(6+F)$


    つまり、長方形近似>シンプソンの公式>台形近似の順で早いことになる。
    特に$f(x)$の計算ステップが異常にかかる系だと、台形近似が大変

 結論
シンプソンの公式が収束率、計算速度が優れていてよさげ
でも、実装が一番楽な長方形近似で分割数を膨大に増やして計算するのが、実装ミス、手間が省けて効率いいかも思った
以上

2015年5月24日日曜日

WINDOWS7でFORTRAN77の開発環境を作る

やっぱ数式とかプログラムしていく上で必要になってきたので、
FORTRAN77の開発環境を作っていく。
 FORTRAN77とは
科学技術計算に向いた手続き型プログラミング言語。その長い歴史の間に開発された非常に多くの数学関数やサブルーチンを数値解析ソフトウェアの形で持っている
by Wiki
まぁ簡単言えば計算特化型のプログラム言語。むしろ計算しかできない

 環境構築
  1. ダウンロード
  2. http://kkourakis.tripod.com/g77.htm
    ココから下記をダウンロード
    • g77exe.zip
    • g77lib.zip
    この二つが「bin」と「lib」。あと一つの「g77doc.zip」ってのは説明書だからいらない

  3. 環境変数の設定
  4. ダウンロードした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 (Virtual Network Computing)とは、リモートマシンのデスクトップを手元のPCから使えるようにするやつ。つまり、テラタームみたいなリモートでコマンドしか使えないのではなく、リモートでGUIを使えるツール
 ラズパイにVNCサーバを導入
  1. インストール
  2. ラズパイにVNCをインストールする
  3. パスワード設定
  4. 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.log
    
    
    17行目の数字はディスプレイ番号と呼ばれるもの
    後で使うので覚えておく
    当然ながら、パスワードも後で使うので覚えておく
 PC側からVNCを見れるようにする
次にPC側の設定
  1. インストール
  2. Viewerをインストールする。
    http://www.realvnc.com/download/viewer/
    家のPCはwin7 64bitだったのでコレ
  3. セットアップ
  4. exe起動させてセットアップしていく
    ラズパイのIPアドレス、ラズパイでパスワード設定したときのディスプレイ番号を入力する
    「IPアドレス:ディスプレイ番号」の形式で入力して、「Connect」を押す

    警告ポイの出るけど、気にしない。
    チェックボックスにチェック付けると、二度とこの画面でなくなるので、警告ウィンドウがうざいならチェック付けとく
  5. ログイン
  6. ラズパイで設定したパスワード入力してログイン

完成!

 消し方と次回からのアクセス
  • 消し方
  • 消すときはViewerの右上の×ボタンでOK
    このときまだプロセス(?)は生きているので、再びWindowsのVNC Viewerから接続を行うと消す前と同じ画面になる
    完全に消すにはテラタームで
    $ vncserver -kill :X # X はディスプレイ番号
    
    または、ラズパイをシャットダウンさせる
  • 次回からのアクセス
  • ラズパイをシャットダウンさせるとVNCも落ちるので、再起動時はVNC起動コマンドが必要
    $ 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

 カメラの取り付け方
下画像の赤い枠に注目
奥で観にくいけど、白い部分を持ち上げて、カメラモジュールのピラピラを差し込む。
カメラのピラピラは金の線が入ってる方を手前にする必要があるので注意

 カメラモジュールの設定
ラズパイの電源を入れてカメラの設定していく。普段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を制御する方法をまとめる
 文字の逆さ表示(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)
この画像を回転させながら重ね合わせて、扇風機の羽を作る
文字反転と同じようにCanvasをView中心で回転させて、onDraw()を何度も呼ぶ。
/** 重ね合わせ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表示を制す!
というだけあっていろいろなテクニックが必要だった

 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として後々使用するので適当すぎると困る
今回は曜日、開始時間、放送局、タイトルをTextViewで表示する
こんな感じで設定

<?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;
    }
}
次に一番やりたかった処理。レイアウトにデータを詰めて表示する処理
  1. セル用レイアウトにコンバート
  2. セル用レイアウトにデータを突っ込む処理
    ListViewは生成されたとき全てのセルを作ってるわけではない。セルのViewは画面に出たときに生成されてる
    そして、スクロールして新しいセルが表示されたとき、このメソッド(# getView())は呼ばれViewを生成する
    引数は
    ・position表示すべきリストのポジション
    ・convertViewListViewで表示するセル用レイアウトのインスタンス
    ・parentよくわからん(今回使わんので無視)
    最初convertViewはnullになっていて、実装上で設定する必要がある
    そこで必要なのがレイアウトのインスタンスを取得する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;
    }
    
  3. 高速処理
  4. 上記の方法でも処理できるが、毎回findViewById()をするとViewの呼び出し時間がかかってしまう難点がある
    それを回避するために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
とりあえず、まとめる

I2C通信とは

I2C通信は周辺機器とシリアル通信する仕組み
具体的にはラズパイがマスタ、周辺機器(温度計、加速度センサ、気圧)がスレーブとなって、データのやり取り(温度取得、加速度取得、気圧取得)ができるようになる
しかも、通信するための線が下の2本だけでできるってのも魅力
  • データのやり取りする線(SDA)
  • タイミング調整する線(SCL)

お買い物リスト

機材個数画像
16文字×2行LCDモジュール
ACM1602N1-FLW-FBW
今回の目玉。2行の文字を表示する
1個
ハンダごて
LCDモジュールをブレッドボードに接続する道具
1個
なまり線
ハンダでLCDの接着するためのもの。接着材みたいなの
5cm
ブレッドボード
ハンダ付けとかせず、
簡単に配線ができるやつ
1個
ジャンパー線(雄雌)
ラズパイのGPIOと
ブレッドボードをつなぐ線
5個
ジャンパー線(雄雄)
ブレッドボード上で配線を するための線
X個
可変抵抗(10kΩ)
抵抗値を変化させられるやつ。今回のクセモノ。てか、軽視しすぎてた
1個
抵抗(1kΩ)
電流を安定させる為に必要
1個
テスター
電流、電圧、抵抗とか調べれる。可変抵抗を調べるときに活躍する
1個

ラズパイで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
    
  • Pythonで制御するライブラリをインストール
  • $ sudo apt-get install python-smbus
    
準備完了!

配線

作成中

実装

作成中

2015年3月1日日曜日

javaで画像処理する方法

画像処理は計算に時間かかるから普通はCとかでするんだけど、javaであえて書いてみる!
対象は「人によって青&黒または白&金に見える」って話題になったこのドレスの画像で

画像の仕組み

画像とは大量の点からできている
画像の情報を見るとき、下のかけ算をよくみると思うけどコレがこの画像を表示するための点の数

上記画像は、横方向に288個、縦方向に437個。つまり、125856個の点で構成されているってこと

この点を「ピクセル」といい、色の情報を持ってる
一般的にはピクセルは4Byteで表されてるんだけど、振り分けはこんな感じ
ビット00000000000000000000000000000000
意味
透過率
透過率はまぁいいとして、 それぞれ赤、青、緑を色深度を0x00〜0xFF(16進表記)で表してる
色深度は数値が低いほど暗い。例として、下記に示す※透過は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クラスを使って通信処理を書き直してみる。

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()が終了してから処理が実行される
    この違いを意識しないと、通信中に表示するダイアログやヌルポに影響が出るので注意
  • 処理をキャンセルするときの処理
  • 通信中画面遷移したりすると、通信キャンセルせざるおえない場合がある
    キャンセル処理をAsyncTask #onCancelled()にオーバライドしておき、
    AsyncTask #cansel()を実行することで、上記メソッドが呼ばれキャンセルできる仕組みだが、 非同期処理中 ( doInBackground() ) にキャンセルすると下記の2パターンに分岐する
    • Android2.3以前
    • すぐAsyncTask #onCancelled()が呼ばれる
    • Android3.0以降
    • doInBackground()が終了してからAsyncTask #onCancelled()が呼ばれる
    まぁ結局どっちもdoInBackground()が終了するまで処理は実行されるんだけど、onCancelled()がきても非同期処理が続くことを考えた実装が必要になる。
    画面遷移のため非同期処理をキャンセルする場合は、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上で公開されている機能や情報を利用できるサービス
んで、ついでにAndroidの通信処理でよく引っかかるところについてもまとめとく
  1. メインスレッド(UIスレッド)で通信処理ができない
  2. UIスレッドでないとViewの編集ができない
  3. マニフェストに定義しないと通信処理できない
  4. JSONの使い方

アニメマップAPIの使い方

  1. WebAPIの基本的な動き
  2. 通信は基本的にXMLかJSONの2通りのやり方がある。
    違いは取得するデータの形。試しに今週のアニメ情報で比較
    XMLhttp://animemap.net/api/table/osaka.xml
    JSONhttp://animemap.net/api/table/osaka.json
    最近はJSON方式の方をよく見るので、JSONでつくってく。流れはこんな感じ

  3. アニメマップAPIへのリクエストとレスポンス
  4. アニメマップのAPIのサイト(http://animemap.net/pages/api/)を見ると地域ごとに取得できるみたい
    大阪の番組表が欲しいので、リクエスト(サーバへの要求)はコレ(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();
    }
    
  5. JSONを解析
  6. JSONは{}で囲まれた領域に"変数名":"value"見たいな感じで定義される。
    ほんとの定義はこの中にも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に表示させる処理がいる、、 なに言ってるのかわからなくなるので、一個ずつ処理していく
  1. ベース作り
  2. まず下準備。大本のマニフェストやレイアウトの設定
    アニメのタイトル一覧をListViewで表示させる
    • Manifest.xml
    • これ入れないと通信系動かないので注意!!
      <uses-sdk/>タグの後らへんに追記
       <uses-permission android:name="android.permission.INTERNET" />
      
    • MainActivity.java
    • 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) {……}
      
      }
      
  3. 通信用スレッド立てる
  4. もはやAndroid以前にjavaの問題なんだけど、別スレッドを立てるにはThreadクラスのインスタンスがいる。 Thread#start()メソッドを実行することで、Runnable#run()メソッドが別スレッドで実行される。
    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!

  5. Handler使ってUIスレッドで描画
  6. 現在通信スレッド上ににアニメタイトル一覧があるので、ここからViewにデータをセットしたい。そこで直面するのが「通信のスレッドではViewに入れることができない」問題。解決策はHandlerクラス
    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でもブラウザからみれる感じ
じゃー作ってこ!

環境準備

  1. OSの用意
  2. 仮想はvagrant使ってく。じゃないとCentOS6.4はなかなか用意できないw
    手順は「Linux仮想環境構築(ssh接続)」参照
  3. アップデート
  4. インストールしたてだと不便なので、yumをアップデート
        $ sudo yum update
    
  5. セキュリティを無効に
  6. SELinuxを切ってセキュリティをガバガバにしておいた方が作りやすいので切っておく
    vagrantで作成したら最初から切れてるっぽい?下のコマンドでDisabledがかえってきたら切れてる
    $ getenforce
    Disabled
    
      もし、Disabledでなければ
      vi /etc/sysconfig/selinux 
      
      # SELINUX=enforcing  # 先頭に#付けてコメントアウトする
      SELINUX=disabled       # 追記文:SELINUXを切る
      
      できたら再起動(これCent0S6.5でやったらbootでエラー出るえげつないバグでたw回避方法は覚えてたら書く)
      $ reboot
      

インストールしてく

  1. yum強化
  2. epel導入してyumをさらに強化する https://dl.fedoraproject.org/pub/epel/6/x86_64/
    上記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
    
  3. いろいろインストール
  4. # 開発ツール(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
    
  5. ルビーインストール
  6. RedmineはRubyで動いてるのでインストール
    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
    
  7. Redmineが使用するGemを一括インストールするツールをインストール
  8. bundlerっていうツール。コレをインストールするにはrootユーザである必要があるので、rootユーザでコマンドを行う
    $ su -
    passwd:  # vagrantでつくるとrootユーザのパスもvagrant
    $ gem install bundler --no-rdoc --no-ri
    $ exit # rootユーザをログアウト
    

MySQLの設定

  1. 文字をutf8に設定
  2. RedmineではUTF-8でDBとやり取りするので、文字をUTF-8に設定する
    $ 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=utf8
    
    
    MySQLを起動させる
    $ 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終了
    
  3. MySQLの初期化
  4. $ mysql_secure_installation
    # あとは言われるがまま設定をしていけばOK
    
    ここで設定するパスワードは今後rootユーザでMySQLを起動する度に必要になるので忘れないように
  5. Redmineで使用するDB作成
  6. $ 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の設定

  1. Redmineをダウンロード
  2. 下のアドレスから欲しいRedmineバージョンを選択
    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
    
  3. RedmineのDB接続設定
  4. 接続設定はRedmineディレクトリのなかのconfigの中に「database.yml」作ってそこで設定する
    # redmineディレクトリに移動
    $ cd /var/lib/redmine
    $ cd config
    # DB接続設定のテンプレートが用意されているのでコピー
    $ cp database.yml.example database.yml
    $ vi database.yml
    
    database.ymlの編集箇所
    production:
      adapter: mysql2
      database: redmine_db
      host: localhost
      username: redmine_user
      password: "redmine"
      encoding: utf8
    
  5. Gemパッケージのインストール
  6. bundlerを使つかってRedmineで使用するGemをインストール
    ついでに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) 
  1. Passengerインストール
  2. Redmineは「Ruby on Rails」というフレームワークで動いてるので、このフレームワークを動かすためのPassengerをインストール
    $ gem install passenger --no-rdoc --no-ri
    $ passenger-install-apache2-module  # apache用モジュールインストール
    
  3. Passengerの設定をApatheに追加
  4. /etc/httpd/conf.dにpassenger.confをつくる
    $ 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
    
  5. Apathe再起動
  6. service httpd start
    chkconfig httpd on # Linux再起動時もApathe onにする
    
  7. WebサーバをRedmine専用機に編集
  8. Apacheを実行するユーザー・グループで読み書きできるよう、オーナー変更
    chown -R apache:apache /var/lib/redmine
    
    httpd.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は
IDadmin
PASSadmin