관리 메뉴

moozi

그래픽 2D 5 - 아이콘 여러개 띄우기 본문

안드로이드개발강좌

그래픽 2D 5 - 아이콘 여러개 띄우기

moozi 2010. 3. 12. 22:59

이번 강좌는 지난 강좌 '[안드로이드 개발 2.0 ] 그래픽 2D 4 - onTouchEvent 사용하기' 에서 이어집니다.

이번 강좌에서는 터치하는 위치에 새로운 아이콘이 추가되도록 해보겠습니다. 

중요한 점을 미리 정리하면,

a. 새로운 비트맵 아이콘들을 생성하기 위한 클래스를 추가합니다.
b. a에서 만든 클래스로 생성된 객체들을 담기위해서 ArrayList를 사용합니다.
c. CustomView클래스의 onDraw메서드에서 for문을 사용해서 아이콘이미지들을 그립니다.
d. onTouchEvent 에서 SurfaceHolder를 동기화하여 처리합니다.

와 같습니다.


그럼 단계적으로 살펴보겠습니다.


1. 새로운 클래스 GraphicIcon 를 MyGraphics2D02 클래스 내부에 다음과 같이 작성합니다.

class GraphicIcon {
        private Bitmap bm;
        private Coordinates co;
    
        public GraphicIcon(Bitmap bitmap) {
            bm = bitmap;
            co = new Coordinates();
        }
    
        public Bitmap getGraphic() {
            return bm;
        }
    
        public Coordinates getCoordinates() {
            return co;
        }
    
      
        public class Coordinates {
            private int x = 0;
            private int y = 0;
    
            public int getX() {
                return x;
            }
    
            public void setX(int value) {
                x = value - bm.getWidth() / 2;
            }
    
            public int getY() {
                return y;
            }
    
            public void setY(int value) {
                y = value - bm.getHeight() / 2;
            }  
           
        }
    }

GraphicIcon 클래스 내부에 Coordinates 클래스가 정의 되어 있습니다. Coordinates 클래스는 좌표처리를 담당합니다.
GraphicIcon 클래스는 비트맵 아이콘들을 생성하는 역할을 담당합니다.


2. CustomView 클래스에 ArrayList graphics 를 정의합니다.

class CustomView extends SurfaceView implements SurfaceHolder.Callback {
        private CustomViewThread CVThread;
        private ArrayList<GraphicIcon> graphics = new ArrayList<GraphicIcon>();
 
... 이하 생략 ...

}

ArrayList graphics는 GraphicIcon 클래스를 사용해 생성한 객체들을 저장하게 됩니다.


3. CustomView 클래스의 onDraw를 다음과 같이 수정합니다.

@Override
        public void onDraw(Canvas canvas) {
         Bitmap bm;
            canvas.drawColor(Color.parseColor("#dedede"));
            GraphicIcon.Coordinates co;
            for (GraphicIcon graphic : graphics) {
                bm = graphic.getGraphic();
                co = graphic.getCoordinates();
                canvas.drawBitmap(bm, co.getX(), co.getY(), null);
            }
        }

 for (GraphicIcon graphic : graphics) 에서 ArrayList graphics에 들어 있는 객체들을 하나씩 꺼내어 graphic변수에 저장하여 그림을 그립니다.


4. CustomView 클래스의 onTouchEvent 를 다음과 같이 수정합니다.

@Override
        public boolean onTouchEvent(MotionEvent event) {
         GraphicIcon graphic = new GraphicIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon));
         graphic.getCoordinates().setX((int) event.getX());
         graphic.getCoordinates().setY((int) event.getY());
         return graphics.add(graphic);    
        }

graphic 객체를 생성해서 ArrayList graphics에 추가합니다.


5. 지금까지 작성한 CustomView 클래스는 다음과 같습니다. Ctrl + F11 로 실행해 봅니다.

class CustomView extends SurfaceView implements SurfaceHolder.Callback {
        private CustomViewThread CVThread;
        private ArrayList<GraphicIcon> graphics = new ArrayList<GraphicIcon>();
 
        public CustomView(Context context) {
            super(context);
            getHolder().addCallback(this);
            CVThread = new CustomViewThread(getHolder(), this);
            setFocusable(true);
        }
 
        @Override
        public void onDraw(Canvas canvas) {
         Bitmap bm;
            canvas.drawColor(Color.parseColor("#dedede"));
            GraphicIcon.Coordinates co;
            for (GraphicIcon graphic : graphics) {
                bm = graphic.getGraphic();
                co = graphic.getCoordinates();
                canvas.drawBitmap(bm, co.getX(), co.getY(), null);
            }
        }
       
        @Override
        public boolean onTouchEvent(MotionEvent event) {
         GraphicIcon graphic = new GraphicIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon));
         graphic.getCoordinates().setX((int) event.getX());
         graphic.getCoordinates().setY((int) event.getY());
         return graphics.add(graphic);    
        }
       
 
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            // TODO Auto-generated method stub
 
        }
 
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            CVThread.setRunning(true);
            CVThread.start();
        }
 
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
           
            boolean retry = true;
            CVThread.setRunning(false);
            while (retry) {
                try {
                    CVThread.join();
                    retry = false;
                } catch (InterruptedException e) {
                   
                }
            }
        }
    }


[ 실행결과 - Exception 발생 ]

실행해서 화면을 클릭하다 보면 Exception이 발생합니다. 이 때 발생하는 Exception은 ConcurrentModificationException 인데, onDraw의 for문에서 ArrayList안의 객체들을 처리하는 중간에 사용자가 화면을 터치하면 새로운 객체가 ArrayList에 추가되면서 ArrayList의 요소그 갯수에 변화가 생겨 발생하게 됩니다.

이를 방지하기 위해서, surfaceholder를 동기화하여 줍니다.


6. CustomViewThread 클래스 내부에 다음과 같이 surfaceholder 멤버변수를 읽을 수 있는 getSurfaceHolder메서드를 추가합니다.

public SurfaceHolder getSurfaceHolder() {
            return surfaceholder;
        }


7. onTouchEvent 를 다음과 같이 수정합니다.

@Override
        public boolean onTouchEvent(MotionEvent event) {
         synchronized (CVThread.getSurfaceHolder()) {
          GraphicIcon graphic = new GraphicIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon));
          graphic.getCoordinates().setX((int) event.getX());
          graphic.getCoordinates().setY((int) event.getY());
          return graphics.add(graphic);
         }
   }

CVThread.getSurfaceHolder() 를 이용해서 sufaceholder를 동기화하여 처리합니다.


8. Ctrl + F11 로 실행합니다.


화면을 클릭할 때마다 안드로이드 아이콘이 생성되는 것을 볼 수 있습니다.

( 참고 : http://www.droidnova.com/playing-with-graphics-in-android-part-iv,182.html )
Comments