반응형

//날짜에 점을 넣어줌
public static String setDotDate(String day) {
int size = day.length();
StringBuffer sb = new StringBuffer(day);

String dot = ".";

if (size == 4) {
sb.insert(2, dot);
} else if (size == 6) {
sb.insert(4, dot);
} else if (size == 8) {
sb.insert(4, dot);
sb.insert(7, dot);
}
return sb.toString();
}

//시간에 ":"을 넣어줌

public static String setDotTime(String time) {
int size = time.length();
StringBuffer sb = new StringBuffer(time);

String dot = ":";

if (size == 4) {
sb.insert(2, dot);
} else if (size == 8) {
sb.insert(4, dot);
sb.insert(7, dot);
} else if (size == 6) {
sb.insert(2, dot);
sb.insert(5, dot);
}
return sb.toString();
}

//현재 날짜가저오기

public static String getCurrentDay() {
String day = "";
Calendar calendar = Calendar.getInstance();
int today = calendar.get(Calendar.DATE);

calendar.set(Calendar.DAY_OF_MONTH, 1);

int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;

day = addZero(year) + addZero(month) + addZero(today);

return day;
}

//년월

public static String getCurrentMonth() {
String day = "";
Calendar calendar = Calendar.getInstance();
int today = calendar.get(Calendar.DATE);

calendar.set(Calendar.DAY_OF_MONTH, 1);

int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;

day = addZero(year) + addZero(month);

return day;
}

//내일

public static String getTomorrow() {
String day = "";
Calendar cal = new GregorianCalendar();
cal.add(Calendar.DATE, 1);

int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int today = cal.get(Calendar.DAY_OF_MONTH);

day = addZero(year) + addZero(month) + addZero(today);

return day;
}

//"0"이 없을때 붙여줌.

public static String addZero(int arg) {
String val = String.valueOf(arg);

if (arg < 10)
val = "0" + val;

return val;
}

//현재시간

public static String getCurrentTime() {
String strNow = "";
// 현재 시간을 msec으로 구한다.
long now = System.currentTimeMillis();
// 현재 시간을 저장 한다.
Date date = new Date(now);
// 시간 포맷으로 만든다.
SimpleDateFormat sdfNow = new SimpleDateFormat("yyMMddHHmmss");
strNow = sdfNow.format(date);

return strNow;
}

//요일

public static String getDayOfWeek() {

String dayOfWeek = "";

Calendar cal = Calendar.getInstance();
int day_of_week = cal.get(Calendar.DAY_OF_WEEK);

switch (day_of_week) {
case 1:
dayOfWeek = "";
break;
case 2:
dayOfWeek = "";
break;
case 3:
dayOfWeek = "";
break;
case 4:
dayOfWeek = "";
break;
case 5:
dayOfWeek = "";
break;
case 6:
dayOfWeek = "";
break;
case 7:
dayOfWeek = "";
break;
}

return dayOfWeek;
}

//현재시간

public static String getCurrentTimeHHMM() {
String strNow = "";
// 현재 시간을 msec으로 구한다.
long now = System.currentTimeMillis();
// 현재 시간을 저장 한다.
Date date = new Date(now);
// 시간 포맷으로 만든다.
SimpleDateFormat sdfNow = new SimpleDateFormat("HH:mm");
strNow = sdfNow.format(date);

return strNow;
}

//8자리 날짜를 넣으면 요일구함

public static String getDayOfWeek(String date) {

int year = 0;
int month = 0;
int day = 0;

if (date.length() > 7) {
year = Integer.parseInt(date.substring(0, 4));
month = Integer.parseInt(date.substring(4, 6));
day = Integer.parseInt(date.substring(6, 8));
}
String dayOfWeek = "";

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DATE, day);

int day_of_week = cal.get(Calendar.DAY_OF_WEEK);

switch (day_of_week) {
case 1:
dayOfWeek = "";
break;
case 2:
dayOfWeek = "";
break;
case 3:
dayOfWeek = "";
break;
case 4:
dayOfWeek = "";
break;
case 5:
dayOfWeek = "";
break;
case 6:
dayOfWeek = "";
break;
case 7:
dayOfWeek = "";
break;
}

return dayOfWeek;
}

//"년,월,일"을 붙여줌

public static String addDate(String day) {
int size = day.length();
StringBuffer sb = new StringBuffer(day);

if (size == 8) {
sb.insert(4, " ");
sb.insert(8, " ");
sb.insert(12, " ");
}
return sb.toString();
}

반응형

WRITTEN BY
아카이시

,
반응형
 
public static String setPhoneNumber(String result) {

boolean flag = false;

if (result.startsWith("0"))
flag = true;

DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setGroupingSeparator('-');

DecimalFormat df = new DecimalFormat("###,####,####");
df.setDecimalFormatSymbols(dfs);

try {
double inputNum = Double.parseDouble(result);
result = df.format(inputNum).toString();

if (flag) {
if (!result.equals("0"))
result = "0" + result;
}

} catch (NumberFormatException e) {
// TODO: handle exception
}

return result;
}
반응형

WRITTEN BY
아카이시

,
반응형
// 화폐단위 숫자 세자리마다 쉼표넣기
public static String currentpoint(String result) {

DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setGroupingSeparator(',');

DecimalFormat df = new DecimalFormat("###,###,###,###");
df.setDecimalFormatSymbols(dfs);

try {
double inputNum = Double.parseDouble(result);
result = df.format(inputNum).toString();
} catch (NumberFormatException e) {
// TODO: handle exception
}

return result;
}
반응형

WRITTEN BY
아카이시

,
반응형
public static void callAction(Context context, String num) {
try {
// 전화 걸기
Uri uri = Uri.parse("tel:" + num);
Intent it = new Intent(Intent.ACTION_CALL, uri);
context.startActivity(it);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, "" + e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
}

public static void smsAction(Context context, String num, String body) {
try {
// SMS 발송
Uri uri = Uri.parse("smsto:" + num);
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", body);
context.startActivity(it);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, "" + e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
}
반응형

WRITTEN BY
아카이시

,
반응형

예전에 이클립스로 ZXing을 구현을 한적이 있었다. 라이브러리 파일을 가져다가 커스터마이징을 하여 구현을 했었었는데... 최근 안드로이드 스튜디오를 사용하다보니 라이브러리파일을 어디다가 넣어야할지.. 어떻게 해야할지 모르겠다.

방법을 찾다보니.. 아주 간단히 구현이 가능하다.

build.gradle파일에

dependencies {

compile 'com.journeyapps:zxing-android-embedded:3.0.0@aar'
compile 'com.google.zxing:core:3.2.0'
}

그리고 intent를 호출하듯이...

new IntentIntegrator(this).initiateScan();

 

이것만으로 내프로젝트 내부의 바코드스캐너가 호출이된다.

 

또한...

 

IntentIntegrator integrator = new IntentIntegrator(getActivity());
integrator.setCaptureActivity(ActivityScanner.class);
integrator.setOrientationLocked(true);
integrator.initiateScan();

setCaptureActivity 로 나만의 엑티비티를 지정할 수가 있다.

 

setOrientationLocked이 false이면 가로, 세로일 경우 자동으로 바뀐다.

 

 

그리고... 스캔한 값은 .. 호출한 엑티비티 안에 onActivityResult를 구현해준다.

 

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

// QR코드/바코드를 스캔한 결과 값을 가져옵니다.
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);

// 결과값 출력
new AlertDialog.Builder(this)
.setTitle(R.string.app_name)
.setMessage(result.getContents() + " [" + result.getFormatName() + "]")
.setPositiveButton("확인", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
}
 
10분만에 간단히 구현 완료~~~^^

 

반응형

WRITTEN BY
아카이시

,
반응형

 

byte[]에서 Bitmap으로 변환후 가로길이가 세로길이보다 클 경우 Bitmap 이미지를 회전시킨다.

Bitmap bitmap = byteArrayToBitmap(data);
if (bitmap.getHeight() < bitmap.getWidth()){
bitmap = imgRotate(bitmap, 90);
}
imgView.setImageBitmap(bitmap);
 
 
//byte[] bitmap으로 변환
private Bitmap byteArrayToBitmap(byte[] byteArray){
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
return bitmap;
}

//bitmap이미지를 원하는각으로 회전
private Bitmap imgRotate(Bitmap bitmap, int angle){
int width = bitmap.getWidth();
int height = bitmap.getHeight();

Matrix matrix = new Matrix();
matrix.postRotate(angle);

Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
bitmap.recycle();

return resizedBitmap;
}
 
반응형

WRITTEN BY
아카이시

,
반응형

안드로이드 오픈소스 모음 끝판왕....

http://www.java2s.com/Open-Source/Android_Free_Code/

https://android-arsenal.com/free

웬만한 오픈소스는 다있음....

 

반응형

WRITTEN BY
아카이시

,
반응형

메뉴에 해당하는 각 액티비티들을 실행시킬때 유용하다고 생각된다.

각 클래스들을 이름으로 저장해 두고 포지션에 해당하는 액티비티들을 실행시킨다.

아래는 소스의 일부이다.

final ArrayList<HomeMenu> menus = getData();
...
recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerView,new ClickListener() {
@Override
public void onClick(View view, int position) {

if (!menus.get(position).getName().equals("")){
String className = getActivity().getPackageName() + menus.get(position).getName();
Intent intent = new Intent();
intent.setClassName(getActivity(), className);
startActivity(intent);
}

mDrawerLayout.closeDrawer(Gravity.START);
}

}));

반응형

WRITTEN BY
아카이시

,
반응형

퍼온글입니다.

이번엔 간단히 (정말 정말 제일 간단한것 맞습니다)

카메라를 건드려 볼까합니다. 

(소스는 대부분 dev.android.com 에서 예제 퍼온거 조금 수정한 정도입니다. 영어 되시는 분들은 그냥 안드로이드 홈페이지에서 직접 읽어보시는게 더 이해가 빠를 수 있습니다)

우선 안드로이드 카메라는 몇가지 어이 없으면서 그러려니 해야하는 기본 원칙이 있습니다. (하나도 중요하진 않습니다 패스 하셔도 됩니다)

 

1. 카메라는 미리보기 화면을 가져야한다.

2. 카메라는 가로로 들고 찍는다.

 

어처구니가 없습니다.

일단 우리는 몰래카메라를 찍고 싶을 수 도 있고, 세로로 들고 셀카를 찍고 싶을 수 도 있는데 말이죠.

 

위의 두가지가 "기본" 일뿐이지 "안된다" 고는 안했습니다. 미리보기를 만들어 놓고 투명하게 만들어 버릴 수도, 1*1 사이즈로 숨겨 버릴 수도 있죠. 가로만 된다고요? 아뇨, 기본으로 가로모드라고 가정한다고요. 화면을 돌리거나 주변 UI만 세로로 돌려 버려도 됩니다. 그것도 아니라면 사진을 찍어놓고 데이터를 세로로 변환해도 되겠죠.

 

어쨌든 안드로이드 카메라 코딩을 하다보면 왜 이러지? 싶은 부분이 생긴다면, 그냥 저 위의 두가지 기본 원칙이 그러하니 그렇구나 하고 넘어가자는 말입니다.

 

 

서론이 길어지는군요.

 

어플에서 사진을 찍는 방법은 두가지가 있습니다. 

 

1. 다른 카메라앱을 호출해서 찍고, 사진을 리턴받기.

2. 직접 사진찍고 처리하기.

 

저는 2번으로 가보겠습니다. 1번은 재미 없어 보이잖아요.

 

우선 전체 절차는 크게 다음과 같습니다.

 

1. 카메라 하드웨어 유무 확인및, 액세스 가능한지 확인.

2. 프리뷰 클래스 생성

3. 촬영 시작용 리스너 생성

4. 캡쳐 후 처리

5. 카메라 리소스 반환

 

네, 다른 방법도 있고, 생략 가능한 편법들이 존재 하지만 우린 기본기를 배우는 중이니까 위의 5단계는 모두 중요합니다. 그러니까 빨간색.

 

1. 카메라 확인

 

카메라 리소스는 기기 전체에 한번에 한개만 할당 가능하기 때문에 다른 어플리케이션에서 사용중이거나, 사용후 반환하지 않은 경우에는 다른 앱에서 카메라를 사용 할 수 없게 될 뿐만 아니라 runtime exception을 발생시켜 버리므로 주의해야합니다.

 

 


 private boolean checkCameraHardware(Context context) {
     if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
         // 카메라가 최소한 한개 있는 경우 처리
      Log.i(TAG, "Number of available camera : "+Camera.getNumberOfCameras());
         return true;
     } else {
         // 카메라가 전혀 없는 경우 
      Toast.makeText(mContext, "No camera found!", Toast.LENGTH_SHORT).show();
         return false;
     }
 }
 

 

사용가능 여부를 확인했으니 이제 카메라 인스턴스를 하나 호출합니다.

 

 

 public static Camera getCameraInstance(){
     Camera c = null;
     try {
         c = Camera.open(); 
     }
     catch (Exception e){
         // 사용중이거나 사용 불가능 한 경우
     }
     return c;
 }

 

 

open() 의 매개변수로 int 값을 받을 수 도 있는데, 일반적으로 0이 후면 카메라, 1이 전면 카메라를 의미합니다. 

보다 명확히 하자면, 카메라 id  값의 범위는 0 부터 Camera.getNumberOfCameras()-1  까지 인데, 대체로 Camera.getNumberOfCameras() 가 2 입니다. (폰의 경우 대게 전후 각각 한개씩 카메라가 있으므로...)

 

2. 프리뷰 클래스 생성

 

카메라 프리뷰 클래스는 SurfaceView 클래스를 상속받아서 뷰 레이아웃안에 들어갈 대상 클래스입니다. 프리뷰가 생성/파괴시, 혹은 해상도 변경, 화면 회전등에 대한 프리뷰 변경을 위한 콜백 인터페이스를 구현해야하므로 SurfaceHolder.Callback을 implement 합니다.

 

 



각 대상들의 관계는 위 도표와 같이 이해 할 수 있습니다.


일단 기본적인 내용으로만 구성한 프리뷰 클래스의 소스는 다음과 같습니다.


 

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
 String TAG = "CAMERA";
    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // SurfaceHolder 가 가지고 있는 하위 Surface가 파괴되거나 업데이트 될경우 받을 콜백을 세팅한다 
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated 되었지만 3.0 이하 버젼에서 필수 메소드라서 호출해둠.
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // Surface가 생성되었으니 프리뷰를 어디에 띄울지 지정해준다. (holder 로 받은 SurfaceHolder에 뿌려준다. 
        try {
   Camera.Parameters parameters = mCamera.getParameters();
   if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
    parameters.set("orientation", "portrait");
    mCamera.setDisplayOrientation(90);
    parameters.setRotation(90);
   } else {
    parameters.set("orientation", "landscape");
    mCamera.setDisplayOrientation(0);
    parameters.setRotation(0);
   }
   mCamera.setParameters(parameters);

   mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // 프리뷰 제거시 카메라 사용도 끝났다고 간주하여 리소스를 전부 반환한다
     if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    private Camera.Size getBestPreviewSize(int width, int height)
    {
            Camera.Size result=null;    
            Camera.Parameters p = mCamera.getParameters();
            for (Camera.Size size : p.getSupportedPreviewSizes()) {
                if (size.width<=width && size.height<=height) {
                    if (result==null) {
                        result=size;
                    } else {
                        int resultArea=result.width*result.height;
                        int newArea=size.width*size.height;

                        if (newArea>resultArea) {
                            result=size;
                        }
                    }
                }
            }
        return result;

    }
    
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // 프리뷰를 회전시키거나 변경시 처리를 여기서 해준다.
        // 프리뷰 변경시에는 먼저 프리뷰를 멈춘다음 변경해야한다.

        if (mHolder.getSurface() == null){
          // 프리뷰가 존재하지 않을때
          return;
        }

        // 우선 멈춘다 
        try {
            mCamera.stopPreview();
        } catch (Exception e){
         // 프리뷰가 존재조차 하지 않는 경우다 
        }

        
        // 프리뷰 변경, 처리 등을 여기서 해준다.
<pre>        Camera.Parameters parameters = mCamera.getParameters();
        Camera.Size size = getBestPreviewSize(w, h);
        parameters.setPreviewSize(size.width, size.height);
        mCamera.setParameters(parameters);</pre>
        // 새로 변경된 설정으로 프리뷰를 재생성한다 
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

여기서 getBestPreviewSize() 메소드는 꼭 저처럼 짤 필요는 없고, 유저가 선택 하도록 만들어도 되는데, 일단 가장 넓이(가로*세로)가 가장 큰 해상도가 고해상도 프리뷰라고 간주해서 가장 고해상도의 프리뷰 사이즈를 반환토록 만들었습니다.

그 다음에는 만든 프리뷰 클래스를 메인 레이어에 삽입해줍니다. 프리뷰 위에 다른 버튼등의 UI 컴포넌트들을 배치 시킬수 있도록 RelativeLayout을 사용했고, 프리뷰는 FrameLayout 으로 불러오게 만들어둡니다.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />

  <Button
      android:id="@+id/button_capture"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentRight="true"
      android:layout_centerVertical = "true"
      android:text="Capture" />

</RelativeLayout>

 대체로 카메라는 기본적으로 가로모드를 정상 모드로 간주하기때문에 여기 예제에서도 일단 가로모드 UI로 만들어 두지만, 실제 카메라를 만들경우에는 기기의 상태에 따라 가로 세로 UI가 변하도록 만들어 줘야합니다.

 

 

 

3. 메인 액티비티 마저 마무리

 

메인 액티비티에서는 아까 위에서 미리 짜둔 메소드들과 만들어둔 레이아웃을 뷰로 가져오고, 촬영을 하기 위한 버튼 button_capture 에 리스너를 달아줍니다.

 

다음은 메인 액티비티 소스코드입니다.

 

 


public class MainActivity extends Activity{
    String TAG = "CAMERA";
    private Context mContext = this;
    private Camera mCamera;
    private CameraPreview mPreview;
    
    private PictureCallback mPicture = new PictureCallback() {
  @Override
  public void onPictureTaken(byte[] data, Camera camera) {
   // JPEG 이미지가 byte[] 형태로 들어옵니다
   
   File pictureFile = getOutputMediaFile();
   if (pictureFile == null) {
    Toast.makeText(mContext, "Error saving!!", Toast.LENGTH_SHORT).show();
    return;
   }

   try {
    FileOutputStream fos = new FileOutputStream(pictureFile);
    fos.write(data);
    fos.close();
    //Thread.sleep(500);
    mCamera.startPreview();
    
   } catch (FileNotFoundException e) {
    Log.d(TAG, "File not found: " + e.getMessage());
   } catch (IOException e) {
    Log.d(TAG, "Error accessing file: " + e.getMessage());
   } /*catch (InterruptedException e) {
    e.printStackTrace();
   }*/
  }
 }; 

        
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                                         WindowManager.LayoutParams.FLAG_FULLSCREEN);
                
        setContentView(R.layout.activity_main);
        
  mContext = this;
  // 카메라 인스턴스 생성
  if (checkCameraHardware(mContext)) {
   mCamera = getCameraInstance();

   // 프리뷰창을 생성하고 액티비티의 레이아웃으로 지정합니다
   mPreview = new CameraPreview(this, mCamera);
   FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
   preview.addView(mPreview);

   Button captureButton = (Button) findViewById(R.id.button_capture);
   captureButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
     // JPEG 콜백 메소드로 이미지를 가져옵니다
     mCamera.takePicture(null, null, mPicture);
    }
   });
  }
  else{
   Toast.makeText(mContext, "no camera on this device!", Toast.LENGTH_SHORT).show();
  }
        
    }

 /** 카메라 하드웨어 지원 여부 확인 */
 private boolean checkCameraHardware(Context context) {
     if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
         // 카메라가 최소한 한개 있는 경우 처리
      Log.i(TAG, "Number of available camera : "+Camera.getNumberOfCameras());
         return true;
     } else {
         // 카메라가 전혀 없는 경우 
      Toast.makeText(mContext, "No camera found!", Toast.LENGTH_SHORT).show();
         return false;
     }
 }
 
 /** 카메라 인스턴스를 안전하게 획득합니다 */
 public static Camera getCameraInstance(){
     Camera c = null;
     try {
         c = Camera.open(); 
     }
     catch (Exception e){
         // 사용중이거나 사용 불가능 한 경우
     }
     return c;
 }
 
 /** 이미지를 저장할 파일 객체를 생성합니다 */
 private static File getOutputMediaFile(){
     // SD카드가 마운트 되어있는지 먼저 확인해야합니다
     // Environment.getExternalStorageState() 로 마운트 상태 확인 가능합니다 

     File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
               Environment.DIRECTORY_PICTURES), "MyCameraApp");
     // 굳이 이 경로로 하지 않아도 되지만 가장 안전한 경로이므로 추천함.

     // 없는 경로라면 따로 생성한다.
     if (! mediaStorageDir.exists()){
         if (! mediaStorageDir.mkdirs()){
             Log.d("MyCamera", "failed to create directory");
             return null;
         }
     }

     // 파일명을 적당히 생성. 여기선 시간으로 파일명 중복을 피한다.
     String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
     File mediaFile;
     
     mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg");
     Log.i("MyCamera", "Saved at"+Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));

     return mediaFile;
 }
 @Override
 public void onPause(){
  super.onPause();
  // 보통 안쓰는 객체는 onDestroy에서 해제 되지만 카메라는 확실히 제거해주는게 안전하다.
  
 }
}

주석을 하도 깨알 같이 달았더니 딱히 더 추가로 이야기 할게 없어 보이는군요.
파일 저장을 하기 전에 콜백 메소드에서 추가적으로 이미지 처리를 할 수도 있는데요, 이럴때는 콜백 메소드에서 직접 처리 할게 아니라 AsyncTask (다음 포스트에서 설명) 등을 사용하여 별도의 스레드를 생성하여 처리를 해주는것이 좋습니다. 

처리 시간이 일정 시간 (제조사 마다 다를텐데 보통 5sec 라고 합니다)을 넘어서면 안드로이드 os 상에서 바로 ANR을 띄워 버리고 (어플리케이션 응답없음 팝업 ) 강제 종료 시켜버립니다. 사실 그런 이유가 아니더라도 사진 한장 찍고 다음 사진 찍을 준비를 하는데 시간이 오래 걸리는것도 좋은 설계는 아니니까 추가적인 처리를 넣게 된다면 스레드를 따로 생성 해야겠죠.

4. 매니페스트 정리
이번에는 딱히 매니페스트에 복잡한 내용은 없고 그냥 하드웨어 사용 권한, 기능 필터(마켓에서 카메라가 없는 경우 아얘 검색되지 않도록 하는 기능의 코드) 등의 추가가 있습니다.


    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
    <uses-feature android:name="android.hardware.camera" />
 
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name="MainActivity"
                  android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

여기서 액티비티 태그에 저는 아얘 스크린 방향을 가로모드로 지정해 버렸지만, 화면 방향은 이미 자바 메인액티비티 소스에서도 지정하고 있기 때문에 매니페스트에 추가 할 필요는 없었습니다. 단지 이렇게도 설정 가능하다는것 정도로 생각하시면 되겠네요.
이제 컴파일해서 기기에서 직접 실행해 보시고, 로그도 곳곳에 심어 두었으니 이클립스에서 LogCat을 통해 값의 변화나 내용을 확인 해보시는것도 도움이 될것 같습니다.

반응형

'안드로이드 개발' 카테고리의 다른 글

안드로이드 오픈소스 모음  (2) 2015.05.28
안드로이드 intent 클래스명으로 호출하기  (0) 2015.05.28
정적 변수와 메소드 (static)  (0) 2015.05.28
안드로이드 폰 정보  (0) 2015.05.27
Fake dragging  (0) 2015.05.22

WRITTEN BY
아카이시

,
반응형

정적 변수와 메소드 (static)

이번에는 static 에 대해서 알아보자.

static 은 보통 변수나 메소드 앞에 static 키워드를 붙여서 사용하게 된다.

static 변수

예를 들어 다음과 같은 클래스가 있다고 하자.

public class HousePark  {
    String lastname = "박";

    public static void main(String[] args) {
        HousePark pey = new HousePark();
        HousePark pes = new HousePark();
    }
}

박씨 집안을 나타내는 HousePark이라는 클래스이다. 위와 같은 클래스를 만들고 객체를 생성하면 객체마다 lastname을 저장하기 위한 메모리를 별도로 할당해야 한다. 하지만 가만히 생각해 보면 HousePark 클래스의 lastname은 어떤 객체이던지 동일한 값인 "박"이어야 할 것 같지 않은가? 이렇게 항상 값이 변하지 않는 경우라면 static 사용 시 메모리의 이점을 얻을 수 있다.

다음의 변경된 예제를 보자.

public class HousePark  {
    static String lastname = "박";

    public static void main(String[] args) {
        HousePark pey = new HousePark();
        HousePark pes = new HousePark();
    }
}

위와 같이 lastname 변수에 static 키워드를 붙이면 자바는 메모리 할당을 딱 한번만 하게 되어 메모리 사용에 이점을 볼 수 있게된다.

※ 만약 HousePark 클래스의 lastname값이 변경되지 않기를 바란다면 static 키워드 앞에 final이라는 키워드를 붙이면 된다. final 키워드는 한번 설정되면 그 값을 변경하지 못하게 하는 기능이 있다. 변경하려고 하면 예외가 발생한다.

static을 사용하는 또 한가지 이유로 공유의 개념을 들 수 있다. static 으로 설정하면 단 한곳의 메모리 주소만을 바라보기 때문에 static 변수의 값을 공유하게 되는 것이다. 다음의 예를 보면 더욱 명확하게 파악할 수 있을 것이다.

웹 사이트 방문시마다 조회수를 증가시키는 Counter 프로그램이 다음과 같이 있다고 가정 해 보자.

public class Counter  {
    int count = 0;
    Counter() {
        this.count++;
        System.out.println(this.count);
    }

    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
    }
}

c1, c2 객체 생성시 count 값을 1씩 증가하더라도 c1과 c2의 count는 서로 다른 메모리를 가리키고 있기 때문에 원하던 결과(카운트가 증가된)가 나오지 않는다.

결과값은 다음과 같이 나온다.

1
1

이제 다음 예를 보자.

public class Counter  {
    static int count = 0;
    Counter() {
        this.count++;
        System.out.println(this.count);
    }

    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
    }
}

int count = 0 앞에 static 키워드를 붙였더니 원하던데로 count 값이 공유되어 다음과 같이 방문자수가 증가된 결과값이 나오게 된다.

1
2

보통 변수의 static 키워드는 프로그래밍 시 메모리의 효율보다는 두번째 처럼 값을 공유하기 위한 용도로 더 많이 사용된다.

static method

static이라는 키워드가 메소드 앞에 붙으면 이 메소드는 클래스 메소드(static method)가 된다. 무슨 말인지 알쏭달쏭하겠지만 예제를 보면 매우 쉽다.

public class Counter  {
    static int count = 0;
    Counter() {
        this.count++;
    }

    public static int getCount() {
        return count;
    }

    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();

        System.out.println(Counter.getCount());
    }
}

getCount() 라는 static 메소드가 추가되었다. main 메소드에서 getCount() 메소드는 Counter.getCount() 와 같이 클래스를 통해 호출할 수 있게 된다.

※ 클래스 메소드 안에서는 인스턴스 변수 접근이 불가능 하다. 위 예에서 count는 static 변수이기 때문에 클래스 메소드(static method)에서 접근이 가능한 것이다.

보통 클래스 메소드는 유틸리티 성 메소드를 작성할 때 많이 사용된다. 예를 들어 "오늘의 날짜 구하기", "숫자에 콤마 추가하기"등의 메소드를 작성할 때에는 클래스 메소드를 사용하는 것이 유리하다.

다음은 "날짜"를 구하는 Util 클래스의 예이다.

import java.text.SimpleDateFormat;
import java.util.Date;


public class Util {
    public static String getCurrentDate(String fmt) {
        SimpleDateFormat sdf = new SimpleDateFormat(fmt);
        return sdf.format(new Date());
    }

    public static void main(String[] args) {
        System.out.println(Util.getCurrentDate("yyyyMMdd"));
    }
}

Util클래스의 getCurrentDate라는 클래스 메소드(static method)를 이용하여 오늘의 날짜를(yyyyMMdd) 구하는 예제이다. 위 클래스를 실행하면 오늘의 날짜가 출력될 것이다.

싱글톤 패턴 (singleton pattern)

디자인 패턴중 하나인 싱글톤에 대해서 알아보자. 여러분은 static 에 대한 개념이 생겼기 때문에 싱글톤을 이해하는것이 어렵지 않을 것이다.

싱글톤은 단 하나의 객체만을 생성하게 강제하는 패턴이다. 즉 클래스를 통해 생성할 수 있는 객체는 Only One, 즉 한 개만 되도록 만드는 것이 싱글톤이다.

다음의 예를 보자.

SingletonTest.java

class Singleton {
    private Singleton() {
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton = new Singleton();
    }
}

위와 같은 코드를 작성하면 컴파일 에러가 발생한다. 왜냐하면 Singleton 클래스의 생성자에 private 키워드로 외부 클래스에서 Singleton 클래스의 생성자로의 접근을 막았기 때문이다. 이렇게 생성자를 private 으로 만들어 버리면 외부 클래스에서 Singleton 클래스를 new 를 이용하여 생성할 수 없게 된다.

new를 이용하여 무수히 많은 객체를 생성한다면 싱글톤의 정의에 어긋나지 않겠는가? 그래서 일단 new로 객체를 생성할 수 없도록 막은 것이다.

그렇다면 Singletone 클래스의 객체는 어떻게 생성할 수 있을까? 다음처럼 코드를 작성 해 보자.

class Singleton {
    private Singleton() {
    }

    public static Singleton getInstance() {
        return new Singleton();
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
    }
}

위와 같이 코드를 변경하면 이제 getInstance라는 static 메소드를 이용하여 Singleton 객체를 돌려 받을 수 있다. 하지만 getInstance를 호출 할 때마다 새로운 객체가 생성되게 된다. 그렇다면 싱글톤이 아니다. 어떻게 해야 할까?

다음처럼 코드를 작성해 보자.

class Singleton {
    private static Singleton one;
    private Singleton() {
    }

    public static Singleton getInstance() {
        if(one==null) {
            one = new Singleton();
        }
        return one;
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1 == singleton2);
    }
}

Singleton 클래스에 one 이라는 static 변수를 두고 getInstance 메소드에서 one 값이 null 인 경우에만 객체를 생성하도록 하여 one 객체가 단 한번만 만들어지도록 했다.

getInstance 메소드의 동작원리를 살펴보자.

최초 getInstance가 호출 되면 one이 null이므로 new에 의해서 객체가 생성이 된다. 이렇게 한번 생성이 되면 one은 static 변수이기 때문에 그 이후로는 null이 아니게 된다. 그런 후에 다시 getInstance 메소드가 호출되면 이제 one은 null이 아니므로 이미 만들어진 싱글톤 객체인 one을 항상 리턴하게 된다.

main 메소드에서 getInstance를 두번 호출하여 각각 얻은 객체가 같은 객체인지 조사 해 보았다. 역시 예상대로 "true"가 출력되어 같은 객체임을 확인 할 수 있다.

싱글톤 패턴은 static에 대한 이해만 있다면 참 알기쉬운 패턴 중 하나이다.

※ 위에서 예제로 든 싱글톤은 Thread Safe 하지는 않다. 쓰레드 환경에서도 안전한 싱글톤을 만드는 방법은 이곳에 어울리지 않으므로 생략한다.

윗글은 https://wikidocs.net/228 에서 퍼온글입니다.

내용정리가 잘되어있어 가져왔습니다. 혹시 문제가 있으면 연락주세요.

반응형

'안드로이드 개발' 카테고리의 다른 글

안드로이드 intent 클래스명으로 호출하기  (0) 2015.05.28
안드로이드 카메라 소스  (1) 2015.05.28
안드로이드 폰 정보  (0) 2015.05.27
Fake dragging  (0) 2015.05.22
안드로이드 OS버젼체크  (0) 2015.05.08

WRITTEN BY
아카이시

,