SQLの窓

2016年09月30日


jQuery で mobile(スマホ) かどうかのチェックをしたくて調べたら、Stack Overflow の記事でいろんな方法が答えられていました。

What is the best way to detect a mobile device in jQuery?

結局 jQuery に特化した回答は無く、但し何通りもの回答がたくさんよせられていました。その中でもよさげなのが 3つほどありますのでご紹介します。

userAgent を使ったとてもシンプルなもの
jQuery.isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
jQuery で $ で使いたいので、左辺を jQuery にしてあります。とても簡単ですが、ちょっと拡張性が低く、メンテナンスはしづらいパターンです。

userAgent を使った拡張性の高いもの

これはとても応用が利き、最も読みやすくて理解しやすくてメンテナンスが楽なものです。

Detecting Mobile Devices with JavaScript
( このリンク先のものを少し改造しています )
<script>
jQuery.isMobile = {
    Android: function() {
        return navigator.userAgent.match(/Android/i) == null ? false : true;
    },
    BlackBerry: function() {
        return navigator.userAgent.match(/BlackBerry/i) == null ? false : true;
    },
    iOS: function() {
        return navigator.userAgent.match(/iPhone|iPad|iPod/i) == null ? false : true;
    },
    Opera: function() {
        return navigator.userAgent.match(/Opera Mini/i) == null ? false : true;
    },
    Windows: function() {
        return navigator.userAgent.match(/IEMobile/i) == null ? false : true;
    },
    any: function() {
        return (jQuery.isMobile.Android() || jQuery.isMobile.BlackBerry() || jQuery.isMobile.iOS() || jQuery.isMobile.Opera() || jQuery.isMobile.Windows());
    }
};

console.log($.isMobile.any())
</script>

CSS レスポンシブに同期するもの

個別にデバイス一覧をメンテするよりも、元々の画面の状況でチェックするようになっています。CSS を使うので、判定用の BR 要素を一つ画面の最終に置くという前提でのサンプルです。
<style>
@media screen and (max-width:479px) {
  #lastbr { display: none; }
}
</style>

<script>
$( function() {      
    jQuery.isMobile = false;
    if( $('#lastbr').css('display')=='none') {
        jQuery.isMobile = true;       
    }


 });

</script>


<br id="lastbr">

PHP 側で判定したものを使う

あと、PHP のライブラリで判定したものを埋め込むという手も、開発環境によっては一番有効であろうと思われるものもあります。
<script>
//set defaults
var device_type = 'desktop';
</script>

<?php
require_once( 'Mobile_Detect.php');
$detect = new Mobile_Detect();
?>

<script>
device_type="<?php echo ($detect->isMobile() ? ($detect->isTablet() ? 'tablet' : 'mobile') : 'desktop'); ?>";
alert( device_type);
</script>

userAgent を使うと、Chrome でテストしやすい

厳密に判定してしまうと、その環境でしかテストできなくなるので、あまりお勧めではありません。例で言うと、スマホデバイスでしか判定できないタッチイベントの有無で判定しているものもありました。
function isMobile() {
  try{ document.createEvent("TouchEvent"); return true; }
  catch(e){ return false; }
}

それに、これだと将来的には常に true になってしまう可能性があります。



【jQueryの最新記事】
posted by lightbox at 2016-09-30 13:24 | Comment(0) | jQuery | このブログの読者になる | 更新情報をチェックする

2016年09月27日


Android : Data Binding + Firebase API で ListView にデータを表示する

ListView の扱いとしては、『Android : Data Binding で ListView へのデータ表示を凄く簡単にする』 の拡張です。

Firebase 部分のセットアップは、『Firebase API + Android Studio : Database 処理の基本設定』を参照して下さい。

Firebase API で Databaseデータ を読み込む方法に関しては『Firebase API + Android Studio : Database のデータを Java に取得する方法は3通りあります。』を参照して下さい

Firebase の ArrayList データを使用したソース
public class MainActivity extends Activity {

	private FirebaseDatabase database;
	private DatabaseReference mDatabase;
	private ArrayList<Item> users;
	private User user;

	TestArrayAdapter<Item> adapter = null;

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

		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
		user = new User();
		user.setFirstName("山田");
		user.setLastName("タロウ");
		binding.setUser(user);

		adapter = new TestArrayAdapter<Item>(
				MainActivity.this,
				R.layout.myitem,
				new TestArrayAdapter.OnGetViewListener() {
					@Override
					public View onGetViewListener(int position, View convertView, ViewGroup parent) {

						MyitemBinding myitem;

						if ( convertView == null ) {
							LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService
								(Context.LAYOUT_INFLATER_SERVICE);
							myitem = DataBindingUtil.inflate(inflater, R.layout.myitem, parent, false);
						}
						else {
							myitem = DataBindingUtil.getBinding(convertView);
						}


						ListView lv = (ListView)parent;
						TestArrayAdapter taa = (TestArrayAdapter)lv.getAdapter();
						myitem.setItem(  taa.getItem(position) );

						// ここが重要です
						return myitem.getRoot();
					}
				}
		);

		((ListView)MainActivity.this.findViewById(R.id.listView)).setAdapter(adapter);

		// Firebase Database より読み込み
		MainActivity.this.findViewById(R.id.buttonFirebase).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				mDatabase.child("users").addListenerForSingleValueEvent(new ValueEventListener() {
					@Override
					public void onDataChange(DataSnapshot dataSnapshot) {
						if ( dataSnapshot.exists() ) {
							Log.i("lightbox", dataSnapshot.getValue().toString());

							GenericTypeIndicator<ArrayList<Item>> t = new GenericTypeIndicator<ArrayList<Item>>() {};
							users = dataSnapshot.getValue(t);

							adapter.clear();
							adapter.addAll(users);

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

					@Override
					public void onCancelled(DatabaseError databaseError) {
						Log.i("lightbox","onCancelled");
						StringWriter sw = new StringWriter();
						PrintWriter pw = new PrintWriter(sw);
						databaseError.toException().printStackTrace(pw);
						pw.flush();
						String stackTrace = sw.toString();
						Log.i("lightbox",stackTrace);

					}
				});
			}
		});

		// ボタンを押して、オブジェクトの変更のみで反映されます
		MainActivity.this.findViewById(R.id.buttonSetValue).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				user.setFirstName("鈴木");
				user.setLastName("ジロウ");
				if ( users != null ) {
					users.get(0).setText("getView での記述が簡単になりました");
					users.get(1).setText("しかも、オブジェクトを変更すると ListView も変わります");
				}
			}
		});
	}

	// バインド用クラス(1)
	public static class User extends BaseObservable {
		private String firstName;
		private String lastName;

		@Bindable
		public String getFirstName() {
			return this.firstName;
		}
		@Bindable
		public String getLastName() {
			return this.lastName;
		}

		public void setFirstName(String firstName) {
			this.firstName = firstName;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.firstName);
		}
		public void setLastName(String lastName) {
			this.lastName = lastName;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.lastName);
		}
	}

	// バインド用クラス(2) : ListView 用
	public static class Item extends BaseObservable {
		private String title;
		private String text;
		private boolean flg = false;

		public Item() {}

		public Item(String text,String title) {
			this.text = text;
			this.title = title;
		}

		@Bindable
		public String getText() {
			return this.text;
		}
		@Bindable
		public String getTitle() {
			return this.title;
		}

		public void setText(String text) {
			this.text = text;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.text);
		}

		public void seTitle(String title) {
			this.title = title;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.title);
		}

		public String getCode() {
			return title;
		}
		public String getName() {
			return text;
		}
		public void setCode(String code) {
			this.title = code;
		}
		public void setName(String name) {
			this.text = name;
		}

	}

}

Item クラスに、Firebase で定義したデータを使用する為に、Firebase 用の Setter を作成して、元々のデータ処理と互換性を持たせています。

setText と setTitle 側で、notifyPropertyChanged を使っているので、そちらでデータの変更を行っています。

このデータは、以下のようなフォーマットであり、ArrayList として直接取得しています。



しかし、個別のデータとしては上記データは特殊な部類になるので、以下のようなデータで ListView にデータをセットします。



Firebase の HashMap データを使用したソース
public class MainActivity extends Activity {

	private FirebaseDatabase database;
	private DatabaseReference mDatabase;
	private HashMap<String,Item> users;
	private ArrayList<Item> userList;
	private User user;

	TestArrayAdapter<Item> adapter = null;

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

		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
		user = new User();
		user.setFirstName("山田");
		user.setLastName("タロウ");
		binding.setUser(user);

		adapter = new TestArrayAdapter<Item>(
				MainActivity.this,
				R.layout.myitem,
				new TestArrayAdapter.OnGetViewListener() {
					@Override
					public View onGetViewListener(int position, View convertView, ViewGroup parent) {

						MyitemBinding myitem;

						if ( convertView == null ) {
							LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService
								(Context.LAYOUT_INFLATER_SERVICE);
							myitem = DataBindingUtil.inflate(inflater, R.layout.myitem, parent, false);
						}
						else {
							myitem = DataBindingUtil.getBinding(convertView);
						}

						ListView lv = (ListView)parent;
						TestArrayAdapter taa = (TestArrayAdapter)lv.getAdapter();
						myitem.setItem(  taa.getItem(position) );

						// ここが重要です
						return myitem.getRoot();
					}
				}
		);

		((ListView)MainActivity.this.findViewById(R.id.listView)).setAdapter(adapter);

		// Firebase Database より読み込み
		MainActivity.this.findViewById(R.id.buttonFirebase).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				mDatabase.child("class").addListenerForSingleValueEvent(new ValueEventListener() {
					@Override
					public void onDataChange(DataSnapshot dataSnapshot) {
						if ( dataSnapshot.exists() ) {
							Log.i("lightbox", dataSnapshot.getValue().toString());

							GenericTypeIndicator<HashMap<String,Item>> t = new GenericTypeIndicator<HashMap<String,Item>>() {};
							users = dataSnapshot.getValue(t);

							// 必要ならば、ここでデータ順序の変更が必要です
							userList = new ArrayList<Item>(users.values());

							adapter.clear();
							adapter.addAll(userList);

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

					@Override
					public void onCancelled(DatabaseError databaseError) {
						Log.i("lightbox","onCancelled");
						StringWriter sw = new StringWriter();
						PrintWriter pw = new PrintWriter(sw);
						databaseError.toException().printStackTrace(pw);
						pw.flush();
						String stackTrace = sw.toString();
						Log.i("lightbox",stackTrace);

					}
				});
			}
		});

		// ボタンを押して、オブジェクトの変更のみで反映されます
		MainActivity.this.findViewById(R.id.buttonSetValue).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				user.setFirstName("鈴木");
				user.setLastName("ジロウ");
				if ( userList != null ) {
					userList.get(0).setText("getView での記述が簡単になりました");
					userList.get(1).setText("しかも、オブジェクトを変更すると ListView も変わります");
				}
			}
		});
	}

	// バインド用クラス(1)
	public static class User extends BaseObservable {
		private String firstName;
		private String lastName;

		@Bindable
		public String getFirstName() {
			return this.firstName;
		}
		@Bindable
		public String getLastName() {
			return this.lastName;
		}

		public void setFirstName(String firstName) {
			this.firstName = firstName;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.firstName);
		}
		public void setLastName(String lastName) {
			this.lastName = lastName;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.lastName);
		}
	}

	// バインド用クラス(2) : ListView 用
	public static class Item extends BaseObservable {
		private String title;
		private String text;
		private boolean flg = false;

		public Item() {}

		public Item(String text,String title) {
			this.text = text;
			this.title = title;
		}

		@Bindable
		public String getText() {
			return this.text;
		}
		@Bindable
		public String getTitle() {
			return this.title;
		}

		public void setText(String text) {
			this.text = text;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.text);
		}

		public void seTitle(String title) {
			this.title = title;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.title);
		}

		public String getCode() {
			return title;
		}
		public String getName() {
			return text;
		}
		public void setCode(String code) {
			this.title = code;
		}
		public void setName(String name) {
			this.text = name;
		}

	}

}




posted by lightbox at 2016-09-27 20:36 | Comment(0) | Android Studio 2016 | このブログの読者になる | 更新情報をチェックする

Android : Data Binding で ListView へのデータ表示を凄く簡単にする

2016/09/27 : Data Binding の最新の仕様に合わせて内容を調整しました
( Gradle の変更は Module(app)用の gradle のみです )
2015/10/5 時点で、バグと思われる注意事項があります。XML のレイアウト定義内に日本語があると、バインドする為の解析処理が失敗するようです。文字列リソースに日本語を定義して参照するのなら問題ありません。 また、何か(ビルド上の不可解な)問題が出た場合、以下のような処理で復帰できると思います。
1) 画面定義の data 要素部分を削除してすぐ戻す
2) エラーが出るソースを開くと DataBindingUtil 部分のエラーがなくなる
3) binding.メソッドのエラーが残るので、プロジェクトをいったん閉じて開く
バインド用のクラスは自動作成されますが、画面定義したレイアウトファイル名から作成されます。 activity_main なら、ActivityMainBinding となります MainActivity.java
package sample.lightbox.myapplication;

import android.app.Activity;
import android.content.Context;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

import sample.lightbox.myapplication.databinding.ActivityMainBinding;
import sample.lightbox.myapplication.databinding.MyitemBinding;


public class MainActivity extends Activity {

	TestArrayAdapter<Item> adapter = null;

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


		ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
		final User user = new User();
		user.setFirstName("山田");
		user.setLastName("タロウ");
		binding.setUser(user);

		final Item[] items = {
				new Item("あ","0001"),new Item("い","0002"),new Item("う","0003"),
				new Item("え","0004"),new Item("お","0005"),new Item("か","0006"),
				new Item("き","0007"),new Item("く","0008"),new Item("け","0009"),
				new Item("こ","0010")
		};

		adapter = new TestArrayAdapter<Item>(
				MainActivity.this,
				R.layout.myitem,
				new TestArrayAdapter.OnGetViewListener() {
					@Override
					public View onGetViewListener(int position, View convertView, ViewGroup parent) {

						MyitemBinding myitem;

						if ( convertView == null ) {
							LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService
								(Context.LAYOUT_INFLATER_SERVICE);
							myitem = DataBindingUtil.inflate(inflater, R.layout.myitem, parent, false);
						}
						else {
							myitem = DataBindingUtil.getBinding(convertView);
						}
						myitem.setItem(items[position]);

						// ここが重要です
						return myitem.getRoot();
					}
				}
		);

		adapter.addAll(items);
		((ListView)MainActivity.this.findViewById(R.id.listView)).setAdapter(adapter);

		// ボタンを押して、オブジェクトの変更のみで反映されます
		MainActivity.this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				user.setFirstName("鈴木");
				user.setLastName("ジロウ");
				items[0].setText("getView での記述が簡単になりました");
				items[9].setText("しかも、オブジェクトを変更すると ListView も変わります");
			}
		});
	}

	// バインド用クラス(1)
	public static class User extends BaseObservable {
		private String firstName;
		private String lastName;

		@Bindable
		public String getFirstName() {
			return this.firstName;
		}
		@Bindable
		public String getLastName() {
			return this.lastName;
		}

		public void setFirstName(String firstName) {
			this.firstName = firstName;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.firstName);
		}
		public void setLastName(String lastName) {
			this.lastName = lastName;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.lastName);
		}
	}

	// バインド用クラス(2) : ListView 用
	public static class Item extends BaseObservable {
		private String title;
		private String text;
		private boolean flg = false;

		public Item(String text,String title) {
			this.text = text;
			this.title = title;
		}

		@Bindable
		public String getText() {
			return this.text;
		}
		@Bindable
		public String getTitle() {
			return this.title;
		}

		public void setText(String text) {
			this.text = text;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.text);
		}

		public void seTitle(String title) {
			this.title = title;
			notifyPropertyChanged(sample.lightbox.myapplication.BR.title);
		}
	}

}

TestArrayAdapter は、getView をカスタマイズしやすくする為のクラスです。

activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">

	<data>
		<variable name="user" type="sample.lightbox.myapplication.MainActivity.User"/>
	</data>
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="10dp">

		<TextView android:layout_width="match_parent"
				  android:layout_height="wrap_content"
				  android:textSize="26dp"
				  android:text="@{user.firstName}"
				  android:id="@+id/textView"/>
		<TextView android:layout_width="match_parent"
				  android:layout_height="wrap_content"
				  android:textSize="26dp"
				  android:text="@{user.lastName}"/>

		<Button
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:text="@string/button_1"
			android:id="@+id/button"/>

		<ListView
			android:layout_width="match_parent"
			android:layout_height="match_parent"
			android:id="@+id/listView"
			android:layout_below="@+id/button"/>

        </LinearLayout>

</layout>

myitem.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="item" type="sample.lightbox.myapplication.MainActivity.Item"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp">

        <TextView
            android:text="@{item.title}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textView3"/>

        <TextView
            android:text="@{item.text}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/text1"
        />
    </LinearLayout>
</layout>

Module(app)用の gradle
android {
    compileSdkVersion 22
    buildToolsVersion "22.0.0"
    dataBinding {
        enabled = true
    }
    defaultConfig {
        applicationId "sample.lightbox.myapplication"
        minSdkVersion 19
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors {
    }
}
モジュール用の build.gradle には、apply plugin: 'com.android.databinding' を追加します

データ部分は、Google Gson を使ってバインド用クラスに直接セットするようにするとさらに簡単になると思います。

自動作成されたクラス

Android デベロッパーの情報


posted by lightbox at 2016-09-27 20:27 | Comment(0) | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年09月26日


シンプル Android Data Binding : Android Studio 2.2 / 古い定義との違いと、以前のプロジェクトでエラーが出る場合の対処

▼ 記事内のソースコードへ移動
@BindingAdapter でカスタムセッター
Syain クラスへ直接セットして表示
Google Gson と tools.jar を使用した読み込み
Firebase API を使用した読込み


プロジェクトの Gradle に classpath "com.android.databinding:dataBinder:1.0-rc1" は必要ありません
Module(app) の Gradle に
    dataBinding {
        enabled = true
    }
があれば動作します
去年より地味に進化していました。build.gradle(Module: app) の簡単な設定で利用可能になります build.gradle(Module: app)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.3"

    dataBinding {
        enabled = true
    }

    defaultConfig {
        applicationId "lightbox.june.bindingbasic"
        minSdkVersion 19
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:22.2.1'
}

バインド用のクラスには、後々 Google Gson 使うので 超単純にしています。

※ @BindingAdapter はテキストを直接使えないコントロール用のカスタムセッターです

Syain.java
public class Syain {

	public String code;
	public String name;
	public String birthday;

	@BindingAdapter("dateText")
	public static void setDateText(DatePicker datePicker, String text) {

		if ( text != null && !text.equals("")) {
			Log.i("lightbox",text);

			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);
		}

	}
}
画面も、layout 要素の記述がスリムになりました。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable name="user" type="lightbox.june.bindingbasic.Syain"/>
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/textView"
            android:textSize="40dp"
            android:text="@{user.code}"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/textView2"
            android:layout_below="@+id/textView"
            android:layout_alignStart="@+id/textView"
            android:layout_marginTop="20dp"
            android:textSize="40dp"
            android:text="@{user.name}"/>

        <DatePicker
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/datePicker"
            android:layout_below="@+id/textView2"
            android:layout_alignParentStart="true"
            android:datePickerMode="spinner"
            android:calendarViewShown="false"
            app:dateText="@{user.birthday}"/>
    </RelativeLayout>
</layout>

さて、MainActivity ですが、Android の公式ページがいまだに誤記しているのがタマに傷です。R.layout.main_activity では無く、R.layout.activity_main です。当たり前ですけれど。コピペするとえらい目にあいます。

MainActivity.java
package lightbox.june.bindingbasic;

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import lightbox.june.bindingbasic.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

	ActivityMainBinding binding;

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

		binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);

//		String json = "{\"code\": \"0001\",\"name\": \"やまだ たろう\",\"birthday\": \"1959/10/10\"}";
//		Gson gson = new Gson();
//		Syain user = gson.fromJson(json,Syain.class);

		Syain user = new Syain();
		user.code = "0001";
		user.name = "やまだ たろう";
		user.birthday = "1959/10/10";

		binding.setUser(user);

	}
}

これで動くはずですが、良く解らないエラーが出る場合は以下の二つを試すといいはずです。画面に関しては以前からある問題点で、さらに動くまでは画面定義に日本語は直接書かないほうがいいと思います。

エラー時の対処 : パターン1
1) 画面定義の data 要素部分を削除してすぐ戻す
2) エラーが出るソースを開くと DataBindingUtil 部分のエラーがなくなる
3) binding.メソッドのエラーが残るので、プロジェクトをいったん閉じて開く
エラー時の対処 : パターン2
1) 画面定義の activity_main.xml を activity_main2.xml に変更して すぐに activity_main に戻す
2) ファイルメニューの Invalidate Caches / Restart を実行
Data Binding Android - Type parameter T has incompatible upper bounds : ViewDataBinding and MainActivity DataBindingUtil.setContentView - Type parameter T has incompatible upper bounds Google Gson と tools.jar を使用した読み込み ※ URL は、Firebase のログインの必要無いデータを使用しています
public class MainActivity extends AppCompatActivity {

	ActivityMainBinding binding;

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

		binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);

		String url = "https://freebase-44e8b.firebaseio.com/class/0001.json?print=pretty";
		Tools.callHttpGet(url, "utf-8", new Tools.OnAsyncTaskListener() {
			@Override
			public void onAsyncTaskListener(String s) {
				Log.i("lightbox", s);
				Gson gson = new Gson();
				Syain user = gson.fromJson(s,Syain.class);

				binding.setUser(user);
			}
		});


	}
}
Google Gson のダウンロード
tools.jar のダウンロード


Firebase API を使用した読込み
public class MainActivity extends AppCompatActivity {

	ActivityMainBinding binding;
	private FirebaseDatabase database;
	private DatabaseReference mDatabase;

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

		binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);

		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		mDatabase.child("class/0001").addListenerForSingleValueEvent(new ValueEventListener() {
			@Override
			public void onDataChange(DataSnapshot dataSnapshot) {
				Syain user = dataSnapshot.getValue(Syain.class);
				binding.setUser(user);

			}

			@Override
			public void onCancelled(DatabaseError databaseError) {

			}
		});

	}
}

Firebase API + Android Studio : Database のデータを Java に取得する方法は3通りあります。
Firebase API + Android Studio : Database 処理の基本設定




posted by lightbox at 2016-09-26 16:09 | Comment(0) | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

Firebase API + Android Studio : Database のデータを Java に取得する方法は3通りあります。

記事末に、@IgnoreExtraProperties アノテーションの説明を追加しました。
記事末に、『JSON データを直接使用する』もあります。

ArrayList として取得
HashMap として取得
Userクラスとして取得
全体ソース
まず、環境とプロジェクトの作成方法は、『Firebase API + Android Studio : Database 処理の基本設定』にまとめました。そちらを参照して下さい。 Firebase の Database は、JSON をイメージした格納方法になっており、データはツリー構造の末端にあると考えて、そこまでのパスを指定して読み出します。ここでは、以下のようなデータを想定しており、0、1、2、の部分は配列のインデックスを意味しています。 ArrayList として取得 users を パスとして与えて取得すると、その下は配列なので ArrayList として取得する事ができます。また、users/0 のパスで取得するデータは クラス(User)を作成して、User.class を使用して Userクラスのインスタンスに直接セットする事ができます。そして、users を パス とした場合は ArrayList<User> としても取得する事ができます。
GenericTypeIndicator<ArrayList<User>> t = new GenericTypeIndicator<ArrayList<User>>() {};

ArrayList<User> user = dataSnapshot.getValue(t);
Iterator<User> it = user.iterator();
while( it.hasNext() ) {
	User work = it.next();
	Log.i("lightbox", work.getCode() );
	Log.i("lightbox", work.getName() );
}


DataSnapshot dataSnapshot は、Database のデータを取得する onDataChange イベントのパラメータとして使用可能なもので、この中にデータがセットされています。

そして、ArrayList は 以下のようにして配列に変換して利用してもいいでしょう。
User[] users = user.toArray(new User[0]);

for( int i = 0; i < users.length; i++){
	Log.i("lightbox", users[i].getCode() );
	Log.i("lightbox", users[i].getName() );
}


HashMap として取得

User クラスを作成しない場合、users を パスとした場合の一般的な方法として ArrayList<HashMap<String,Object> として取得する事ができます。
GenericTypeIndicator<ArrayList<HashMap<String,Object>>> t2
	= new GenericTypeIndicator<ArrayList<HashMap<String,Object>>>() {};

ArrayList<HashMap<String,Object>> useList = dataSnapshot.getValue(t2);

Iterator<HashMap<String,Object>> it2 = useList.iterator();
while( it2.hasNext()) {
	HashMap<String,Object> userMap = it2.next();
	Log.i( "lightbox", userMap.get("code").toString() );
	Log.i( "lightbox", userMap.get("name").toString() );
}


Userクラスとして取得
User user = dataSnapshot.getValue(User.class);

TextView tv1 = (TextView) MainActivity.this.findViewById(R.id.textCode);
tv1.setText(user.getCode());
TextView tv2 = (TextView) MainActivity.this.findViewById(R.id.textName);
tv2.setText(user.getName());

※ クラスで定義するフィールドの整数は long で定義して下さい。(関連 API ドキュメント情報)

クラス内の変数は、public のフィールドで定義してもかまいませんし、setter / getter で定義してもかまいません。排除したいフィールドは、@Exclude アノテーションを使用できます。

また、クラスには引数の無い処理の無いデフォルトコンストラクタを定義しておいて下さい。以下のような但し書きがあります
Default constructor required for calls to DataSnapshot.getValue(User.class)
※ 概要印刷用 PDF ※ ソース全体印刷用 PDF データを取得する為の addListenerForSingleValueEvent ドキュメントには、『Read data once』とタイトルして解説されていますが、必要な時にデータを取得する為のイベントの登録処理です。他のリスナーは、状態が変化した時に取得可能なもので、上のリンク先のページの『Listen for events』で解説されています。 addListenerForSingleValueEvent は、一般的にサーバよりデータを取得するメソッドとして使用され、この中のイベントで DataSnapshot を使ってデータにアクセスします。 全体ソース
public class MainActivity extends AppCompatActivity {

	private FirebaseDatabase database;
	private DatabaseReference mDatabase;

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

		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		Button readButton = (Button) MainActivity.this.findViewById(R.id.readButton);
		readButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
					@Override
					public void onDataChange(DataSnapshot dataSnapshot) {
						if ( dataSnapshot.exists() ) {
							Log.i("lightbox", dataSnapshot.getValue().toString());
						}
					}

					@Override
					public void onCancelled(DatabaseError databaseError) {

					}
				});


				mDatabase.child("users").addListenerForSingleValueEvent(new ValueEventListener() {
					@Override
					public void onDataChange(DataSnapshot dataSnapshot) {
						if ( dataSnapshot.exists() ) {
							Log.i("lightbox", "-- users の状態");
							Log.i("lightbox", dataSnapshot.getValue().toString());
							Log.i("lightbox", "-- ArrayList<User>");

							GenericTypeIndicator<ArrayList<User>> t = new GenericTypeIndicator<ArrayList<User>>() {};
							ArrayList<User> user = dataSnapshot.getValue(t);
							Iterator<User> it = user.iterator();
							while( it.hasNext() ) {
								User work = it.next();
								Log.i("lightbox", work.getCode() );
								Log.i("lightbox", work.getName() );
							}

							Log.i("lightbox", "-- 配列");

							User[] users = user.toArray(new User[0]);
							for( int i = 0; i < users.length; i++){
								Log.i("lightbox", users[i].getCode() );
								Log.i("lightbox", users[i].getName() );
							}

							Log.i("lightbox", "-- ArrayList<HashMap<String,Object>>");

							GenericTypeIndicator<ArrayList<HashMap<String,Object>>> t2
								= new GenericTypeIndicator<ArrayList<HashMap<String,Object>>>() {};
							ArrayList<HashMap<String,Object>> useList = dataSnapshot.getValue(t2);
							Iterator<HashMap<String,Object>> it2 = useList.iterator();
							while( it2.hasNext()) {
								HashMap<String,Object> userMap = it2.next();
								Log.i( "lightbox", userMap.get("code").toString() );
								Log.i( "lightbox", userMap.get("name").toString() );
							}
						}
						else {
							Log.i("lightbox","データを読み込めませんでした");
						}
					}

					@Override
					public void onCancelled(DatabaseError databaseError) {
						Log.i("lightbox","onCancelled");
						StringWriter sw = new StringWriter();
						PrintWriter pw = new PrintWriter(sw);
						databaseError.toException().printStackTrace(pw);
						pw.flush();
						String stackTrace = sw.toString();
						Log.i("lightbox",stackTrace);

					}
				});

			}
		});

		Button readUserButton = (Button) MainActivity.this.findViewById(R.id.readUserButton);
		readUserButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				mDatabase.child(String.format("users/%d",0)).addListenerForSingleValueEvent(new ValueEventListener() {
					@Override
					public void onDataChange(DataSnapshot dataSnapshot) {
						if ( dataSnapshot.exists() ) {
							Log.i("lightbox", dataSnapshot.getValue().toString());

							User user = dataSnapshot.getValue(User.class);

							TextView tv1 = (TextView) MainActivity.this.findViewById(R.id.textCode);
							tv1.setText(user.getCode());
							TextView tv2 = (TextView) MainActivity.this.findViewById(R.id.textName);
							tv2.setText(user.getName());
						}
					}

					@Override
					public void onCancelled(DatabaseError databaseError) {

					}
				});

			}
		});


	}

	public static class User {

		private String code;
		private String name;

		public User() {}

		public User(String code, String name) {
			this.code = code;
			this.name = name;
		}

		public String getCode() {
			return code;
		}
		public String getName() {
			return name;
		}
		public void setCode(String code) {
			this.code = code;
		}
		public void setName(String name) {
			this.name = name;
		}

	}

}


※ 印刷用 PDF
※ 印刷用 PDF(Module Gradle)

@IgnoreExtraProperties アノテーションについて



結局付けても付けなくても直接の動作には影響ありません。ただ、付けない場合はログにワーニングが表示され、付けるとワーニングは表示されません。

これは、開発中に Database 側にあって、クラス側に無いデータをクラスを使って読み込んだ時に確認できる内容になると思います。

Firebase ドキュメント : Update your Java model objects

@Exclude アノテーションについて

上のリンク先では、@Exclude についても記述されています。これを使用すると、意図的にそのフィールドを排除できます。サンプルでは、public なフィールドに設定されていますが、setter/getter に対して指定できます。

補足 : JSON データを直接使用する

データベースのルールが ".read": true になっている場合は、Firebase のプロジェクトの Database の URL で直接 JSON 文字列を使用できます。ルートは https://プロジェクトid.firebaseio.com/.json で取得でき、他のパスは、.json を拡張子として付加すると取得できます。
※ ?print=pretty というパラメータを指定できます(整形します)

▼ 公開サンプル
https://json-sample-b69b7.firebaseio.com/.json?print=pretty
https://json-sample-b69b7.firebaseio.com/users/0.json
▼ JSONLint にて整形
http://jsonlint.com/?json=https://json-sample-b69b7.firebaseio.com/.json

また、このアクセスは、Access-Control-Allow-Origin:* を返すので、jQuery で簡単に利用可能です。(ルールが要ログインの場合は、アクセストークンが必要になり、Android だけで利用するのは結構面倒になります)

関連する記事

Firebase API + Android Studio : Database にデータを保存は単純で、DatabaseReference の setValue メソッドを使用します

Firebase API + Android Studio : Database 処理の基本設定

Android Studio 2.2 で新規プロジェクトを作成すると『Could not reserve enough space for 1572864KB object heap』というエラーが出る場合の対処方法



posted by lightbox at 2016-09-26 15:45 | Comment(0) | Android Studio 2016 | このブログの読者になる | 更新情報をチェックする

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

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