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);
  }
 }


2015년 3월 2일 월요일

How To Vibrato Flute, 비브라토 하는 법 플룻

잘 안알려준다. 보통. 비브라토

꽃이다. 플룻의....


걍 답을 말해버립니다.

저도 이거 알려고 돈도 많이 들이고 고민도 많이 하고 노력도 많이 했습니다.


비브라토는....

후후후....

???

1. 후를 세게 불어보세요.

   후를 불면서 마지막에 뚝 끊어 보아요.

   그럼 배가 움직이고 움찔거리는게 정상입니다.

2. ㅋㅋ...그럼 그 후를 반복적으로 해보아요.

   훗 훗 훗 훗

3. 자 이걸 플룻을 입에 갔다 대고 소리를 내면서 해보아요 ^^

4. 한번 소리를 요동(떨어줄)칠 때마다 배가 움찔거린다고 생각하시면 되요~



For foreigner, I tell you how to vibrato with flute.

You spit your breath sounding "hu-" and stop it with power to your stomach.

(The important point is that you stop "hu-" with power, not long tone. Do not breathe long tone.

You have to sudden stop. It is similar to staccato, but not using your tongue. Just apply a strain to the stomach)

Yes. Your stomach will be moved back and forth.

You do it repeatedly. Then now you can vibrato.

Very simple.

Android Web 요청 및 응답 받기 ( Web Request and Read) & AsyncTask

Android에서 웹 요청을 하는 코드를 넣을 때에는

AsyncTask 를 사용하여 그 안에서 웹 요청을 하고

끝나고 나면 onPostExecute 함수 안에서 UI 수정등을 수행하도록 한다.

예제 코드 (Example)

private void sendMsgBackground() {
 new AsyncTask() {
  @Override
  protected String doInBackground(Void... params) {
   String msg = "";
   try {
    msg = sendMsg();
   }
   catch (Exception e) {
   }

   return msg;
  }

  @Override
  protected void onPostExecute(String msg) {
  }
 }.execute(null, null, null);
}

private void sendMsg() {
 HttpURLConnection urlConnection = null;

 try {
  URL url = new URL("http://mywebsite");
  urlConnection = (HttpURLConnection) url.openConnection();
  urlConnection.setRequestMethod("POST");
  urlConnection.setDoInput(true);
  urlConnection.setDoOutput(true);

  String urlParameters = "param1=paramVal1";
  urlParameters = urlParameters +"&¶m2=paramVal2";

  DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
  BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(wr, "UTF-8"));
  writer.write(urlParameters);
  writer.close();
  wr.close();

  int responseCode = urlConnection.getResponseCode();

  BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));

  String inputLine;
  StringBuffer response = new StringBuffer();

  while ((inputLine = in.readLine()) != null) {
   response.append(inputLine);
  }
  in.close();
 }
 catch (Exception e) {
  //TODO: exception handling
 }
 finally {
  urlConnection.disconnect();
 }
}
  

SurfaceView 와 OnResume, OnPause, Thread

Activity 안에 SurfaceView 넣어서 앱을 만들 때,

Activity의 OnPause, OnResume 그리고 SurfaceView의 Thead와의 관계에 대하여

구글링으로도 쉽게 나오지 않아서 블로그에 올려야 겠다고 생각을 했다.

제대로 만들었으면

화면을 껏다가 켜도(Screen Off and On)

SurfaceView의 Thread가 다시 돌아가야 한다.

결론으로 정답은 다음과 같다.



Activity

public void onPause() {
 super.onPause();
 view.pause();
}

@Override
protected void onResume()  
{
 super.onResume();  
 view.resume();
}
  

SurfaceView

   
public void surfaceCreated(SurfaceHolder holder) {
 isItOk = true;
 thread = new Thread(this);
 thread.start();
}

public void surfaceDestroyed(SurfaceHolder holder) {
 try {
  if (thread != null) thread.join();
 }
 catch (InterruptedException e) {}
 thread = null;
}

public void pause() {
 isItOk = false;
 while (true) {
  try { thread.join(); }
  catch (InterruptedException e) {}
  break;
 }
 thread = null;
}

public void resume() {
 isItOk = true;

 if (thread == null && holder.getSurface().isValid()) {
  thread = new Thread(this);
  thread.start();
 }
}

@Override
public void run() {
 Canvas canvas;
 while (isItOk) {
  canvas = holder.lockCanvas();
  canvas.drawColor(Color.rgb(184, 210, 234));
  holder.unlockCanvasAndPost(canvas);
 }
}

  

명료한 답입니다.

Screen Off 했다 On해도 다시 돌아가고
back key를 눌러도, Background에 갔다가 다시 와도
다시 정상적으로 돌아갑니다 ^^