2011年7月30日土曜日

開発環境バージョンアップ

開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT 12.0.0.v201106281929-138431
JavaSDK 1.6.0_24
AndroidSDK_r12


開発環境のバージョンアップをした。

「AndroidSDK_r11」から「AndroidSDK_r12」へ。

それに伴い「ADT 11.なんとか」から「ADT 12.0.0.v201106281929-138431」へ。

その後、再起動しエミュレータを起動しようとしたら以下のメッセージが出て起動せず。

> invalid command-line parameter: s.
> Hint: use '@foo' to launch a virtual device named 'foo'.
> please use -help for more information

原因がさっぱり分からず。

調査した所、元々インストールしてあったパスに半角スペースが含まれているのが原因かも。

確かに、「C:\Program Files\Android-r11\」にインストールしてあった。

「D:Android-r12」に変更し、環境変数のパスを変更してみたらうまく動いた。
 

   ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月26日火曜日

続・フリック

開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT_今日の時点で最新の物(バージョン不明)
JavaSDK 1.6.0_24
AndroidSDK_r11
開発・ターゲットVer=Android2.1


テスト環境:
媒体 = エミュレータ
↑SDK ManagerでVer2.1のHVGAで作成


フリックした時に特定のオブジェクト(部品)だけ反応して欲しい。

しかし、今の作りだとどこを押しても「onTouchEvent」が走ってしまう。


private GestureDetector mDetector;

public void onCreate(Bundle savedInstanceState) {
    mDetector = new GestureDetector(MyActivity.this, MyActivity.this);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    mDetector.onTouchEvent(event);
    return true;
}

@Override
public boolean onFling(MotionEvent e0, MotionEvent e1,
                        float velocityX, float velocityY)
{
    // 右方向へフリック
    if (e0.getX() < e1.getX()) {
        // 処理
    }
}


タッチした対象オブジェクトを知りたい場合どうすればいいのか。

そもそも、フリックは「GestureDetector」と「onTouchEvent」と「onFling」を使っているが、これを使い続けた場合タッチされたオブジェクトの特定は出来ないかもしれない。

そこで、オブジェクトごとにタッチイベントを捕らえる事にした。


public class MyActivity extends Activity implements OnTouchListener{

    private TextView mTvView;
    private GestureDetector mDetector;

    public void onCreate(Bundle savedInstanceState) {
        mTvView = (TextView)findViewById(R.id.tvMenu);
        // クリックリスナー登録
        mTvView.setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // 処理
        return mDetector.onTouchEvent(event);
    }

    @Override
    public boolean onFling(MotionEvent e0, MotionEvent e1,
                            float velocityX, float velocityY)
    {
        // 右方向へフリック
        if (e0.getX() < e1.getX()) {
            // 処理
        }
    }
}


「onTouch」の引数にViewが渡ってくるし、フリックしたいオブジェクトだけ「setOnTouchListener」すればいい。

これで「onFling」を引き続き使えると思っていたら、ScrollViewに対してはタッチイベントは来る物の、フリックイベントは走らない。

原因不明。

なので、「onFling」は使うのをやめて「onTouch」で泥臭いコードを書く事にする。


private float lastTouchX;
private float currentX;

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {

    case MotionEvent.ACTION_DOWN:
        lastTouchX = event.getX();
        break;

    case MotionEvent.ACTION_UP:
        currentX = event.getX();
        if (lastTouchX < currentX) {
            //前に戻る動作
        }
        if (lastTouchX > currentX) {
            //次に移動する動作
        }
        break;

    case MotionEvent.ACTION_CANCEL:
        currentX = event.getX();
        if (lastTouchX < currentX) {
             //前に戻る動作
        }
        if (lastTouchX > currentX) {
             //次に移動する動作
        }
        break;
    }
    return false;
}

ちなみに、 event.getX()は相対座標が帰ってくる。

部品の上でタッチしたらその部品の座標が帰ってくる。
ボタンの上でタッチしたらボタンのX座標が帰ってくるが、押したまま移動(MOVE)させるとボタンから指がはずれて、スクリーン全体の座標が取れるようになる。

タッチダウン → ムーブ で指を動かしている最中に突然、おかしな値が取れるように見える。

タッチした部品の座標ではなく、常にスクリーンの絶対座標が取得したい場合は、event.getRawX()」で取得できる。
 

   ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月25日月曜日

Androidプロジェクト作成時のビルド・ターゲット

開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT_今日の時点で最新の物(バージョン不明)
JavaSDK 1.6.0_24
AndroidSDK_r11


いつも使っているワークスペースの使用を一旦やめた。

新規にフォルダを作成し、そこを新しいワークスペースにした。

Androidプロジェクトを新規作成した。

クラス名やパッケージ名を入れるが、ビルド・ターゲットには何もでない。

エミュレータデバイスは作成済み。

調べた結果、eclipseのAndroid設定の「SDK ロケーション」の設定が間違っているか設定されてないのでは?との事。

確認してみたら、確かに空白になっていた。

さっきまで入っていたのに。

いつも使っているワークスペースから他のスペースに移動したから?

結局、Androidに限らずEclipseの仕様と思う。


   ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月24日日曜日

findViewByIdでnullが返る

開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT_今日の時点で最新の物(バージョン不明)
JavaSDK 1.6.0_24
AndroidSDK_r11
開発・ターゲットVer=Android2.1


テスト環境:
媒体 = エミュレータ
↑SDK ManagerでVer2.1のHVGAで作成


nullが返るソース。

・aaa.xml

<?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="fill_parent">
    <LinearLayout
        android:id="@+id/linLay"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
    </LinearLayout>
</LinearLayout>


・bbb.xml

<?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="fill_parent">
    <TableLayout
        android:id="@+id/tblLay" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
    </TableLayout>
</LinearLayout>


MainActivity.java(修正前)
public class MainActivity extends Activity{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aaa); ← ここに注目(aaa.xmlを設定)
 
        // 正常な値が返ってくる(aaa.xml内のidから取得)
        LinearLayout LinLay = (LinearLayout)findViewById(R.id.linLay);
        // nullが返ってくる(bbb.xml内のidから取得)
        TableLayout TabLay = (TableLayout)findViewById(R.id.tblLay);
    }
}


自分自身のレイアウトは「aaa.xml」になっている。

setContentView(R.layout.aaa);
これで決定する。

findViewByIdで現在設定されていない他のレイアウトからオブジェクトを取得しようとしても、普通に取得しようとしてもnullが返ってくる。

いくつか方法があるが、今回は「LayoutInflater」を使う。


MainActivity.java(修正後)
public class MainActivity extends Activity{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aaa); ← ここに注目(aaa.xmlを設定)

        // 正常な値が返ってくる(aaa.xml内のidから取得)
        LinearLayout LinLay = (LinearLayout)findViewById(R.id.linLay);

        // レイアウトインフレーター使用
        LayoutInflater factory = LayoutInflater.from(this);
        // 他のレイアウトファイルを指定
        View layInfView = factory.inflate(R.layout.bbb, null);

        // 正常な値が返ってくる(bbb.xml内のidから取得)
        TableLayout TabLay = (TableLayout)layInfView.findViewById(R.id.tblLay);
    }
}

   ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月22日金曜日

Layout関連のClassCastException

開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT_今日の時点で最新の物(バージョン不明)
JavaSDK 1.6.0_24
AndroidSDK_r11
開発・ターゲットVer=Android2.1


テスト環境:
媒体 = エミュレータ
↑SDK ManagerでVer2.1のHVGAで作成


現象:
アプリ起動時にExceptionが発生し起動しない

内容は以下


java.lang.RuntimeException: Unable to start activity ComponentInfo{jp.test/jp.test.CTestActivity}: java.lang.ClassCastException: android.widget.TextView
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2496)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)





Caused by: java.lang.ClassCastException: android.widget.TextView
    at jp.test.CTestActivity.onCreate(CTestActivity.java:93)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)


ExceptionはレイアウトファイルのIDを取得しようとすると発生


レイアウトファイルの記述は以下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout
        android:id="@+id/linearLayout01"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_weight="1.9">
        <GridView
            android:id="@+id/gridView1"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:numColumns="1">
        </GridView>
    </LinearLayout>

    <View
        android:id="@+id/Separator01"
        android:layout_width="wrap_content"
        android:background="#FFFFFF"
        android:layout_height="1dp"
        android:layout_marginBottom="2dp">
    </View>

    <LinearLayout
        android:id="@+id/linearLayout02"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent">
        <Button
            android:text="注文完了"
            android:id="@+id/btnDone"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <Button
            android:text="キャンセル"
            android:id="@+id/btnCancel"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <Button
            android:text="ダミー"
            android:id="@+id/btnDamy"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>

    <View android:id="@+id/Separator02"
        android:layout_height="1dp"
        android:layout_width="wrap_content"
        android:background="#FFFFFF"
        android:layout_marginBottom="2dp">
    </View>

    <LinearLayout
        android:id="@+id/linearLayout03"
        android:orientation="vertical"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent">
    </LinearLayout>

    <View
        android:id="@+id/Separator03"
        android:layout_width="wrap_content"
        android:background="#FFFFFF"
        android:layout_height="1dp"
        android:layout_marginBottom="2dp">
    </View>

    <ScrollView
        android:id="@+id/svMenu"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_weight="1">
        <TableLayout
            android:id="@+id/tlMenu"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:layout_weight="1">
        </TableLayout>
    </ScrollView>

</LinearLayout>
 

ソースは以下


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mLinearLayout01 = (LinearLayout)findViewById(R.id.linearLayout01);
    mLinearLayout02 = (LinearLayout)findViewById(R.id.linearLayout02);
    mLinearLayout03 = (LinearLayout)findViewById(R.id.linearLayout03);
}

Exceptionは「findViewById(R.id.linearLayout03)」を実行すると発生。

id名が間違っているわけでもない。

レイアウトファイル側は「LinearLayout」として作ってあるのでキャストも「LinearLayout」にしている。
01と02がうまく行っているのに03だけうまくいかない。

エラー行はコメントアウトし、03を分解してみた。


Object aaa = findViewById(R.id.linearLayout03);
mLinearLayout03 = (LinearLayout)aaa;


ワンクッション置いただけ。ステップ実行したがうまくいく。

エラー行と同じ記述で新規にもう一行作る。


//mLinearLayout03 = (LinearLayout)findViewById(R.id.linearLayout03);
Object aaa = findViewById(R.id.linearLayout03);
mLinearLayout03 = (LinearLayout)aaa;
mLinearLayout03 = (LinearLayout)findViewById(R.id.linearLayout03);


うまくいく。

エラー行にごみデータでもあったのか。

eclipseかAndroidのバグなのか。

原因不明。

 
   ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月20日水曜日

ダイアログ表示で、Window Leaked(メモリリーク)

開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT_今日の時点で最新の物(バージョン不明)
JavaSDK 1.6.0_24
AndroidSDK_r11
開発・ターゲットVer=Android2.1


テスト環境:
媒体 = エミュレータ
↑SDK ManagerでVer2.1のHVGAで作成


普通にダイアログを表示しようとした所、Exception「Window Leaked」と言われた。


・最初に書いたコード

@Override
public void onCreate(Bundle savedInstanceState) {
    // 表示項目の配列
    final String[] colors = { "RED", "BLUE", "YELLOW" };
    // タイトルを設定
    alertDialogBuilder.setTitle("タイトル");
    // 表示項目とリスナの設定
    alertDialogBuilder.setItems(colors,
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // 項目が選択されたときの処理
            }
        }
    );

    // ダイアログを表示
    alertDialogBuilder.create().show();
}


調べたらライフサイクルの管理の問題の可能性が高い事が分かった。

上記の方法はライフサイクルを自前で管理する方法。

Android側に管理させる方法を取らないと難しいかもしれない。


・Android側に管理させる方法

@Override
public void onCreate(Bundle savedInstanceState) {
    // 引数は何でもいい。onCreateDialogメソッドに渡っていくだけ。
    showDialog(0);
}

@Override
protected Dialog onCreateDialog(int id) {
    return createDialog(id);
}

/**
 * ダイアログ生成
 * @param id
 * @return
 */
private Dialog createDialog(int id) {
    if (id == DIALOG_ID_ABESHI) {
        // 表示項目の配列
        final String[] colors = { "RED", "BLUE", "YELLOW" };
        // タイトルを設定
        alertDialogBuilder.setTitle("タイトル");
        // 表示項目とリスナの設定
        alertDialogBuilder.setItems(colors,
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 項目が選択されたときの処理
                }
            }
        );

        return alertDialogBuilder.create();
    }
    return null;
}


まず「showDialog」メソッドを実行する。

すると、ダイアログ作成イベントが走り、onCreateDialog」が実行される。

こうする事でAndroid側に管理させる事になるようだ。

   ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月19日火曜日

引数=値渡しと参照渡し

Java全般の話。

通常、Javaのメソッド引数は値だけが渡される。


void aaa(){
    String strDamy="000";
    bbb(strDamy);
    System.out.println("strDamy = " + strDamy);
}

void bbb(String _str){
    _str="123";
}


こんなコードを書いてもメソッドの呼び元ではStringの値は変わらない。

表示されるのは「000」のはず。

C言語だと「ポインタ」を使うし、VBなら「ByRef」を使う

Javaはどうするか。

コレクション等でラップしてやる。


void aaa(){
    String strDamy="000";
    ArrayList arrDamy = new ArrayList();
    arrDamy.add(strDamy);
    bbb(arrDamy);
    strDamy = (String)arrDamy.get(0);
    System.out.println("strDamy = " + strDamy);
}

void bbb(ArrayList _arr){
    String str="";
    str=(String)_arr.get(0);
    _str="123";
    _arr.clear();
    _arr.add(str);
}

初歩的な事だけど、知らずに苦労した。

通常は返値をvoidからStringにしてreturnすれば事たりる。

しかし、すでに返値が使用されている場合、しょうがなく引数で返さないといけない場面も出てこよう。

返値をArrayListにして全部突っ込めばいいんじゃないかと言う話もある。

その辺は設計しだいなので各自の判断で。

そして、この方法がベストかと言うと分からない。

いくつかある方法の一つなのかもしれない。

知ってる人は教えて下さい。

-----------------
2011/07/20 追記:
-----------------
コメントで「StringBuffer」を使うと楽になるとの事なので、修正コードを転記する。

void aaa(){
     StringBuffer strDamy = new StringBuffer("000");
     bbb(strDamy);
     System.out.println("strDamy = " + strDamy);
}

void bbb(StringBuffer _str){
     _str.setLength(0);
     _str.append("123");
}

String型は良く使うのでStringBufferで代用すると言うのはかなり有益だ。

話は変わり、この記事で言いたかった事は、オブジェクト内の値の受け渡しについて。

実際にあった話として
CConnectDB conDB; // DB接続用のオブジェクト
Statement st;         // DB操作用のオブジェクト

conDB」や「st」を引数に渡して、その先のメソッドで値を変えても戻ってきたら何も変わってなかった。困った。
なので、String型やその他の変数やオブジェクトで代用が効く方法があればいいが、通常はやはりArrayなどのコレクションに入れて渡すのが手っ取り早いと思われ。

できればスッキリした分かり易いコードを書きたいが・・・


   ↓クリックで、このブログの評価が上がり執筆者が喜びます
 にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月15日金曜日

フリック

開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT_今日の時点で最新の物(バージョン不明)
JavaSDK 1.6.0_24
AndroidSDK_r11
開発・ターゲットVer=Android2.1


テスト環境:
テスト媒体:エミュレータ
↑SDK ManagerでVer2.1のHVGAで作成


フリック=指を画面にタッチして上下左右、どっちかにタッチしたまま動かし指を離す
これをすばやく行う


・フリックの実装

// タッチダウン、移動、タッチアップを全部自分で計算してやる方法もあるがめんどくさい
// Gesture系のオブジェクトがあるのでそれを使って実装
private GestureDetector mDetector;

// ジェスチャー検出クラス(フリック等)
public class TestActivity extends Activity implements GestureDetector.OnGestureListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);




        // ジェスチャーオブジェクトの生成
        mDetector = new GestureDetector(this, this);
    }

    /**
    * @param e0 指を置いた時の位置
    * @param e1 現在の位置
    * @param velocityX X軸の移動距離
    * @param velocityY Y軸の移動距離
    */
    // フリックを司るメソッド
    @Override
    public boolean onFling(MotionEvent e0, MotionEvent e1,
                            float velocityX, float velocityY)
    {
        // ここにフリック時の実装をする
        if (e0.getX() < e1.getX()) {
            // 右へフリック(指をタッチした後、右へ動かす)
        } else {
            // 左へフリック(指をタッチした後、左へ動かす)
        }
        // getY() で上下に対応
    }

    /* (非 Javadoc)
     * 背景画面でもボタンの上でもどこでもタッチイベントが取れる万能メソッド
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        super.dispatchTouchEvent(event);
        mDetector.onTouchEvent(event);
        return onTouchEvent(event);
    }
}

「onTouchEvent」メソッドもあるが、これは条件によってはイベントが走らない。

なので常にどこをタッチしてもイベントが走る「dispatchTouchEvent」を使用する。


    ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
   にほんブログ村

2011年7月14日木曜日

MySQLサーバーへの生死確認(タイムアウト値の設定)

サーバー環境:
アプリケーションサーバー:アパッチ
SQLサーバー:MySQL 5.1
↑これはXamppを導入したもの


開発環境:
Windows Vista Home
eclipse 3.5(ガリレオ)
ADT_今日の時点で最新の物(バージョン不明)
JavaSDK 1.6.0_24
AndroidSDK_r11
開発・ターゲットVer=Android2.1


テスト環境:
テスト媒体:エミュレータ
↑SDK ManagerでVer2.1のHVGAで作成


事前準備:
プロジェクトフォルダに「libs」と言う名前のフォルダを作成した
そこに、MySQLコネクタjarファイルを入れる
eclipse上からプロジェクトのプロパティで上記のビルドパスを通した
その時、「外部JARの追加」ではなく「JARの追加」で行った


今回はマシンの構成とかサーバー構成とかは関係ない。

MySQLサーバーに接続しようとしたらサーバの電源が入ってなく、接続結果の応答も30秒位ない。

これでは、長すぎるので自分でタイムアウトを設定したい。


・最初に書いてあったコード

import java.sql.*;

Class.forName("com.mysql.jdbc.Driver");
String url = String strCon = "jdbc:mysql://192.168.0.1:3306/pos_manage";
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement();

これがタイムアウトするまでにかなり待たされる。

これで調べたら、「DriverManager.setLoginTimeout(seconds)」と言うのがあったので設定してみたが、変わらず。

さらに調べた結果、「DriverManager」を使うのが良くないとの事。


・次にに書いたコード

Class driverClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)driverClass.newInstance();
Properties prpt = new Properties();
prpt.put("user", "root");
prpt.put("password", "123");
prpt.put("connectTimeout", "10000");//多分DBサーバーへの接続タイムアウト
prpt.put("socketTimeout", "10000"); //多分ネットワークタイムアウト
prpt.put("autoReconnect", "false"); //タイムアウトしたら再接続するか
conn = driver.connect(strCon, prpt);

こんな感じでやれと、ネットに書いてあったがやはりダメ。

まずはサーバーの電源が入ってない事による通信タイムアウトを設定したい。

上記のコードはあくまでもSQLオブジェクト。

電源が入ってても何らかの原因で通信できなくなった時の事も含まれる。

そこで考えたのが、単純にサーバーが通信できる状態にあるかどうかだけ確認する。


・最終コード

// テスト用のURLを作成
//XamppにはApatchが入っててMySQLと一緒に起動している前提
URL urlTest = new URL("http://192.168.0.1");
// MySQLサーバー接続用オブジェクトの作成
URLConnection connection =  (HttpURLConnection)urlTest.openConnection();
// タイムアウト値の設定
connection.setReadTimeout(5000);
connection.setConnectTimeout(5000);
// 接続開始
// タイムアウトになったらExceptionが発生する
connection.connect();


通信できる状態なのかの確認だけならこれでOK

 

  ↓クリックで、このブログの評価が上がり執筆者が喜びます
にほんブログ村 IT技術ブログ プログラム・プログラマーへ
  にほんブログ村