SQLの窓

2017年07月10日


Okhttp を使用した、GET、POST、ファイルアップロードを楽に実装できる HttpAccess クラス

▼ HttpAccess パッケージのダウンロード


( 解凍して、main\java フォルダの中に コピーして下さい )
※ OkHttp より 最新の Gradle の記述を取得して build.gradle の dependencies の最後に貼り付けて下さい
例) compile 'com.squareup.okhttp3:okhttp:3.8.1'

OkHttp
okio

厳密に言うと、GET 時に渡す URL の QueryString 部分はクラス外部で作成するようになっています。仮にデータ部分を日本語で指定しても、UTF8 であれば自動的にエンコードしてくれます。また、set メソッドで URL の変更も可能です。

コンストラクタで渡す URL は、GET では必ず使用されますが、POST では実行時に指定した url KEY を使う事ができます(省略すると、コンストラクタ作成時の url を使用します)

url に関しては、Getter と Setter があります。

ファイルアップロードは1件のみの処理を想定しています。入力データとして InputStream を使い、内部で ByteArray に変換しています。Okhttp のメソッドが InputStream には対応していないので、複数ファイルアップロードは、一度にひとつのファイルになるよう外部でコントロールが必要になるでしょう。
import android.os.AsyncTask;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;

public class HttpAccess {

	private OkHttpClient okHttpClient;
	private String url;

	public interface OnAsyncTaskListener {
		abstract public void onAsyncTaskListener(String s);
	}

	// url の Getter と Setter
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}

	// *******************************
	// コンストラクタ
	// *******************************
	public HttpAccess(String url) {
		this.okHttpClient = new OkHttpClient();
		this.url = url;
	}

	// *******************************
	// GET
	// *******************************
	public void sendGet(final OnAsyncTaskListener listener ) {

		new AsyncTask<Void,Void,String>(){
			@Override
			protected String doInBackground(Void... params) {
				String result = "[\"error\"]";

				Request.Builder builder = new Request.Builder();
				builder.url(HttpAccess.this.url);
				Request request = builder.build();

				Response response = null;
				try {
					response = HttpAccess.this.okHttpClient.newCall(request).execute();
					result = response.body().string();
				}
				catch (IOException e) {
					e.printStackTrace();
				}

				return result;
			}

			// UI スレッド処理
			@Override
			protected void onPostExecute(String s) {
				super.onPostExecute(s);

				listener.onAsyncTaskListener(s);

			}
		}.execute();

	}

	// *******************************
	// POST
	// *******************************
	public void sendPost(HashMap<String,String> postData, final OnAsyncTaskListener listener ) {

		new AsyncTask<HashMap,Void,String>(){
			@Override
			protected String doInBackground(HashMap... params) {
				String result = "[\"error\"]";

				HashMap<String,String> postData = (HashMap<String,String>)params[0];

				// POST 用 FormBody の内容の作成
				FormBody.Builder formbodyBuilder = new FormBody.Builder();
				for(Map.Entry<String, String> e : postData.entrySet()) {
					if (  !(e.getKey()).equals("url") ) {
						formbodyBuilder.add(e.getKey(), e.getValue());
					}
				}

				// 送信用ユニットの作成
				FormBody formbody = formbodyBuilder.build();
				Request.Builder builder = new Request.Builder();

				// 引数の Map に url が無い場合は、コンストラクタの url を使用する
				if ( postData.get("url") == null ) {
					builder.url(HttpAccess.this.url) ;
				}
				else {
					builder.url( postData.get("url") );
				}

				builder.post(formbody);
				Request request = builder.build();

				Response response = null;
				try {
					response = HttpAccess.this.okHttpClient.newCall(request).execute();
					result = response.body().string();
				}
				catch (IOException e) {
					e.printStackTrace();
				}

				return result;
			}

			// UI スレッド処理
			@Override
			protected void onPostExecute(String s) {

				listener.onAsyncTaskListener(s);

			}
		}.execute(postData);

	}

	// *******************************
	// 1ファイルのアップロード(  Byte 配列使うので一つ )
	// *******************************
	public void sendUpload(String url, String field, String name, InputStream inputStream, String mime, final OnAsyncTaskListener listener ) {

		// 引数は、専用クラス
		UploadParam uploadParam = new UploadParam(url,field,name,inputStream,mime);
		new AsyncTask<UploadParam,Void,String>(){
			@Override
			protected String doInBackground(UploadParam... params) {
				String result = "[\"error\"]";

				UploadParam uploadParam = params[0];
				InputStream inputStream = uploadParam.getInputStream();
				Source source = Okio.source(inputStream);
				ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
				BufferedSink bufferedSink = Okio.buffer(Okio.sink(byteArrayOutputStream));
				try {
					bufferedSink.writeAll(source);
					bufferedSink.close();
					source.close();
				} catch (IOException e) {
					e.printStackTrace();
				}

				MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder();
				multipartBodyBuilder.setType(MultipartBody.FORM);
				multipartBodyBuilder.addFormDataPart(
						uploadParam.getField(),
						uploadParam.getName(),
						RequestBody.create(MediaType.parse(uploadParam.getMime()),byteArrayOutputStream.toByteArray())
				);
				RequestBody requestBody = multipartBodyBuilder.build();

				// 送信用のデータを作成
				Request.Builder requestBuilder = new Request.Builder();
				requestBuilder.url(uploadParam.getUrl());
				requestBuilder.post(requestBody);
				Request request = requestBuilder.build();

				// 受信用のオブジェクトの準備
				Call call = HttpAccess.this.okHttpClient.newCall(request);

				// 送信と受信
				try {

					Response response = call.execute();
					result = response.body().string();

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

				return result;

			}

			@Override
			protected void onPostExecute(String s) {

				listener.onAsyncTaskListener(s);

			}

		}.execute(uploadParam);


	}

	private class UploadParam {
		private String url;
		private String mime;
		private String field;
		private String name;
		private InputStream inputStream;

		public UploadParam(String url, String field, String name, InputStream inputStream, String mime) {
			this.url = url;
			this.mime = mime;
			this.field = field;
			this.name = name;
			this.inputStream = inputStream;
		}

		public String getUrl() {
			return url;
		}

		public String getMime() {
			return mime;
		}

		public String getField() {
			return field;
		}

		public String getName() {
			return name;
		}

		public InputStream getInputStream() {
			return inputStream;
		}
	}

}


ファイルアップロード呼び出しサンプル
				try {
					InputStream inputStream = assetManager.open("sample.jpg");
					httpAccess.sendUpload(
						"http://10.0.2.2/r101/php_upload/file_upload.php",
						"target",
						"sample.jpg",
						inputStream,
						"image/jpeg",
						new HttpAccess.OnAsyncTaskListener() {
							@Override
							public void onAsyncTaskListener(String s) {

								Log.i("lightbox", s);

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

Post 呼び出しサンプル
					HashMap<String,String> postData = new HashMap<String,String>();
					// localhost の 環境
					postData.put("url", "http://10.0.2.2/r101/login-action/syain/syain_update.php");
					postData.put("fld_code", syain_base.code );
					postData.put("fld_simei", syain_base.name );

					httpAccess.sendPost(postData,new HttpAccess.OnAsyncTaskListener() {
						@Override
						public void onAsyncTaskListener(String s) {

							Log.i("lightbox",s);

							if ( !s.equals("[\"error\"]") ) {
								// 再表示
								listView.setAdapter(arrayAdapter);
							}
							else {
								Toast.makeText(MainActivity.this,"インターネットアクセスに失敗しました",Toast.LENGTH_LONG).show();
							}

						}
					});






posted by lightbox at 2017-07-10 12:04 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年07月04日


Android の assets フォルダーに保存した 400x320 の画像ファイルの扱い

『Android の drawable フォルダーに保存した 400x320 の画像ファイルの6種類の扱いと Density』では、ファイルでは無く『表示用のリソース』という扱いでした。

それに対して assets フォルダーに保存するファイルは画像である必要も無く、単純に InputStream としてアクセスできます。(但し、assets からは読み込む事のみ可能です)

そこで、直接画像として扱う方法と、一旦別の場所に保存してから画像として扱う方法を試してみました。

前提条件

1) エミュレータとして Nexus 4 ( 768x1280 : xhdpi : 320 ) を使用しています。
2) 画像サイズは 400x320 です。
3) 画像は、assets フォルダに保存しました。
4) ImageView に表示します
5) 以下の3つの画像です(いずれも 400x320 jpeg)
  

drawable との違い

1) 画像をファイルとして扱えるので、基本的には Bitmap として扱います。

2) ファイルのコピーには okio を使用して InputStream から OutputStream にコピーします。
	// *************************
	// ファイルコピー
	// *************************
	private void streamCopy(InputStream inputStream, OutputStream outputStream) throws Exception {

		Source source = Okio.source(inputStream);
		BufferedSink bufferedSink = Okio.buffer(Okio.sink(outputStream));
		bufferedSink.writeAll(source);

		bufferedSink.close();
		source.close();

	}


Drawable で表示 (27行〜)

ファイルからの読み込みであっても、リソースとして Drawable に読み込む場合はやはり、幅が 200 に設定されて表示は小さくなりました。



Drawable から Bitmap に変換して大きく表示 (47行〜)

Drawable から Bitmap に変換すると本来の横幅(400) になり、Density も 320 でこのまま表示すると正しい大きさで表示されるので、setDensity(160) で大きく表示するようにしました。



Bitmap で表示 (74行〜)

普通に Bitmap として読み込んで、Bitmap として表示します。サイズも密度も思った通りの状態となります。



いったんコピーしてBitmap で表示 (95行〜)

コピー後は書き込み可能なフォルダに環境が変わる事を除いて、assets から読み込むのと変わり無く普通に表示されます。



コピー後のファイルを Bitmap で大きく表示 (123行〜)

setDensity(160) で大きく表示します



コピー後のファイルを Bitmap で小さく表示 (154行〜)

setDensity(640) で小さく表示します



public class MainActivity extends AppCompatActivity {

	AssetManager assetManager;
	ImageView imageView;
	ImageView imageViewSaved;
	Drawable drawable;
	Bitmap bitmap;
	InputStream inputStream;
	OutputStream outputStream;

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

		assetManager = MainActivity.this.getResources().getAssets();

		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
		imageViewSaved = (ImageView) MainActivity.this.findViewById(R.id.imageViewSaved);

		// カラー(Drawable)
		MainActivity.this.findViewById(R.id.buttonAsset1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					inputStream = assetManager.open("sample.jpg");
					drawable = new BitmapDrawable(MainActivity.this.getResources(), inputStream);
					inputStream.close();

					Log.i("lightbox", drawable.getIntrinsicWidth()+"");		// Nexus 4 : 200, Nexus S : 267

				} catch (IOException e) {
					e.printStackTrace();
				}
				imageView.setImageDrawable(drawable);

			}
		});

		// モノクロ最大(Drawable > Bitmap > DENSITY_DEFAULT)
		MainActivity.this.findViewById(R.id.buttonAsset2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					inputStream = assetManager.open("sample_mono.jpg");
					drawable = new BitmapDrawable(MainActivity.this.getResources(), inputStream);
					inputStream.close();

					Log.i("lightbox", drawable.getIntrinsicWidth()+"");		// Nexus 4 : 200, Nexus S : 267

					bitmap = ((BitmapDrawable) drawable).getBitmap();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 大きく
					bitmap.setDensity(160);

				} catch (IOException e) {
					e.printStackTrace();
				}
				imageView.setImageBitmap(bitmap);

			}
		});

		// スケッチ、そのまま(Bitmap)
		MainActivity.this.findViewById(R.id.buttonAsset3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					inputStream = assetManager.open("sample_sketch.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

				} catch (IOException e) {
					e.printStackTrace();
				}
				imageView.setImageBitmap(bitmap);

			}
		});

		// カラーを保存して表示
		MainActivity.this.findViewById(R.id.buttonSave1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					// 読み込むファイル
					inputStream = assetManager.open("sample.jpg");
					// 書き込むファイル
					outputStream = MainActivity.this.openFileOutput("sample.jpg", MODE_PRIVATE);
					streamCopy(inputStream,outputStream);

					// コピーしたファイルを読み込む
					inputStream = MainActivity.this.openFileInput("sample.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

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

				imageViewSaved.setImageBitmap(bitmap);

			}
		});

		// モノクロを保存して表示
		MainActivity.this.findViewById(R.id.buttonSave2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					// 読み込むファイル
					inputStream = assetManager.open("sample_mono.jpg");
					// 書き込むファイル
					outputStream = MainActivity.this.openFileOutput("sample_mono.jpg", MODE_PRIVATE);
					streamCopy(inputStream,outputStream);

					// コピーしたファイルを読み込む
					inputStream = MainActivity.this.openFileInput("sample_mono.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 大きく
					bitmap.setDensity(160);

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

				imageViewSaved.setImageBitmap(bitmap);

			}
		});

		// スケッチ
		MainActivity.this.findViewById(R.id.buttonSave3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					// 読み込むファイル
					inputStream = assetManager.open("sample_sketch.jpg");
					// 書き込むファイル
					outputStream = MainActivity.this.openFileOutput("sample_sketch.jpg", MODE_PRIVATE);
					streamCopy(inputStream,outputStream);

					// コピーしたファイルを読み込む
					inputStream = MainActivity.this.openFileInput("sample_sketch.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 小さく
					bitmap.setDensity(640);

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

				imageViewSaved.setImageBitmap(bitmap);

			}
		});
	}

}





posted by lightbox at 2017-07-04 20:21 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

Android の drawable フォルダーに保存した 400x320 の画像ファイルの6種類の扱いと Density

前提条件

1) エミュレータとして Nexus 4 ( 768x1280 : xhdpi : 320 ) を使用しています。
2) 画像サイズは 400x320 です。

3) 画像は、drawable フォルダの直下に置かれています。


4) ImageView に表示します
5) 以下の3つの画像です(いずれも 400x320 jpeg)
  

6) デバイスを変更すると、getWidth()・getDensity() の値は場合によって変化します


リソース ID で表示 (25行〜)

setImageResource で リソース IDを使用するので、Android が自動的に表示する状態です。



これは、Nexus 4 の解像度(768x1280)から言えば大きく拡大して表示されています。

Drawable に変換後、Bitmap に変換して表示  (35行〜)

単純に表示してしまうと、リソース ID で表示したものと同じ大きさになります。ここでは、横幅情報を取得して表示しており、その情報では横幅は 800(Nexus4 の場合) になっています。

そこで、setDensity で密度を倍にする事によって結果的に表示サイズを半分にしています。



これは、Nexus 4 の解像度(768x1280)から言えば正しい大きさで表示されています。

Bitmap に直接変換後表示  (54行〜)

リソースを経由して変換しているので、リソース ID で表示したものと同じ大きさになります。ここでも、横幅情報を取得して表示していますが、その情報では横幅は 800(Nexus4 の場合) になっています。

なので、setDensity で密度を倍にして妥当な大きさで表示するようにしています。



本来のサイズで保存し、そこから読み込んで表示  (70行〜)

リソースを経由した場合倍のサイズになっているので、BitmapFactory.OptionsinScaled を false にして Bitmap に変換します。この Bitmap は本来のサイズになっているので、compress メソッドでこのまま保存します。ただし、元が JPEG であるため、クオリティを保持する為に PNG として保存しています。

そして、再度読み込みむと幅は 400 で Density は 320 になっているので、そのまま表示します。



保存後 Dawable として読込んで Bitmap に変換して表示  (105行〜)

BitmapFactory.OptionsinScaled を false にして Bitmap に変換して保存します(PNG として保存)。

そして、Drawable として読み込みむと幅が 200 になるようです。こちらの思った通りの大きさにする為にさらに Bitmap に変換すると、幅は 400 に戻りました。



しかし、最初の Drawable のまま setImageDrawable で表示すると以下のように小さくなります( 200 )



JPEG 保存後 大きくして表示  (142行〜)

クオリティが劣化しますが、90% クオリティでJPEG として保存して Bitmap として読み込みます

そして、大きく表示する為に Density を小さく(160)して表示しています。




public class MainActivity extends AppCompatActivity {

	ImageView imageView;
	ImageView imageViewSaved;
	Drawable drawable;
	Bitmap bitmap;
	BitmapFactory.Options options;
	InputStream inputStream;
	OutputStream outputStream;

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

		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
		imageViewSaved = (ImageView) MainActivity.this.findViewById(R.id.imageViewSaved);

		// カラー
		MainActivity.this.findViewById(R.id.buttonResource1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				// リソース ID
				imageView.setImageResource(R.drawable.sample);

			}
		});

		// モノクロ
		MainActivity.this.findViewById(R.id.buttonResource2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				drawable =  MainActivity.this.getDrawable(R.drawable.sample_mono);
				Log.i("lightbox", drawable.getIntrinsicWidth()+"");	// Nexus 4 : 800, Nexus S : 600 (注) setDensity 後変化します

				bitmap = ((BitmapDrawable)drawable).getBitmap();
				Log.i("lightbox", bitmap.getWidth()+"");		// Nexus 4 : 800, Nexus S : 600
				Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

				// 密度を倍にすれば、表示サイズは半分に
				bitmap.setDensity(640);
				imageView.setImageBitmap(bitmap);

			}
		});

		// スケッチ
		MainActivity.this.findViewById(R.id.buttonResource3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample_sketch);
				Log.i("lightbox", bitmap.getWidth()+"");		// Nexus 4 : 800, Nexus S : 600
				Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

				// 密度を倍にすれば、表示サイズは半分に
				bitmap.setDensity(640);
				imageView.setImageBitmap(bitmap);

			}
		});

		// カラー
		MainActivity.this.findViewById(R.id.buttonSave1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				options = new BitmapFactory.Options();
				options.inScaled = false;
				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample,options);

				Log.i("lightbox", bitmap.getWidth()+"");		// 400
				Log.i("lightbox", bitmap.getDensity()+"");	// 160

				try {
					// 書き込みするファイル名
					outputStream = MainActivity.this.openFileOutput("sample.png", MODE_PRIVATE);
					// PNG なので quality は無視される( クオリティ維持のため PNG として保存 )
					bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
					outputStream.close();

					inputStream = MainActivity.this.openFileInput("sample.png");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();

					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					imageViewSaved.setImageBitmap(bitmap);

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

			}
		});

		// モノクロ
		MainActivity.this.findViewById(R.id.buttonSave2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				options = new BitmapFactory.Options();
				options.inScaled = false;
				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample_mono,options);

				Log.i("lightbox", bitmap.getWidth()+"");		// 400
				Log.i("lightbox", bitmap.getDensity()+"");	// 160

				try {
					// 書き込みするファイル名
					outputStream = MainActivity.this.openFileOutput("sample_mono.png", MODE_PRIVATE);
					// PNG なので quality は無視される( クオリティ維持のため PNG として保存 )
					bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
					outputStream.close();

					inputStream = MainActivity.this.openFileInput("sample_mono.png");
					drawable = new BitmapDrawable(MainActivity.this.getResources(), inputStream);
					inputStream.close();
					Log.i("lightbox", drawable.getIntrinsicWidth()+"");	// Nexus 4 : 200, Nexus S : 267

					bitmap = ((BitmapDrawable)drawable).getBitmap();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					imageViewSaved.setImageBitmap(bitmap);

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

			}
		});

		// スケッチ
		MainActivity.this.findViewById(R.id.buttonSave3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				options = new BitmapFactory.Options();
				options.inScaled = false;
				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample_sketch,options);

				Log.i("lightbox", bitmap.getWidth()+"");		// 400
				Log.i("lightbox", bitmap.getDensity()+"");	// 160

				try {
					// 書き込みするファイル名
					outputStream = MainActivity.this.openFileOutput("sample_sketch.jpg", MODE_PRIVATE);
					// 90% JPEGとして保存
					bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
					outputStream.close();

					inputStream = MainActivity.this.openFileInput("sample_sketch.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 大きく
					bitmap.setDensity(160);
					imageViewSaved.setImageBitmap(bitmap);

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

			}
		});

	}

}

関連する記事

Android の assets フォルダーに保存した 400x320 の画像ファイルの扱い



posted by lightbox at 2017-07-04 14:52 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年06月20日


Android での保存用テキストデータの扱いを okio で簡素化する

OkHttp はとても簡単にインターネットへのアクセスを提供してくれます。その Okhttp が依存しているストリームの処理が okio です。

当然 okio は一般的なテキストファイルのアクセスにも簡単に使えて便利なので、Android の内部で扱われる『位置付け』別に処理を実際に使用して整理してみました。

★ 全体のソースコード
build.gradle への記述 ▼ 2018/10/31 時点 api 'com.squareup.okio:okio:2.1.0' ( api は、依存関係に関して compile と同等らしいので / compile は 2018年の終わりに削除 ) ▼ 2017/06/20 時点 compile 'com.squareup.okio:okio:1.13.0' Runtime Permission の対応 ▼ 以下のテンプレートを使用しています Android 6.0 : テストの為の Runtime Permission の対応を自動化するテンプレート okio を使用した2つのメソッドを用意 okio には、BufferedSource と BufferedSink というクラスがあり、それぞれ 入力と出力を担当しています。一応サンプルとして OkioTest.java を読むと雰囲気がつかめると思いますが、欲しい機能はそれほど複雑では無いので以下の2つのメソッドだけでも十分使いこなせると思います。
// *************************
// テキストファイル一括読込
// "UTF-8" or "windows-31j"
// *************************
private String readTextAll(InputStream inputStream, String charset) throws Exception {
	String result;

	BufferedSource bufferedSource = Okio.buffer(Okio.source(inputStream));
	result = bufferedSource.readString(Charset.forName(charset));

	bufferedSource.close();
	inputStream.close();

	return result;
}

// *************************
// ファイルコピー
// *************************
private void copy(InputStream inputStream, OutputStream outputStream) throws Exception {

	Source source = Okio.source(inputStream);
	BufferedSink bufferedSink = Okio.buffer(Okio.sink(outputStream));
	bufferedSink.writeAll(source);

	bufferedSink.close();
	source.close();

}
assets に syain.csv ファイルを置いて他の場所にコピー(書込み) syain.csv は、Windows の一般的な SHIFT_JIS のテキストファイル(CSV) です。このデータを パッケージの領域と 外部ストレージにコピーして、後でテキストファイルの読み込みをテストします。 他にバリエーションとして、okio で直接パッケージの領域にテキストファイル(test.txt)を "UTF-8" として書き込んでいます(23〜30)。
1) UTF-8 を指定して書き込み

Charset charset = Charset.forName("UTF-8");
sink.writeString("ストレージ情報\n",charset);

2) 専用メソッドで UTF-8 として書き込み

sink.writeUtf8(Manifest.permission.CAMERA + "\n");

※ sink は BufferedSink です
さらに、プリファレンスの書き込みも行います(36〜46)。
try {

	InputStream inputStream;
	AssetManager assetManager = MainActivity.this.getResources().getAssets();

	// ****************************************
	// syain.csv を assets から プライベートエリアへコピー
	// ****************************************
	inputStream = assetManager.open("syain.csv");
	copy(inputStream, openFileOutput("syain.csv", MODE_PRIVATE));

	// ****************************************
	// syain.csv を assets から 外部ストレージへコピー
	// ****************************************
	createDir();
	inputStream = assetManager.open("syain.csv");
	String csvPath = Environment.getExternalStorageDirectory().getPath() + "/textfiletest/syain.csv";
	copy(inputStream, new FileOutputStream(csvPath));

	// ****************************************
	// プライベートなテキストファイルの作成と書き込み
	// ****************************************
	BufferedSink sink = Okio.buffer(Okio.sink(openFileOutput("test.txt", MODE_PRIVATE)));
	Charset charset = Charset.forName("UTF-8");
	sink.writeString("ストレージ情報\n",charset);
	sink.writeUtf8(String.format("%s\n", Environment.getExternalStorageDirectory().getPath()));
	sink.writeUtf8(Manifest.permission.CAMERA + "\n");
	sink.writeUtf8(Manifest.permission.WRITE_EXTERNAL_STORAGE + "\n");
	sink.writeUtf8(PackageManager.PERMISSION_GRANTED + "\n");
	sink.close();

	// ****************************************
	// プライベートな小さな情報を保存する為の本来の方法
	// private_data.xml の作成
	// ****************************************
	SharedPreferences data = getSharedPreferences("private_data", MODE_PRIVATE);
	SharedPreferences.Editor editor = data.edit();

	// 現在の年月日・時分秒を取得する為の準備
	Calendar cal = Calendar.getInstance();
	SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");

	// データの書き込み
	editor.putString("now", sf.format(cal.getTime()));
	editor.putString("japan", "日本語");
	editor.commit();

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

外部ストレージにフォルダの作成

Runtime Permission の対象処理です

// *************************
// フォルダ作成
// *************************
private void createDir() {
	String imageDir = Environment.getExternalStorageDirectory().getPath() + "/textfiletest";
	Log.i("lightbox", imageDir );
	File file = new File(imageDir);
	// ディレクトリ初期作成
	if (!file.exists()) {
		if (file.mkdir() == false) {
			Log.i("lightbox", "ディレクトリを作成できませんでした");
			return;
		}
	}
}

画面にファイルを読込み(1)

まずは、パッケージ内の領域から読み込んで画面に表示します( openFileInput )

※ 画面定義


StringBuilder sb = new StringBuilder();

InputStream inputStream;
String text;

Log.i("lightbox", "読み込み1");
try {
	// ****************************************
	// プライベートなテキストファイルの読み込み
	// "UTF-8" or "windows-31j"
	// ****************************************
	inputStream = openFileInput("test.txt");
	text = readTextAll(inputStream,"UTF-8");

	sb.append(text);

	// ****************************************
	// プライベートなテキストファイルの読み込み
	// "UTF-8" or "windows-31j"
	// ****************************************
	inputStream = openFileInput("syain.csv");
	text = readTextAll(inputStream,"windows-31j");

	sb.append(text);

	EditText editText = (EditText) MainActivity.this.findViewById(R.id.editText);
	editText.setText(sb.toString());
	TextView textView = (TextView) MainActivity.this.findViewById(R.id.textView);
	textView.setText(sb.toString());

	// ****************************************
	// private_data.xml からデータの取得
	// ****************************************
	SharedPreferences data = getSharedPreferences("private_data", MODE_PRIVATE);
	Log.i("lightbox", data.getString("now", "このデータはありません"));
	Log.i("lightbox", data.getString("japan", "このデータはありません"));

} catch (Exception e) {
	e.printStackTrace();
}
UTF-8 と SHIFT_JIS で保存してあるファイルの一括読み込みです。キャラクタセットに関する Oracle のドキュメントはこちらになります。

※ プリファレンスは Log.i で表示しています

画面にファイルを読込み(2)

外部ストレージより読み込みます( Runtime Permision の対象処理 )
new FileInputStream(path)

※ test.txt は、画面表示の都合で読み込んでいます
StringBuilder sb = new StringBuilder();

InputStream inputStream;
BufferedSource bufferedSource;
String text;

Log.i("lightbox", "読み込み2");
try {
	// ****************************************
	// プライベートなテキストファイルの読み込み
	// "UTF-8" or "windows-31j"
	// ****************************************
	inputStream = openFileInput("test.txt");
	text = readTextAll(inputStream,"UTF-8");

	sb.append(text);

	// ****************************************
	// 外部ストレージからテキストファイルの読み込み
	// "UTF-8" or "windows-31j"
	// ****************************************
	String csvPath = Environment.getExternalStorageDirectory().getPath() + "/textfiletest/syain.csv";
	inputStream = new FileInputStream(csvPath);
	text = readTextAll(inputStream,"windows-31j");

	sb.append(text);

	EditText editText = (EditText) MainActivity.this.findViewById(R.id.editText);
	editText.setText(sb.toString());
	TextView textView = (TextView) MainActivity.this.findViewById(R.id.textView);
	textView.setText(sb.toString());


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


関連する記事 : 一般的な Java のテキスト読込み

Java : テキストファイルを読み込んで正規表現で置換



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

Android 6.0 : テストの為の Runtime Permission の対応を自動化するテンプレート

Runtime Permission の対応はしないとアプリが動作しないのでせざるを得ないですが、本来の処理とは無関係なのでかなり面倒な上にソースコードそのものが第三者への説明の邪魔になります。

そこで、ほぼ定型化して処理する為のテンプレートとして MainActivity への配置例と、そのために必要なクラスを用意しました。
手順1 ▼ Runtime Permission の処理は、以下から permission_170610.zip をダウンロードして下さい。 ※ CheckMyPermission の内容 手順2 ▼ 解凍して、プロジェクトの "app\src\main\java" の下に com フォルダをコピーして下さい ▼ エクスプローラから見た状態 手順3 使用したい Permission を AndroidManifest.xml に記述します
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.lightbox.runtimepermissiontest">

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

    <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">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

</manifest>

手順4

MainActivity を以下のように記述して下さい
package com.example.lightbox.runtimepermissiontest;

import android.Manifest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.example.lightbox.permission.CheckMyPermission;
import com.example.lightbox.permission.MyPermission;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

	private CheckMyPermission checkMyPermission;

	@Override
	public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
		// 全ての必要なパーミッションが許可された場合
		if ( checkMyPermission.checkPermission( requestCode,  grantResults )) {
			// onCreate で初期処理できるように、MainActivity をリスタート
			Intent intent = MainActivity.this.getIntent();
			MainActivity.this.finish();
			MainActivity.this.startActivity(intent);
		}
	}

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

		// 必要なパーミッションのリスト
		ArrayList<MyPermission> myPermission_list = new ArrayList<MyPermission>();
		// AndroidManifest.xml に記述した Permission の一覧
		myPermission_list.add(new MyPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,100));
		myPermission_list.add(new MyPermission(Manifest.permission.CAMERA,101));
		myPermission_list.add(new MyPermission(Manifest.permission.WRITE_CALENDAR,102));
		// Runtime Permission 用のクラスのインスタンス
		checkMyPermission = new CheckMyPermission(MainActivity.this,myPermission_list);
		if ( checkMyPermission.checkPermission() ) {
			// ここから通常の初期処理
			initSettings();
		}
	}

	private void initSettings() {

	}

}


手順5

実行します。すると、初回は許可ダイアログが指定した Permission と同数表示されて、onRequestPermissionsResult が実行されます。すると、Activity が再起動され、checkMyPermission.checkPermission() が true になって initSettings() を実行する事になります。

2回目以降(全て許可した場合)は onCreate で initSettings() が常に実行される事になります。(許可ダイアログを再度表示したい場合は、アプリを削除して再度実行します)

許可ダイログ

  


関連する記事

『Android 6.0 エミュレータで 複数の Runtime Permission の対応を簡潔に吸収するクラス( CheckMyPermission )』

Android Studio : Runtime Permission 等の裏方作業を MainActivity にさせて、本来の処理は継承したサブクラスで行う( カメラを呼び出して画像を保存させ、ImageView に表示する )


posted by lightbox at 2017-06-20 19:35 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年06月10日


Android Studio : Runtime Permission 等の裏方作業を MainActivity にさせて、本来の処理は継承したサブクラスで行う( カメラを呼び出して画像を保存させ、ImageView に表示する )

Runtime Permission の処理は、『Android 6.0 エミュレータで 複数の Runtime Permission の対応を簡潔に吸収するクラス( CheckMyPermission )』でテストしたものをダウンロードできます。


※ 解凍して、プロジェクトの "app\src\main\java" の下に com フォルダをコピーして下さい


MainActivity

画面の表示はこちらで行います。onCreate で Runtime Permission のチェックの準備を行って、onRequestPermissionsResult での受け取り処理もここで完結します。

MainActivity を継承したサブクラスの onCreate でチェックを行い、既に許可済みであれば通常の初期処理を行います。

ここでは、カメラの呼び出しを行うので、カメラの Permission は必要ありませんが、ExternalStorage に画像ファイルを作成するので、AndroidManifest.xml への記述と Runtime Permission の許可処理が必要です。

また、カメラ呼び出しで private 変数がクリアされる事を想定して(そういう仕様である上に、カメラが保存したパスを返さない事から保存せざるを得ない)、MainActivity では onSaveInstanceState と onRestoreInstanceState で変数の保存と復帰を行っています。

package com.example.lightbox.cameracalltest;

import android.Manifest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.example.lightbox.permission.CheckMyPermission;
import com.example.lightbox.permission.MyPermission;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private String imagePath;
    private CheckMyPermission checkMyPermission;

    public String getImagePath() {
        return imagePath;
    }

    public void setImagePath(String imageUri) {
        this.imagePath = imageUri;
    }

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

        // 必要なパーミッションのリスト
        ArrayList<MyPermission> myPermission_list = new ArrayList<MyPermission>();
        myPermission_list.add(new MyPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,100));
        // Runtime Permission 用のクラスのインスタンス
        checkMyPermission = new CheckMyPermission(MainActivity.this,myPermission_list);

    }

    // 全ての必要なパーミッションが既に許可されているかどうか
     protected boolean  checkMyPermission() {
        return checkMyPermission.checkPermission();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // 全ての必要なパーミッションが許可された場合
        if ( checkMyPermission.checkPermission( requestCode,  grantResults )) {
            // onCreate で初期処理できるように、MainActivity をリスタート
            Intent intent = getIntent();
            finish();
            startActivity(intent);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putString("image_uri", imagePath);
    }

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

        imagePath = savedInstanceState.getString("image_uri");
    }
}


CameraCallActivity
package com.example.lightbox.cameracalltest;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class CameraCallActivity extends MainActivity {

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

        // 画面表示は super 側
        Log.i("lightbox", "CameraCallActivity : onCreate");

        if ( CameraCallActivity.this.checkMyPermission() ) {
            // 通常処理
            initSettings();
        }
    }

    private void initSettings() {

        // ボタンイベントの登録
        Button button = (Button) CameraCallActivity.this.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("lightbox", "クリックされました");

                callCamera();
            }
        });

    }

    // カメラの呼び出し
    private void callCamera() {

        Intent intent = new Intent();
        intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

        String imageDir = Environment.getExternalStorageDirectory().getPath() + "/cameratest";
        File file = new File(imageDir);
        // ディレクトリ初期作成
        if (!file.exists()) {
            if (file.mkdir() == false) {
                Log.i("lightbox", "ディレクトリを作成できませんでした");
                return;
            }
        }

        // ギャラリー用画像保存パス
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
        String imagePath = imageDir + "/" + sf.format(cal.getTime()) + ".jpg";
        Log.i("lightbox",  "imagePath : " + imagePath );
        // 画像ファイルの パス を保存
        CameraCallActivity.this.setImagePath(imagePath);
        file = new File( imagePath );

        // 保存してほしい URI を引き渡す
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));

        CameraCallActivity.this.startActivityForResult(intent, 101);

    }

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

        if ( requestCode == 101 ) {
            if ( resultCode == Activity.RESULT_OK ) {

                // 画像表示
                Bitmap bitmap = BitmapFactory.decodeFile(CameraCallActivity.this.getImagePath());
                ImageView imageView = (ImageView) CameraCallActivity.this.findViewById(R.id.imageView);
                imageView.setImageBitmap(bitmap);

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


AndroidManifest.xml

実行するアクティビティは、CameraCallActivity なので、修正が必要です( 12行目 )
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.lightbox.cameracalltest">

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

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

</manifest>



画面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.lightbox.cameracalltest.MainActivity">

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:srcCompat="@mipmap/ic_launcher" />
</LinearLayout>




posted by lightbox at 2017-06-10 15:08 | 2017 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 ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり