SQLの窓

2016年09月20日


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

環境とプロジェクトの作成方法は、『Firebase API + Android Studio : Database 処理の基本設定』にまとめています。そちらを参照して下さい。

データの読み出し方法については、『Firebase API + Android Studio : Database のデータを Java に取得する方法は3通りあります』をご覧ください。

DatabaseReference で更新場所の参照を作成

child メソッドでパス文字列を渡すか、child メソッドの戻り値も DatabaseReference なので、さらに child メソッドでツリーをチェーンして参照を作成します。データベースへの更新は、基本的にこの DatabaseReference で参照した場所に対して setValue を実行する事によって、サーバへ反映されます。

setValue の引数の形式

単純な setValue は、Object を引数として一般的なオブジェクトに加えて、以下のデータ型を使用する事ができます。
Boolean
Long
Double
Map<String, Object>
List<Object>
また、setValue に対してのサーバーのステータスは、意図的にイベントを追加する必要がありますが、多くの場合そこまでの処理を行う必要は無いと思います。ただ、パーミッションによって、アクセスできない事を想定した処理が必要な場合は以下のようにします。 databaseError が null の場合、正常に更新されているはすです。一つ目の参照は、fri に対する更新の DatabaseReference が返って来るので、更新した内容のみ表示しています。二つ目の参照は、getParent によって、データ全体の内容を表示しています。 これに対して、書き込み結果を考慮しない場合は以下のように単純になります。 setValue の戻り値(Task)よりイベントを作成する 成功か失敗かを知りたいだけならば、こつらのほうがすっきりした印象があります。もちろん、task より、Exception を知る事ができるので、再度の参照を行わないのであればイベント内だけで全てが完結します。 データの削除 データの削除は、setValue に null をセットする事によって 簡潔に実行できます。但し、誤ってツリーの途中を削除してしまうと、その下位にあるデータは全て削除されるので注意が必要です クラスによる保存 データの構造を クラスで定義して、そのクラスを使用して保存する事ができます。データは、public なフィールドまたは、setter / getter で定義します。さらに、@Exclude アノテーションによって、メソッドの実行対象からフィールドを除外する事ができます。
public class MyUser {

	public String code;
	public String name;
	public String furi;
	public long kyuyo;
	private long teate = 10000;

	public MyUser() {}

	public MyUser(String code, String name, String furi, long kyuyo) {
		this.code = code;
		this.name = name;
		this.furi = furi;
		this.kyuyo = kyuyo;
		this.teate = 0;
	}

	long getTeate(){
		return this.teate;
	}
	@Exclude
	void setTeate(long teate){
		this.teate = teate;
	}

}


push メソッドによる、ユニークキーの作成

ツリーにデータ構造をクラスで保存する際、ユニークなキーをサーバ側で追加して、その下にデータを保存できます。



▼ 結果サンプル


ArrayList とクラスを使用した一括更新

他のWEBから取得して JSON フォーマットのデータを、Google Gson を使用してデシリアライズ(fromJson)して更新に使用できると思います。



updateChildren メソッドよる、HashMap での更新

固定フォーマットではない場合、HashMap を使用する事によって、自由度の高いデータを作成する事もできます






ソースコード
public class MainActivity extends AppCompatActivity {

	private DatabaseReference mDatabase;

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

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

		Button saveButton1 = (Button) MainActivity.this.findViewById(R.id.saveButton1);
		saveButton1.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox","単純データ保存");

				// 書き込み結果を考慮しない
				mDatabase.child("users/3/code").setValue("0004");
				mDatabase.child("users/3/kyuyo").setValue(10000);

				// setValue の戻り値(Task<Void>) の addOnCompleteListener で処理を行う
				// ( 通常、パーミッションによる書き込みの失敗を対処 )
				mDatabase
					.child("users")
					.child("3")
					.child("name")
					.setValue("高田 冬美")
					.addOnCompleteListener(new OnCompleteListener<Void>() {
					@Override
					public void onComplete(@NonNull Task<Void> task) {
						if (task.isSuccessful()) {
							Log.i("lightbox", "書き込みに成功しました");
							Log.i("lightbox", task.toString());
						}
						else {
							Log.i("lightbox", task.getException().toString());
							task.getException().printStackTrace();
						}
					}
				});

				// setValue の第二引数にイベントを登録する
				// ( 通常、パーミッションによる書き込みの失敗を対処 )
				mDatabase
					.child("users")
					.child("3")
					.child("fri")
					.setValue("タカタ フユミ", new DatabaseReference.CompletionListener() {
						@Override
						public void onComplete(DatabaseError databaseError,
											   DatabaseReference databaseReference) {
							if (databaseError == null) {
								Log.i("lightbox", "書き込みに成功しました");
								Log.i("lightbox", databaseReference.toString());

								databaseReference
									.addListenerForSingleValueEvent(new ValueEventListener() {
									@Override
									public void onDataChange(DataSnapshot dataSnapshot) {
										Log.i("lightbox",dataSnapshot.getValue().toString());
									}
									@Override
									public void onCancelled(DatabaseError databaseError) {

									}
								});

								databaseReference.getParent()
									.addListenerForSingleValueEvent(new ValueEventListener() {
									@Override
									public void onDataChange(DataSnapshot dataSnapshot) {
										Log.i("lightbox",dataSnapshot.getValue().toString());

										MyUser user = dataSnapshot.getValue(MyUser.class);
										Log.i("lightbox",String.format("%s",user.code));
										Log.i("lightbox",String.format("%d",user.getTeate()));

									}
									@Override
									public void onCancelled(DatabaseError databaseError) {

									}
								});

							}
							else {
								Log.i("lightbox", databaseError.toString());
								databaseError.toException().printStackTrace();
							}
						}
					});

			}
		});

		Button deleteButton1 = (Button) MainActivity.this.findViewById(R.id.deleteButton1);
		deleteButton1.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox","インデックスでエントリを削除");

				mDatabase.child("users").child("3").setValue(null);
				mDatabase.child("post").setValue(null);
				mDatabase.child("users").child("4/furi").setValue(null);

			}
		});

		Button saveButton2 = (Button) MainActivity.this.findViewById(R.id.saveButton2);
		saveButton2.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox","MyUserクラスによるデータ保存");

				MyUser user = new MyUser("0005","内高 友之","ウチタカ トモユキ",150000);
				mDatabase.child("users/4").setValue(user);

			}
		});

		Button saveButton3 = (Button) MainActivity.this.findViewById(R.id.saveButton3);
		saveButton3.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox","push and MyUser");

				DatabaseReference treeKey = mDatabase.child("post").push();
				MyUser user = new MyUser("0005","内高 友之","ウチタカ トモユキ",150000);
				treeKey.setValue(user);

			}
		});

		Button saveButton4 = (Button) MainActivity.this.findViewById(R.id.saveButton4);
		saveButton4.setAllCaps(false);
		saveButton4.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox","Hash Map");

				HashMap<String, Object> entry = new HashMap<String, Object> ();
				entry.put("code", "A001");
				entry.put("name", "山田 太郎");
				entry.put("kyuyo", 100000);

				DatabaseReference treeKey = mDatabase.child("post/entry").push();
				treeKey.updateChildren(entry);

			}
		});

		Button saveButton5 = (Button) MainActivity.this.findViewById(R.id.saveButton5);
		saveButton5.setAllCaps(false);
		saveButton5.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox","Array List");

				ArrayList<MyUser> userList = new ArrayList<MyUser>();
				DatabaseReference listTree = mDatabase.child("post/list");
				userList.add(new MyUser("0001","内高 001","ウチタカ 001",10000));
				userList.add(new MyUser("0002","内高 002","ウチタカ 002",20000));
				userList.add(new MyUser("0003","内高 003","ウチタカ 003",30000));
				listTree.setValue(userList);

			}
		});

	}
}



関連する記事

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

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

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




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

2016年09月16日


Google Chrome で IFRAME を display:none で消してから表示すると、スクロールバーが消える。仕方ないので、visibility:hidden で代替



Google Chrome は、現在 53.0.2785.116 m です。Google Chrome は時々こういうバグが出ますが、そのうち修正されると思います。

webkit 的な css で 対処方法をいろいろ探ったのですが対応できなかったので、visibility:hidden で代替し、visibility:hidden だとその場所のスペースがそのままになるので、position:absolute を併用しました。

※ 他のブラウザでは display:none は 正しく動作します



上の IFRAME は 再表示するとスクロールバーが消えてしまいます(Google Chrome 53.0.2785.116 m)

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<input type="button" id="hide" value="display:none">
<input type="button" id="show" value="display:"><br>
<iframe
	id="iframe_target"
	src="about:blank"
	name="myframe"
	width="400"
	height="200"
	style='border:solid #000000 1px;'
></iframe>
<br>
<input type="button" id="hide2" value="visibility:hidden">
<input type="button" id="show2" value="visibility:visible"><br>
<div style='position:relative'>
<iframe
	id="iframe_target2"
	src="about:blank"
	name="myframe"
	width="400"
	height="200"
	style='border:solid #000000 1px;'
></iframe>
</div>
上の IFRAME は 再表示するとスクロールバーが消えてしまいます(Google Chrome 53.0.2785.116 m)
<script>
str="";
str+="<img src=\"https://lh3.googleusercontent.com/-c4ugzdOcnSU/VRt3mbqzu0I/AAAAAAAAZXU/41DpjImnF7ILujQmvRRRcok0rgIaYCptACHM/s400/b17brandon016.jpg\"> ";

$("#iframe_target").get(0).contentWindow.document.write(str);
$("#iframe_target").get(0).contentWindow.document.close();
$("#iframe_target2").get(0).contentWindow.document.write(str);
$("#iframe_target2").get(0).contentWindow.document.close();

$("#hide").on("click", function(){
	$("#iframe_target").css("display", "none");	
});
$("#show").on("click", function(){
	$("#iframe_target").css("display", "");	
});

$("#hide2").on("click", function(){
	$("#iframe_target2").css({"visibility": "hidden","position":"absolute"});	
});
$("#show2").on("click", function(){
	$("#iframe_target2").css({"visibility": "visible","position":"static"});	
});

</script>


IFRAME 内のコンテンツを document.write で作成すると、同一ドメインとして IFRAME 内を参照できます



posted by lightbox at 2016-09-16 04:33 | Google Chrome | このブログの読者になる | 更新情報をチェックする

2016年09月09日


jQuery と FileReader オブジェクトによる、ローカルの複数画像ファイルのプレビュー表示

INPUT 要素の type="file" 属性に加えて multiple 属性を追加する事によって、ローカルのファイルを複数選択が可能になります。

ここでは、画像表示を前提としていますが、ファイルの種類を accept 属性によって指定が可能です。指定方法もいろいろあるので、コンボボックスで選択可能にしました。
(ファイルを選択するダイアログで、accept 属性に指定したファイルグループが作成されて選択された状態になります)

ファイル選択後の処理の流れ

ファイルを選択後、INPUT 要素のオブジェクトの files プロパティに複数のファイルの情報がセットされるので、その配列に対して for ループ処理を作成します。

一回のループ毎に FileReader オブジェクトを作成して、それに対するイベントを登録します。そして、その FileReader オブジェクトに readAsDataURL で、INPUT 要素が保持する各ファイルの blob オブジェクトをセットします。

そうすると、イベントが実行されて、jQuery の appendTo メソッドによって、それぞれの画像用の img 要素が作成されて行きます。

イベント内でオリジナルファイル名を参照する

FileReader オブジェクトに name プロパティとして this.files[i].name をセットしておくと、後から this で参照が可能になります( ここでは title 属性にセットしました )

<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
$(function(){
	$("#myfile").on("change", function(){

		$("#images").html("");
		for( i = 0; i < this.files.length; i++ ) {

			// FileReader は毎回作成(同時に複数のファイルを扱えない)
			var reader = new FileReader();
			// オリジナルファイル名をプロパティとして追加しておく
			reader.name = this.files[i].name;

			// FileReader に画像が読み込まれた時のイベント
			$(reader).on("load", function () {
				// div の中に img 要素を追加してその都度 this.result(ArrayBuffer) をセット
				$("<img>").appendTo("#images")
				.prop( {"src": this.result, "title": this.name } )	// title にはオリジナルファイル名
				.css("width", "100px");
			});

			// 上記イベントを発動するための処理( this.files[i] は blob )
			if (this.files[i]) {
				reader.readAsDataURL(this.files[i]);
			}
		}

	});

	// ファイルの種類の選択
	$("#accept").on("change", function(){
		$("#myfile").prop("accept", $(this).val() );
	});
});

</script>
<select id="accept">
<option value="image/jpeg">MIME 指定 image/jpeg</option>
<option value="text/plain">MIME 指定 text/plain</option>
<option value=".txt,.jpg,.png,.zip">拡張子指定 .txt,.jpg,.png,.zip</option>
<option value="image/*">画像全て image/*</option>
<option value="video/*">動画全て video/*</option>
<option value="audio/*">音声全て audio/*</option>
</select>
<br>
<input type="file" id="myfile" multiple accept="image/jpeg">
<div id="images"></div>



デモ実行



タグ:jquery FileReader
posted by lightbox at 2016-09-09 17:18 | jQuery | このブログの読者になる | 更新情報をチェックする
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 終わり