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 になってしまう可能性があります。



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

イメージユニット作成サービス / window.open と Lightbox2 と Shadowbox

大きい画像を表示する為の選択肢として、

1) window.open
2) Lightbox2
3) Shadowbox

のいずれかを選択して貼り付けコードを作成します。試してみたい場合、lightbox や shadowbox のライブラリを意識する必要はありません。ご自分でホスティングする方法は、以下のリンク先を参考にして下さい。

Lightbox2 ライブラリの今時の使い方
このユニットは以下のコードで設置できます
<script type="text/javascript" src="http://lightbox.on.coocan.jp/js/createImageUnit.js" charset="utf-8"></script>
関連する記事

JavaScript : 入力文字列を htmlentity に変換する
( Lightbox2 では、タイトル部分に htmlentity で HTML を設定できます )


タグ:Webサービス
posted by lightbox at 2016-09-30 03:16 | WEBサービス | このブログの読者になる | 更新情報をチェックする

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) | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

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) | 2 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年09月26日


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

Firebase のデータベースは、JSON データです。読み込みはイベントを使用し、保存は API の呼び出しで直接行います。

具体的には、DatabaseReference のスーパークラス である Query の addListenerForSingleValueEvent 経由で DataSnapshot を取得してその中からデータを取得します。

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

保存は、DatabaseReference のメソッドを使用する事によって完結します。もちろん、更新された事のイベントを取得はできますが、最初のアプローチとしてはこの二つを設定するだけでいいと思います。

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

Android Studio に必要なもの

1) Google Play services を SDK マネージャからインストールしておきます。



2) Google APIs を含む System Image を SDK マネージャからインストールしておきます。

実機であれば必要ありませんが、エミュレータから実行する場合に必要です。この System Image を使用して AVD を作成しておく必要があります。


※ 経験則上、Use Host GPU をチェックしておくと、エミュレータを正しく回転してくれます。
※ 時々インストールしたアプリは削除したほうがいいです。

3) Android Studio の Settings で、Instant Run を無効にする

▼ 2.2 がリリースされたので、アップデートした場合は必要ないです


これは、Android Studio 2.2 以降で使用可能な機能の絡みで、Database 機能に直接は関係無いようですが コアなライブラリと関係しているようなので設定しておきます。

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

※ 手順確認用 PDF 

Android Studio プロジェクトの準備手順

Firebase のドキュメントページで、『Install the Firebase SDK.』という記述が出て来ますが、これは プロジェクトレベルの Gradle に 『classpath 'com.google.gms:google-services:3.0.0'』を追加する事を指します。また、目的の API によって Module レベルの Gradle にライブラリを追加する作業が必要です。

1) Firebase コンソールで プロジェクトを作成

Firebase でログインすると、右上に『コンソールへ移動』とあるのでクリックして移動します。そこで『新規プロジェクトを作成』からプロジェクトを作成します。

2) Android Studio に Firebase 用の新しいプロジェクトを作成

Firebase に『新しいアプリ』を作成して、Android Studio と関係付ける必要があるのですが、Firebase 側の登録処理で、Android Studio のパッケージ名が必要になります。

3) Android アプリに Firebase を追加

作業的には、Firebase に追加するのですが、目的として 『Android アプリから参照可能な Firebase API を追加する作業』という意味になります。Android Studio のパッケージ名を入力して進めると、google-services.json のダウンロードが始まるので、Android Studio で作成したプロジェクトの app フォルダ内へダウンロードします。



4) プロジェクト build.gradle と app build.gradle の変更


▼ クリックするとクリップボードへコピーします


上が、『Install the Firebase SDK』です。この画像には、Module(アプリ) レベルの変更として apply plugin について書いてありますが、これは最後の行です。つまりは、以下を参考にして下さい。
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "lightbox.sep.fire3"
        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'
    compile 'com.google.firebase:firebase-core:9.4.0'
    compile 'com.google.firebase:firebase-database:9.4.0'
}

apply plugin: 'com.google.gms.google-services'
※ 印刷用 PDF

firebase のライブラリとして core と database を追加しています。使用する処理によっていろいろなライブラリが用意されており、こちらから一覧を参照できます。


5) AndroidManifest の変更

インターネットアクセスが必要なので、android.permission.INTERNET を追加しておきます。

6) Firebase Database のセキュリティ設定の変更

デフォルトでは、ログイン処理をしないとアクセスできないので、最初は無条件にアクセス可能にしておきます。ルールタブから以下のように変更して下さい。



7) データの作成(サンプル)

以下のデータは、JSON 的には配列データです。Firebase では、表現上以下のようになります。途中のインデックスが飛んでしまうと、そこは null のデータが設定された事になり、インデックスとしては存在する事になります。



作成後、右上のメニューからエクスポートとして内容を確認して下さい。
{
  "users" : [ {
    "code" : "0001",
    "name" : "浦岡 友也"
  }, {
    "code" : "0002",
    "name" : "山村 洋代"
  }, {
    "code" : "0003",
    "name" : "多岡 冬行"
  } ]
}


※ 手順確認用 PDF


関連する記事

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

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

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





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

2016年09月21日


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

org.gradle.jvmargs=-Xmx1536m をコメントにすると動くようになります。

元々あったプロジェクトは普通に動作しました。その場合の gradle.properties は以下のようになっていました。
# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
新しいプロジェクトは以下のようになりました。
# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
なので、コメントにすると動くようになります。また、1536 は 1536x1024 = 1572864 で、エラーメッセージの数値です。Xmx は最大値の指定なので、設定が大きすぎるという事です。テストすると、-Xmx1380m までは動作しました。 何故こうなるのかは不明ですが、コメントにするほうが無難なような気はします。
タグ:Android Studio
posted by lightbox at 2016-09-21 21:41 | Comment(0) | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

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 | Comment(0) | 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 | Comment(0) | 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 | Comment(0) | jQuery | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

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

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

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

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


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

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

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

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

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


Windows
container 終わり

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

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