Android/View | Posted by 덩치 2014. 4. 3. 16:21

나인패치 (9patch) 이미지 만드는 방법

펌 OK (출처 표시), 상업적 이용 NO, 컨텐츠 변경 NO

나인패치 이미지란


(http://developer.android.com/tools/help/draw9patch.html) <- 디벨로퍼사이트의 나인패치에 대한 설명


해상도가 다른 단말기에서 아이콘 등이 확대 될 때, 확대 되는 영역을 특정지어서 확대되서


픽셀이 깨지는 (라운드,그라데이션) 현상 없이 확대되는 이미지.


확대되도록 설정한 영역 이외의 부분은 확대가 되지 않는다.


쉽게말해 말풍선 같은 이미지를 네 모서리와 하단 꼭짓점 크기는 고정시키고 안의 크기만 늘려주며,


고정된 해상도라기 보다 어떤 해상도로 늘어나더라도 이미지가 깨지지 않도록 하는 방식이다.


다음 이미지를 보자




위 이미지가 나인패치 이미지이다. 확장자가 .9.png로 특이한 형태를 취하고 있으며,


풍선 주위에 검은색 선은 확장 영역과 콘텐츠 영역을 표시하는것으로, 실제 이미지가 적용되면 나타나지 않는다.


나인패치 사용법에 앞서 우선 위의 이미지가 어떻게 확장되는지 설명을 하자면


다음 이미지를 보며 설명해주겠다.




왼쪽변의 검은 선은 세로로 확장되는 영역, 상단변의 짧은 선 두개는 각각 세로로 확장되는 영역이며,


우측변의 검은 선은 콘텐츠(텍스트 등)가 들어가는 세로영역, 하단의 변은 마찬가지 콘텐츠가 들어가는 가로 영역이다.


분홍색 기둥 두개가 보일텐데, 간단히 말해 저 두개의 영역이 확대,축소되며 이미지를 표시한다.


즉, 노란부분과 분홍색 막대 상,하단, 왼쪽분홍막대 좌측, 오른쪽 분홍막대 우측 영역은


이미지 크기가 불변한다. (돼지꼬리)


분홍색 막대만이 가로,세로로 확장되고 축소되며 전체 이미지가 커지거나 작아지는것처럼 표시된다.


그렇기때문에 작은 해상도에서는 라운드가 엄청 커보이지만, 큰 해상도에서는 라운드가 작아보이게 되고


엄청 커지면 라운드는 거의 없는것처럼 보이게 된다.


밑의 말풍선 꼬리 영역도 마찬가지로 크기가 변하지 않는다.


위 이미지처럼 라운드가 있거나, 또는 그라데이션이 있어서 그라데이션 영역은 확장하지 않고, 이외의 부분만


확장될 수 있을 때, 나인패치 이미지를 사용한다.


나인패치의 한계는 하트모양이나 동그라미처럼 특정 영역만 확대하면 모양이 이상해져버리는 


도형에는 사용할 수 없다는것이다.


나인패치를 만드는법을 알아보자. 우선 이미지를 준비한다. 포토샵으로 그려도 되고 기존의 이미지를 사용해도 된다.



나는 간단하게 아래의 이미지로 실습을 해 보겠다.




다음은 나인패치 이미지를 만들 수 있는 툴을 실행시킨다.

(툴은 안드로이드 sdk에 있으며, 툴이 없는 경우 포토샵으로 맨처음 이미지처럼 만들고, 확장자를 name.9.png 식으로 저장하여도 된다.)


나의 경로는

adt-bundle-windows-x86_64-20130522 -> sdk -> tools -> draw9patch.bat


draw9patch.bat 파일을 실행시키면 잠시후 다음과 같은 창이 나타나는데,




이미지를 드래그해서 위 창에 드랍시킨다.




그럼 이렇게 표시된다. 우측은 미리보기이고..


상하좌우 맨 마지막 1픽셀을 마우스로 클릭하여 드래그하면 검은색 선이 생기는데,


이런 방식으로 영역을 설정 해 준다.


일반적으로.콘텐츠영역은 확대되는 영역의 최대 영역으로 설정한다.


영역 취소는 시프트를 누르고 클릭 하여 드래그하면 된다.




(이런식으로 색상이 구분되는것은 하단에 show patches 를 체크해 주면 된다.)


분홍색 부분이 확대되는 영역, 초록색과 붉은부분이 확대되지 않는 영역이며


우측을 보면 확대를 엄청 했을경우 미리보기인데, 라운드부분은 확대되지 않아서 상대적으로 작아보인다.


하지만 이런 특성 덕분에 라운드가 깨져보일 염려는 없다.




나름대로 알아듣기 쉽게 설명하기 위해 노력했는데 잘 전달 되었는지 모르겠습니다.


문의사항은 댓글 남겨주세요



펌 환영. 출처만 남겨주세요



Android/View | Posted by 덩치 2013. 12. 24. 14:56

SurfaceView 구현 / 사용법

펌 OK (출처 표시), 상업적 이용 NO, 컨텐츠 변경 NO

SurfaceViewView를 상속받은 클래스로 Video Memory로 바로 접근하기 때문에 일반 View에서의 랜더링 

속도보다 빠르다.

일반 View는 많은 그리기 작업을 하면 메인 스레드의 자원을 다 잡아먹어버리기때문에 상당히 느려지게 되는데

이러한 단점을 보완하기 위한 클래스라고 보면 된다


우선 액티비티에 SurfaceHolder.Callback을 해 주고, add unimplemented methods를 해 준다.


public class DrawClass implements SurfaceHolder.Callback{


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}


@Override

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.main, menu);

return true;

}


@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width,

int height) {

}


@Override

public void surfaceCreated(SurfaceHolder holder) {

}


@Override

public void surfaceDestroyed(SurfaceHolder holder) {

}

}


그럼 셋팅은 끝. 사용되는건 surfaceCreatedsurfaceDestoryed 정도가 되겠다.

surfaceCreated는 처음 서피스뷰가 생성될 때 호출되는데, 주의할점은 백그라운드 상태로 내려갔다가, 다시 올라오게 되면

surfaceview가 다시 created된다는점이다. created에서 thread를 실행시키는 작업을 할 경우

반드시 스레드가 동작중인지 여부를 먼저 확인해야한다. 그렇지않으면 익셉션 발생


surfaceChanged는 업데이트를 대비해 만들었다, 뭐라뭐라 하던데 잘 모르겠다. 사용하지않는듯하다.


이제 SurfaceView 구성은 하였고, 실시간으로 그리기를 실행하려면 Thread를 구동해야한다.


private Class PaintThread extends Thread {

public void run() {

while(!this.isInterrupted()){    //스레드가 인터럽트 될 때까지 반복 실행.

if (그리기 실행조건) {

//그리기 실행

doDraw();

}

}

}

}


나는 주로 드로우 관련부를 따로 만들어서 호출하는방식으로 사용한다.

비트맵을 그려보겠다. surfaceCreated에 다음과 같이 선언한다. (그 위에 Bitmap img, tempBitmap 전역변수로 선언해둔다)

팁 하나, 비트맵 이미지가 큰 경우 메모리를 상당히 많이 잡아먹어 종종 익셉션이 발생하게 되는데,

BitmapFactory.Options option = new BitmapFactory.Options();

option.inSampleSize = 1;

option.inPurgeable = true;

option.inDither = true;

이렇게 해 주면 비트맵이 먹는 메모리를 줄일 수 있다. 리사이클만으로는 감당이 안되는 경우가 생겨 찾다보니 이렇게 해주니 해결되더라. 이렇게 했을경우 단점은 잘 모르겠다. 고퀄리티를 요하는 작업이 아니었기때문에..

그리고 

tempBitmap = BitmapFactory.decodeResource(res, R.drawable.img30x30, option);

img = Bitmap.createScaledBitmap(tempBitmap, imgWidth, imgHeight, true);


private void doDraw() {

try {

canvas = mHolder.lockCanvas(); //그리기 준비

     int size = MainActivity.ARR_X.size();

     canvas.drawColor(Color.GRAY);  //배경색 설정

Paint p = new Paint();

p.setAntiAlias(true);

     canvas.drawBitmap(img, imgX, imgY, p);  //이미지를 X,Y위치에 그리기

catch (Exception e) {

}

finally {

mHolder.unlockCanvasAndPost(canvas); //그리기 종료

}

}


그리고 MainActivityxml에서


<패키지명.DrawClass

android:layout_width="500px"

android:layout_height="500px" />

와 같은식으로 호출한다.


여기까지가 SurfaceView 사용 방법이다.


참고로 위의 소스는 새로운 클래스를 만들어서 기존 액티비티에 영역추가하는식으로 사용한것이고

뷰와 마찬가지로 액티비티에서 바로 setContentView하여 적용 할 수도 있다.

문의점은 댓글 남겨주기바람


펌 OK (출처 표시), 상업적 이용 NO, 컨텐츠 변경 NO

이 글에서는 

터치이벤트(TouchEvent)로 비트맵이미지 이동시키기

에서 발전하여 특정 영역 밖으로 이미지가 이동하지 않도록 하는 방법을 알아보겠다. 아주 간단하다.


화면 영역 밖으로 나가지 않게 하려면 단말기의 Height와 Width를 받아오면 되고, 특정 수치를 부여하려면

이런 과정이 필요없다.

Display dis = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

int width = dis.getWidth();

int height = dis.getHeighy();


그리고 다음과 같은 조건을 준다.

@Override

public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_DOWN) {

//디스플레이 영역 설정. 이미지X,Y는 이미지의 X좌표,Y좌표를 뜻함

if (이미지X >= 0 && 이미지X <= width && 이미지Y >= 0 && 이미지Y <= height) {

//그리기 실행

}

//여기

   }

}

이상태로 끝내버리면 위 조건이 false, 즉 화면영역 밖으로 좌표가 튀어나갔을경우 그리기가 멈춰버린다. 이미 값이

튀어 나갔기때문에 true가 들어오지 않는다. 그러므로

//여기 위치에서 이미지를 영역 안으로 새로고침해준다.

if (이미지X < 0) {

이미지X = 0;

}

if (이미지X > width) {

이미지X = width

}

if (이미지Y < 0) {

이미지Y = 0;

}

if (이미지Y > height) {

이미지Y = height

}


이렇게 해주면 이미지가 조건을 벗어나게 되더라도 새로고침하여 그 다음 입력이 true가 되므로,

실행을 이어서 할 수 있다.


단말 전체가아니라 특정 영역에서만 움직이게 하고싶다면 width,height는 상황에 맞게 조절하면 된다.


문의점은 댓글로 !