P94 ギャラリー

Android開発に関するメモランダム

カテゴリー: Androidプログラミング入門 - 独りで学べるスマホアプリの作り方 -  閲覧数:305 配信日:2018-05-14 13:12


コード


▼/sample4/MainActivity.java
package biz.answerlead.sample4;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.io.File;
import java.util.Arrays;
import java.util.Comparator;

/** メインアクティビティ */
public class MainActivity extends AppCompatActivity implements OnClickListener {

/** カメラ撮影用リクエストコード */
private final static int FILE_SELECT_CODE = 1;

/** デフォルトの行数,列数 */
private final static int DEFAULT_ROW_NUM = 2, DEFAULT_COLUMN_NUM = 3;

/** サムネイル読込みサイズ */
private final static int THUMBNAIL_WIDTH = 160, THUMBNAIL_HEIGHT = 90;

/** 行数、列数 */
private int column_num, row_num;

/** カメラ撮影用一時ファイル */
File temp_file;

/** ページ管理View */
ViewPager view_pager;

/** 初期化処理 */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 設定ボタンとカメラ起動ボタンにクリックイベントを設定
findViewById(R.id.ConfigButton).setOnClickListener(this);
findViewById(R.id.LaunchCameraButton).setOnClickListener(this);

// プリファレンスから設定を読込み
SharedPreferences pref = getPreferences(MODE_PRIVATE);
column_num = pref.getInt("column_num", DEFAULT_COLUMN_NUM);
row_num = pref.getInt("row_num", DEFAULT_ROW_NUM);

// ページ管理Viewを取得
view_pager = (ViewPager) findViewById(R.id.PictureArea);

// カメラ撮影用一時ファイルにパスを設定
temp_file = new File(getExternalCacheDir(), "temp.jpg");
}

/** 画面表示イベント(初回も呼ばれる) */
@Override
protected void onResume() {
super.onResume();

// ファイル一覧を取得
String[] file_paths = fileList();

// ファイル名で降順ソート(最新が1番目に来るようソート)
Arrays.sort(file_paths, new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
return rhs.compareTo(lhs);
}
});

// ページ管理Viewにファイル一覧を設定
view_pager.setAdapter(new PicturePagerAdapter(file_paths));
}

/** クリックイベント */
@Override
public void onClick(View v) {
switch (v.getId()) {

// 設定ボタン
case R.id.ConfigButton:

// レイアウトファイル読込み
final View view = getLayoutInflater().inflate(R.layout.config_popup, null);

// 読み込んだレイアウトファイルから各Viewを取得
((EditText) view.findViewById(R.id.RowEditText)).setText(String.valueOf(row_num));
((EditText) view.findViewById(R.id.ColumnEditText)).setText(String.valueOf(column_num));

// ダイアログを作成
final AlertDialog.Builder builder = new AlertDialog.Builder(this);

// ダイアログにタイトルとメッセージを設定
builder.setTitle(getString(R.string.config));
builder.setMessage(getString(R.string.config_message));

// ダイアログに読み込んだViewを設定
builder.setView(view);

// ダイアログにキャンセルボタンを設定
builder.setNegativeButton(R.string.cancel, null);

// ダイアログにOKボタンを設定(イベントを匿名クラスで実装)
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {

// ボタン選択イベント
@Override
public void onClick(DialogInterface dialog, int which) {

// 入力内容を取得
String row_text = ((EditText) view.findViewById(R.id.RowEditText)).getText().toString();
String column_text = ((EditText) view.findViewById(R.id.ColumnEditText)).getText().toString();

// 数値に変換(最低1とする)
row_num = Math.max(Integer.parseInt(row_text), 1);
column_num = Math.max(Integer.parseInt(column_text), 1);

// プリファレンスに設定を保存
SharedPreferences pref = getPreferences(MODE_PRIVATE);
Editor editor = pref.edit();
editor.putInt("column_num", column_num);
editor.putInt("row_num", row_num);
editor.apply();

// ページ管理Viewを更新
view_pager.setAdapter(view_pager.getAdapter());
}
});

// ダイアログを表示
builder.show();

break;

// カメラ起動ボタン
case R.id.LaunchCameraButton:

// カメラを起動する
Intent intent_camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent_camera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(temp_file));
startActivityForResult(intent_camera, FILE_SELECT_CODE);
break;

// 一覧データ選択
case R.id.PictureBlock:

// DetailActivityに遷移
Intent intent = new Intent(this.getApplicationContext(), DetailActivity.class);
intent.putExtra(DetailActivity.INTENT_KEY_DATA, (PictureData) v.getTag());
startActivity(intent);
break;
}
}

/** Activityの戻り値を取得するイベント */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

// リクエストコードが「FILE_SELECT_CODE」のイベントであれば
if (requestCode == FILE_SELECT_CODE) {

// 結果がOKであれば
if (resultCode == RESULT_OK) {

// DetailActivityに遷移
Intent intent = new Intent(this.getApplicationContext(), DetailActivity.class);
intent.putExtra(DetailActivity.INTENT_KEY_FILE, temp_file);
intent.putExtra(DetailActivity.INTENT_KEY_IS_NEW, true);
startActivity(intent);
}
}
}

/** ページ管理アダプタークラス */
public class PicturePagerAdapter extends PagerAdapter {

/** ファイルパス一覧 */
String[] file_paths = null;

/** コンストラクタ */
public PicturePagerAdapter(String[] file_paths) {
super();
this.file_paths = file_paths;
}

/** ページ数返却 */
@Override
public int getCount() {
int count = (int) Math.ceil((double) file_paths.length / (column_num * row_num));
return Math.max(count, 1);
}

/** ページを作成する */
@Override
public Object instantiateItem(ViewGroup container, int position) {

// 撮影データがあるかどうか
if (file_paths.length > 0) {

// LinearLayout(垂直方向)を動的に生成する
LinearLayout lines = new LinearLayout(MainActivity.this);

// LinearLayoutに各属性値を設定
lines.setOrientation(LinearLayout.VERTICAL);
lines.setWeightSum(row_num);

// レイアウトパラメータを作成(横幅最大、縦幅最大)
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);

// ページのコンテナにLinearLayoutを追加
container.addView(lines, lp);

// 表示するデータの開始インデックス(ページ数×1ページあたりの件数)
int index = position * column_num * row_num;

// 行数分ループ
ROW:
for (int y = 0; y < row_num; y++) {

// LinearLayout(水平方向)を生成
LinearLayout line = new LinearLayout(MainActivity.this);
line.setOrientation(LinearLayout.HORIZONTAL);
line.setWeightSum(column_num);

// 横幅最大、縦幅0、重み1
lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1);

// 外側のLinearLayoutに追加
lines.addView(line, lp);

// 列数分ループ
for (int x = 0; x < column_num; x++) {

// インデックスがデータ件数以上となった場合はbreak
if (file_paths.length <= index) {
break ROW;
}

// 撮影データ取得
PictureData data = PictureData.load(MainActivity.this, file_paths[index++]);
if (data != null) {

// レイアウトファイル読込み
View picture_block = getLayoutInflater().inflate(R.layout.picture_block, line, false);

// 読み込んだViewに撮影データをタグとして設定
picture_block.setTag(data);

// Viewにクリックイベントを設定
picture_block.setOnClickListener(MainActivity.this);

// Viewから日時表示欄とメモ表示欄を取得し、情報を表示
((TextView) picture_block.findViewById(R.id.DateTextView)).setText(data.getDateText());
((TextView) picture_block.findViewById(R.id.MemoTextView)).setText(data.memo);

// サムネイル画像を取得して表示
ImageView image_view = ((ImageView) picture_block.findViewById(R.id.ThumbnailImage));
File picture_file = data.getPictureFile(MainActivity.this);
image_view.setImageBitmap(ImageLoader.loadBitmap(picture_file, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT));

// 行に追加
line.addView(picture_block);
}
}
}

// 作成したViewを返却
return lines;
} else {

// 撮影データがない場合はデータが無い旨を表示
TextView text_view = new TextView(MainActivity.this);
text_view.setText(R.string.data_nothing);
container.addView(text_view);

// 作成したViewを返却
return text_view;
}
}

/** instantiateItemで返却したアイテムと表示中のViewを比較して、等しい場合はtrueを返す関数 */
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}

/** 不要になったページを削除 */
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
}


▼/sample4/DetailActivity.java
package biz.answerlead.sample4;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;

/** 詳細画面 */
public class DetailActivity extends AppCompatActivity implements OnClickListener {

/** 表示画像読込みサイズ */
private static final int PICTURE_WIDTH = 1600, PICTURE_HEIGHT = 900;

/** インテント用キー */
public static final String
INTENT_KEY_FILE = "file",
INTENT_KEY_IS_NEW = "is_new",
INTENT_KEY_DATA = "data";

/** 表示画像ファイル */
private File file;

/** 撮影データ */
private PictureData data;

/** 新規撮影フラグ */
boolean is_new;

/** 初期化処理 */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);

// Intentを取得
Intent intent = getIntent();

// Intentから各種情報を取得
is_new = intent.getBooleanExtra(INTENT_KEY_IS_NEW, false);
if (is_new) {
// 新規の場合は空の撮影データと、撮影されたファイルを取得
data = new PictureData();
file = (File) intent.getSerializableExtra(INTENT_KEY_FILE);

// 新規撮影の場合はDeleteボタンは非活性に
findViewById(R.id.DeleteButton).setEnabled(false);
} else {
// 新規でない場合は撮影データと、対応する画像ファイルを取得
data = (PictureData) intent.getSerializableExtra(INTENT_KEY_DATA);
file = data.getPictureFile(this);
}

// 日付を表示
((TextView) findViewById(R.id.DateTextView)).setText(data.getDateText());

// メモの内容を復元
((EditText) findViewById(R.id.MemoEditText)).setText(data.memo);

// 各ボタンにイベントを設定
findViewById(R.id.CancelButton).setOnClickListener(this);
findViewById(R.id.DeleteButton).setOnClickListener(this);
findViewById(R.id.SaveButton).setOnClickListener(this);

// 画像ファイルを読み込んで表示
Bitmap bitmap = ImageLoader.loadBitmap(file, PICTURE_WIDTH, PICTURE_HEIGHT);
((ImageView) findViewById(R.id.DetailImage)).setImageBitmap(bitmap);
}

/** ビュークリックイベント */
@Override
public void onClick(View v) {
switch (v.getId()) {

// キャンセルボタンの場合
case R.id.CancelButton:

// 画面を閉じる
finish();
break;

// 削除ボタンの場合
case R.id.DeleteButton:

// 確認ダイアログ
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.confirm);
builder.setMessage(R.string.delete_message);
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

// 削除に成功した場合は画面を閉じて終了
if (data.delete(DetailActivity.this)) {
finish();
}
}
});
builder.create().show();

break;

// 保存ボタンの場合
case R.id.SaveButton:

// 新規の場合は、撮影した一時ファイルを保管場所に移動
if (is_new) {
File dst = data.getPictureFile(this);

// ディレクトリがなければ作成
boolean directory_exists = dst.getParentFile().exists() || dst.getParentFile().mkdirs();

// ディレクトリが存在し、ファイル移動に成功したら
boolean result = directory_exists && file.renameTo(dst);

// 失敗時はエラーを表示
if (!result) {
Toast.makeText(this, getString(R.string.save_failed), Toast.LENGTH_SHORT).show();
break;
}
}

// メモの内容を設定
data.memo = ((EditText) findViewById(R.id.MemoEditText)).getText().toString();

// 撮影データを保存
data.save(this);

// 画面を閉じる
finish();

break;
}
}
}


▼/sample4/ImageLoader.java
package biz.answerlead.sample4;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/** 画像読み込みユーティリティクラス */
public class ImageLoader {

/** ファイルから画像を読み込む */
public static Bitmap loadBitmap(File file, int width, int height) {

try {
// コンテンツプロバイダにURIを渡し、入力ストリームを取得
InputStream input = new FileInputStream(file);

// 画像サイズを取得するOptionを作成
BitmapFactory.Options get_size_option = new BitmapFactory.Options();

// データは読み込まず、画像サイズのみ取得するよう設定
get_size_option.inJustDecodeBounds = true;

// 画像サイズを取得(get_size_option変数のメンバ(outWidth,outHeight)に格納)
BitmapFactory.decodeStream(input, null, get_size_option);

// 入力ストリームを一旦閉じる
input.close();

// 読込みたいサイズとの比率を取得(最低1)
int ratio = Math.max(Math.min(get_size_option.outWidth / width, get_size_option.outHeight / height), 1);

// 再度入力ストリームを取得
input = new FileInputStream(file);

// 実際に画像データを取得するためのOption
BitmapFactory.Options options = new BitmapFactory.Options();

// 読み込むデータのサンプリングの間隔を指定(読込みたいサイズの4倍なら、読み込むのは4分の1でよいため)
// ※ただし、実際は2の累乗数に丸められる。5~7を指定しても、4と同じ扱いとなる。
options.inSampleSize = ratio;

// 画像データを取得
Bitmap bitmap = BitmapFactory.decodeStream(input, null, options);

// 入力ストリームを閉じる
input.close();

// 読み込んだ画像のExif情報を取得
ExifInterface exif = new ExifInterface(file.getAbsolutePath());

// Exif情報から画像の向きを取得
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

// 画像の向きが、通常か不明でなければ
if (orientation != ExifInterface.ORIENTATION_NORMAL && orientation != ExifInterface.ORIENTATION_UNDEFINED) {

// 回転・反転用Matrix
Matrix mat = new Matrix();

// 要左右反転の向きなら、左右反転を行う
switch (orientation) {
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
case ExifInterface.ORIENTATION_TRANSPOSE:
case ExifInterface.ORIENTATION_TRANSVERSE:
mat.postScale(-1, 1);
break;
}

// 向きに応じて回転
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
mat.postRotate(270);
break;

case ExifInterface.ORIENTATION_ROTATE_180:
case ExifInterface.ORIENTATION_TRANSPOSE:
mat.postRotate(180);
break;

case ExifInterface.ORIENTATION_ROTATE_90:
case ExifInterface.ORIENTATION_TRANSVERSE:
mat.postRotate(90);
break;
}

// Matrixを使用して新しいBitmapを作成
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mat, true);
}

// bitmapを返却
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}

return null;
}
}


▼/sample4/PictureData.java
package biz.answerlead.sample4;

import android.content.Context;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/** 撮影データクラス */
public class PictureData implements Serializable {

/** シリアライズ時のチェック用UID(値はユニークであれば何でも良い) */
static final long serialVersionUID = -5566246729549155833L;

/** 表示用日時フォーマット */
private static final DateFormat DATE_FORMAT_FOR_DISPLAY = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());

/** ファイル名用日時フォーマット */
private static final DateFormat DATE_FORMAT_FOR_FILE_NAME = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());

/** 撮影日時 */
private Date date;

/** メモ */
public String memo;

/** コンストラクタ */
public PictureData() {
this.date = new Date();
this.memo = "";
}

/** 表示用日時を返却 */
public String getDateText() {
return DATE_FORMAT_FOR_DISPLAY.format(date);
}

/** 各ファイルに使用するファイル名を返却 */
private String getDataFileName() {
return DATE_FORMAT_FOR_FILE_NAME.format(date);
}

/** 画像ファイルのパスを返却 */
public File getPictureFile(Context context) {
return new File(context.getExternalFilesDir(null), getDataFileName() + ".jpg");
}

/** 撮影データを保存する */
public boolean save(Context context) {
try {
/** 撮影日時をファイル名として、OutputStreamを開く */
FileOutputStream fos = context.openFileOutput(getDataFileName(), Context.MODE_PRIVATE);

/** オブジェクトをSerializeして保存するため、ObjectOutputStreamでラップ */
ObjectOutputStream oos = new ObjectOutputStream(fos);

/** オブジェクトをSerializeして保存 */
oos.writeObject(this);

/** OutputStreamを閉じる */
oos.close();

return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}

/** ファイル名から撮影データを読込み */
public static PictureData load(Context context, String name) {
try {
// InputStreamを開く
FileInputStream fis = context.openFileInput(name);

// Serializeされたオブジェクトを読み込むため、ObjectInputStreamでラップ
ObjectInputStream ois = new ObjectInputStream(fis);

// Serializeされたオブジェクトを復元
PictureData data = (PictureData) ois.readObject();

// InputStreamを閉じる
ois.close();

return data;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}

/** 撮影データを削除する */
public boolean delete(Context context) {

// 撮影データファイル
File this_file = new File(context.getFilesDir(), getDataFileName());

// 画像ファイル
File picture_file = getPictureFile(context);

// 削除
return this_file.delete() && picture_file.delete();
}
}


週間人気ページランキング / 2-16 → 2-22
順位 ページタイトル抜粋 アクセス数
アクセスが、ありませんでした! 0
2025/2/23 1:01 更新
指定期間人気ページランキング / 1970-1-1 → 2025-2-22
順位 ページタイトル抜粋 アクセス数
アクセスが、ありませんでした! 0
2025/2/23 1:01 更新