Calendarクラスの落とし穴?

Calendarクラス、便利ですよね。
Androidアプリで日時を扱うときには必須のクラスですが、ググッてもあまり出てこない情報があります。
それは「ミリ秒の設定」についてです。

例えば、Calendarクラスの日時を「2001年2月3日 4時56分」に設定するとします。
すると、たいていのサイトでは

Calendar calendar = Calendar.getInstance();
calendar.set(2001, 1, 3, 4, 56, 0);

と解説してあると思います。

また、一方で、2つの異なるCalendarクラスを比較して、どちらが未来かを判別する場合は、こう書かれています。

int different = calendar1.compareTo(calendar2);

これで、differentが0になれば同じ日時、正の値になればcalendar1の方が未来、負の値になればcalendar2の方が未来ということになります。

では、試しにこのコードを実行してみてください。

Calendar calendar1 = Calendar.getInstance();
calendar1.set(2001, 1, 3, 4, 56, 0);

Calendar calendar2 = Calendar.getInstance();
calendar2.set(2001, 1, 3, 4, 56, 0);

int different = calendar1.compareTo(calendar2);

calendar1も、calendar2も、同じく「2001年2月3日 4時56分」に設定しました。
しかし、differentに返ってきた結果は0ではないはず。おそらく、「calendar2の方が未来」という結果になったかと思います。

それは、ミリ秒の設定を行っていないからです。
Calendarクラスにミリ秒を設定を設定するには以下のようにします。

Calendar calendar1 = Calendar.getInstance();
calendar1.set(2001, 1, 3, 4, 56, 0);
calendar1.set(Calendar.MILLISECOND,0);

Calendar calendar2 = Calendar.getInstance();
calendar2.set(2001, 1, 3, 4, 56, 0);
calendar2.set(Calendar.MILLISECOND,0);

int different = calendar1.compareTo(calendar2);

これで、はじめてdifferentが0なります。
Calendarクラスを利用して日時比較をする際は、この点に気を付けてください!

2つのCalendarクラスを比較して、日数差を取得する

異なるCalendarクラスの、経過した日数を調べたいときってありませんか?
そういうときに便利なメソッドです。

Calendar calendarNow = Calendar.getInstance();
Calendar calendarPast = Calendar.getInstance();
calendarPast.add(Calendar.MONTH, -3);

/**
 * 経過日数を取得する。
 */
int getDiffDays(Calendar calendar1, Calendar calendar2) {
	//==== ミリ秒単位での差分算出 ====//
	long diffTime = calendar1.getTimeInMillis() - calendar2.getTimeInMillis();

	//==== 日単位に変換 ====//
	int MILLIS_OF_DAY = 1000 * 60 * 60 * 24;
	int diffDays = (int)(diffTime / MILLIS_OF_DAY);

	return diffDays;
}

1つ目の引数の方が未来の日時だと正の値が返ってきます。
2つ目の引数の方が未来だと負の値が返ってきます。

アプリ内で表示されているViewそのものを画像として保存する

アプリ内で表示されているViewを、画像として保存したいと思ったことはありませんか?
動的なViewを、そのまま画像としてSDカードに保存する方法は、このようになります。

[MainActivity]

//合成したViewからBitmapを取得
save_view.setDrawingCacheEnabled(true);
save_view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
save_view.layout(0, 0, save_view.getMeasuredWidth(), save_view.getMeasuredHeight()); 
save_view.buildDrawingCache(true);
Bitmap newBitmap = Bitmap.createBitmap(save_view.getDrawingCache());
save_view.setDrawingCacheEnabled(false); //キャッシュをクリアー

//nullだったらリターン
if (newBitmap == null) {
	return;
}

//Uri・ファイル名を取得
Uri mUri = ComnUtil.getSimulationUri(MainActivity.this);
//保存
Cursor c = getContentResolver().query(mUri, null, null, null, null);
c.moveToFirst();
String filename = c.getString(c.getColumnIndex(MediaStore.MediaColumns.DATA));

OutputStream outputStream = null;
try {
    outputStream = getContentResolver().openOutputStream(mUri);
    newBitmap.compress(CompressFormat.JPEG, 100, outputStream);
} catch (FileNotFoundException e) {
} finally {
    if (outputStream != null) {
        try {
            outputStream.close();
        } catch (IOException e) {
			return;
        }
    }
}

//ここまできたら正常に成功しているのでダイアログを出してあげると親切かも
String menu_text = res.getString(R.string.check_exp, filename);
new AlertDialog.Builder(MainActivity.this)
.setMessage(menu_text)
.setPositiveButton(R.string.common_ok,null)
.show();

[ComnUtil]

public static Uri getSimulationUri(Context context) {
	long currentTimeMillis = System.currentTimeMillis();
	Date today = new Date(currentTimeMillis);
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
	String title = dateFormat.format(today);
	String dirPath = getDirPath(context);
	String fileName = "Simulation" + title + ".jpg";	//ご自由にお名前をつけてください
	String path = dirPath + fileName;
	File file = new File(path);
	ContentValues values = new ContentValues();
	values.put(Images.Media.TITLE, title);
	values.put(Images.Media.DISPLAY_NAME, fileName);
	values.put(Images.Media.MIME_TYPE, "image/jpeg");
	values.put(Images.Media.DATA, path);
	values.put(Images.Media.DATE_TAKEN, currentTimeMillis);
	if (file.exists()) {
		values.put(Images.Media.SIZE, file.length());
	}
	Uri uri = context.getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);
	return uri;
}

[strings.xml]

<string name="common_ok">OK</string>
<string name="check_exp">画像を\n%1$s\nに保存しました</string>

画像だけのダイアログを出す方法

もともとアプリ内のResフォルダに格納している画像だけを表示する、ダイアログの出し方を書きます。
「戻る」や「OK」ボタンなどないので、端末のバックキーでのみ元の画面に戻ることができます。

// 画像のダイアログ
ImageView iv = new ImageView(this);
iv.setImageResource(R.drawable.○○○(ファイル名));
iv.setScaleType(ImageView.ScaleType.FIT_XY);
iv.setAdjustViewBounds(true);
Dialog dialog = new Dialog(this);
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(iv);
dialog.show();		

カメラで撮った画像を白黒にするメソッド

撮影した画像のパスは、Uriで管理します。

[MainActivity]

SetImages(uri);

private void SetImages(Uri uri) {
	//まずは画像がちゃんと存在するかをチェック
	boolean exist = ComnUtil.CheckExistImage(MainActivity.this, uri);
	
	if (exist) {
		//画像処理開始
		ProgressDialog mProgress =null;
		Handler mHandler = new Handler();
		
        mProgress = new ProgressDialog(this);  
        mProgress.setMessage(AnalysisImage.message);
        
        mProgress.show();  
        AnalysisImage t = new AnalysisImage(MainActivity.this, mHandler, mProgress, uri, face_image);
        t.start();
	}
}

[ComnUtil]

public class ComnUtil {
	//ファイルが存在するか確認するメソッド
    public static boolean CheckExistImage(Context context, Uri uri) {
    	//Uriをファイルパスに
    	Cursor c = context.getContentResolver().query(uri, null, null, null, null);
    	c.moveToFirst();
    	String filename = c.getString(c.getColumnIndex(MediaStore.MediaColumns.DATA));

    	//有無チェック
    	File image_file = new File(filename);
        if (image_file.exists()) {
        	return true;
        }
        return false;
    }
}

[AnalysisImage]

public class AnalysisImage extends Thread {
	Context context;
	Handler mHandler;
	ProgressDialog mProgress;

	Uri uri;
	ImageView face_image;

	float max_y;

	Bitmap grey_image;

	public static String message = "画像処理中……";

	public AnalysisImage(Context context, Handler mHandler, ProgressDialog mProgress, Uri uri, ImageView face_image) {
		this.context = context;
		this.mHandler = mHandler;
		this.mProgress = mProgress;

		this.uri = uri;
		this.face_image = face_image;

		max_y = 100;
	}

	//スレッド内処理
    public void run() {
    	grey_image = ToGreyImage(uri);

        //スレッドが終了した場合、終了したことをHandlerに知らせる。
        mHandler.post(new Runnable() {
            public void run() {
            	//ダイアログを消す
                mProgress.dismiss();

                if (grey_image != null) {
                	face_image.setImageBitmap(grey_image);
                }
            }
        });
    }

	// 画像を解析
	private Bitmap ToGreyImage(Uri uri) {
		// 画面の半分のサイズで画像読み込み
		Bitmap image_base = ComnUtil.getBitmapFromPath(context, uri, 2);

		// 画像の縦横の大きさを取得
		int Width = image_base.getWidth();
		int Height = image_base.getHeight();
		// 出力用画像領域確保
		Bitmap output = Bitmap.createBitmap(Width, Height,
				Bitmap.Config.ARGB_8888);
		// 画像処理用配列
		int[] pixels = new int[Width * Height];
		// pixelsの配列にimage_baseのデータを格納する
		image_base.getPixels(pixels, 0, Width, 0, 0, Width, Height);

		//解析
		for (int x = 0; x < Width; x++) {
			for (int y = 0; y < Height; y++) {
				// 1画素ずつ取得
				int pixel = pixels[x + y * Width];
				// RGB各色に分離
				int r = Color.red(pixel);
				int g = Color.green(pixel);
				int b = Color.blue(pixel);
				// 今回の画像処理はカラー画像をグレースケールに変換する
				int gray = (int) (r * 0.3 + g * 0.59 + b * 0.11);// 輝度値=R×0.30+G×0.59+B×0.11
				// データを戻す
				pixels[x + y * Width] = Color.argb(Color.alpha(pixel), gray,
						gray, gray);
			}
		}

		// 出力用の領域にセットする
		output.setPixels(pixels, 0, Width, 0, 0, Width, Height);

		return output;
	}
}
Top