とりあえず移転してみました

ニュースねたや、IT系の記事を書いていくつもり・・・ですが、どうなるかわかりません。まあ、とりあえず やってみます。

地域ごとや年代で食べてる物が違うよね

昔の話をしている時に、「昔は、あれをよく食べたな・・・」なんて言っていると、周りから知らないと言われることがあったりする。
ところが、周りの人間の話を聞いても、知らない場合も多い。
地方出身者が多いと、それぞれの地域で全く知らないものの話になってしまう。

www.iitxs.com

久々に食べたくなるのが、この「マンハッタン」!!!

って、話をしても全然共感してもらえなかった。まあ、そりゃそうだ。福岡限定なの?
北部九州で食べれた記憶があったのだけどね?

このマンハッタンは、サクサクの生地にチョコがかけてある。サクサクなんだけど、中はしっとり。その食感の違いがGOOD
更に、チョコが全部かかっているわけではないので、チョコの味とノーマルの味が不定期で楽しめる。

子供の頃によく食べていたような・・・でも、どこにでも置いてあったように思ったので、特に貴重という意識はなかった。

似たようなものはあるのだけど、やっぱりマンハッタンは旨かった。

東京で売っているところないかな・・・

 

三段撃ちはあったのか?

有名な長篠の合戦。なぜ、有名なのかと言えば織田信長が鉄砲3千丁を使って、3段撃ちを行い最強騎馬軍団の撃破した戦いだったから。
これを検証している番組を見た。

まずは、火縄銃の撃つ間隔を計測していた。1発撃つのに30秒ほどかかっていた。
前装式の銃なので、銃を上向きにして火薬を入れて、弾を入れて棒を差し込んで奥まで装填。
そこから、構えて火蓋に火薬を入れ、火蓋を一旦閉じる。

火縄をセットしてから、狙いをつけて火蓋を空け、引き金を引く。

結構、大変。熟練の人でも30秒くらいはかかる。
3段撃ちは、装填に時間のかかる弱点を克服するために、撃った人が下がり後ろで装填。次に、用意できている人が撃つ。
また、後方に移動して装填という流れを順番に行う。

火縄銃というのは命中精度が50m以内であれば高く威力もあるので、50m以内に近づかないと効果が期待できない。

ところが、騎馬は50mの距離くらいでは8秒ほどで到達してしまう。

それを考慮して、3段撃ちを行っても騎馬のほうが早かった。

信長の3段撃ちというのは実際には、無理ではないか?というのが結論だった。
結局、それぞれの持ち場で準備できた人から撃つというのが早い。ということだった。

移動する時間がなくなるので、実際にはそっちが正解なのかも知れない。

この戦いについては、そもそも武田騎馬軍団というのが実際にはそれほど騎馬が多くなかったのでは?という話も出ている。
騎馬隊が有名だけど、全部騎馬なわけではないし、騎馬率は高いかも知れないけど、普通の編成だったのでは?と。

WWIIでは、ドイツの電撃作戦が有名になったけど、機械化歩兵と言われる車両をメインにしていた部隊は少なくて、後方の部隊では馬とか馬車で荷物を運んでいたらしい。

それは、現代でも同じようなもので自衛隊の戦車でも10式という最新戦車や90式という1つ前の戦車は少なくて、74式戦車がいまだに使われている。74式という名前が示すように1974年に制式化された戦車・・・

74式も少なくなってきたけど、まだ現役というのが凄い。同じようにF-4ファントムIIがまだまだ現役。
アメリカで開発された機体で、1960年に配備された機体。日本に配備されているのは改良がされているF-4EJ改で、生産されてから30年以上経過している。

F-4を改良した偵察機RF-4E、RF-4EJは、いまだにフィルム式なので偵察してから現像する必要があるとか・・・
このデジタルな時代で・・・
F-15偵察機に改修する案は、メーカーと揉めていた記憶が。

話は、戻って長篠合戦の番組の中で、火縄銃などの前装式と現代の後装式の違いを説明していた。
前装式は、ライフリングという溝がないので、安定しない。後装式は、ライフリングで弾が回転して安定すると。

ちょっと、説明していた絵がね・・・

前装式は、前から詰めていくので弾と銃身の間には隙間がないと弾が入っていかない。そのため、発射ガスが逃げてしまうので、威力も弱くなり、ライフリングを彫っても銃身と接していないので意味がない。

ところが、後装式は後ろから詰めるので弾が銃身の内側の幅より少し大きめ。その中を無理やり通っていくので、ライフリングの溝に沿って回転することになる。
説明の絵では、銃身と弾に隙間があった・・・残念・・・

 

android Fragmentまとめなど

FragmentとViewPagerで、はまったところのまとめ。
TabLatoutを使用して、タブ内でFragmentを表示する際に色々とはまってしまった。

流れとしては、以下のような感じ

・画面1(Activity)からアクセスするURLを決定

・webアクセスを実行して、結果を取得

・結果の中から次にアクセスするURLを2つ取得する

・webアクセスを2回実行して、結果を次の画面2(Activity)に表示する

・画面2には、TabLayoutを配置して、結果の2つをそれぞれのFragmentに表示

こんな流れだけど、問題だったのはwebアクセスは非同期処理ということ・・・
結果が返ってきてから、次の画面を表示としたのだけど、webアクセスをして画面2を表示してから、結果が返ってきたら反映・・・と想定していたけど、なかなか困難だった。

結果的には、結果が返ってきてから画面2の表示を行った。


まずは、Fragmentのおさらい

基本の部分は、以下のリンクで確認できる
フラグメント | Android Developers

newInstanseで初期設定を行い、onCreateViewで表示が行われる。
はじめから、Fragmentを生成する段階で、表示する値などを渡すほうがいいと思うのだけど、その方法だとFragmentがなんらかの要因で再表示される際に、データがないため例外処理で落ちてしまうらしい・・・

そこで、newInstanseで表示データなどをBundleで渡しておけば、再表示でもBundleからデータを持ってくるのでエラーにならない。

Fragmentのこの仕様のため、基本的に表示するデータを取得してから、Fragmentを生成しないと面倒。
TabLayoutを使う場合には、更にViewPagerAdapterを使ってFragmentを管理するので、ViewPagerAdapterから初期値を渡すことになる。

ViewPagerAdapterにBundleでデータを渡し、そのデータはViewPagerAdapterからFragmentにBundleでデータを渡す。
ListViewAdapterとかAdapterを使うことで、メリットは多いのだけど、データを渡すなどの処理を追加する場合は横断的に処理を行わないといけないので、面倒。

直接Fragmentにインターフェースを使って渡すことも可能なのだけど、Adapterが管理している以上はAdapterに処理を委譲しないと更新とかの処理が変になってしまう。

 

android 非同期処理まとめ

非同期処理は必要か?

androidで時間のかかる処理を行う場合には、マルチスレッドを利用してバックグランドで処理をする必要があります。新しいAPIでは、メインのスレッドでURLアクセスやDBアクセスを記述するだけでエラーになります。厳しい!!!

なので、必ず非同期処理が必要になってきます。
まずは、URLアクセス

okhttp3

便利なライブラリがあるので使います。okhttp3です。
build.gradle(app)にライブラリを使う宣言をします。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.squareup.okhttp3:okhttp:3.6.0' //<==追加

使用するclassでは、いつものようにimport文が必要。

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

これからが、本番です。

protected void urlAccess(String str){
        OkHttpClient client = new OkHttpClient();
        //RequestBody requestbodey = new FormBody.Builder()
        //        .build();

        Request request = new Request.Builder()
                .url(str)
                //.post(requestbodey)
                .get()
                .build();

        client.newCall(request).enqueue(new Callback() {
            final Handler mainHandler = new Handler(Looper.getMainLooper());
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                String callString = call.toString();
		//ここにViewの操作を記述する
                }
	}
}

メソッドを作成して、URLアクセスをする場合は、メソッドを呼び出すようにしています。URLは、引数として渡します。
POSTの場合の処理は、変わるのでコメントアウトしている部分がPOSTの場合の処理です。
サーバーからレスポンスがあった場合には、onResponseメソッドが実行されます。しかし、Handlerに登録するだけで実行されるのはHandlerの処理が空いた時になります。

この記述だけで非同期でのURLアクセスが可能になります。
ただ、このままでは足りません。非同期処理をメインスレッドで行わないのは、時間のかかる処理を別で行う間に画面上では別の処理を行いプログラムが止まったのか?重い処理をしているのか?を知らせる必要があります。

そのための非同期処理なので、追加でプログレスダイヤログを表示させます。

//フィールドに追加
private ProgressDialog progressDialog;

//メソッドとして追加
private void showProgressDialog(){
        progressDialog = new ProgressDialog(this);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.setMessage("WEBアクセス中です");
        progressDialog.setCancelable(true);
        progressDialog.show();
    }

showProgressDialog()を呼び出せば「WEBアクセス中です」と表示されたプログレスダイヤログが表示されます。
これで、プログラムがなにをやっているかわかります。

URLアクセスがあったら、次のコードで表示を消します。

progressDialog.dismiss();

Viewの操作になりますので、onResponseメソッド内に追加します。

@Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                String callString = call.toString();
		//ここにViewの操作を記述する
        progressDialog.dismiss();
                }

okhttp3ライブラリのおかげで、非同期処理も比較的楽に実装できます。

次に、AsyncTaskLoaderを使う方法です。ネット上でも色々とコードが出ていますので、参考に。

AsyncTaskLoader

まずは、AsyncTaskLoaderを継承した独自クラスを作成します。だいたい、こんなパターンで始まります。面倒なのですが。

public class AsyncDbLoader extends AsyncTaskLoader<List> implements LoaderManager.LoaderCallbacks{
    Bundle bundle;

    public AsyncDbLoader(Context context,Bundle args) {
        super(context);
        bundle = args;
    }

    @Override
    public List loadInBackground() {
        List listData;
        SQLiteDatabase myDatabase;
        Sql_db helper = new Sql_db(getContext());
        myDatabase = helper.getReadableDatabase();

        Cursor c = null;
        try {
            c = myDatabase.query("mainTable", null, null, null, null, null, null);

            listData = new ArrayList();

            //adapterにDBから文字列を追加
            boolean isEof = c.moveToFirst();
            while (isEof) {
                ArrayList<String> rowstring = new ArrayList<String>();
                rowstring.add(c.getString(1));
                rowstring.add(c.getString(2));

                listData.add(rowstring);
                isEof = c.moveToNext();
            }
        }
            finally {
            if (c != null) {
                c.close();
            }
            myDatabase.close();
            helper.close();
        }
        return listData;
    }

    @Override
    public void onStartLoading(){
        forceLoad();
    }

    @Override
    public Loader onCreateLoader(int id, Bundle args) {
        return null;
    }

    @Override
    public void onLoadFinished(Loader loader, Object data) {

    }

    @Override
    public void onLoaderReset(Loader loader) {

    }
}

実際にSQLiteからデータを取得しているコードですが、ここで重要なのはloadInBackgroundメソッドで結局、ここに実行する中身を記述するだけです。実行した結果を戻り値として返しています。

非同期でなければ、DBから取得した値をTextViewなどに書き込みわけですが、一旦List形式などにして返す必要があります。それを、メインスレッド側が受け取って、Viewに反映するという事になります。

呼び出す側のコードは、こんな感じで使います。showProgressDialogメソッドも入っていますので、プログレスダイヤログも表示されます。

getLoaderManager().initLoader(0,args, new AsyncDbLoader(this,args) {
            @Override
            public Loader<List> onCreateLoader(int id, Bundle args) {
                showProgressDialog();
                return new AsyncDbLoader(getContext(),args);
            }

            @Override
            public void onLoadFinished(Loader loader, Object data) {
                progressDialog.dismiss();
                List listData = (List)data;
                CustomAdapter adapter = new CustomAdapter(getContext(),2,listData,"list");
                ListView listView = (ListView)findViewById(R.id.listView);
                listView.setAdapter(adapter);
            }
        });

onCreateLoaderメソッドで、AsyncDbLoaderクラスのloadInBackgroundが実行されます。処理が終わるとonLoadFinishedが呼び出されます。ここでは、引数として渡されたdataをListとして格納して、ListViewに表示するまでの処理を行っています。
Viewへの変更ができるのはメイン側なので、ここでしか変更できません。

これで、非同期の処理としては2つの方法が可能で、URLアクセスの場合はokhttp3で簡単に記述でき、別の処理の場合は独自クラスを作成して、そのクラス内で処理を記述。
呼び出す側は、結果を受け取ってViewに反映という処理が必要です。

まあ、面倒といえば面倒なのですが、一度中身を理解してしまえば難しく感じません。

 

android ListViewでのはまりポイント

カスタムアダプターでListView

ListViewを使って、リスト形式の表示を行う場合が多いのだけど、色々とはまってしまった。
ListViewは、単純なテキストをリスト形式するのは簡単なのだけど、ちょっとカスタムしようとする場合は、カスタムしたクラスやレイアウトが必要になってくる。

ネットでも沢山情報があるので、基本的なところはできるのだが・・・

ArrayAdapterを継承したカスタムクラスを作成

CustomAdapter
public class CustomAdapter extends ArrayAdapter {
    private LayoutInflater layoutInflater_;

    public CustomAdapter(Context context, int textViewResourceId, List<String> objects) {
        super(context, textViewResourceId, objects);
        layoutInflater_ = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {


        if (convertView == null) {
                convertView = layoutInflater_.inflate(R.layout.rowdata_list, null);
        }

        ArrayList<String> item = (ArrayList<String>)getItem(position);

                TextView textView1;
                TextView textView2;
                textView1 = (TextView) convertView.findViewById(R.id.XXX);
                textView1.setText(item.get(1).toString());
                textView2 = (TextView) convertView.findViewById(R.id.XXX);
                textView2.setText(item.get(2).toString());

        return convertView;
    }
}

R.layout.rowdata_listというレイアウトファイルも作成

レイアウトファイル
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Medium Text"
            android:id="@+id/textView1"
            android:layout_weight="3"
            android:textColor="@android:color/black"
            android:textSize="10sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Medium Text"
            android:id="@+id/textView2"
            android:layout_weight="1"
            android:textColor="@android:color/black"
            android:textSize="10sp" />
    </LinearLayout>
</LinearLayout>

単純にTextViewが2つ並んでいるレイアウト・・・この部分を変更することで、レイアウトの変更も可能。

使う時は、CustomAdapterを使う。表示するListViewは(R.id.listView1)として指定している。

使う時のコード例
CustomAdapter adapter = new CustomAdapter(getContext(),0,listData);//listDataは表示させるデータ
                ListView listView = (ListView)findViewById(R.id.listView1);
                listView.setAdapter(adapter);

カスタムアダプターを使う場合は、色々と用意するものがあるけど、一度作れば使いまわせる。

はまったのは、カスタムアダプターを使わない場合。
カスタムアダプターは、ArrayAdapterを継承している。ArrayAdapterだけでも、単純なものでは使える。

ArrayAdapterでの使用例(失敗パターン)
ArrayAdapter adapter = new ArrayAdapter(getContext(),0,listData);
      ListView listView = (ListView)findViewById(R.id.listView1);
            listView.setAdapter(adapter);

CustomAdapterを使わずにArrayAdapterで指定してみた。
ここではまってしまった。テストをしてみるとアプリが落ちてしまう・・・しばらく原因がわからなかった。
ArrayAdapterを調べてみると、2つの引数が間違っていた・・・ここは、レイアウトファイルを指定しなければいけない。
カスタムアダプターと違うのだ。

R.layout.rowdataというファイルを新たに作成

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:background="@android:color/holo_blue_bright"
    android:padding="3dp">
</TextView>

TextViewが1つしかない単純なレイアウト・・・

ArrayAdapterでの使用例(成功パターン)
ArrayAdapter adapter = new ArrayAdapter(getContext(),R.layout.rowdata,listData);
      ListView listView = (ListView)findViewById(R.id.listView1);
            listView.setAdapter(adapter);

通常の場合でも、レイアウトファイルはいるのか・・・(面倒だ)

次に、カスタムアダプターに戻って、2行や3行のレイアウトはできるだろうか?リストによっては、1つの行の中で色々と表示したい場合がある。複数行にまたがる場合も当然あるだろうし、表示するものが多い場合もある。ウェブで見れるのはアイコンがあったり、2行くらいしなかない。

そこで、複数行のがいけるのか?やってみた。

結果としては、出来た。ファイルが長くなるので貼り付けはしないけど、LinerLayoutの下にLinerLayoutを4つ配置しても表示された。
そのレイアウトファイルをカスタムアダプターの中で読み出してあげればいい。
R.layout.rowdata_listの部分を変更する。もしくは、R.layout.rowdata_list自体を変更してもいい。

 

android SQLiteでの重複データの扱い

androidでは、データベースとしてSQLiteが使うことができる。ライブラリを使えば色々なDBも使えるのだけど、それほどデータがない場合などでは、SQLiteで十分でしょう。

今回はまったのは、重複データの扱い。WEBアクセスをした結果を必要な部分だけ抽出してDBに保管するのだけど、毎回データが更新されるわけではないので、同じデータの場合もある。
それをDBには記録させたくない・・・

最初に考えたのは、DB上に同じデータがあるか?比較して、なければ書き込み。あれば何もしないというフロー。

考えただけで大変そうな気がする。そこで、色々と調べてみてもDBによってはMARGEというコマンドとかあるようだけど、SQLiteにはない。
困った・・・

DBにアクセスするのは、レコードを1件ずつ取得するので、レコードの読み込み => データと比較 => 書き込みorそのまま とい処理をレコードの数だけ実行することになる。

DBのテーブル設定で、uniqueを使えば同じ内容の書き込みが制限できるというで、これを活用することにした。実際には、2つの項目が同じ場合は書き込みをしないという設定。

例えば・・・アクセスした人のIDと時間。

create table Table (_id integer primary key autoincrement,name TEXT,time TEXT,unique(name,time))

こんな感じで、テーブルを作成。

nameとtimeが同じ場合は、データへの書き込みが行われなかった。

 

android 今更ながらレイアウトでのweight指定での注意点

レイアウトの作成時に、縦にテキストやらボタンを配置する場合が多い。その場合に、同じようなものであればドンドン配置すればいいのだけど、大きさを変えないとしっくりこない。

LinerLayout(Vertical)を配置して、その中に部品を配置する方法が一番簡単。LinerLayoutの高さを調整すれば、その中の部品の高さも変更できるから。

f:id:imsdJava:20170519111950p:plain
これを使って配置していく

基本の指定方法として、Layout:heightで「match_parent」(最大)か「wrap_content」(最小)の指定が可能だけど、細かい指定ができない。その場合は、Layout:weightで重みつけをすることになる。

ただ・・・ここで注意しなければいけないのは、他のブログとかでも沢山書かれているのだけど、weightで重みをつける場合は「Layout:height」は「0dp」と指定しなければいけない。

f:id:imsdJava:20170519112848p:plain
0dpと指定したうえで重みつけを指定

上の階層のレイアウトに、「weightSum」という項目があるので、weightの合計値を指定しておくと便利。

合計値を10としておいて、

パーツA 1
パーツB 3
パーツC 2
パーツD 4

という感じで指定すると全体のイメージがつかみやすい。
weightの値と、height(高さ)もしくはwidth(幅)を0dpにする。これが必須。