Androidプログラミング入門 - 独りで学べるスマホアプリの作り方 -

書籍 Androidプログラミング入門 - 独りで学べるスマホアプリの作り方 -

Link

 状態:-  閲覧数:1,015  投稿日:2018-05-09  更新日:2018-05-09

P17 数当てゲーム

 閲覧数:273 投稿日:2018-05-09 更新日:2018-06-19

エラー対応


Unsupported method: BaseConfig.getApplicationIdSuffix().

範囲の選択項目


ArrayAdapterを使用して用意

選択項目に使用するユーザ定義クラスRange
・ジェネリクスを使用して指定
ArrayAdapter<Range> adapter = new ArrayAdapter<>(
       this,
       android.R.layout.simple_spinner_dropdown_item,
       RANGES
);

ArrayAdapterクラス
・データの一覧をリストなどのビューに渡すために使用されるクラス
ジェネリクス(Generics)

コード


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

import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

/** メイン画面。画面を担当するクラスは、Activityクラスを継承する必要がある */
public class MainActivity extends Activity implements AdapterView.OnItemSelectedListener, View.OnClickListener, TextView.OnEditorActionListener {

   /** デバッグログ用タグ */
   private static final String TAG = "SAMPLE_LOG";

   /** 範囲の種類を定義 */
   private static final Range[] RANGES = new Range[]{
           new Range(1, 100),
           new Range(1, 1000),
           new Range(1, 10000)
   };

   /** 範囲選択用スピナー */
   private Spinner range_spinner;

   /** 答え入力欄 */
   private EditText answer_edit_text;

   /** 答え決定ボタン */
   private Button answer_button;

   /** 正解値 */
   private int right_value;

   @Override
   protected void onCreate(Bundle savedInstanceState) {

       // デバッグログを出力
       Log.d(TAG, "onCreate");

       // 親クラスのonCreateを呼ぶ
       super.onCreate(savedInstanceState);

       // layoutの内容を画面に反映。レイアウトのリソースを読み込んで画面に反映するメソッド。プログラム中でリソースを参照する場合は、自動で作成されるRクラスに定義されているリソースIDを使用する
       setContentView(R.layout.activity_main);

       // スピナーに選択項目を設定するためのアダプターを作成
       ArrayAdapter<Range> adapter = new ArrayAdapter<>(
               this,
               android.R.layout.simple_spinner_dropdown_item,
               RANGES
       );

       // IDから範囲選択スピナーのインスタンスを取得
       range_spinner = (Spinner) findViewById(R.id.rangeSpinner);

       // スピナーにアダプターを設定
       range_spinner.setAdapter(adapter);

       // スピナーの項目が選択された際のイベントを設定
       range_spinner.setOnItemSelectedListener(this);

       // 答え入力欄に、キーボードの「実行」ボタンが押された時のイベントを設定
       answer_edit_text = (EditText) findViewById(R.id.answerEditText);
       answer_edit_text.setOnEditorActionListener(this);

       // 答えの決定ボタンに、押された際のイベントを設定
       answer_button = (Button) findViewById(R.id.answerButton);
       answer_button.setOnClickListener(this);
   }

   @Override
   public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

       // デバッグログを出力
       Log.d(TAG, "onItemSelected");

       // イベントが発生したViewのIDで処理を分岐
       switch (parent.getId()) {

           // 範囲選択スピナーならば
           case R.id.rangeSpinner:

               // 正解の値を抽選
               chooseValue();
               break;
       }
   }

   @Override
   public void onNothingSelected(AdapterView<?> parent) {
       // 項目の選択が解除された際のイベント。Spinnerでは発生しない。
   }

   /** 正解の値を抽選するメソッド */
   private void chooseValue() {

       // 範囲選択スピナーから選択されている項目を取得
       Range range = (Range) range_spinner.getSelectedItem();

       // 正解の値を抽選
       right_value = range.getRandomValue();
   }

   @Override
   public void onClick(View v) {

       // デバッグログを出力
       Log.d(TAG, "onClick");

       // イベントが発生したViewのIDで処理を分岐
       switch (v.getId()) {

           // 答え決定ボタンならば
           case R.id.answerButton:

               // 正解チェック
               answerCheck();
               break;
       }
   }

   @Override
   public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

       // デバッグログを出力
       Log.d(TAG, "onEditorAction");

       // イベントが発生したViewのIDで処理を分岐
       switch (v.getId()) {

           // 答え入力欄ならば
           case R.id.answerEditText:

               // 「実行」イベントならば
               if (actionId == EditorInfo.IME_ACTION_GO) {

                   // 正解チェック
                   answerCheck();

                   // デフォルトの動作をキャンセル
                   return true;
               }
               break;
       }
       return false;
   }

   /** 正解判定 */
   private void answerCheck() {

       // 入力内容を取得
       String input_text = answer_edit_text.getText().toString();

       // 判定結果を格納する変数
       String result;

       try {
           // 入力内容は文字列として取得されているので、数値に変換
           int answer = Integer.parseInt(input_text);

           // 正解値の方が大きい場合
           if (answer < right_value) {

               // リソースから文字列を取得し、%dに入力値を当てはめる
               result = String.format(getString(R.string.bigger), answer);
           }
           // 正解値の方が小さい場合
           else if (answer > right_value) {

               // リソースから文字列を取得し、%dに入力値を当てはめる
               result = String.format(getString(R.string.smaller), answer);
           }
           // どちらでもない(等しい)場合
           else {

               // リソースから文字列を取得する
               result = getString(R.string.right);

               // 値を再抽選する
               chooseValue();
           }
       } catch (NumberFormatException e) {

           // 何らかの不正な値が入力された場合はエラーメッセージをリソースから取得
           result = getString(R.string.error_input);
       }

       // IDから結果表示欄のインスタンスを取得
       EditText result_edit_text = (EditText) findViewById(R.id.resultEditText);

       // 結果表示欄の現在のテキストを取得
       Editable text = result_edit_text.getText();

       // 現在のテキストが空でなければ改行を結合
       if (text.length() > 0) {
           text.append("\n");
       }

       // 今回の結果を結合
       text.append(result);

       // 結果表示欄を末端までスクロール
       result_edit_text.setSelection(text.length());

       // 答え入力欄を空にする
       answer_edit_text.setText("");
   }
}


▼biz/answerlead/sample1/Range.java
package biz.answerlead.sample1;

/** 値の範囲を管理するクラス */
class Range {

   /** 最小値 */
   private int min;

   /** 最大値 */
   private int max;

   /** コンストラクタ */
   public Range(int min, int max) {
       this.min = min;
       this.max = max;
   }

   /** 範囲の中から整数を取得する */
   public int getRandomValue(){
       return (int)Math.floor(Math.random() * (max - min + 1)) + min;
   }

   @Override
   public String toString() {
       return String.valueOf(min + "~" + max);
   }
}


P45 ドラム

 閲覧数:270 投稿日:2018-05-14 更新日:2018-05-16

コード


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

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

import biz.answerlead.sample2.Drum.Type;

/** メイン画面 */
public class MainActivity extends AppCompatActivity implements OnTouchListener {

/** ドラム音管理オブジェクト */
private final Drum drum = new Drum();

/** ドラム画像横幅 */
private static final int DRUM_WIDTH = 1920;

/** ドラム画像縦幅 */
private static final int DRUM_HEIGHT = 1411;

/** 初期化イベント */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// レイアウト設定
setContentView(R.layout.activity_main);

// ドラム描画領域設定
ImageView drum_image = (ImageView) findViewById(R.id.DrumImage);
drum_image.setOnTouchListener(this);
}

/** アプリ開始時、再開時イベント */
@Override
protected void onResume() {
super.onResume();

// 指定ドラム音の読み込み
drum.load(this);
}

/** アプリ中断時イベント */
@Override
protected void onPause() {
super.onPause();

// アプリ未使用時はメモリ節約のため、ドラム音データを開放
drum.release();
}

/** 画面タッチイベント */
@Override
public boolean onTouch(View v, MotionEvent event) {

// 動作ID
int action = event.getActionMasked();

// ポインタID
int index = event.getActionIndex();

// タッチX座標
double touch_x = event.getX(index);

// タッチY座標
double touch_y = event.getY(index);

// 画面横幅
int wx = v.getWidth();

// 画面縦幅
int wy = v.getHeight();

// 動作IDからログ表示用文字列を設定
String action_name;
switch (action) {
case MotionEvent.ACTION_DOWN:
action_name = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
action_name = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
action_name = "ACTION_UP";
break;
case MotionEvent.ACTION_POINTER_DOWN:
action_name = "ACTION_POINTER_DOWN";
break;
case MotionEvent.ACTION_POINTER_UP:
action_name = "ACTION_POINTER_UP";
break;
default:
action_name = "OTHER";
break;
}

// ログ出力
Log.v("Touch", String.format(
"A:%20s P:%d/%d X:%5.1f Y:%5.1f",
action_name,
index, event.getPointerCount(),
touch_x, touch_y));

// タッチ開始イベントの場合
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {

// 画面と用意した画像の比
double multi;

// 画面の縦横比と用意した画像の縦横比を比べて処理を分岐
if (wx * DRUM_HEIGHT > wy * DRUM_WIDTH) {
// 画面の方が横長な場合は、縦の比率を基準に
multi = (double) DRUM_HEIGHT / wy;
} else {
// 画面の方が縦長な場合は、横の比率を基準に
multi = (double) DRUM_WIDTH / wx;
}

// タッチした座標を画像上での座標に変換
double x = (touch_x - wx / 2.0) * multi + DRUM_WIDTH / 2.0;
double y = (touch_y - wy / 2.0) * multi + DRUM_HEIGHT / 2.0;

// バスドラム部分(円で判定)
if (getDistance(x, y, 960, 960) < 361) {
drum.play(Type.BASS);
}

// ロータム部分
if ((getDistance(x, y, 388, 689) < 261) || (getDistance(x, y, 1532, 689) < 261)) {
drum.play(Type.LOW_TOM);
}

// ハイタム部分
if ((getDistance(x, y, 714, 244) < 196) || (getDistance(x, y, 1206, 244) < 196)) {
drum.play(Type.HI_TOM);
}

// シンバル部分
if ((getDistance(x, y, 288, 127) < 238) || (getDistance(x, y, 1632, 127) < 238)) {
drum.play(Type.CYMBAL);
}

// 座標を情報をデバッグログに表示
Log.d("DEBUG", String.format("画面サイズ(%4d, %4d) 変換前座標(%4.0f, %4.0f) 変換後座標(%4.1f, %4.1f)", wx, wy, touch_x, touch_y, x, y));
}

// 続けてタッチイベントを取得する場合はtrueを返却
// ※falseの場合は1点目のタッチ開始イベントしか掴めない
return true;
}

/** 2点間の距離を求める関数(三平方の定理) */
private double getDistance(double x1, double y1, double x2, double y2) {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
}


▼/sample2/Drum.java
package biz.answerlead.sample2;

import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;

/** ドラム音管理クラス */
public class Drum {

   /** 再生音種別 */
   public enum Type {
       CYMBAL,
       HI_TOM,
       LOW_TOM,
       BASS,
   }

   /** 効果音数 */
   private static final int SOUND_NUM = Type.values().length;

   /** 読込み済みフラグ */
   private boolean loaded = false;

   /** 音声管理オブジェクト */
   private final SoundPool sound_pool;

   /** 効果音IDリスト */
   private final Integer[] sound_ids = new Integer[SOUND_NUM];

   /** 初期化処理 */
   public Drum() {

       // 音声管理機能初期化(バージョンにより分岐)
       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {

           // 5.0未満のSoundPool作成方法
           sound_pool = new SoundPool(SOUND_NUM, AudioManager.STREAM_MUSIC, 0);
       } else {

           // 5.0以降のSoundPool作成方法

           // 再生する効果音がどういうものなのか用途とデータ種別を指定する
           // 今回は最も近い用途としてUSAGE_GAME(ゲーム用)と、
           // CONTENT_TYPE_SONIFICATION(ユーザアクションに合わせて再生されるデータ)を指定
           AudioAttributes attr = new AudioAttributes.Builder()
                   .setUsage(AudioAttributes.USAGE_GAME)
                   .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();

           // 属性と最大再生音数を指定し、SoundPoolを作成
           sound_pool = new SoundPool.Builder()
                   .setAudioAttributes(attr)
                   .setMaxStreams(SOUND_NUM)
                   .build();
       }
   }

   /** 指定ドラム音データ読込み */
   public void load(Context context) {

       // まだ読込みが完了していなければ
       if (!loaded) {

           // 効果音読込み
           sound_ids[Type.CYMBAL.ordinal()] = sound_pool.load(context, R.raw.cymbal, 1);
           sound_ids[Type.HI_TOM.ordinal()] = sound_pool.load(context, R.raw.hi_tom, 1);
           sound_ids[Type.LOW_TOM.ordinal()] = sound_pool.load(context, R.raw.low_tom, 1);
           sound_ids[Type.BASS.ordinal()] = sound_pool.load(context, R.raw.bass, 1);

           // 読込み済みとする
           loaded = true;
       }
   }

   /** 全てのドラム音データを開放 */
   public void release() {

       // 読込み済み効果音を開放
       for (int i = 0; i < SOUND_NUM; i++) {
           if (sound_ids[i] != null) {
               sound_pool.unload(sound_ids[i]);
               sound_ids[i] = null;
           }
       }

       // 未読込みに戻す
       loaded = false;
   }

   /** 指定音階の再生 */
   public void play(final Type sound) {

       // 指定Typeのインデックスを取得
       final int index = sound.ordinal();

       // 指定音階の有効値チェック、及び指定音階の読込み済みチェック
       if (sound_ids[index] != null) {

           // 音データを再生
           sound_pool.play(
                   sound_ids[index], // 効果音ID
                   1.0f,               // 左音量
                   1.0f,               // 右音量
                   0,                  // 優先度
                   0,                  // ループ回数
                   1                   // 再生速度
           );
       }
   }
}


P65 ボール転がし

 閲覧数:255 投稿日:2018-05-14 更新日:2018-05-17

コード


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

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

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

/** メイン画面 */
public class MainActivity extends AppCompatActivity implements SensorEventListener, SurfaceHolder.Callback {

/** ファイル選択インテント用リクエストコード */
private static final int FILE_SELECT_CODE = 1;

/** メニュー項目用ID */
private static final int MENU_SELECT_PICTURE = 1;

/** 画像読み込みサイズ */
private static final int PICTURE_SIZE = 300;

/** 1ループの時間 */
private static final int LOOP_DELAY = 20;

/** 減速率 */
private static final float SPEED_DECREASE = 0.97f;

/** 描画領域のビュー */
SurfaceView screen;

/** 描画領域管理オブジェクト */
SurfaceHolder screen_holder;

/** ボール画像 */
Bitmap ball_image;

/** ボール座標 */
float ball_x, ball_y;

/** ボール速度 */
float vx, vy;

/** ボール加速度 */
float ax, ay;

/** タイマー処理用ハンドラ */
Handler handler = new Handler();

// センサマネージャを取得
SensorManager sensor_manager;

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

/** 初期化処理 */
@Override
protected void onCreate(Bundle savedInstanceState) {

// 用意された初期化処理
super.onCreate(savedInstanceState);

// 画面を設定
setContentView(R.layout.activity_main);

// SurfaceViewからSurfaceHolderを取得し、コールバックを設定する
screen = (SurfaceView) findViewById(R.id.Screen);
screen.getHolder().addCallback(this);

// センサマネージャを取得
sensor_manager = (SensorManager) getSystemService(SENSOR_SERVICE);

// 繰り返し処理
timer_event.run();

// 一時ファイルパスを作成
temp_file = new File(getExternalCacheDir(), "temp.jpg");

// 画像選択処理
selectPicture();
}

/** 画像選択イベント */
private void selectPicture() {

// 画像ファイル選択Intentを作成
Intent intent_file = new Intent(Intent.ACTION_GET_CONTENT);
intent_file.setType("image/*");

// カメラ起動Intentを作成
Intent intent_camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent_camera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(temp_file));

// カメラ起動Intentとファイル選択Intentを結合
Intent intent = Intent.createChooser(intent_camera, getString(R.string.select_picture));
intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{intent_file});

// Intentを戻り値有りで発行(リクエストコードに「FILE_SELECT_CODE」を指定)
startActivityForResult(intent, FILE_SELECT_CODE);
}

/** 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) {

// 読込みURI
Uri uri;

// data.getDataの存在チェック
if (data != null && data.getData() != null) {

// data.getDataが空でなければ(ファイル選択)そのURIから読込み
uri = data.getData();
} else {

// data.getDataが空の場合(カメラ)、一時ファイルを指すURIを使用
uri = Uri.fromFile(temp_file);
}

// URIから画像を読込み
Bitmap bitmap = loadBitmap(uri);

// 一時ファイルを削除
if (temp_file.exists() && !temp_file.delete()) {
Log.e("ERROR", "Cannot delete file.");
}

// Bitmapが空でなければ
if (bitmap != null) {

// 丸型に切り出してボール画像とする
ball_image = getCircularImage(bitmap);
} else {

// 画像読み込みが失敗したことをToastで表示する
Toast.makeText(this, getString(R.string.load_failed), Toast.LENGTH_SHORT).show();
}
} else {

// 画像読み込みがキャンセルされたことをToastで表示する
Toast.makeText(this, getString(R.string.load_canceled), Toast.LENGTH_SHORT).show();
}
}
}

/** ファイルから画像を読み込む */
private Bitmap loadBitmap(Uri uri) {

try {
// URIから入力ストリームを取得
InputStream input = openInputStreamFromUri(uri);

// 画像サイズを取得する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();

// 短い方の辺を取得
int short_side = Math.min(get_size_option.outWidth, get_size_option.outHeight);

// 読込みたいサイズとの比率を取得(最低1)
int ratio = Math.max(short_side / PICTURE_SIZE, 1);

// 再度入力ストリームを取得
input = openInputStreamFromUri(uri);

// 実際に画像データを取得するための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();

// 再度入力ストリームを取得
input = openInputStreamFromUri(uri);

// 画像のExif情報を取得して、回転が必要かチェックする
// Orientation(向き)を示す値を取得する
int orientation = OrientationReader.get(input);

// 画像の向きが、通常か不明でなければ
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;
}

/** URIからファイルパスを取得 */
private InputStream openInputStreamFromUri(Uri uri) throws IOException {

// 選択結果のURIをデバッグログに表示
Log.d("DEBUG", "Selected File URI = " + uri.toString());

// URIのスキームをチェック
switch (uri.getScheme()) {

// コンテンツの場合はコンテンツプロバイダを通してInputStreamを返す
case "content":
return getContentResolver().openInputStream(uri);

// ファイルの場合はgetPathでファイルパスが得られるので、FileInputStreamを返す
case "file":
return new FileInputStream(uri.getPath());

// その他はサポートしないものとして例外を発生
default:
throw new IOException("Not support URI.");
}
}

/** Bitmapを円形の画像に切り出す */
private Bitmap getCircularImage(Bitmap original) {

// ベースとなるBitmapを作成
Bitmap bitmap = Bitmap.createBitmap(PICTURE_SIZE, PICTURE_SIZE, Config.ARGB_8888);

// 中心座標をあらかじめ計算
float half = PICTURE_SIZE / 2.0f;

// 描画用のキャンバスを作成
Canvas canvas = new Canvas(bitmap);

// 白でアンチエイリアス有りのPaint設定
Paint white = new Paint();
white.setColor(Color.WHITE);
white.setAntiAlias(true);

// Bitmapに白で塗りつぶした円を描く
canvas.drawCircle(half, half, half, white);

// 選択画像描画用のPaint設定
// Xfermodeに「SRC_ATOP」を指定すると、現在透明な部分には描画されないようマスク出来る。
// つまり、上で描画した円の外には描画されない設定のPaintとなる。
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));

// 画像を中央に表示するためのMatrix
float ratio = (float) PICTURE_SIZE / Math.min(original.getWidth(), original.getHeight());
Matrix matrix = new Matrix();
matrix.postTranslate(
-(original.getWidth() - PICTURE_SIZE) / 2,
-(original.getHeight() - PICTURE_SIZE) / 2
);
matrix.postScale(ratio, ratio, half, half);

// 選択画像を描画
canvas.drawBitmap(original, matrix, paint);

return bitmap;
}

/** メニュー項目作成 */
@Override
public boolean onCreateOptionsMenu(Menu menu) {

/** 「画像選択」の項目を追加 */
menu.add(Menu.NONE, MENU_SELECT_PICTURE, Menu.NONE, R.string.select_picture);

return super.onCreateOptionsMenu(menu);
}

/** メニュー項目選択イベント */
@Override
public boolean onOptionsItemSelected(MenuItem item) {

// 「画像選択」が選択されていたら画像選択処理を呼び出す
if (item.getItemId() == MENU_SELECT_PICTURE) {
selectPicture();
}

return super.onOptionsItemSelected(item);
}

@Override
protected void onResume() {
super.onResume();

// 加速度センサの一覧を取得(通常は1つ)
Sensor sensor = sensor_manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

// 加速度センサが存在する場合は、加速度センサの値の変化を検知するようリスナを設定する
if (sensor != null) {
sensor_manager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}

@Override
protected void onPause() {
super.onPause();

// 加速度センサのリスナを削除する
sensor_manager.unregisterListener(this);
}

/** センサの値が変化した際のイベント */
@Override
public void onSensorChanged(SensorEvent event) {

// 取得したイベントが加速度センサのイベントならば
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

// 取得した加速度をボールの加速度とする
ax = -event.values[0];
ay = event.values[1];

// 加速度の値をデバッグログに表示する
//Log.d("DEBUG", String.format("X=%8.5f, Y=%8.5f", ax, ay));
}
}

/** センサの精度が変化した際のイベント */
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 処理なし
}

/** サーフェスビューの準備が完了した際のイベント */
@Override
public void surfaceCreated(SurfaceHolder holder) {
screen_holder = holder;
}

/** サーフェスビューのサイズが変化した際のイベント */
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// 処理なし
}

/** サーフェスビューが破棄された際のイベント */
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
screen_holder = null;
}

/** タイマーイベント */
private Runnable timer_event = new Runnable() {
public void run() {

// 描画領域とボール画像が準備できているなら
if (screen_holder != null) {

// 描画用のキャンバスを取得
Canvas canvas = screen_holder.lockCanvas();

// 初めにキャンバスを白でクリア
canvas.drawColor(Color.WHITE);

// ボール画像の読み込みが完了していたら
if (ball_image != null) {

// 速度に加速度を加算
vx += ax;
vy += ay;

// 座標に速度を加算
ball_x += vx;
ball_y += vy;

// 速度を落とす
vx *= SPEED_DECREASE;
vy *= SPEED_DECREASE;

// ボール可動範囲計算(画面サイズからボールのサイズを引いた分が可動範囲)
float wx = canvas.getWidth() - ball_image.getWidth();
float wy = canvas.getHeight() - ball_image.getHeight();

// 端まで到達した場合は速度を反転
if ((ball_x < 0) || (ball_x > wx)) {
vx = -vx;
ball_x = Math.min(Math.max(0, ball_x), wx);
}
if ((ball_y < 0) || (ball_y > wy)) {
vy = -vy;
ball_y = Math.min(Math.max(0, ball_y), wy);
}

// キャンバスにボールを描画
canvas.drawBitmap(ball_image, ball_x, ball_y, null);
}

// キャンバスを反映
screen_holder.unlockCanvasAndPost(canvas);
}

// LOOP_DELAY(20)ms後に再度実行
handler.postDelayed(this, LOOP_DELAY);
}
};
}


▼/sample3/OrientationReader.java
package biz.answerlead.sample3;

import java.io.IOException;
import java.io.InputStream;

/** Orientation読込みクラス */
public class OrientationReader {

/** Exifの入ったデータかチェックするための値 */
private static final Byte[] EXIF_HEADER = {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xE1, null, null, 'E',
'x', 'i', 'f', 0, 0};

/** Orientationタグの値 */
private static final short TAG_ORIENTATION = 274;

/** InputStreamからOrientationを取得する */
public static int get(InputStream is) {

try {

// Exifが存在するか判断するために12バイト読み込む
byte[] exif_header = new byte[12];
if (is.read(exif_header) == exif_header.length) {

// ヘッダの内容を確認してExifが存在するかチェック
boolean is_exif = true;
for (int i = 0; i < EXIF_HEADER.length; i++) {
if (EXIF_HEADER[i] != null && EXIF_HEADER[i] != exif_header[i]) {
is_exif = false;
break;
}
}

// Exifが存在するなら
if (is_exif) {

// TIFFヘッダ(8バイト)を読み込む
byte[] tiff_header = new byte[8];
if (is.read(tiff_header) == tiff_header.length) {

// ビッグエンディアン(MM)かリトルエンディアン(II)か読み込む
boolean is_little_endian = tiff_header[0] == 'I' && tiff_header[1] == 'I';

// タグ数(2バイト)を読み込む
short tag_num = getShort(is.read(), is.read(), is_little_endian);

// タグ数分ループ
while (tag_num-- > 0) {

// Exifタグ(12バイト)を読み込む
byte[] body = new byte[12];
if (is.read(body) == body.length) {

// 先頭2バイトからタグ種別を取得
int tag = getShort(body[0], body[1], is_little_endian);

// 種別がOrientationなら
if (tag == TAG_ORIENTATION) {

// 値の型を取得
int type = getShort(body[2], body[3], is_little_endian);

// 型別に値を読んで返す
switch (type) {
case 1:
case 2:
return body[8];
case 3:
return getShort(body[8], body[9], is_little_endian);
case 4:
case 9:
return getInt(body[8], body[9], body[10], body[11], is_little_endian);
}

}
}
}
}
}
}

} catch (Exception e) {
// IGNORE
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return 0;
}

/** 2バイトをshortに変換して返す */
private static short getShort(int a, int b, boolean reverse) {
if (a < 0 || b < 0)
throw new IllegalArgumentException();

return (short) (reverse ? (b << 8 | a) : (a << 8 | b));
}

/** 4バイトをintに変換して返す */
private static int getInt(int a, int b, int c, int d, boolean reverse) {
if (a < 0 || b < 0 || c < 0 || d < 0)
throw new IllegalArgumentException();

return reverse ? (d << 24 | c << 16 | b << 8 | a) : (a << 24 | b << 16 | c << 8 | d);
}
}


P94 ギャラリー

 閲覧数:244 投稿日:2018-05-14 更新日:2018-05-18

コード


▼/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();
}
}


P123 シューティングゲーム

 閲覧数:290 投稿日:2018-05-14 更新日:2018-05-19

コード


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

import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
import android.view.KeyEvent;

import biz.answerlead.sample5.game.Game;

/** メインアクティビティ */
public class MainActivity extends Activity {

/** ゲームオブジェクト */
Game game;

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

// ゲーム管理インスタンスを作成
game = new Game(this);

// 端末デフォルトの向きを取得して、ゲーム管理インスタンスに設定
Display display = getWindowManager().getDefaultDisplay();
game.setTerminalRotation(display.getRotation());

// ゲーム画面を表示する
setContentView(game);
}

/** アプリが終了した場合に発生するイベント */
@Override
protected void onDestroy() {
super.onDestroy();

// ゲーム情報の保存
game.save();
}

/** キー押下イベント */
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

// キーコードが「戻る」ボタンのものならば
if (keyCode == KeyEvent.KEYCODE_BACK) {

// onBackで処理が行われなければ、本来の戻る動作を行う
if(!game.onBack()){
return false;
}
}

// 本来の動作を行う
return super.onKeyDown(keyCode, event);
}
}



-



週間人気ページランキング / 5-11 → 5-17
順位 ページタイトル抜粋 アクセス数
アクセスが、ありませんでした! 0
2024/5/18 1:01 更新