SQLの窓

2016年05月21日


Android の Spinner に関するいろいろな実装と知識 / Android Studio

Android の Spinner は、一般的にはコンボボックスと言われるものと同様で、何も指定しなければ以下のような動作をします。



Spinner には、spinnerMode というプロパティがあり、デフォルトでは MODE_DROPDOWN として動作しています。( XML で定義せずに、インスタンスを作成する場合は、コンストラクタで指定できます )



この部分を MODE_DIALOG に変更すると、以下のように動作します



リスト部分の実装について

リスト部分は、一般的なリストビューと同じです。ArrayAdapter を使用します。単純に一つの TextView を持つ、Android で定義済みの simple_spinner_dropdown_item を使って実装しています。( simple_spinner_dropdown_item である必要はありません )
package lightbox.may.combobox;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

	// ******************************
	// 文字列配列 Spinner
	// ******************************
	private ArrayAdapter<String> adapter;
	private Spinner spinner;
	private String[] list_data = {"大阪","東京","愛知","岡山"};
	private String[] list_value = {"27","13","23","33"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 配列を格納して Android のリストを表示する ArrayAdapter クラス
		// レイアウトは Android で定義されたもの。
		// TextView を継承したクラスであれば自前のレイアウトで OK
		adapter = new ArrayAdapter(
			MainActivity.this,
			android.R.layout.simple_spinner_dropdown_item);

		// Spinner のインスタンスを取得
		spinner = (Spinner) MainActivity.this.findViewById(R.id.spinner);

		// ArrayAdapter に配列を格納
		adapter.addAll(list_data);

		// Spinner に ArrayAdapter をセット
		spinner.setAdapter(adapter);

		// ボタンの インスタンスを取得
		Button button = (Button) MainActivity.this.findViewById(R.id.button);
		// アルファベットの小文字を反映できるようにする
		button.setAllCaps(false);
		// ボタンのイベントの定義
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// ******************************************
				// Spinner より選択されているテキストを取得
				// ******************************************

				// simple_spinner_dropdown_item に定義されている id から
				String text = ((TextView)(MainActivity.this.findViewById(android.R.id.text1))).getText().toString();
				Log.i("lightbox", text);

				// sppiner から
				text = (String) spinner.getSelectedItem();
				Log.i("lightbox", text);

				// ポジション番号から、用意しておいた配列を使用して取り出す
				int pos = spinner.getSelectedItemPosition();
				Log.i("lightbox", list_data[pos]);

			}
		});


	}
}

Spinner とは関係ありませんが、setAllCaps でボタンの文字が全て大文字にならないようにしています。

Spinner の情報の取り出し

ここでは、Spinner に表示されるテキストを取り出しています。しかし、実際は Spinner の選択 されたインデックスより、テキストの元となるコードを list_value より取り出すのが正解です。ですが、これは最も簡単なサンプルとして、ArrayAdapter に 文字列の配列を引き渡しているからそうなるのであって、本来は専用クラスを作成し、その配列を最初に渡しておいて、その中の値を直接取り出すのが汎用的になります。

Spinner 用のクラスを作成して利用する
package lightbox.may.combobox;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;

public class MainActivity extends AppCompatActivity {

	// ******************************
	// MyData 配列 Spinner
	// ******************************
	private ArrayAdapter<MyData> adapter;

	// セットする情報の配列
	private MyData[] my_data = {
		new MyData("大阪","27"),
		new MyData("東京","13"),
		new MyData("愛知","23"),
		new MyData("岡山","33")} ;

	// Spinner のインスタンス
	private Spinner spinner;

	// 情報格納用のクラス
	private class MyData {
		// 表示される文字列用
		private String myString = null;
		// 内部コード
		private String myValue = null;

		// コンストラクタ
		public MyData(String myString,String myValue) {
			this.myString = myString;
			this.myValue = myValue;
		}

		// 値取得用
		String getValue() {
			return myValue;
		}

		// ArrayAdapter の仕様に合わせて toString を 上書きしておく
		@Override
		public String toString() {
			return myString;
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 配列を格納して Android のリストを表示する ArrayAdapter クラス
		// レイアウトは Android で定義されたもの。
		// TextView を継承したクラスであれば自前のレイアウトで OK
		adapter = new ArrayAdapter(
			MainActivity.this,
			android.R.layout.simple_spinner_dropdown_item);

		// Spinner のインスタンスを取得
		spinner = (Spinner) MainActivity.this.findViewById(R.id.spinner);

		// ArrayAdapter に MyData 配列を格納
		adapter.addAll(my_data);

		// Spinner に ArrayAdapter をセット
		spinner.setAdapter(adapter);

		// ボタンの インスタンスを取得
		Button button = (Button) MainActivity.this.findViewById(R.id.button);
		// アルファベットの小文字を反映できるようにする
		button.setAllCaps(false);
		// ボタンのイベントの定義
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// ******************************************
				// Spinner より選択されている情報を取得
				// ******************************************
				// sppiner から MyData を取得
				MyData md = (MyData) spinner.getSelectedItem();
				// 内部コードの取得
				Log.i("lightbox", md.getValue());
				// テキストの取得
				Log.i("lightbox", md.toString());

			}
		});


	}
}

データをセットするのに、addAll を使用しているのは、インターネット経由で JSON を使用して読み込む事が通常想定されるので、一括となっています。

リストの行の表示のカスタマイズ

これらのサンプルでは、画面定義は simple_spinner_dropdown_item を使用していますが、単純に TextView のみの画面定義をして利用可能です。


<?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:textSize="40dp"
          android:padding="10dp">

</TextView>

完全にカスタマイズするには

Spinner の見え方は、閉じた一つの View と リストで表示されるビューと二つあります。また、二つ以上のコントロールのある View を作った場合は ArrayAdapter を継承したクラスを作成して利用する必要があります。それらの機能を汎用的に実装したのが以下の SpinnerArrayAdapter です。
package lightbox.may.combobox;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;

/**
 * Created by lightbox on 2016/05/21.
 */
public class SpinnerArrayAdapter<T> extends ArrayAdapter<T> {

	public interface OnGetViewListener {
		abstract public View onGetViewListener(int position, View convertView, ViewGroup parent);
	}

	private OnGetViewListener listener = null;
	private OnGetViewListener listener_drop_down = null;

	public SpinnerArrayAdapter(Context context, int resource, OnGetViewListener listener, OnGetViewListener listener_drop_down) {
		super(context, resource);
		this.listener = listener;
		this.listener_drop_down = listener_drop_down;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if ( listener != null ) {
			return listener.onGetViewListener(position, convertView, parent);
		}
		return super.getView(position, convertView, parent);
	}

	@Override
	public View getDropDownView(int position, View convertView, ViewGroup parent) {
		if ( listener != null ) {
			return listener_drop_down.onGetViewListener(position, convertView, parent);
		}
		return super.getDropDownView(position, convertView, parent);
	}
}


画面定義 / my_spinner_custom
<?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">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:textSize="40dp"
        android:padding="10dp"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView2"
        android:textSize="20dp"
        android:padding="5dp"/>
</LinearLayout>



使用方法
package lightbox.may.combobox;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

	// ******************************
	// MyData 配列 Spinner
	// ******************************
	private SpinnerArrayAdapter <MyData> adapter;

	// セットする情報の配列
	private MyData[] my_data = {
		new MyData("大阪","27"),
		new MyData("東京","13"),
		new MyData("愛知","23"),
		new MyData("岡山","33")} ;

	// Spinner のインスタンス
	private Spinner spinner;

	// 情報格納用のクラス
	private class MyData {
		// 表示される文字列用
		private String myString = null;
		// 内部コード
		private String myValue = null;

		// コンストラクタ
		public MyData(String myString,String myValue) {
			this.myString = myString;
			this.myValue = myValue;
		}

		// 値取得用
		String getValue() {
			return myValue;
		}

		// ArrayAdapter の仕様に合わせて toString を 上書きしておく
		@Override
		public String toString() {
			return myString;
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// my_spinner_custom を使用したカスタマイズ
		adapter = new SpinnerArrayAdapter<MyData>(
			MainActivity.this,
			R.layout.my_spinner_custom,
			// リストでは無い表示部分
			new SpinnerArrayAdapter.OnGetViewListener() {
				@Override
				public View onGetViewListener(int position, View convertView, ViewGroup parent) {

					View rowView = convertView;
					if (rowView == null) {
						// 現在の View の取得
						LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService
							(Context.LAYOUT_INFLATER_SERVICE);
						rowView = inflater.inflate(R.layout.my_spinner_custom, null);
					}

					// Adapter にセットされているこのポジションの MyData を取得
					MyData md = adapter.getItem(position);

					// my_spinner_custom 内の TextView にデータをセット
					TextView tv1 = (TextView) rowView.findViewById(R.id.textView);
					tv1.setText(md.toString());
					TextView tv2 = (TextView) rowView.findViewById(R.id.textView2);
					tv2.setText(md.getValue());

					return rowView;
				}
			},
			// リストの表示部分
			new SpinnerArrayAdapter.OnGetViewListener() {
				@Override
				public View onGetViewListener(int position, View convertView, ViewGroup parent) {
					View rowView = convertView;
					if (rowView == null) {
						// 現在の View の取得
						LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService
							(Context.LAYOUT_INFLATER_SERVICE);
						rowView = inflater.inflate(R.layout.my_spinner_custom, null);
					}

					// Adapter にセットされているこのポジションの MyData を取得
					MyData md = adapter.getItem(position);

					// my_spinner_custom 内の TextView にデータをセット
					TextView tv1 = (TextView) rowView.findViewById(R.id.textView);
					tv1.setText(md.toString());
					TextView tv2 = (TextView) rowView.findViewById(R.id.textView2);
					tv2.setText(md.getValue());

					return rowView;
				}
		});

		// Spinner のインスタンスを取得
		spinner = (Spinner) MainActivity.this.findViewById(R.id.spinner);

		// ArrayAdapter に MyData 配列を格納
		adapter.addAll(my_data);

		// Spinner に ArrayAdapter をセット
		spinner.setAdapter(adapter);

		// ボタンの インスタンスを取得
		Button button = (Button) MainActivity.this.findViewById(R.id.button);
		// アルファベットの小文字を反映できるようにする
		button.setAllCaps(false);
		// ボタンのイベントの定義
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// ******************************************
				// Spinner より選択されている情報を取得
				// ******************************************
				// sppiner から MyData を取得
				MyData md = (MyData) spinner.getSelectedItem();
				// 内部コードの取得
				Log.i("lightbox", md.getValue());
				// テキストの取得
				Log.i("lightbox", md.toString());

			}
		});


	}
}


関連するソースコード

ArrayAdapter

simple_spinner_dropdown_item.xml



posted by lightbox at 2016-05-21 18:28 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年04月25日


AsyncTask を継承して、Drawable を取得する専用クラスを作成する : Android Studio

Drawable を取得して、UIスレッドで処理する事を直接の目的としています。なので、onPostExecute を Override して、専用に定義した onDrawable メソッドを呼び出しておきます。これによって、HttpDrawable のインスタンスを使う時に onDrawable を Override すれば引数に Drawable のインスタンスを得る事ができます。

また、引数が文字列の配列で、戻り値が Drawable と固定の仕様の元作成するので、ジェネリックスを利用せずに作られています。
package lightbox.april.androidtest;

import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.Log;

import java.io.InputStream;
import java.net.URL;

/**
 * Created by lightbox on 2016/04/24.
 */
public class HttpDrawable extends AsyncTask {

	@Override
	protected Object doInBackground(Object[] params) {

		// 画像データ
		Drawable image = null;
		try {
			// インターネット上の画像を取得して、Drawable に変換
			URL url = new URL((String) params[0]);
			InputStream is = (InputStream)url.getContent();
			image = Drawable.createFromStream(is, "");
			is.close();
		}
		catch(Exception e) {
			Log.i("button", "インターネットのアクセスでエラーが発生しました");
		}
		return image;

	}

	protected void onDrawable(Drawable d) {

	}

	@Override
	protected void onPostExecute(Object o) {
		onDrawable((Drawable)o);
	}
}


▼ ボタンクリックから画像の表示
package lightbox.april.androidtest;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

	private HttpDrawable hd;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// ボタンのインスタンスを格納する変数を作成
		Button button;

		// ボタンのインスタンスを作成
		button = (Button) MainActivity.this.findViewById(R.id.button);

		// ボタンのインスタンスにイベントを登録
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// logcat にメッセージを表示
				Log.i("lightbox", "ボタンが押されました");
				hd.execute("https://goo.gl/WNzpRl");

			}
		});

		hd = new HttpDrawable() {
			@Override
			protected void onDrawable(Drawable d) {

				if (d != null) {
					ImageView imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
					imageView.setImageDrawable(d);
				}

			}

		};



	}

}

https://goo.gl/WNzpRl は画像です




HttpDrawable は、一般的な new AsyncTask の意味を解りやすく解説する為に作成しています。

▼ new AsyncTask で記述されたもの
package lightbox.april.androidtest;

import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import java.io.InputStream;
import java.net.URL;

public class MainActivity extends AppCompatActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// ボタンのインスタンスを格納する変数を作成
		Button button;

		// ボタンのインスタンスを作成
		button = (Button) MainActivity.this.findViewById(R.id.button);

		// ボタンのインスタンスにイベントを登録
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// logcat にメッセージを表示
				Log.i("lightbox", "ボタンが押されました");
				new AsyncTask<String,Void,Drawable>() {
					@Override
					protected Drawable doInBackground(String... params) {
						// 画像データ
						Drawable image = null;
						try {
							// インターネット上の画像を取得して、Drawable に変換
							URL url = new URL(params[0]);
							InputStream is = (InputStream)url.getContent();
							image = Drawable.createFromStream(is, "");
							is.close();
						}
						catch(Exception e) {
							Log.i("button", "インターネットのアクセスでエラーが発生しました");
						}
						return image;
					}

					@Override
					protected void onPostExecute(Drawable drawable) {

						if (drawable != null) {
							ImageView imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
							imageView.setImageDrawable(drawable);
						}
					}

				}.execute("https://goo.gl/WNzpRl");

			}
		});

	}

}


さらに、ボタンイベントのように、インターフェイスを登録するようにすると以下のようになります
package lightbox.april.androidtest;

import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.Log;

import java.io.InputStream;
import java.net.URL;

/**
 * Created by lightbox on 2016/04/24.
 */
public class HttpDrawable extends AsyncTask {

	public interface OnDrawableListener {
		abstract public void onDrawableListener(Drawable d);
	}

	private OnDrawableListener listener = null;

	@Override
	protected Object doInBackground(Object[] params) {

		// 画像データ
		Drawable image = null;
		try {
			// インターネット上の画像を取得して、Drawable に変換
			URL url = new URL((String) params[0]);
			InputStream is = (InputStream)url.getContent();
			image = Drawable.createFromStream(is, "");
			is.close();
		}
		catch(Exception e) {
			Log.i("button", "インターネットのアクセスでエラーが発生しました");
		}
		return image;

	}

	public void setOnDrawableListener( OnDrawableListener listener ) {
		this.listener = listener;
	}

	@Override
	protected void onPostExecute(Object o) {
		if ( listener != null ) {
			listener.onDrawableListener((Drawable)o);
		}
	}
}

内部で専用のインターフェイスを作成し、setOnDrawableListener の引数にする事によって、内部で保存しておいて、onPostExecute から呼び出す(外側のインターフェイス内のメソッド)ようにすれば、ボタンイベントと同様の扱いになります


posted by lightbox at 2016-04-25 22:40 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年04月10日


Android Studio が 2.0 になりました。前からの内容も含めて整理してます。

SDK の更新が必須のようです。おかげでエミュレータが進化します。人によって違う可能性もありますが、最初のダイアログからでは更新できずに、本体画面の HELP からしか更新をする事ができませんでした。



また、最新の SDK で無い状態で、2.0 へ更新すると、英語でいろいろ指示されるので、慣れてる人はリンクをクリックしたりしてうまく行くとは思いますが、最初から SDK(Tool) を最新にする事をお勧めします。(ホント毎度面倒くさい・・・)

開始のダイアログは見栄えが変わっています。基本的な機能は同じのようですが、以前より格段に目的を達成する為の選択が容易になっています。





そして、何より最も目立った UI の改善がエミュレータに見られます。



一目瞭然ですが、右サイドにならんでいるアイコンで多くの操作が可能です。カメラアイコンはキャプチャーですが、デフォルトでデスクトップに保存するようになっていて、一番下の『more』で設定画面を表示させてその場所も変える事ができます。



※ 回転は何故か表示されないという不具合はありましたが・・・・


本体にも地味に素敵な機能が・・・

Rerun(変更無しでの再実行) と Stop(Android内でのアプリケーションの終了) 機能が追加されているようで、これは便利。



ざっと、目新しいのはこれぐらいしか解りませんでしたが、速度は速くなっているような気はします。ただ、バージョンアップ後の旧プロジェクトが使えるようになるまでの Gradle の処理はかなり長かったです。それと、新たなプロジェクト作成後、画面のレンダリングに失敗する不具合は相変わらずあるようです。その場合は、Refresh で OK です。



Android Studio の設定

このあたりは特に変わってませんでした。

1) 開始時にプロジェクトを開かない


2) エディタのフォント変更


3) キャラクタセット


4) 行番号とホワイトスペース


5) タブ設定


6) 既存のスペースインデンントをタブに変更
7) 行をコードの途中で改行した時のインデント
8) CTRL + マウスホイールでフォントサイズ変更
9) マウスのホバーでドキュメント表示
10) ライブラリの参照の追加
11) ツリーと表示内容の相互同期


▼ こちらはショートカット
Android Studio 予習開始!




posted by lightbox at 2016-04-10 17:26 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年01月28日


Android Studio のとんでもないエラー『Some conflicts found in installation area』のなんとか対処法( 最悪は再インストールなので )



Android Studio をアップグレードしようとすると、最後の最後にこのようなダイアログが出て終わってしまいます。当然アップグレートはできていませんでした。世の中を探しても、StackOverflow ですら再インストールをすすめています。何かの整合性の問題(conflict/矛盾)だと思われますが、さすがに再インストールは時間が無いので、正常に動作している PC よりまるごとコピーして利用しました。

この際、上書きするのではなく以下のような手順を取っています。

1) 他のPCより適当な場所へコピー
2) 壊れているオリジナルフォルダをリネーム
3) この場所へ 1) のフォルダを移動

1) と インストールフォルダは同じドライブである必要があります。こうすれば、トラブルを最小限に抑えて復帰の準備ができます。ファイルやフォルダの移動は、余程の特別な理由が無いかぎり、同一ドライブでは管理情報の変更のみで実体の場所変更はありません。なので、3) の処理はすぐ済みます。上書きコピーをしてしまうと、上書きの確認やら、権限の問題やらなにやら、経験から言ってロクな事が発生しない可能性が多いのでこうした処置が必要になります。
※ また、これなら元へ戻すのも簡単なわけです。

結果うまく動作しました。コピーした元の Android Studio は既にアップグレード済み(1.5.1)なので、そのバージョンで正しく動作しました。( 次のアップグレードでどうなるかは保証できませんけれど )

再インストールよりははるかに早い復帰となりますので、各バージョン毎のバックアップを取っておくのもいいかと思われます。

ビルド時のよく解らないエラーの対処

gradle のエラーが明示される場合は、Users\User\.gradle を全て削除する方法もありますが、とにかく Androd Studio 内のソース全体でエラーがたくさん出る場合は、ユーザフォルダに .AndroidStudiofバージョン数字 のフォルダがあって、中の system\caches の中にあるファイルを全て削除すると良い場合があります(自己責任)。




posted by lightbox at 2016-01-28 19:58 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年01月03日


Android Studio : Intel の HAXM と System Image を最新にしたら、installation failed since the device possibly ... というエラーが出ました

▼ こういうダイアログが出ました。( たぶん System Image が原因だと思いますが )

( Stack overflow )

原因はいろいろあるようですが、自分の今回の場合の対処は、仮想デバイスの RAM を増やす事で対応できました。



HAXM を更新する前までは、RAM は 400 に足りなかったはずですが、ほぼデフオルトで動作していた記憶があります。このダイアログは Android Studio 側ですが、エミュレータ側ではシステム領域が足りないというメッセージが出ていたのですが、アプリを削除しても変わる事が無く、RAM の変更で対処しました。


この際、Java のバージョンを最新 ( Java8 Update 66 )にしたのですが、関係無く、その代わりにコントロールパネルに Java の表示がされないというトラブルに遭遇し( コントロールパネルから更新するとそうなりました )、結果的にはダウンロードして更新する事によって元に戻りました

もし、それでも表示されない場合は、jre のフォルダ内の bin の中に javacpl.exe があるのでそれを実行するとコントロールパネル用のアプリが起動されます。 


posted by lightbox at 2016-01-03 03:45 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2015年10月26日


Android の Data Binding( "クラス > 画面" の片方向 )における特殊コントロールの処理をデータクラス内に実装する

画面定義

Android の 公式の Data Binding は、データクラスから 画面への片方向です。また、対応しているのは setText メソッドを持つコントロールのようなので、ここで利用している NumberPickerSpinnerDatePicker では、個別にデータクラスのデータをセットする処理を実装する必要があるので、データクラス内に全て収めました。

このデータクラスは、Google Gson を使用して JSON から一括でデータをセットする事を想定しています。

注 : Data Binding と Google Gson を利用する事を目的としているため、画面用のデータは全て String で定義し、Getter や Setter を定義していません。必要ならば、Getter や Setter で拡張し、その場合でも Data Binding と Google Gson は正しく動作します

Syain クラス
import android.databinding.BaseObservable;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.DatePicker;
import android.widget.NumberPicker;
import android.widget.Spinner;

import java.util.Arrays;

public class Syain extends BaseObservable {

	// シリアライズ・デシリアライズをしない( static )
	static public MainActivity context = null;
	// NumberPicker 用
	static public NumberPicker np = null;
	static public String[] nums = {"男性","女性","不明"};
	static public int[] values = {-10,20,100};
	// Spinner 用
	static public Spinner sp = null;
	// DatePicker 用
	static public DatePicker dp = null;

	public String scode;
	public String kj;
	public String furi;
	public String syozoku;
	public String seibetu;
	public String kyuyo;
	public String teate;
	public String kanri;
	public String birth;
	public String sname;

	public Syain() {
		this.scode = "";
		this.kj = "初期状態";
		this.furi = "";
		this.syozoku = "";
		this.seibetu = "";
		this.kyuyo = "";
		this.teate = "";
		this.kanri = "";
		this.birth = "";
		this.sname = "";
	}

	// ******************************************
	// NumberPicker 用
	// ******************************************
	public void setupSeibetuControl() {
		np.setMinValue(0);
		np.setMaxValue(2);
		np.setDisplayedValues(nums);
		np.setValue(1);

	}
	public void setSeibetuControl(String seibetu) {
		if ( seibetu != null ) {
			this.seibetu = seibetu;
		}
		np.setValue(Integer.parseInt(this.seibetu));
	}
	public void getSeibetuControl() {
		this.seibetu = Integer.toString(np.getValue());
	}

	// ******************************************
	// Spinner 用
	// ******************************************
	public void setupSyozokuControl(String[] list_data) {
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(
				context,
				android.R.layout.simple_spinner_item);
		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		adapter.addAll(list_data);
		sp.setAdapter(adapter);
	}
	public void setSyozokuControl(String syozoku,String[] list_value) {
		if ( syozoku != null ) {
			this.syozoku = syozoku;
		}
		int idx = Arrays.binarySearch(list_value,this.syozoku);
		sp.setSelection(idx);
	}
	public void getSyozokuControl(String[] list_value) {
		int idx = sp.getSelectedItemPosition();
		this.syozoku = list_value[idx];
	}

	// ******************************************
	// DatePicker 用
	// ******************************************
	public void setupBirthControl(Boolean spinners, Boolean calendar) {
		dp.setSpinnersShown(spinners);
		dp.setCalendarViewShown(calendar);
	}
	public void setBirthControl(String birth) {
		if ( birth != null ) {
			this.birth = birth;
		}
		if ( this.birth != null ) {
			String dt[] = this.birth.split("-");
			if ( dt.length == 3 ) {
				dp.updateDate(Integer.parseInt(dt[0]), Integer.parseInt(dt[1])-1, Integer.parseInt(dt[2]));
			}
		}
	}
	public void getBirthControl() {
		this.birth = String.format("%d/%d/%d",dp.getYear(),dp.getMonth()+1,dp.getDayOfMonth());
	}


}

※ 再利用の為、先頭のパッケージ記述は削除しています

各コントロール用の Getter の引数について

Google Gson がデータを全てセットしてくれるので、その直後の処理としては、引数を必要としません。ですから、Google Gson のデシリアライズで受け取った値を使用する場合は、引数に null を使用します。
// 現在の内容でコントロールセット( fromJson 用なので、null 指定 )
syain.setSeibetuControl(null);
syain.setSyozokuControl(null, getResources().getStringArray(R.array.list_value));
syain.setBirthControl(null);


BaseObservable の継承について

ここでは、必要無かったのでこの BaseObservable の機能を使用していませんが、データクラス内のデータを個別に変更してリアルタイムで画面に反映させる場合に BaseObservable の通知機能を使います。しかし、自動生成されるバインド用のクラスの invalidateAll() を使ったほうが簡単であると思います。( Google Gson は、Setter を使用しないので利用できませんでした )

static フィールドについて

Google Gson のシリアライズとデシリアライズの対象外にする為に使用しています。当然、クラス共通となる為、画面定義と一対一と言う認識での実装です。



posted by lightbox at 2015-10-26 13:49 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり