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 | 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 | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする
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 ドロップシャドウの参考デモ
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり