페이지

2011년 6월 20일 월요일

SD 카드 파일 생성 / 삭제

일반적으로 외장카드 영역을 불러올때 /mnt/sdcard 이런 식으로 하드 코딩하시는 분들이 많은데
이런 경우 외장카드를 사용하지 않는 사용자인 경우 에러가 발생하게 됩니다.

사용자 폰에 외장카드 존재유무를 확인하여 처리하는 모듈을 간단히 구현해 봤으니 참고하세요!!

        String mSdPath;
        String ext = Environment.getExternalStorageState();
        if (ext.equals(Environment.MEDIA_MOUNTED)) {
            mSdPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/";
        } else {
            mSdPath = Environment.MEDIA_UNMOUNTED+"/";
        }
        File file = new File(mSdPath);
        if (!file.exists()) mSdPath = "";       

        // 파일 생성
        BufferedWriter out = new BufferedWriter(new FileWriter(mSdPath+"androes.txt"));
        out.write("test text messages");
        out.newLine();
        out.close();

        // 파일 삭제
        File file = new File(mSDPath + "/" + "androes.txt");
        file.delete();

SD 카드 생성 방법

안드로이드 에뮬레이터에서 사용자가 임의로 파일을 저장하려면 안드로이드 에뮬레이터에 가상의 SD카드가 마운트 되어 있어야 합니다. 오늘은 mksdcard를 이용한 가상의 SD카드 생성부터 시작해서 이클립스 내의 설정 변경, DDMS를 이용하여 가상의 SD카드에 파일 넣기까지 알아보겠습니다.

일단, 에뮬레이터에 마운트시킬 가상의 SD카드를 생성해야합니다. 가상 SD카드를 생성하려면 안드로이드 SDK폴더\tools 폴더의 mksdcard.exe 을 이용하면 됩니다.



실행-cmd를 입력하여 명령 프롬프트 창을 열고, 안드로이드 sdk가 설치된 폴더\tools폴더에 간 후, mksdcard [용량] [파일명] 을 입력하여 가상의 SD카드를 만듭니다. 저는 128MB의 크기로 만들었습니다.


생성된 가상 SD카드 파일


가상 SD카드 파일도 만들었으니, 이제 에뮬레이터에 마운트를 시켜야겠지요?
에뮬레이터를 실행시킬 때 sd카드를 마운트시키는 옵션을 활성화시켜주어야 하는데요, 그 설정은 Run-Run Configurations-Target 탭의 Additional Enulator Command Line Options에 넣어주면 됩니다.


저같은 경우는 생성한 가상SD카드를 c:\ 드라이브에 넣어주어서 경로를 저렇게 설정하였습니다. 각자 경로에 맞게끔 넣어주시면 됩니다.

이렇게 설정을 끝낸 후, 에뮬레이터를 실행시키면 가상 SD카드가 마운트 된 채로 에뮬레이터가 실행됩니다.
그럼, 이 SD카드에 파일을 넣어볼까요?

이클립스에서 DDMS를 실행시킨 후, File Explorer탭을 클릭해보면 아래와 같은 아이콘이 보이실겁니다. 에뮬레이터에 파일을 넣어야하니 Push a file onto the device를 눌러줍니다.



요로코롬 파일을 선택할 수 있습니다. 개발하는데 깝깝한데~~ 음악이나 듣게 음악을 넣어봅시다. 이 때, 파일명은 영어로 되어 있어야 합니다. 안그러면 에러납니다. -_-


자, 저렇게 해주면 이렇게! 뿅! 들어가있는 것을 볼 수 있습니다.
자, 그럼 이제 이 파일을 안드로이드 에뮬레이터에서 들어볼까요?
파일을 추가했으니, 바로 음악을 듣기 전에 Dev Tools의 Media Scanner를 실행해서 파일을 인식시켜주어야 합니다.


Media Scanner가 새로 추가된 파일을 검색중인 화면.

검색이 끝났다면, 음악을 들어볼까요?
Music을 눌러보시면, 다음과 같이 음악이 추가되어있는 것을 보실 수 있을 겁니다!



음악 파일 뿐만 아니라 다른 파일을 넣을 떄에도 지금과 동일한 과정으로 수행하시면 됩니다. :)

출처 : 커니의 안드로이드

2011년 6월 16일 목요일

안드로이드 메모리 사용량 알기

int availProc = Runtime.getRuntime().availableProcessors();
                long total = Runtime.getRuntime().totalMemory();
                long free = Runtime.getRuntime().freeMemory();
                long max = Runtime.getRuntime().maxMemory();

                Toast.makeText(getApplicationContext(),availProc + "\nTM "
                                + Long.toString(total) + "\nFM " +
Long.toString(free)
                                + "\nMM" + Long.toString(max),

Toast.LENGTH_SHORT).show();
                }
--~--~---------~--~----~------------~-------~--~----~

최소 heap 정하기, MAX는 못바꿈

VMRuntime.getRuntime().setMinimumHeapSize( long l );

------------------------------------------------------
cat proc/meminfo

http://www.suppul.com/blog/main/192?TSSESSIONwwwsuppulcomblog=51f5eebf5ad510e43b1dbf99485bd5e9

2011년 6월 15일 수요일

CustomView 생성하기

안드로이드에서 기본적으로 지원하지 않는 UI 를 만들때 CustomView 를 사용합니다.
이러한 CustomView 의 기본적인 작성방법을 알아보도록 하겠습니다.

CustomView 는 “android.view.View” 클래스를 상속해서 만들어 집니다.
기본적으로 onDraw() 메소드만 재정의해서 xml 에 view 태그만 추가하면 오류없이 출력되는것을 볼 수 있습니다.

이번 포스트에서는 간단히 클릭하면 반응하는 CustomView 를 만들어 보도록 하겠습니다.
먼저 CustomView 소스를 확인해 보도록 하겠습니다.

- CustomView.java
package net.cranix.android.customviewtest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

public class CustomView extends View {
    
    private String text = null;
    private int backgroundColor = Color.RED;

    private String tempText;
    
    
    // 속성이 없는 생성자는 소스상에서 직접 생성할때만 쓰인다. 
    public CustomView(Context context) {
        super(context);
        Log.w(Constants.TAG,"CustomView("+context+")");
    }
    /*
     *  리소스 xml 파일에서 정의하면 이 생성자가 사용된다.
     *  
     *  대부분 this 를 이용해 3번째 생성자로 넘기고 모든 처리를 3번째 생성자에서 한다.
     */
    public CustomView(Context context,AttributeSet attrs) {
        this(context,attrs,0);
        Log.w(Constants.TAG,"CustomView("+context+","+attrs+")");
    }
    
    /*
     * xml 에서 넘어온 속성을 멤버변수로 셋팅하는 역할을 한다.
     */
    public CustomView(Context context,AttributeSet attrs,int defStyle) {
        super(context,attrs,defStyle);
        
        this.text = attrs.getAttributeValue(null,"text");
        
        Log.w(Constants.TAG,"CustomView("+context+","+attrs+","+defStyle+"),text:"+text);
    }
    
    /*
     * xml 로 부터 모든 뷰를 inflate 를 끝내고 실행된다.
     * 
     * 대부분 이 함수에서는 각종 변수 초기화가 이루어 진다.
     * 
     * super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
     */
    @Override
    protected void onFinishInflate() {
        setClickable(true);
        Log.w(Constants.TAG,"onFinishInflate()");
    }
    
    /*
     * 넘어오는 파라메터는 부모뷰로부터 결정된 치수제한을 의미한다.
     * 또한 파라메터에는 bit 연산자를 사용해서 모드와 크기를 같이 담고있다.
     * 모드는 MeasureSpec.getMode(spec) 형태로 얻어오며 다음과 같은 3종류가 있다.
     *         MeasureSpec.AT_MOST : wrap_content (뷰 내부의 크기에 따라 크기가 달라짐)
     *         MeasureSpec.EXACTLY : fill_parent, match_parent (외부에서 이미 크기가 지정되었음)
     *      MeasureSpec.UNSPECIFIED : MODE 가 셋팅되지 않은 크기가 넘어올때 (대부분 이 경우는 없다)
     *  
     *   fill_parent, match_parent 를 사용하면 윗단에서 이미 크기가 계산되어 EXACTLY 로 넘어온다.
     *   이러한 크기는 MeasureSpec.getSize(spec) 으로 얻어낼 수 있다.
     *   
     *   이 메소드에서는 setMeasuredDimension(measuredWidth,measuredHeight) 를 호출해 주어야 하는데
     *   super.onMeasure() 에서는 기본으로 이를 기본으로 계산하는 함수를 포함하고 있다.
     *   
     *   만약 xml 에서 크기를 wrap_content 로 설정했다면 이 함수에서 크기를 계산해서 셋팅해 줘야한다.
     *   그렇지 않으면 무조껀 fill_parent 로 나오게 된다.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        
        // height 진짜 크기 구하기
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = 0;
        switch(heightMode) {
        case MeasureSpec.UNSPECIFIED:    // mode 가 셋팅되지 않은 크기가 넘어올때
            heightSize = heightMeasureSpec;
            break;
        case MeasureSpec.AT_MOST:        // wrap_content (뷰 내부의 크기에 따라 크기가 달라짐)
            heightSize = 20;
            break;
        case MeasureSpec.EXACTLY:        // fill_parent, match_parent (외부에서 이미 크기가 지정되었음)
            heightSize = MeasureSpec.getSize(heightMeasureSpec);
            break;
        }
        
        // width 진짜 크기 구하기
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = 0;
        switch(widthMode) {
        case MeasureSpec.UNSPECIFIED:    // mode 가 셋팅되지 않은 크기가 넘어올때
            widthSize = widthMeasureSpec;
            break;
        case MeasureSpec.AT_MOST:        // wrap_content (뷰 내부의 크기에 따라 크기가 달라짐)
            widthSize = 100;
            break;
        case MeasureSpec.EXACTLY:        // fill_parent, match_parent (외부에서 이미 크기가 지정되었음)
            widthSize = MeasureSpec.getSize(widthMeasureSpec);
            break;
        }

        
        Log.w(Constants.TAG,"onMeasure("+widthMeasureSpec+","+heightMeasureSpec+")");
        
        setMeasuredDimension(widthSize, heightSize);
    }
    
    
    /*
     *  onMeasure() 메소드에서 결정된 width 와 height 을 가지고 어플리케이션 전체 화면에서 현재 뷰가 그려지는 bound 를 돌려준다.
     *  
     *  이 메소드에서는 일반적으로 이 뷰에 딸린 children 들을 위치시키고 크기를 조정하는 작업을 한다.
     *  유의할점은 넘어오는 파라메터가 어플리케이션 전체를 기준으로 위치를 돌려준다.
     *  
     *  super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        Log.w(Constants.TAG,"onLayout("+changed+","+left+","+top+","+right+","+bottom+")");
    }
    
    
    /*
     * 이 뷰의 크기가 변경되었을때 호출된다.
     * 
     * super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        
        Log.w(Constants.TAG,"onSizeChanged("+w+","+h+","+oldw+","+oldh+")");
    }
    
    
    /*
     * 실제로 화면에 그리는 영역으로 View 를 상속하고 이 메소드만 구현해도 제대로 보여지게 된다.
     * 
     * 그릴 위치는 0,0 으로 시작해서 getMeasuredWidth(), getMeasuredHeight() 까지 그리면 된다.
     * 
     * super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
     */
    @Override
    protected void onDraw(Canvas canvas) {
        final Paint p = new Paint();
        p.setColor(backgroundColor);
        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(), p);
        if (text != null) {
            p.setColor(Color.BLACK);
            canvas.drawText(text, 10, 15, p); // 왼쪽 아래를 0,0 으로 보고있음
        }
        Log.w(Constants.TAG,"onDraw("+canvas+")");
    }
    
    
    /*
     * 현재 view 가 focus 상태일때 key 를 누르면 이 메소드가 호출됨.
     * 즉 이 메소드를 사용하려면 setFocusable(true) 여야함. 
     * 
     * 그리고 super 메소드에서는 기본적인 키 작업(예를들면 BACK 키 누르면 종료)을 처리하기 때문에 일반적으로 return 시에 호출하는게 좋다.
     * 만약 기본적인 작업을 하지않게 하려면 super 함수를 호출하지 않아도 된다.
     * 
     * 다른 event 메소드들도 유사하게 동작한다.
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.w(Constants.TAG,"onKeyDown("+keyCode+","+event+")");
        return super.onKeyDown(keyCode, event); 
    }
    
    /*
     * 이 view 에 touch 가 일어날때 실행됨.
     * 
     * 기본적으로 touch up 이벤트가 일어날때만 잡아내며 
     * setClickable(true) 로 셋팅하면 up,move,down 모두 잡아냄
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.w(Constants.TAG,"onTouchEvent("+event+")");
        switch(event.getAction()) {
        case MotionEvent.ACTION_UP:
            backgroundColor = Color.RED;
            text = tempText;
            break;
        case MotionEvent.ACTION_DOWN:
            backgroundColor = Color.YELLOW;
            tempText = text;
            text = "Clicked!";
            break;
        case MotionEvent.ACTION_MOVE:
            backgroundColor = Color.BLUE;
            text = "Moved!";
            break;
        }
        invalidate();
        return super.onTouchEvent(event);
    }
    
    
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
}
- 크기 계산하기
여기서 중요한 메소드는 onMeasure() 메소드 입니다.
이 메소드는 뷰의 전체 크기를 정하는 메소드 인데 안드로이드의 크기 정하는 방법에 따라 구현법이 달라져야 합니다.

안드로이드 레이아웃 xml 파일에서 크기를 지정하는 방법은 4가지가 있습니다.
   - fill_parent (상위 View 의 크기에 따름)
   - match_parent (상위 View 의 크기에 따름)
   - fixed (100px 와 같이 픽셀로 박아놨을때)
   - wrap_content (현재 뷰의 내용에 따름)

이렇게 4가지 방법의 특성에 따라서 넘어오는 크기의 종류는 3가지로 구분됩니다.
   - MeasureSpec.EXACTLY : fill_parent, match_parent, fixed 와 같이 상위에서 이미 결정되어버린 크기가 넘어올때 선택됩니다.
   - MeasureSpec.AT_MOST : wrap_content 를 선택했을때 선택됩니다.
   - MeasureSpec.UNSPECIFIED : xml 에 의하지 않고 소스상에서 직접 넣었을 때 나옵니다.

여기서 EXACTLY 과 UNSPECIFIED 는 외부에서 크기가 구해져서 내려오는 것이기 때문에 따로 계산할 것이 없으나 AT_MOST 는 내부적으로 크기계산을 해 주어야 합니다.
위의 소스에서는 간단하게 100,20 으로 박아놨지만 실제로 CustomView 를 구현하게 된다면 뷰의 특성에 따라 구현이 달라져야 할 것입니다.


- xml 에서 파라메터 받아내기
안드로이드 리소스 xml 에서 파라메터를 받아내려면 위 소스의 3번째 생성자에 있는것 처럼 아래와 같은 구문을 써야 합니다.

this.text = attrs.getAttributeValue(null,"text");


- xml 파일 구성하기
이렇게 만든 CustomView 를 xml 파일에서 사용하려면 아래와같은 xml 구성이 필요합니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <view class="net.cranix.android.customviewtest.CustomView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        text="test"
    /></LinearLayout>

- 실행해 보기
이렇게 구성된 뷰를 Activity 에 넣고 실행해 보면 아래와 같은 화면이 나옵니다.
마우스를 클릭,이동 할때마다 색깔이 변경되는것을 볼 수 있습니다.


image

image

image