2015년 3월 14일 토요일

SurfaceView 통해서 커다란 이미지 스크롤하면서 특정 영역 클릭 반응하게 하기

게임 같은 거 보면

일단 맵을 크게 그려놓고, 그 안에서 스크롤도 하고 

특정 영역을 클릭하면 반응하게 하는 화면을 쉽게 볼 수 있었을 것이다. 

이는 SurfaceView를 통해 구현이 가능하다. 

SurfaceView의 Thread관리에 대해서는 이전에 포스팅한게 있으니 

그것을 참고하여 죽지 않는 SurfaceView를 구현하시길 바랍니다. 

일단 큰 이미지를 준비해주세요

예를 들어, 휴대폰 해상도가 1280 * 704 라고 하면 

이미지의 사이즈가 1500 * 1000 정도는 되어야 스크롤을 할 껀덕지가 생깁니다. 

1. 화면 스크롤 구현

전역 변수로 다음 변수를 두고
int cur_x = 0;
int cur_y = 0;

화면 터치해서 움직임이 있는 만큼, cur_x, cur_y를 움직여 줍니다. 

화면을 터치해서 오른쪽으로 20만큼 움직이는 행동을 했으면

cur_x를 + 20 해줍니다. 

그리고, 배경 이미지를 그려주는 쪽에서는 

canvas.drawBitmap(backImg,  
                          new Rect(cur_x, cur_y, cur_x+screenWidth,cur_y+screenHeight),  
                          new Rect(0, 0, screenWidth, screenHeight), 
                          null);

이것의 의미가 뭐냐면, 2번째 인자로 넘겨준 Rect는 
이미지 원본 크기에서 그려줄 영역을 지정하는 것입니다. 

즉, 우리가 1500 * 1000 의 이미지를 준비했다면, 
이 이미지의 2번째 인자의 Rect 에 해당하는 영역을 crop(잘라내기) 해서
3번쨰 인자로 넘겨준 화면의 크기에 그리겠다는 의미가 된다. 

다시 한번 정리하자면, 
이미지 원본 크기 (1500 * 1000) 
2번쨰 인자의 Rect : 이미지 원본 크기에서 잘라낼 영역 
3번째 인자의 Rect : 잘라낸 이미지를 그릴 스마트폰 화면상의 영역

따라서, 2번째 인자의 Rect가 크기가 
3번쨰 인자의 Rect보다 작으면
그만큼 확대가 되어서 보이게 됩니다. (화질이 떨어지겠죠)

20*20 의 사이즈의 이미지를 40 * 40 에 그리라고 하면 2배로 확대가 됩니다. 

2. 특정 영역을 버튼으로 지정하기 

먼저 버튼으로 지정하고 싶은 특정 영역을 Rect로 지정합니다.

Rect clickRect = new Rect(500, 500, 650, 650);

여기서의 Rect의 좌표는 원본 이미지에서의 위치입니다. 

그리고, 같은 값으로 Rect를 하나 더 만듭니다. 

Rect newRect = new Rect(500, 500, 650, 650);

이 newRect는 화면의 스크롤에 따라서 값이 바뀌어야 합니다. 

newRect.set(clickRect.left - cur_x, clickRect.top - cur_y, clickRect.right - cur_x, clickRect.bottom - cur_y);

화면의 스크롤에 따라서 버튼 영역이 상대적으로 계산이 되게 됩니다

그럼, onTouchEvent 에서는 newRect의 영역안에 터치한 좌표가 들어가는지 

체크하여 반응시키면 됩니다. Rect클래스에 contains(x,y)라는 함수가 있으니

활용하면 됩니다. 


코드






 int past_x = 0;
 int past_y = 0;

 int cur_x = 0;
 int cur_y = 0;
 
 Rect clickRect = new Rect(500, 500, 650, 650);
 Rect newRect = new Rect(500, 500, 650, 650);
 
 @Override
 public boolean onTouchEvent(MotionEvent event) {
  int keyAction = event.getAction();
  int x = (int)event.getX();
  int y = (int)event.getY();
  switch (keyAction){
  case MotionEvent.ACTION_DOWN:
   past_x = x;
   past_y = y;
   break;
  case MotionEvent.ACTION_MOVE: 
   cur_x = cur_x + (past_x - x );
   cur_y = cur_y + (past_y - y);
   if (cur_x <0) cur_x = 0;
   if (cur_x  > screenWidth) cur_x = screenWidth;
   
   if (cur_y <0) cur_y = 0;
   if (cur_y  > screenHeight) cur_y = screenHeight;
   
   past_x = x;
   past_y = y;
   break;
  }
  return true;
 }

 boolean first = true;
 @Override
 public void run() {
  Canvas canvas;
  while (isItOk) {
   canvas = holder.lockCanvas();

   if (first) {
    screenWidth = canvas.getWidth();
    screenHeight = canvas.getHeight();
    first = false;
   }

   //배경 이미지의 크기
   int width = worldmap.getWidth();
   int height = worldmap.getHeight();

   canvas.drawBitmap(worldmap,  new Rect(cur_x, cur_y, cur_x+screenWidth,cur_y+screenHeight),  new Rect(0, 0, screenWidth * 1, screenHeight * 1), null);
 
   canvas.drawText("screen " +screenWidth + "," + screenHeight + " image " + width + " " + height, 0, 50, p);
   
   newRect.set(clickRect.left - cur_x, clickRect.top - cur_y, clickRect.right - cur_x, clickRect.bottom - cur_y);
   
   canvas.drawRect(newRect, p);
   
   holder.unlockCanvasAndPost(canvas);
  }
 }


댓글 없음:

댓글 쓰기