SQLの窓

2016年11月01日


ListView + カスタム ArrayAdapter + ViewSwitcher + Firebase API + Data Binding : 更新処理 / Android Studio

Firebase の環境とプロジェクトの作成方法

シンプル Android Data Binding : Android Studio 2.2

tools.jar

画面定義
MainActivity 主な処理は、初期画面に表示する ListView の処理です。ListView 用の一覧データは、Firebase より取得します。API を使用して(orderByChild メソッド) でフリガナでソートしています。 また、初期画面には表示されていませんが、ViewSwitcher を使用しているので、次画面用の処理として NextPage クラスを用意して、ここで初期化しています。 特に、Spinner 用データ(性別・所属)も Firebase より取得し、KeyValue という汎用クラスを用いて処理を構築しています。(この参照用のデータの取得が成功してから、一覧データを取得しています) ListView 用の MyArrayAdapter は、Data Binding を使用しています
public class MainActivity extends AppCompatActivity {

	// ViewSwitcher
	public static int FIRST_PAGE = 0;
	public static int NEXT_PAGE = 1;

	// Data Binding
	private ActivityMainBinding binding;

	// Firebase
	private FirebaseDatabase database;
	private DatabaseReference mDatabase;

	// データフォーマット
	private JsonData json;

	// ローディング中のダイアログ
	private ProgressDialog progress;

	// 画面関連
	private ViewSwitcher vs;
	private NextPage nextPage;
	private ListView listview;
	private MyArrayAdapter adapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Data Binding を使用した画面表示
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

		// Firebase Database 用
		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		// ローディング中のダイアログ
		progress = new ProgressDialog(MainActivity.this);

		// ViewSwitcher で複数画面処理
		vs = (ViewSwitcher) MainActivity.this.findViewById(R.id.viewSwitcher);
		// 次画面処理
		nextPage = new NextPage(MainActivity.this);
		// 次画面の初期処理( イベント登録等 )
		nextPage.initAction();

		// リストビューの取得
		listview = (ListView) MainActivity.this.findViewById(R.id.listView);
		// リストビューの行をタップした時の処理
		listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				// アダプターを取得( ここ専用 )
				MyArrayAdapter adapter = (MyArrayAdapter)parent.getAdapter();
				// 行データを取得
				json = (JsonData)adapter.getItem(position);
				// この行データを Data Binding で画面にセット
				binding.setJdata( json );
				// この行データを次画面で使用
				nextPage.setData( json );

				// 画面移動
				vs.setDisplayedChild(MainActivity.NEXT_PAGE);
			}
		});

		// ListView 用の アダブターを作成
		adapter = new MyArrayAdapter(MainActivity.this,R.layout.list_item);

		// 最新の Firebase のデータを取得する
		loadData();

	}

	// ******************************
	// データロード用の public メソッド
	// ******************************
	public void loadData() {
		// データロード前に表示
		progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		progress.setMessage("データをロードしています");
		progress.show();

		// Firebase の item ツリーを name でソートして取得
		mDatabase.child("refdata")
			.addListenerForSingleValueEvent(new ValueEventListener() {
				@Override
				public void onDataChange(DataSnapshot dataSnapshot) {
					if ( dataSnapshot.exists() ) {

						// 性別コンボボックスの作成
						DataSnapshot ref1 = dataSnapshot.child("sex");
						GenericTypeIndicator<ArrayList<String>> t1 = new GenericTypeIndicator<ArrayList<String>>() {};
						ArrayList<String> sexData = ref1.getValue(t1);
						ArrayAdapter adapter = new ArrayAdapter(
							MainActivity.this,
							android.R.layout.simple_spinner_item);
						Spinner spinner = (Spinner) MainActivity.this.findViewById(R.id.spinnerSex);
						adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
						adapter.addAll(sexData);
						spinner.setAdapter(adapter);

						// 所属コンボボックスの作成
						DataSnapshot ref2 = dataSnapshot.child("syozoku");
						Iterator<DataSnapshot> child = ref2.getChildren().iterator();
						ArrayList<KeyValue> syozokuData = new ArrayList<KeyValue>();
						while(child.hasNext()) {
							DataSnapshot next = child.next();
							// Firebase API でJsonData に変換
							KeyValue kv = new KeyValue(next.getKey(),next.getValue().toString());
							// ArrayList に追加
							syozokuData.add(kv);
						}
						ArrayAdapter adapter2 = new ArrayAdapter(
							MainActivity.this,
							android.R.layout.simple_spinner_item);
						Spinner spinner2 = (Spinner) MainActivity.this.findViewById(R.id.spinnerSyozoku);
						adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
						adapter2.addAll(syozokuData);
						spinner2.setAdapter(adapter2);

						loadDataList();
					}
					else {
						Log.i("lightbox","コンボボックス用データを読み込めませんでした");

						// ローディングダイアログを閉じる
						progress.dismiss();
					}

				}

				@Override
				public void onCancelled(DatabaseError databaseError) {
					databaseError.toException().printStackTrace();
					Log.i("lightbox","StackTrace(コンボボックス用データ) を出力しました");

					// ローディングダイアログを閉じる
					progress.dismiss();
				}
			});


	}

	public void loadDataList() {

		// Firebase の class ツリーを furi でソートして取得
		mDatabase.child("class").orderByChild("furi")
			.addListenerForSingleValueEvent(new ValueEventListener() {
				@Override
				public void onDataChange(DataSnapshot dataSnapshot) {
					if ( dataSnapshot.exists() ) {

						// 空の ArrayList
						ArrayList<JsonData> listData = new ArrayList<JsonData>();

						// name でソートされたデータを dataSnapshot より取得
						Iterator<DataSnapshot> child = dataSnapshot.getChildren().iterator();
						while(child.hasNext()) {
							DataSnapshot next = child.next();
							// Firebase API でJsonData に変換
							json = next.getValue(JsonData.class);

							// ArrayList に追加
							listData.add(json);
						}

						// アダプタをクリア
						adapter.clear();
						// アダプタにデータセット ( listData は name でソートされた ArrayList )
						adapter.addAll(listData);
						// リストビューにデータを表示
						listview.setAdapter(adapter);

					}
					else {
						Log.i("lightbox","データを読み込めませんでした");

					}

					// ローディングダイアログを閉じる
					progress.dismiss();
				}

				@Override
				public void onCancelled(DatabaseError databaseError) {
					databaseError.toException().printStackTrace();
					Log.i("lightbox","StackTrace を出力しました");

					// ローディングダイアログを閉じる
					progress.dismiss();
				}
			});
	}
}


NextPage クラス

ViewSwitcher の中に include で用意した次画面の処理を行います。MainActivity の中での処理になりますが、別クラスで分離して管理するようにしています。

この中では、Firebase の API を使用して更新処理を行っています。Firebase の更新は、JsonData クラスに必要なデータをセットし、そのまま API へ渡すというとても簡単で解り易い仕様となっています。
public class NextPage {

	private MainActivity mainActivity;
	private ViewSwitcher vs;
	private JsonData json;
	private View include1;

	// Firebase
	private FirebaseDatabase database;
	private DatabaseReference mDatabase;

	// コンストラクタ
	public NextPage(MainActivity mainActivity) {
		this.mainActivity = mainActivity;
	}

	// 処理する行データが外部からセットされる
	public void setData( JsonData json )  {
		this.json = json;
	}

	// 初期処理
	public void initAction(){

		// ViewSwitcher( 複数画面処理 )
		vs = (ViewSwitcher) mainActivity.findViewById(R.id.viewSwitcher);

		// activity_next.xml の 親 view を取得
		include1 = mainActivity.findViewById(R.id.include1);

		// Firebase Database 用
		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		// *****************************************
		// 戻るボタンの処理 ( include1 より取得 )
		// *****************************************
		Button backButton = (Button)include1.findViewById(R.id.backButton);
		backButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// 最初の画面へ移動
				vs.setDisplayedChild(MainActivity.FIRST_PAGE);

			}

		});

		// *****************************************
		// 更新ボタンの処理 ( include1 より取得 )
		// *****************************************
		Button updateButton = (Button)include1.findViewById(R.id.updateButton);
		updateButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				EditText et;

				et = (EditText) include1.findViewById(R.id.editName);
				json.setName(et.getText().toString());

				et = (EditText) include1.findViewById(R.id.editFuri);
				json.setFuri(et.getText().toString());

				et = (EditText) include1.findViewById(R.id.editKyuyo);
				json.setKyuyo(Integer.parseInt(et.getText().toString()));

				Spinner spinner;

				spinner = (Spinner) include1.findViewById(R.id.spinnerSex);
				json.setSex(String.format("%d",spinner.getSelectedItemPosition()));

				spinner = (Spinner) include1.findViewById(R.id.spinnerSyozoku);
				json.setSyozokuFromSpinner(spinner);

				DatePicker dp = (DatePicker) include1.findViewById(R.id.datePickerBirthday);
				String stringDate = String.format("%d/%02d/%02d",
					dp.getYear(),dp.getMonth()+1,dp.getDayOfMonth()
					);
				json.setBirthday(stringDate);

				Tools.messageBox(mainActivity, "確認", "更新しますか?", new Tools.OnMessageBoxListener() {
					@Override
					public void onMessageBoxYesListener() {
						// 更新
						mDatabase
							.child("class")
							.child(json.getCode())
							.setValue(json)
							.addOnCompleteListener(new OnCompleteListener<Void>() {
								@Override
								public void onComplete(@NonNull Task<Void> task) {
									if (task.isSuccessful()) {
										Log.i("lightbox", "書き込みに成功しました");

										mainActivity.loadData();
										// 最初の画面へ移動
										vs.setDisplayedChild(MainActivity.FIRST_PAGE);
									}
									else {
										Log.i("lightbox", task.getException().toString());
										task.getException().printStackTrace();
										Toast.makeText(mainActivity,"更新に失敗しました",Toast.LENGTH_SHORT).show();
									}
								}
							});
					}

					@Override
					public void onMessageBoxNoListener() {

					}
				});


			}

		});
	}
}


JsonData クラス

データの表示時に Data Binding で使用され、データの取得と更新時には Firebase で使用される、とても重要なクラスです。

特に、テキスト以外の入力値が必要な画面コントロールに対して、Data Binding の @BindingAdapter を使用したカスタムセッターを、数値データ(画面へは文字列なので)、DatePicker、Spinner に合わせて作成してあります。
public class JsonData extends BaseObservable {

	private String code;
	private String name;
	private String furi;

	// 画面用のカスタムセッターと保存用の カスタムセッター が必要です
	private String birthday;
	private long kyuyo;
	private String sex;		// コンボボックス
	private String syozoku;		// コンボボックス

	// 未使用( 無いと Firebase のデータが消失するので )
	public String teate;
	public String kanri;

	// **************************************
	// Firebase で必要な空のコンストラクタ
	// **************************************
	public JsonData(){}

	// キーなので setter はありません
	public String getCode() {
		return code;
	}

	// **************************************
	// setter 内で通知するのに、@Bindable を getter に
	// **************************************
	@Bindable
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.name);
	}

	// **************************************
	// フリガナ
	// **************************************
	@Bindable
	public String getFuri() {
		return furi;
	}
	public void setFuri(String furi) {
		this.furi = furi;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.furi);
	}

	// **************************************
	// 生年月日
	// **************************************
	@Bindable
	public String getBirthday() {
		return birthday;
	}
	public void setBirthday(String birthday) {
		this.birthday = birthday;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.birthday);
	}
	@BindingAdapter("dateText")
	public static void setDateText(DatePicker datePicker, String text) {

		if ( text != null && !text.equals("")) {
			int year = Integer.parseInt(text.substring(0, 4));
			int month = Integer.parseInt(text.substring(5, 7)) - 1;
			int day = Integer.parseInt(text.substring(8, 10));
			datePicker.updateDate(year, month, day);
		}

	}
	// **************************************
	// 給与
	// **************************************
	@Bindable
	public long getKyuyo() {
		return kyuyo;
	}
	public void setKyuyo(long kyuyo) {
		this.kyuyo = kyuyo;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.kyuyo);
	}
	@BindingAdapter("longKyuyo")
	public static void setLongKyuyo(EditText editKyuyo, long kyuyo) {
		editKyuyo.setText(String.format("%d",kyuyo));
	}

	// **************************************
	// 性別
	// **************************************
	@Bindable
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.sex);
	}
	@BindingAdapter("stringSex")
	public static void setStringSex(Spinner spinnerSex, String sex) {
		if ( sex != null && !sex.equals("")) {
			spinnerSex.setSelection(Integer.parseInt(sex));
		}
	}

	// **************************************
	// 所属
	// **************************************
	@Bindable
	public String getSyozoku() {
		return syozoku;
	}
	public void setSyozoku(String syozoku) {
		this.syozoku = syozoku;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.syozoku);
	}
	// コントロールから、syozoku をセットする  Setter
	// ※ setSyozoku という名称は使えません
	public void setSyozokuFromSpinner(Spinner spinnerSyozoku) {
		int position = spinnerSyozoku.getSelectedItemPosition();
		ArrayAdapter adapter = (ArrayAdapter) spinnerSyozoku.getAdapter();
		KeyValue kv = (KeyValue)adapter.getItem(position);
		this.setSyozoku(kv.getCode());
	}
	@BindingAdapter("syozokuData")
	public static void setSyozokuData(Spinner spinnerSyozoku, String syozoku) {
		if ( syozoku != null && !syozoku.equals("")) {
			ArrayAdapter adapter = (ArrayAdapter) spinnerSyozoku.getAdapter();
			int count = adapter.getCount();
			for( int i = 0; i < count; i++ ) {
				KeyValue kv = (KeyValue) adapter.getItem(i);
				if ( kv.getCode().equals(syozoku)) {
					spinnerSyozoku.setSelection(i);
					break;
				}
			}
		}
	}

	@Override
	public String toString() {
		return this.name;
	}
}

activity_next.xml

第二画面の定義です。Data Binding 用の記述として、app 名前空間で、カスタムセッターの名称を定義しています。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="jdata" type="lightbox.july.listviewandnextactivity.JsonData"/>
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

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

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/btn_back"
                android:id="@+id/backButton"
                android:layout_alignParentTop="true"
                android:layout_alignParentStart="true"
                android:layout_weight="1"/>

            <Button
                android:text="@string/btn_update"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/updateButton"
                android:layout_weight="1"/>
        </LinearLayout>

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <TextView
                    android:text="@{jdata.code}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textAppearance="?android:attr/textAppearanceLarge"
                    android:id="@+id/textCode"/>

                <EditText
                    android:text="@{jdata.name}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="textPersonName"
                    android:ems="10"
                    android:id="@+id/editName"/>

                <EditText
                    android:text="@{jdata.furi}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="textPersonName"
                    android:ems="10"
                    android:id="@+id/editFuri"/>

                <EditText
                    app:longKyuyo="@{jdata.kyuyo}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="number"
                    android:ems="10"
                    android:id="@+id/editKyuyo"/>

                <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:padding="10dp"
                    android:layout_marginLeft="5dp">

                    <Spinner
                        app:stringSex="@{jdata.sex}"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:id="@+id/spinnerSex"
                        android:layout_weight="1"
                        android:layout_marginRight="5dp"/>

                    <Spinner
                        app:syozokuData="@{jdata.syozoku}"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:id="@+id/spinnerSyozoku"
                        android:layout_weight="1"/>

                </LinearLayout>

                <DatePicker
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:datePickerMode="spinner"
                    android:calendarViewShown="false"
                    android:id="@+id/datePickerBirthday"
                    app:dateText="@{jdata.birthday}"/>

            </LinearLayout>

        </ScrollView>

    </LinearLayout>
</layout>

ListView 用の画面定義( Data Binding 仕様 )
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="jdata" type="lightbox.july.listviewandnextactivity.JsonData"/>
    </data>
    <LinearLayout
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:padding="10dp">

        <TextView
            android:text="@{jdata.name}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/textItem1" />

        <TextView
            android:text="@{jdata.furi}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textItem2"
            android:layout_marginTop="5dp"/>

    </LinearLayout>
</layout>



【Android Studio 2016の最新記事】
posted by lightbox at 2016-11-01 20:17 | Comment(0) | Android Studio 2016 | このブログの読者になる | 更新情報をチェックする
バッチ処理

Microsoft Office
container 終わり

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

Android SDK ポケットリファレンス
改訂版 Webデザイナーのための jQuery入門
今すぐ使えるかんたん ホームページ HTML&CSS入門
CSS ドロップシャドウの参考デモ
PHP正規表現チェッカー
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり