SQLの窓

2016年10月28日


iPhone を EpocCam というアプリで Wi-Fi の WEBカメラにする場合のいろいろ知っておく事。

WebRTC(iOS上のブラウザでは使えません) で、いろいろアプリをテストする上での一つのツールとして、使ってない iPhone4S を WEBカメラにしてみて解った情報です。(Android は DroidCam のほうがいいので詳細は後日調べる予定 / クライアントソフトはこちら)

このアプリ、USB は使えないですし、USB のWEBカメラは安いのがいくらでもあるので、自分の家の特定位置の監視カメラとして使う以外はメリットはあまりありません。(外部からのアクセスは無理です。外部からは、Android 系の IPカメラアプリとルータ設定でできるかもしれません:未確認)

そもそも、古い忘れられたソフトだと思われるので、古い iPhone(iOS8.0以降) 以外で試してみようとは思わないほうがいいと思います。広告も結構大きいのが出て画面ふさぎますし

(その間も WEBカメラとしては使えるので、定点監視カメラなら広告は関係ないです)。

EpocCam のダウンロードとインストール

アプリの更新は、2015年10月17日よりされていません。インターネットで検索しても探すのに一苦労します。

こちらから PC 用のソフトをダウンロードしてインストールします。(自分の環境は Windows7 で、32ビットですが、ダウンロード時の選択はありませんでした)


インストールオプションとして、Barcode Reader を同時にインストールしようとするので、チェックを外す必要があります。

インストール時にいろいろ警告が出ました。たぶん、インターネットアクセスに対しての信頼性の警告だと思いますが、何が起きてもかまわないテストマシンでやってるのでインストールを続行しました。気になる方は、この時点で使う事をあきらめたほうがいいと思います。また、自分以外の環境では警告が出ない可能性もあるので、なんとも言い切れません。

iPhoneアプリのほうは、クライアントの確認が終わってから、インストールして開けばいいです。(当然ですが、Wi-Fi が使える必要があります。USB が使えるような雰囲気がネットではありますが、どうも USB は対応をやめたようです)


サービスが登録されて自動起動になるので注意



Bonjour は、Apple 系アプリ用のサービスなので、既に入っている可能性もあります。しかし、新たに入る場合だと、サービスは当然停止設定にして必要な時だけ起動します。



あと、開発元の Kinoni のサービスも自動起動されるので、停止設定にして必要な場合にのみ起動します。

付属のビュアーは必要無いので起動しないように

単なる、動作確認用です。起動していると肝心の WEB カメラの画像が劣化します。

ファイアーウォールの設定が必要な場合もあります



こうなると専門知識無いとどうにもなりませんが、PC のファイヤーウォールの設定を行うか、ファイヤーウォールを止めてとりあえず動作確認するかです。

昔は USB で使えたであろう残骸



ソースを見ると USB の文字が残ってますが、表示内にはありません。検索用のキーワードにも残ってますし、適当にアップデートした感満載です。ま、でも USB で使うメリットは現在コスト的にほぼ無いので開発をやめるのは当然でしょう。Wi-Fi 使っても十分速いわけですし(ビュアー起動するとアウトですが)

で、上のソース場所は表から見ると以下のようになっています。


※ 何故か、Debut Video Capture software による動作確認となっています。

さて、実行画面



一番下の英文は、x で閉じれます。最初、雷マークが出てタッチすると、なんか説明みたいなのが出ます。矢印は反転指定で、昔は逆になったままだとかネットで見かけましたが、これで正しくできるようになっています。

通常運用では、以下のように上部に広告が出て、何かのタイミング( たぶん画面にユーザがアクセスした時 )で、画面いっぱいの広告が日本語で出たり英語で出たりします。基本、左上の x 閉じるのですが、中には広告が何枚も移り変わって最後にならないと閉じれない場合もあります。



だから、リアルタイムの WEB カメラには使えません

最後に、Google Chrome からの画面です



※ 玄関で定点カメラテスト


posted by lightbox at 2016-10-28 13:45 | Comment(0) | WEBブラウザ | このブログの読者になる | 更新情報をチェックする

2016年10月25日


Firebase storage に画像をアップロードする。1) ギャラリーから、2) 実行中の画面 / Android

Firebase の環境とプロジェクトの作成方法

9.6.1 を使用する場合
エミュレータ : SDK Manager で最新に更新
実機 : Google Play 開発者サービスを最新に更新

Firebase ドキュメント : Android でファイルをアップロードする
実行中の画面は、putBytes で、ギャラリーは putFile でアップロード可能です(putStream もあります)。Firebase storage のルールはテスト時のみ、allow write: if true; に変更して行いました。 Firebase リファレンス : StorageReference
public class MainActivity extends AppCompatActivity {

	private static final int READ_REQUEST_CODE = 42;

	private FirebaseStorage storage;
	private StorageReference storageRef;
	private StorageReference imageRef;
	private ProgressDialog progress;

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

// ▼ テスト用のルール
//		service firebase.storage {
//			match /b/freebase-654b7.appspot.com/o {
//				match /{allPaths=**} {
//					allow read;
//					allow write: if true;
//				}
//			}
//		}

		// アップロード中のダイアログ
		progress = new ProgressDialog(MainActivity.this);

		storage = FirebaseStorage.getInstance();
		storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/");

		// 現在の画面をアップロード( View )
		Button captureButton = (Button) MainActivity.this.findViewById(R.id.captureButton);
		captureButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// View から Bitmap 取得
				View view = v.getRootView();	// 画面全体
				view.setDrawingCacheEnabled(true);
				Bitmap cache = view.getDrawingCache();
				Bitmap rootViewCapture = Bitmap.createBitmap(cache);
				view.setDrawingCacheEnabled(false);

				// 画像アップロード用パス決定
				Calendar cal = Calendar.getInstance();
				SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
				String uploadImagePath = String.format("image/%s.png",sf.format(cal.getTime()));
				imageRef = storageRef.child(uploadImagePath);

				// byte[] に変換
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				rootViewCapture.compress(Bitmap.CompressFormat.PNG, 100, baos);
				byte[] data = baos.toByteArray();

				// アップロード中の表示
				progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				progress.setMessage("画像をアップロードしています");
				progress.show();

				UploadTask uploadTask = imageRef.putBytes(data);
				uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
					@Override
					public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
						progress.dismiss();

						Log.i("lightbox","アップロードに成功しました");
						long size = taskSnapshot.getMetadata().getSizeBytes();
						Log.i("lightbox",String.format("サイズ : %d",size));

					}
				}).addOnFailureListener(new OnFailureListener() {
					@Override
					public void onFailure(@NonNull Exception e) {
						progress.dismiss();

						Log.i("lightbox","アップロードに失敗しました");
					}
				});
			}
		});

		// ギャラリーの画像をアップロード
		Button galleryButton = (Button) MainActivity.this.findViewById(R.id.galleryButton);
		galleryButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
				intent.addCategory(Intent.CATEGORY_OPENABLE);
				intent.setType("image/*");
				startActivityForResult(intent, READ_REQUEST_CODE);
			}
		});

	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		// ギャラリーからの戻り
		if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
			Uri uri = null;
			if (data != null) {
				// Uri が戻されます
				uri = data.getData();
				Log.i("lightbox", "Uri: " + uri.toString());

				// 画像アップロード用パス決定
				ContentResolver contentResolver = MainActivity.this.getContentResolver();
				Cursor cursor = contentResolver.query(uri, null, null, null, null);
				cursor.moveToFirst();
				// とりあえず固定( 2 => _display_name )
				String name = cursor.getString(2);
				Log.i("lightbox", "FileName: " + name);

				//この カーソルの詳細
				String[] columnName = cursor.getColumnNames();
				for(int i = 0; i < columnName.length; i++) {
					Log.i("lightbox", String.format("%d : %s : %s",i, columnName[i], cursor.getString(i)));
				}

				String uploadImagePath = String.format("image/%s",name);
				imageRef = storageRef.child(uploadImagePath);

				// アップロード中の表示
				progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				progress.setMessage("画像をアップロードしています");
				progress.show();

				// 画像アップロード
				UploadTask uploadTask = imageRef.putFile(uri);
				uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
					@Override
					public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
						progress.dismiss();

						Log.i("lightbox","アップロードに成功しました");
						long size = taskSnapshot.getMetadata().getSizeBytes();
						Log.i("lightbox",String.format("サイズ : %d",size));

					}
				}).addOnFailureListener(new OnFailureListener() {
					@Override
					public void onFailure(@NonNull Exception e) {
						progress.dismiss();

						Log.i("lightbox","アップロードに失敗しました");
					}
				});

			}

		}
	}

}



build.gradle(Project)
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


build.gradle(Module: app)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "lightbox.sep.fire1"
        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.6.1'
    compile 'com.google.firebase:firebase-storage:9.6.1'
    compile 'com.google.firebase:firebase-auth:9.6.1'
}

apply plugin: 'com.google.gms.google-services'
※ AndroidManifest.xml には、android.permission.INTERNET を設定


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

Android : 画像関連のテスト用カメラアプリ

カメラそのものをどうこうしたいのでは無く、他の機能(Firebase storage 等)との連携テストに使いたいので、いわゆる旧式のカメラ API を使用しています。

▼ Android Studio では 『非推奨』と言われます


また、撮影は AndroidManifest.xml で縦の portrait 固定にして行う為、実際に横にした時の判定にセンサを使い、縦の時は 90度回転して画像を保存しなおしています。テストの実機は 4.4.4 のみですが、そもそもテスト用なので全ての対応はソース次第で好きに変更できます。

いろいろサンプルを探したのですが、全方向に対応したものが無かったので機能を集めました。

※ センサの判定はかなり適当です

MyCamera クラス
public class MyCamera implements SurfaceHolder.Callback {

	// 古いカメラAPI。カメラで何かしたいわけでは無いのでこれで実装
	private Camera camera;
	private SurfaceHolder holder;
	private Camera.Size size;
	private MainActivity mainActivity;
	private SurfaceView cameraView;
	private List<Camera.Size> sizeList;

	public MyCamera(Context context) {

		Log.i("lightbox", "コンストラクタ");

		this.mainActivity = (MainActivity) context;

		// MainActivity の画面のSurfaceView を使う
		cameraView = (SurfaceView)mainActivity.findViewById(R.id.cameraView);
		// SurfaceView から holder を取り出して、カメラに渡す
		holder = cameraView.getHolder();
		// この中でイベント処理を行う
		holder.addCallback(MyCamera.this);

	}

	public Camera getCamera() {

		return camera;

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {

		Log.i("lightbox", "開く");

		// カメラを開く
		camera = Camera.open();
		try {
			// カメラに holder を渡す
			camera.setPreviewDisplay(holder);
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 縦固定(AndroidManifest.xml) で、portrait 撮影を前提
		camera.setDisplayOrientation(90);

		// カメラの情報
		Camera.Parameters params = camera.getParameters();
		// サポートされているサイズの一覧
		sizeList = params.getSupportedPreviewSizes();
		// 対応しているサイズの先頭のサイズ
		size = sizeList.get(0);

	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

		// 縦固定(AndroidManifest.xml) なので一度しか通らない

		Log.i("lightbox", "開始");

		// 縦横サイズの設定
		Camera.Parameters params = camera.getParameters();
		// ギャラリーでは縦になるが、保存画像は横
		params.setRotation(90);
		Camera.Size optimalSize = getOptimalPreviewSize(sizeList,width,height);
		params.setPreviewSize(optimalSize.width,optimalSize.height);
		camera.setParameters(params);

		camera.startPreview();

	}

	// http://qiita.com/zaburo/items/b5d3815d3ec45b0daf4f
	private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

		final double ASPECT_TOLERANCE = 0.1;
		double targetRatio=(double)h / w;

		if (sizes == null) return null;

		Camera.Size optimalSize = null;
		double minDiff = Double.MAX_VALUE;

		int targetHeight = h;

		for (Camera.Size size : sizes) {
			double ratio = (double) size.width / size.height;
			if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
			if (Math.abs(size.height - targetHeight) < minDiff) {
				optimalSize = size;
				minDiff = Math.abs(size.height - targetHeight);
			}
		}

		if (optimalSize == null) {
			minDiff = Double.MAX_VALUE;
			for (Camera.Size size : sizes) {
				if (Math.abs(size.height - targetHeight) < minDiff) {
					optimalSize = size;
					minDiff = Math.abs(size.height - targetHeight);
				}
			}
		}
		return optimalSize;
	}


	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {

		Log.i("lightbox", "終了");

		// プレビュー終了
		if ( camera != null ) {
			camera.stopPreview();
			camera.release();
			camera = null;
		}

	}
}


MainActivity
public class MainActivity extends AppCompatActivity implements SensorEventListener {

	private String imagePath;
	private MyCamera camera;
	private SensorManager sensor;
	private Sensor acc;
	private boolean portrait;

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

		camera = new MyCamera(MainActivity.this);
		sensor = (SensorManager)getSystemService(SENSOR_SERVICE);
		acc = sensor.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		sensor.registerListener(MainActivity.this,acc,SensorManager.SENSOR_DELAY_NORMAL);
		portrait = true;

		// *************************************
		// ギャラリーに保存
		// *************************************
		Button galleryButton = (Button) MainActivity.this.findViewById(R.id.galleryButton);
		galleryButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox", "クリック");

				Camera.Parameters params = camera.getCamera().getParameters();
				if ( portrait ) {
					// ギャラリーでは縦になるが、保存画像は横
					params.setRotation(90);
				}
				else {
					// ギャラリーで横、保存画像も横
					params.setRotation(0);
				}
				camera.getCamera().setParameters(params);


				// 撮影
				camera.getCamera().takePicture(null, null, new Camera.PictureCallback() {
					@Override
					public void onPictureTaken(byte[] data, Camera camera) {

						if (data != null) {

							// ギャラリー用に内部ストレージにフォルダを作成
							String firebaseImageDir = Environment.getExternalStorageDirectory().getPath() + "/firebase";
							File file = new File(firebaseImageDir);
							// ディレクトリ初期作成
							if (!file.exists()) {
								if (file.mkdir() == false) {
									Log.i("lightbox", "ディレクトリを作成できませんでした");
									return;
								}
							}

							// ギャラリー用画像保存パス
							Calendar cal = Calendar.getInstance();
							SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
							imagePath = firebaseImageDir + "/" + sf.format(cal.getTime()) + ".jpg";

							FileOutputStream jpg;
							try {
								jpg = new FileOutputStream(imagePath);
								if ( portrait ) {
									// 縦の場合、回転して保存する
									Bitmap bmp1 = BitmapFactory.decodeByteArray (data, 0, data.length);
									int width = bmp1.getWidth();
									int height = bmp1.getHeight();
									Matrix matrix = new Matrix();
									matrix.postRotate (90);
									Bitmap bmp2 = Bitmap.createBitmap (bmp1, 0, 0, width, height, matrix, true);

									bmp2.compress(Bitmap.CompressFormat.JPEG,90,jpg);
								}
								else {
									jpg.write(data);
									jpg.close();
								}

								// ギャラリーに反映
								MediaScannerConnection.scanFile(
									MainActivity.this,
									new String[] { imagePath },
									new String[] { "image/jpeg" },
									null);

							} catch (Exception e) {
								e.printStackTrace();
							}

							// イベントの引数のカメラ
							camera.startPreview();

						}
					}
				});

			}
		});

	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {

	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		// センサで実際の縦と横を判定
		if ( -4 <= event.values[0] && event.values[0] <= 4  ) {
			portrait = true;
		}
		else {
			portrait = false;
		}
	}

	public boolean getPortrait (){
		return portrait;
	}

	@Override
	protected void onResume() {
		super.onResume();
		sensor.registerListener(MainActivity.this,acc,SensorManager.SENSOR_DELAY_NORMAL);

	}

	@Override
	protected void onPause() {
		super.onPause();
		sensor.unregisterListener(this);
	}


}


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="lightbox.sep.fire1"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

    </application>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/>

</manifest>



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

2016年10月21日


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

▼ 記事内のソースコードへ移動
@BindingAdapter でカスタムセッター
Syain クラスへ直接セットして表示
Google Gson と tools.jar を使用した REST API 経由の読み込み
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

@{user.birthday} が app:dateText にセットされるよう画面で定義されているので、birthday が setDateText の引数として渡されます。
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>

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="16dp">

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

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/textView2"
                android:layout_marginTop="20dp"
                android:textSize="40dp"
                android:text="@{user.name}"/>

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

        </LinearLayout>

</layout>

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

MainActivity.java

最も単純なバインドのサンプルです。Syain クラスはコンストラクタも無く、JSON のプロパティは、public の同名の変数にセットされます。コメント部分は、JSON 文字列から Syain のインスタンスを作成して使用するパターンです( Google Gson は コンストラクタを使用しません )
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 {

	private 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 {

	private ActivityMainBinding binding;

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

		// Data Binding 仕様の画面表示
		binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);

		// アクセスする URL 
		String url = "https://freebase-44e8b.firebaseio.com/class/0001.json?print=pretty";
		// URL から読み込み
		Tools.callHttpGet(url, "utf-8", new Tools.OnAsyncTaskListener() {
			@Override
			public void onAsyncTaskListener(String s) {
				Log.i("lightbox", s);
				Gson gson = new Gson();
				// JSON 文字列を Syain クラスのインスタンスに変換
				Syain user = gson.fromJson(s,Syain.class);
				// Data Binding で画面にデータをセット
				binding.setUser(user);
			}
		});

	}
}
Firebase Database REST API のドキュメント
Google Gson のダウンロード
tools.jar のダウンロード


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

	private ActivityMainBinding binding;
	private FirebaseDatabase database;
	private DatabaseReference mDatabase;

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

		// Data Binding 仕様の画面表示
		binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main);

		// Firebase database 参照用
		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		// データの参照
		mDatabase.child("class/0001").addListenerForSingleValueEvent(new ValueEventListener() {
			@Override
			public void onDataChange(DataSnapshot dataSnapshot) {
				// 取得したデータを Syain クラスのインスタンスとして取得
				Syain user = dataSnapshot.getValue(Syain.class);
				// Data Binding で画面にデータをセット
				binding.setUser(user);

			}

			@Override
			public void onCancelled(DatabaseError databaseError) {
				// Firebase database の参照に失敗した時の処理
				// ※ 参照権限がない場合等
			}
		});

	}
}

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




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

2016年10月18日


Firebase storage の画像を ファイルとしてダウンロードして ギャラリーに保存する( ImageView にも表示する ) / Android

Firebase の環境とプロジェクトの作成方法

9.6.1 を使用する場合
エミュレータ : SDK Manager で最新に更新
実機 : Google Play 開発者サービスを最新に更新

Firebase ドキュメント : ローカル ファイルにダウンロードする
MainActivity galleryFile に直接ダウンロードすると簡単ですが、createTempFile によって、アプリのキャッシュにいったん保存して処理したほうが、いろんな情報と関連付けできるのでそうしています。キャッシュに作成したファイルは最後で削除はしていますが、アプリの設定画面の『キャッシュを消去』でクリアできました。 ※ ダウンロードする画像のタイプはここではチェックしていません
public class MainActivity extends AppCompatActivity {

	// ダウンロード用
	private FirebaseStorage storage;
	private StorageReference storageRef;
	private StorageReference imageRef;
	private ImageView imageView;
	private File galleryFile;
	private String imagePath;
	private File localFile;
	private ProgressDialog progress;

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

// ▼ テスト用のルール
//		service firebase.storage {
//			match /b/freebase-654b7.appspot.com/o {
//				match /{allPaths=**} {
//					allow read;
//					allow write: if true;
//				}
//			}
//		}

		// ダウンロードするファイル
		storage = FirebaseStorage.getInstance();
		storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/");
		imageRef = storageRef.child("sworc2.png");

		// 表示する場所
		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);

		// ダウンロード中のダイアログ
		progress = new ProgressDialog(MainActivity.this);

		// *************************************
		// ギャラリーにダウンロードして表示
		// *************************************
		Button galleryButton = (Button) MainActivity.this.findViewById(R.id.galleryButton);
		galleryButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// ダウンロード中の表示
				progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				progress.setMessage("画像をダウンロードしています");
				progress.show();

				// ギャラリー用に内部ストレージにフォルダを作成
				String firebaseImageDir = Environment.getExternalStorageDirectory().getPath() + "/firebase";
				File file = new File(firebaseImageDir);
				// ディレクトリ初期作成
				if (!file.exists()) {
					if (file.mkdir() == false) {
						Log.i("lightbox", "ディレクトリを作成できませんでした");
						return;
					}
				}

				// ギャラリー用画像保存パス
				Calendar cal = Calendar.getInstance();
				SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
				imagePath = firebaseImageDir + "/" + sf.format(cal.getTime()) + ".png";
				galleryFile = new File(imagePath);

				localFile = null;
				try {
					// 第三引数省略時、System.getProperty("java.io.tmpdir") に作成
					// ( このアプリの キャッシュ )
					// 例) /data/data/lightbox.sep.fire1/cache
					localFile = File.createTempFile("sworc2", "png");
				} catch (IOException e) {
					e.printStackTrace();
				}
				if ( localFile == null ) {
					Log.i("lightbox","テンポラリファイルを作成できませんでした");
					return;
				}

				Log.i("lightbox","実際の TempFile のパス");
				Log.i("lightbox",localFile.getAbsolutePath());

				// ファイルにダウンロード
				imageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
					@Override
					public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {

						// ダウンロード中の表示解除
						progress.dismiss();

						// ダウンロードした localFile をファイルコピー
						try {
							FileInputStream inStream = new FileInputStream(localFile);
							FileOutputStream outStream = new FileOutputStream(galleryFile);
							FileChannel inChannel = inStream.getChannel();
							FileChannel outChannel = outStream.getChannel();
							inChannel.transferTo(0, inChannel.size(), outChannel);
							inStream.close();
							outStream.close();
						} catch (IOException e) {
							e.printStackTrace();
						}

						Log.i("lightbox","キャッシュ内の一覧");
						File[] files = new File(System.getProperty("java.io.tmpdir")).listFiles();
						for(int i = 0; i < files.length; i++){
							Log.i("lightbox",files[i].getAbsolutePath());
						}

						// ギャラリーに反映
						MediaScannerConnection.scanFile(
							MainActivity.this,
							new String[] { imagePath },
							new String[] { "image/png" },
							null);

						// ImageView に表示
						FileInputStream fis = null;
						try {
							fis = new FileInputStream(localFile);
							if ( fis != null ) {
								Bitmap image = BitmapFactory.decodeStream(fis);
								imageView.setImageBitmap(image);
								// キャッシュファイルの削除
								localFile.delete();
							}
						} catch (FileNotFoundException e) {
							e.printStackTrace();
						}

					}
				}).addOnFailureListener(new OnFailureListener() {
					@Override
					public void onFailure(@NonNull Exception e) {
						Log.i("lightbox","ダウンロードできませんでした");
						Log.i("lightbox",e.getMessage());
						// ダウンロード中の表示解除
						progress.dismiss();
						// メッセージ表示
						Toast.makeText(MainActivity.this,"ダウンロードに失敗しました",Toast.LENGTH_LONG).show();

					}
				});

			}
		});

	}
}

テンポラリファイルを使わずにダイレクトに保存するソースコード

build.gradle(Project)
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.1'
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


build.gradle(Module: app)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "lightbox.sep.fire1"
        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.6.1'
    compile 'com.google.firebase:firebase-storage:9.6.1'
    compile 'com.google.firebase:firebase-auth:9.6.1'
}

apply plugin: 'com.google.gms.google-services'
※ AndroidManifest.xml には、android.permission.INTERNET を設定
※ AndroidManifest.xml には、android.permission.WRITE_EXTERNAL_STORAGE を設定

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

Firebase storage の画像の URL を取得して、通常と同様に Stream でダウンロードして ImageView に表示する / Android

Firebase の環境とプロジェクトの作成方法

9.6.1 を使用する場合
エミュレータ : SDK Manager で最新に更新
実機 : Google Play 開発者サービスを最新に更新

Firebase ドキュメント : URL 経由でデータをダウンロードする
getDownloadUrl メソッドで Uri を取得した後は、通常どおり AsyncTask 内で Stream で読み込みます。 MainActivity
public class MainActivity extends AppCompatActivity {

	// ダウンロード用
	private FirebaseStorage storage;
	private StorageReference storageRef;
	private StorageReference imageRef;
	private ImageView imageView;
	private ProgressDialog progress;

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

// ▼ テスト用のルール
//		service firebase.storage {
//			match /b/freebase-654b7.appspot.com/o {
//				match /{allPaths=**} {
//					allow read;
//					allow write: if true;
//				}
//			}
//		}

		// ダウンロードするファイル
		storage = FirebaseStorage.getInstance();
		storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/");
		imageRef = storageRef.child("sworc2.png");

		// 表示する場所
		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);

		// ダウンロード中のダイアログ
		progress = new ProgressDialog(MainActivity.this);

		// *************************************
		// 表示2 ( Uri を取得 => Stream )
		// *************************************
		Button button = (Button) MainActivity.this.findViewById(R.id.downloadButton);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// ダウンロード中の表示
				progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				progress.setMessage("画像をダウンロードしています");
				progress.show();

				// ダウンロード用の URL を取得
				imageRef.getDownloadUrl()
					.addOnSuccessListener(new OnSuccessListener<Uri>() {
						@Override
						public void onSuccess(Uri uri) {
							Log.i("lightbox","ダウンロード開始");

							new AsyncTask<Uri, Void, Bitmap>(){
								@Override
								protected Bitmap doInBackground(Uri... params) {

									Uri uri = params[0];
									Log.i("lightbox",uri.toString());
									URL url = null;
									try {
										url = new URL(uri.toString());
									} catch (Exception e) {
										e.printStackTrace();
									}
									Bitmap image = null;
									// 読み込みオプション
									BitmapFactory.Options options;
									try {
										// インターネット上の画像を取得して、Bitmap に変換
										options = new BitmapFactory.Options();
										// 実際に読み込む事をオプションに設定
										options.inJustDecodeBounds = false;
										// ストリーム取得
										InputStream is = (InputStream) url.getContent();
										// ストリームよりビットマップを作成
										image = BitmapFactory.decodeStream(is, null, options);
										// ストリームを閉じる
										is.close();
									} catch (Exception e) {
										e.printStackTrace();
									}
									return image;
								}

								@Override
								protected void onPostExecute(Bitmap bitmap) {
									if ( bitmap != null ) {
										imageView.setImageBitmap(bitmap);
									}
									// ダウンロード中の表示解除
									progress.dismiss();

								}
							}.execute(uri);

						}
					})
					.addOnFailureListener(new OnFailureListener() {
						@Override
						public void onFailure(@NonNull Exception e) {
							Log.i("lightbox","データ取得に失敗しました");
							Log.i("lightbox",e.getMessage());
							// ダウンロード中の表示解除
							progress.dismiss();
							// メッセージ表示
							Toast.makeText(MainActivity.this,"ダウンロードに失敗しました",Toast.LENGTH_LONG).show();
						}
					});

			}
		});

	}
}


build.gradle(Project)
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.1'
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


build.gradle(Module: app)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "lightbox.sep.fire1"
        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.6.1'
    compile 'com.google.firebase:firebase-storage:9.6.1'
    compile 'com.google.firebase:firebase-auth:9.6.1'
}

apply plugin: 'com.google.gms.google-services'
※ auth はここでは使用していません
※ AndroidManifest.xml には、android.permission.INTERNET を設定


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

Firebase storage の画像をメモリに直接ダウンロードして ImageView に表示する / Android

Firebase の環境とプロジェクトの作成方法

9.6.1 を使用する場合
エミュレータ : SDK Manager で最新に更新
実機 : Google Play 開発者サービスを最新に更新

Firebase ドキュメント : メモリにダウンロードする
メモリへのダウンロードは、画像の大きさを考慮しておれば、他は特別な内容はありません。ストレージのルールは allow read; にしておいて、ログインなしでテストしています。 ※ 取得した Byte の配列から Bitmap の作成は BitmapFactory.decodeByteArray で行っています。 MainActivity
public class MainActivity extends AppCompatActivity {

	static long MAX_SIZE_BYTE = 1024 * 100;
	// ダウンロード用
	private FirebaseStorage storage;
	private StorageReference storageRef;
	private StorageReference imageRef;
	private ImageView imageView;
	private ProgressDialog progress;

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

// ▼ テスト用のルール
//		service firebase.storage {
//			match /b/freebase-654b7.appspot.com/o {
//				match /{allPaths=**} {
//					allow read;
//					allow write: if true;
//				}
//			}
//		}

		// ダウンロードするファイル
		storage = FirebaseStorage.getInstance();
		storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/");
		imageRef = storageRef.child("sworc2.png");

		// 表示する場所
		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);

		// ダウンロード中のダイアログ
		progress = new ProgressDialog(MainActivity.this);

		// *************************************
		// 表示1 ( メモリに取得 : bytes[] )
		// *************************************
		Button byteButton = (Button) MainActivity.this.findViewById(R.id.byteButton);
		byteButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// ダウンロード中の表示
				progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				progress.setMessage("画像をダウンロードしています");
				progress.show();

				// ダウンロード
				imageRef.getBytes(MainActivity.MAX_SIZE_BYTE)
					.addOnSuccessListener(new OnSuccessListener<byte[]>() {
					@Override
					public void onSuccess(byte[] bytes) {
						Log.i("lightbox","データ取得に成功しました");
						Log.i("lightbox", String.format("%d",bytes.length));
						if (bytes != null) {
							Bitmap image = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
							imageView.setImageBitmap(image);
						}
						// ダウンロード中の表示解除
						progress.dismiss();
					}
				}).addOnFailureListener(new OnFailureListener() {
					@Override
					public void onFailure(@NonNull Exception e) {
						// *****************************************
						// ルールを allow read: if false; にすると失敗します
						// => User does not have permission to access this object.
						// ファイルが存在しない場合
						// => Object does not exist at location.
						// *****************************************
						Log.i("lightbox","データ取得に失敗しました");
						Log.i("lightbox",e.getMessage());
						e.printStackTrace();
						// ダウンロード中の表示解除
						progress.dismiss();
						// メッセージ表示
						Toast.makeText(MainActivity.this,"ダウンロードに失敗しました",Toast.LENGTH_LONG).show();
					}
				});

			}
		});

	}
}


build.gradle(Project)
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.1'
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


build.gradle(Module: app)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "lightbox.sep.fire1"
        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.6.1'
    compile 'com.google.firebase:firebase-storage:9.6.1'
    compile 'com.google.firebase:firebase-auth:9.6.1'
}

apply plugin: 'com.google.gms.google-services'
※ AndroidManifest.xml には、android.permission.INTERNET を設定


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

2016年10月17日


Android : Firebase の データを REST API の PUT コマンドで更新する

Firebase の環境とプロジェクトの作成方法

▼ 表示 / 更新用の Firebase URL
https://freebase-654b7.firebaseio.com/class.json
▼ 構成 インターネットデータの読み込みと メッセージボックスの処理は、tools.jar を使用しています。 Firebase における REST API どうも、単純読み出しと更新しか現時点(2016/10/17)では、あてにならないようです。少なくとも、以前のバージョンではソートができていたはずなのですが、現在正しく動きません。というか、そもそも現在の最新ドキュメントに何も書いてくれていません。なので、User Authentication もダメです。しばらくは放置しておいたほうが賢明だと思います。 JSON 文字列を引き渡す更新に関しては、PHP からも簡単に実装できます。Android からも特別な事は必要ありません。 Android : Firebase 用 HttpPut クラス ( URL を非公開にして、ルールを ".write" : true で使用するといいです ) Firebase API を使用すれば、REST API を使用せずとも、ソートや更新が簡単にできますが、他の API との互換性等を考えた場合、このような方法(REST API)も必要かと思います。 ListView と ViewSwitcher アクティビティは、取り回ししやすいように MainActivity のみです。ListView からの1件の表示は ViewSwitcher に設定した別画面(include)で行い、処理は専用の処理クラス(NextPage) を作成してそこで記述するようにしています。ここでは、new GsonBuilder().setPrettyPrinting().create() を使って、JSON 文字列を整形していますが、処理には Gson gson = new Gson() で十分です。 MainActivity REST API では、ソートができないので、ArrayList をソートして使用します。JSON 文字列の扱いは、Google Gson で行います。
public class MainActivity extends AppCompatActivity {

	public static int FIRST_PAGE = 0;
	public static int NEXT_PAGE = 1;
	private MyArrayAdapter adapter;
	private ListView listview;
	private ViewSwitcher vs;
	private NextPage nextPage;
	private Gson gson;

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

		// 画面表示
		MainActivity.this.setContentView(R.layout.activity_main);

		// 複数画面処理
		vs = (ViewSwitcher) MainActivity.this.findViewById(R.id.viewSwitcher);
		// 次画面処理
		nextPage = new NextPage(MainActivity.this);
		// イベント登録
		nextPage.initAction();

		// アダプタを作成
		adapter = new MyArrayAdapter(MainActivity.this,R.layout.list_item);

		// リストビューの取得
		listview = (ListView) MainActivity.this.findViewById(R.id.listView);
		// リストビューの行をタップした時の処理
		listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				// アダプターを取得
				MyArrayAdapter adapter = (MyArrayAdapter)parent.getAdapter();
				// 行データを取得
				JsonData json = (JsonData)adapter.getItem(position);

				String jsonString = gson.toJson(json,JsonData.class);
				Log.i("lightbox",jsonString);

				View include1 = MainActivity.this.findViewById(R.id.include1);

				TextView tv = (TextView) include1.findViewById(R.id.textKey);
				tv.setText(json.getCode());

				EditText et;
				et =  (EditText) include1.findViewById(R.id.editName);
				et.setText(json.getName());
				et =  (EditText) include1.findViewById(R.id.editFuri);
				et.setText(json.getFuri());

				nextPage.setJson(json);

				// 画面移動
				vs.setDisplayedChild(MainActivity.NEXT_PAGE);
			}
		});
		// リストビューにデータを表示
		listview.setAdapter(adapter);

		// JSON データの URL ( Firebase を使用 )
		String json_url = "https://freebase-654b7.firebaseio.com/class.json?print=pretty";
		// tools.jar の static メソッド
		Tools.callHttpGet(json_url, "utf-8", new Tools.OnAsyncTaskListener() {
			@Override
			public void onAsyncTaskListener(String s) {

				// JSON データをデシリアライズ
				gson = new GsonBuilder().setPrettyPrinting().create();
				Type myMap = new TypeToken<Map<String, JsonData>>(){}.getType();
				Map<String,JsonData> map = gson.fromJson(s, myMap);

				adapter.clear();
				// map.values() から ArrayList を作成
				ArrayList al = new ArrayList<JsonData>(map.values());
				// ソート
				Collections.sort(al, new Comparator<JsonData>() {
					public int compare(JsonData a, JsonData b) {
						String  name_a = a.getFuri();
						String  name_b = b.getFuri();

						return name_a.compareTo(name_b);
					}

				});
				// アダプターにセット
				adapter.addAll(al);

			}
		});

	}

}



NextPage
public class NextPage {

	private MainActivity mainActivity;
	private ViewSwitcher vs;
	private JsonData json;

	public NextPage(MainActivity mainActivity) {
		this.mainActivity = mainActivity;
	}

	public void setJson(JsonData json) {
		this.json = json;
	}

	public void initAction(){

		// 複数画面処理
		vs = (ViewSwitcher) mainActivity.findViewById(R.id.viewSwitcher);

		// activity_next.xml の 親 view です
		View include1 = mainActivity.findViewById(R.id.include1);

		// *****************************************
		// 戻るボタン
		// *****************************************
		Button backButton = (Button)include1.findViewById(R.id.backButton);
		backButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// 最初の画面へ移動
				vs.setDisplayedChild(MainActivity.FIRST_PAGE);

			}

		});

		// *****************************************
		// 更新ボタン
		// *****************************************
		Button updateButton = (Button)include1.findViewById(R.id.updateButton);
		updateButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				Tools.messageBox(mainActivity, "更新確認", "更新しますか?", new Tools.OnMessageBoxListener() {
					@Override
					public void onMessageBoxYesListener() {
						updateData();
					}

					@Override
					public void onMessageBoxNoListener() {

					}
				});

			}

		});


	}

	private void updateData(){
		View include1 = mainActivity.findViewById(R.id.include1);

		EditText et;
		et =  (EditText) include1.findViewById(R.id.editName);
		json.setName(et.getText().toString());
		et =  (EditText) include1.findViewById(R.id.editFuri);
		json.setFuri(et.getText().toString());

		Gson gson = new GsonBuilder().setPrettyPrinting().create();
		String jsonString = gson.toJson(json,JsonData.class);
		String url = String.format("https://freebase-654b7.firebaseio.com/class/%s.json", json.getCode());

		new AsyncTask<String,Void,String>(){
			@Override
			protected String doInBackground(String... params) {
				String result = HttpPut.execute(params[0],params[1],"utf-8");
				Log.i("lightbox", result);
				return result;
			}

			@Override
			protected void onPostExecute(String s) {
				// 最初の画面へ移動
				vs.setDisplayedChild(MainActivity.FIRST_PAGE);
			}
		}.execute(url,jsonString);

	}
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <ViewSwitcher
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/viewSwitcher">

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

        <include
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            layout="@layout/activity_next"
            android:id="@+id/include1"/>

    </ViewSwitcher>

</LinearLayout>


MyArrayAdapter

オーソドックスな、ListView 用の 専用 ArrayAdapter です。
public class MyArrayAdapter extends ArrayAdapter<JsonData> {

	// JsonData を格納する、この処理専用の
	// ArrayAdapter のカスタマイズ

	// コンストラクタで渡された画面の保存
	private int mResource;

	public MyArrayAdapter(Context context, int resource) {
		super(context, resource);
		// ArrayAdapter でも、このようにして保存して利用してます
		mResource = resource;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {

		if (convertView == null) {
			// 現在の View の取得
			// getContext() で super で渡されたものを取得できます
			LayoutInflater inflater
				= (LayoutInflater) MyArrayAdapter.this.getContext().getSystemService
				(Context.LAYOUT_INFLATER_SERVICE);
			// super で渡されたものは取得できないので自前で用意します
			convertView = inflater.inflate(mResource, null);
		}

		// アダプターより行データを取得
		JsonData json = MyArrayAdapter.this.getItem(position);

		// 画面にデータをセット
		TextView tv;

		// キー
		tv = (TextView) convertView.findViewById(R.id.textKey);
		tv.setText(json.getCode());

		// Subject
		tv	= (TextView) convertView.findViewById(R.id.textItem1);
		tv.setText(json.getName());

		// Name
		tv = (TextView) convertView.findViewById(R.id.textItem2);
		tv.setText(json.getFuri());

		// 行の画面をシステムに返す
		return convertView;
	}

}


JsonData

Android Studio では、private の変数を作っておいて、ALT + Insert から Getter and Setter で一気に作成できます。toString() はここでは特に必要ありませんが、Android の単純リソースを使用するなら必要です。

空のコンストラクタも、新規用のコンストラクタもここでは使用しませんが、前者は Firebase API を使用する際には必要になります。後者も使用しませんが、ALT + Insert で簡単に作成できます。

※ kyuyo が int ではなく long なのも、Firebase API の都合です。
public class JsonData {

	private String code;
	private String name;
	private String furi;
	private String birthday;
	private String kanri;
	private long kyuyo;
	private String sex;
	private String syozoku;
	private String teate;

	public JsonData(){}

	public JsonData(String birthday, String code, String furi, String kanri, long kyuyo, String name, String sex, String syozoku, String teate) {
		this.birthday = birthday;
		this.code = code;
		this.furi = furi;
		this.kanri = kanri;
		this.kyuyo = kyuyo;
		this.name = name;
		this.sex = sex;
		this.syozoku = syozoku;
		this.teate = teate;
	}

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

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

	public String getFuri() {
		return furi;
	}
	public void setFuri(String furi) {
		this.furi = furi;
	}

	public String getBirthday() {
		return birthday;
	}
	public void setBirthday(String birthday) {
		this.birthday = birthday;
	}

	public String getKanri() {
		return kanri;
	}
	public void setKanri(String kanri) {
		this.kanri = kanri;
	}

	public long getKyuyo() {
		return kyuyo;
	}
	public void setKyuyo(long kyuyo) {
		this.kyuyo = kyuyo;
	}

	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}

	public String getSyozoku() {
		return syozoku;
	}
	public void setSyozoku(String syozoku) {
		this.syozoku = syozoku;
	}

	public String getTeate() {
		return teate;
	}
	public void setTeate(String teate) {
		this.teate = teate;
	}

	@Override
	public String toString() {
		return this.name;
	}
}



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

2016年10月10日


Android Studio で、ListView アプリケーションを作成するテンプレート

全てのソースコード



仕様の概略
1) MainActivity には ListView コントロールのみ
2) ListView 内に TextView は縦に2項目
3) NextActvity には、元へ戻るボタンと TextView が一つ
※ MyArrayAdapter を private で 一つのソースに記述しています。インターネットから JSON データを取得して ListView に表示します (https://lightbox.sakura.ne.jp/homepage/demo/data/csvtype/json.php) ➡ インターネットへのアクセスは、tools.jarTools.callHttpGet を使用します。 ➡ 読み出した JSON 文字列を Google Gson でデシリアライズして、JsonDataList にセットします。( リンク先には クラス定義があります ) このオブジェクトを ArrayAdapter<JsonData> を継承した MyArrayAdapter にセットし、ListView に MyArrayAdapter をセットして ListView を表示します。 ListView には、setOnItemClickListener で行をクリックしたイベントを定義し、その中で 行データを Intent にセットして次画面へ移動します。 次画面では、受け取った行データより、本文を取得して画面の TextView へ表示します。 MainActivity
public class MainActivity extends AppCompatActivity {

	private final int MY_REQUEST_CODE = 1000;
	private ListView listview;

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

		// リストビューの取得
		listview = (ListView) MainActivity.this.findViewById(R.id.listView);
		// リストビューの行をタップした時の処理
		listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				// アダプターを取得
				MyArrayAdapter adapter = (MyArrayAdapter)parent.getAdapter();
				// 行データを取得
				JsonData json = (JsonData)adapter.getItem(position);

				// 引渡しデータを作成
				Intent intent = new Intent(MainActivity.this,NextActivity.class);
				intent.putExtra("DATA","こんにちは");
				// Serializable インターフェイスなのでまとめて渡す
				intent.putExtra("OBJECT",json);

				// 画面移動
				MainActivity.this.startActivityForResult(intent,MY_REQUEST_CODE);
			}
		});

		// JSON データの URL
		String json_url = "https://lightbox.sakura.ne.jp/homepage/demo/data/csvtype/json.php";
		// tools.jar の static メソッド
		Tools.callHttpGet(json_url, "utf-8", new Tools.OnAsyncTaskListener() {
			@Override
			public void onAsyncTaskListener(String s) {

				// JSON データをデシリアライズ
				Gson gson = new Gson();
				JsonDataList jdl = gson.fromJson(s, JsonDataList.class);

				// アダプタを作成して、データをセット
				MyArrayAdapter adapter
					= new MyArrayAdapter(MainActivity.this,R.layout.list_item);
				// jdl.item は配列
				adapter.addAll(jdl.item);

				// リストビューにデータを表示
				listview.setAdapter(adapter);

			}
		});
	}


	// ******************************
	// 次画面から戻って来た時の処理
	// ******************************
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		if ( requestCode == MY_REQUEST_CODE ) {
			// NextActivity の 戻るボタン以外で戻ると 0
			Log.i("lightbox",String.format("NextActivity からの戻り値 : %d", resultCode));
			if ( data != null ) {
				String message = data.getStringExtra("DATA");
				Log.i("lightbox",String.format("NextActivity からのメッセージ : %s", message));
			}
		}

	}

	// ******************************
	// JsonData を格納する、この処理専用の
	// ArrayAdapter のカスタマイズ
	// ******************************
	private class MyArrayAdapter extends ArrayAdapter<JsonData> {

		// コンストラクタで渡された画面の保存
		private int mResource;

		public MyArrayAdapter(Context context, int resource) {
			super(context, resource);
			// ArrayAdapter でも、このようにして保存して利用してます
			mResource = resource;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			if (convertView == null) {
				// 現在の View の取得
				LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService
					(Context.LAYOUT_INFLATER_SERVICE);
				convertView = inflater.inflate(mResource, null);
			}

			// アダプターより行データを取得
			JsonData json = MyArrayAdapter.this.getItem(position);

			// 画面にデータをセット
			TextView tv;

			// Subject
			tv	= (TextView) convertView.findViewById(R.id.textItem1);
			tv.setText(json.getSubject());

			// Name
			tv = (TextView) convertView.findViewById(R.id.textItem2);
			tv.setText(json.getName());

			// 行の画面をシステムに返す
			return convertView;
		}

	}

}

MainActivity での特記事項onItemClick 内での 行データの参照は、parent.getAdapter() によって、使用しているアダプターを取得します。
JsonData は、そのまま Intent にセットする為に Serializable インターフェイスを実装しています
JsonData へのデータのセットは、Google Gson 経由を想定しているので、setter および コンストラクタは定義していません
JsonData の getText 以外のメソッドはここでは使用しませんが、仕様の拡張を想定した最低限必要と判断したものです。特に、toString は、システムが用意した一つの項目しかない行を定義した画面で使う為に必要です(ここでは必要ありません)。
MyArrayAdapter で画面 id を内部保存しているのは、ArrayAdapter のソースコードに準じたものです


tools.jar は、ソースコードを簡略化する為のテスト用のライブラリです。本番には相応のコードを用意します。

NextActivity


public class NextActivity extends AppCompatActivity {

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

		// 引き渡された Intent を取得
		Intent intent = NextActivity.this.getIntent();
		// 文字列のデータを取り出す
		String message = intent.getStringExtra("DATA");
		Log.i("lightbox",String.format("MainActivity からのメッセージ : %s", message));
		// Serializable オブジェクトを取り出す
		JsonData json = (JsonData) intent.getSerializableExtra("OBJECT");

		// 画面へデータをセット
		TextView tv = (TextView) NextActivity.this.findViewById(R.id.textView);
		// text データ上の 改行文字列を実際の改行に変換する
		String text = json.getText();
		text = text.replace("\\n", "\n");
		tv.setText(text);

		// MainActivity へ戻る為のボタン
		Button button = (Button) NextActivity.this.findViewById(R.id.backButton);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				// 戻る時にも Intent を引き渡す
				Intent intent = new Intent();
				intent.putExtra("DATA", "今戻りました");
				// MainActivity で受け取る int データをセットする
				NextActivity.this.setResult(NextActivity.RESULT_OK,intent);
				// この画面を終了して MainActivity へ戻る
				NextActivity.this.finish();

			}
		});

	}
}

NextActivity での特記事項

Android Studio では、New => Activity で作成しており、AndroidManifest への定義は自動的に追加されます。


全てのソースコード


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

JavaScript : 入力文字列を 変数用16進数表現 に変換する

\u を使って、ソースコードに埋め込む文字列として、キャラクタセットに依存せずにコードを作成する為に使用します。( "あ" == "\u3042" )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
function htmlentities2() {
	var a = $("#do_encode").val();
	var w = "";
	var n = 0;
	var p;
	for( var i = 0; i < a.length; i++ ) {
		p = (a.charCodeAt(i)).toString(16);
		if ( p.length > 2 || a.substr( i, 1 ) == "\"" || a.substr( i, 1 ) == "'" ) {
			w += "\\u" + ("00"+p).substr(2+p.length-4,4);
		}
		else {
			w += a.substr( i, 1 );
		}
	}
	$("#result").text( w );
}
</script>
<input id="do_encode" type="text" size="60">
<br>
<input
	type="button"
	value="入力文字列を alert 用16進数表現 に変換"
	onclick='htmlentities2()'>
<pre id="result"></pre>

※ JavaScript外部ファイル を charset を使用せずに作成する為に htmlentitie と併用します。


関連する記事

JS : 入力文字列を htmlentitie に変換する


posted by lightbox at 2016-10-10 12:55 | JavaScript | このブログの読者になる | 更新情報をチェックする
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 ドロップシャドウの参考デモ
PHP正規表現チェッカー
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり