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>