퍼즐 게임에 들어가는 일부 코드 :
안드로이드 내장 갤러리를 띄워 스마트폰 sd카드에 있는 이미지나 카메라로 찍은 이미지를 불러와서 크롭(잘라내기)해서 새로운 비트맵파일을 생성하는 코드
간단한 로직이지만 스마트폰 내장 갤러리는 이미지가 새로 생성되거나 사용자가 임으로 파일을 삭제할때 그때 그때 스캔해서 이미지를 띄워주지 않는다.
Cursor 클래스를 이용해서 Thumb nail 파일 데이터들을 같이 조작 거나 접근해서 파일들을 처리하는 로직도 있지만 일이 더 커진다... 사용자가 임으로 해당 이미지파일을 삭제하면...썸네일과 실제이미지가 꼬여버리는 일이 발생한다. 파일들을 함부로 건드리면...골치아프다는...
하지만 한번에 이런 것들을 해결하는 코드가 미디어 재스캔
android.content.ContextWrapper.sendBroadcast(Intent intent)
갤러리를 띄울때나 크롭할때 미디어 재스캔 요청하면 알아서 제대로 파일들을 띄워준다.
그래서 MediaStore.EXTRA_OUTPUT 를 마음 편하게 쓰면 된다.
초기화 파일 생성 및 로딩 코드도 있다. 차근 차근 보면 꽤 쓸만한 로직?
Main.java, main.xml, AndroidManifest.xml 세개 파일.
결과: sdcard/division 폴더속에 크롭한 이미지들이 생겨난다. 사용자가 만들어진 크롭파일들을 삭제해도 파일 이름 설정은 초기화 파일(bbaddoCrop.cfg)로 인해 계속 증가치로 저장된다. 재인스톨시 해당폴더 속에 같은 이름의 이미지가 있을시 초기화 파일이 없으므로 10000.png 로 부터 다시 덮어쓰게 됨... 파일들이 꼬여진다.. 다른 로직을 만들어 쓰면 간단히 해결된다. 아래 코드에서는 빠져있다.
package pes.imagecrop;
/*
* Extra Options Table for image/* crop: Intent.putExtra("Options", Value of the data type);
* 카메라 크롭을 이용해 오려진 이미지를 가져오기
*
* Options "" DataType Description
* =======================================================
* crop String Signals the crop feature
* aspectX int Aspect Ratio
* aspectY int Aspect Ratio
* outputX int width of output created from this Intent
* outputY int width of output created from this Intent
* scale boolean should it scale
* return-data boolean Return the bitmap with Action=inline-data by using the data
* crop 이미지를 intent 를 통해서 수신 받을 지 여부
* data Parcelable Bitmap to process, you may provide it a bitmap (not tested)
* circleCrop String if this string is not null, it will provide some circular cr
* output URI Set this URi to a File:/// , (==> MediaStore.EXTRA_OUTPUT)
* crop 이미지를 intent 를 통해서 파일로 저장. 기기마다 오류가 발생
* noFaceDetection boolean camera.face
*
*
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class Main extends Activity{
private static final int PICK_FROM_CAMERA; //카메라
private static final int PICK_FROM_GALLERY; //갤러리
private static final int AFTER_CROP; //크롭 com.android.camera.action.CROP
static {
PICK_FROM_CAMERA = 0
PICK_FROM_GALLERY = 1;
AFTER_CROP = 2;
}
private Uri captureUri; //카메라, 갤러리 파일 경로
private Uri cropUri; //크롭파일 경로
private ImageView iv;
private Button bt;
private int name ; //크롭파일 이름 설정 상수
private File tempFile; //카메라 캡쳐 임시 파일
private boolean cameraFlag = false; //임시파일 지우기 플랙, 카메라일때만 적용
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//이미지파일 저장할 폴더 만든다
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+ "/division");
if(!f.isDirectory()) f.mkdir();
//미디어 재스캔 요청
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory())));
if(!initialFileRead()){
name = 10000;
}
bt = (Button) findViewById(R.id.button);
bt.setText("Load Images");
iv = (ImageView) findViewById(R.id.image);
bt.setOnClickListener(load);
}
//카메라에서 이미지 획득
private void getCameraPhoto(){
cameraFlag = true;
//==MediaStore.ACTION_IMAGE_CAPTURE
Intent cameraIntent = new Intent("android.media.action.IMAGE_CAPTURE");
//카메라로 찍은 최초 이미지 임시파일 설정 및 저장
tempFile = new File(Environment.getExternalStorageDirectory(), "tempo1234.jpg");
captureUri = Uri.fromFile(tempFile);
//==android.provider.MediaStore.EXTRA_OUTPUT == MediaStore.EXTRA_OUTPUT
cameraIntent.putExtra("output", captureUri); //카메라 캡쳐 임시파일생성, 카메라 ok 버튼 눌렀을때
startActivityForResult(cameraIntent, PICK_FROM_CAMERA); //0 : 카메라
}
//갤러리에서에서 이미지 획득
private void getGalleryPhoto(){
cameraFlag = false;
//미디어 재스캔 요청
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory())));
// ACTION_PICK : 데이터 실제주소 이용, ACTION_GET_CONTENT : 데이터 유형 이용
Intent galleryIntent = new Intent("android.intent.action.PICK"); // ==Intent.ACTION_PICK
//==android.provider.MediaStore.Images.Media.CONTENT_TYPE
galleryIntent.setType("vnd.android.cursor.dir/image");
//이미지뷰의 이미지 선택, 두번째 파라메타가 resultCode
startActivityForResult(galleryIntent, PICK_FROM_GALLERY); //1 : 갤러리
}
//결과값 처리 : crop 해서 bitmap 생성, 원본카메라촬영 파일 제거
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
//미디어 재스캔 요청
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory()))); //미디어 새로 스캔
//startActivityForResult(Intent, 상수)파라미터 두번째 상수값이 제대로 되었을때만 통과 -1이 아닐때
if(resultCode != RESULT_OK){ //RSULT_OK == -1
if (resultCode == RESULT_CANCELED){ //카메라 캔슬시
if(cameraFlag){
if(tempFile.exists()) tempFile.delete();
}
}
return;
}
switch(requestCode) {
case PICK_FROM_GALLERY:
captureUri = intent.getData();
crop();
break;
case PICK_FROM_CAMERA:
crop();
break;
//크롭된 후 임시파일 제거와 View 처리를 위해 startActivityForResult 한번 더 호출
case AFTER_CROP:
//저장된 크롭파일 이미지뷰에 보이기
try{
Bitmap cropPhoto = BitmapFactory.decodeFile(cropUri.getPath());
iv.setImageBitmap(cropPhoto);
iv.setScaleType(ImageView.ScaleType.FIT_XY);
} catch(Exception e){ return; }
//카메라 촬영 원본 임시파일 삭제
if(cameraFlag){
if(tempFile.exists()) tempFile.delete();
}
++name; //크롭저장파일 이름 설정
break;
}
}
//이미지 크롭해서 비트맵 파일 생성
public void crop(){
Intent cropIntent = new Intent("com.android.camera.action.CROP"); //크롭 액티비티 실행
//특정확장자 지정은 ex) setType("/image/jpg")
cropIntent.setDataAndType(captureUri, "image/*"); //크롭할 파일들 경로 및 형식 설정
//크롭파일 설정
cropIntent.putExtra("outputX", 300); //이미지 해상도
cropIntent.putExtra("outputY", 300);
cropIntent.putExtra("aspectX", 1); //가로세로 비율
cropIntent.putExtra("aspectY", 1);
cropIntent.putExtra("scale", true); //꽉찬 비율로 저장
cropIntent.putExtra("noFaceDetection", true); //camera.face
cropIntent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString()); //png_ 파일형식저장
//생성될 크롭파일 경로 설정
cropUri = Uri.fromFile(
new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/division",
"c" + name + ".png"));
//크롭파일 저장
//==android.provider.MediaStore.EXTRA_OUTPUT == "output"
cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri); //크롭 파일저장
startActivityForResult(cropIntent, AFTER_CROP);
}
//버튼 클릭이벤트
private OnClickListener load = new OnClickListener(){
@Override
public void onClick(View v) {
new AlertDialog.Builder(Main.this) //일단 이런식으로 얄궂게 클릭이벤트 설정
.setIcon(0) //0 : 헤더아이콘 제거
.setTitle("Choose Image From... ")
.setPositiveButton("Camera", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
getCameraPhoto();
}
})
.setNeutralButton("Gallery", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
getGalleryPhoto();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
}
};
//===============================
// 초기화 설정 파일 저장 및 불러오기
//===============================
public void initialFileWrite(){ //초기 설정 파일 만들기
Properties pp = new Properties();
pp.setProperty("name", "" + name);
FileOutputStream out = null;
try {
out = openFileOutput("bbaddoCrop.config", Context.MODE_PRIVATE);
pp.store(out,"BbaDdo Crop Config Setting");
out.close();
} catch (FileNotFoundException e) {}
catch (IOException e) {}
}
public boolean initialFileRead(){ //초기 설정 파일 불러오기
Properties pp = new Properties();
FileInputStream in = null;
try {
in = openFileInput("bbaddoCrop.config");
pp.load(in);
in.close();
} catch (FileNotFoundException e) {return false;}
catch (IOException e) {return false;}
name = Integer.parseInt(pp.getProperty("name"));
return true;
}
//=========================================
// 폰 디바이스 BACK 키 이벤트 : 종료
//=========================================
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){ //안드로이드 백스페이스 키 종료
new AlertDialog.Builder(this) //프로세서 종료를 위한 얼러트다이얼로그
.setIcon(android.R.drawable.ic_menu_close_clear_cancel) //0 : 헤더아이콘 제거
.setTitle("Quit")
.setMessage("Are you sure?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
initialFileWrite();
moveTaskToBack(true);
finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
return true;
}
return super.onKeyUp(keyCode, event);
}
}
===================================================================
ayout/main.xml
===================================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="@+id/image"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:background="#555781A9" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
=======================================================
AndroindManifest.xml
=======================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pes.imagecrop"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
>
<activity
android:name=".Main"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
'Android' 카테고리의 다른 글
cocos2d-x 1.01 ripple, wave, liquid 응용 (0) | 2013.07.12 |
---|---|
Bresenham 브레센함 알고리듬 : 원 그리기 (0) | 2013.03.07 |
Conway's Game Of Life Java code (0) | 2012.10.05 |
java.lang.reflect.Method.invoke 메서드 활용 (0) | 2012.08.20 |
Android Screen Capture (0) | 2012.07.19 |