괴도군의 블로그

[Android] Frame / View 정리 및 커스텀뷰 본문

#프로그래밍/Android

[Android] Frame / View 정리 및 커스텀뷰

괴도군 2016. 10. 20. 22:13
반응형




안녕하세요 괴도입니다.

오늘은 몇일전 지식IN에 답변을 남기면서 적용해봤던

프레임에 대해서 써보도록 하겠습니다.


View는 onDraw()에서 화면을 그립니다.

그 화면에 표시되는 도화지가 파라미터로 넘어오는 Canvas 이죠.

거기에 자기가 원하는 무언가를 그릴 수 있습니다.

그런데.. 처음 뷰를 상속받아서 커스텀뷰를 만들다보면

왜 화면이 멈춰있지? 라는 생각이 들게됩니다.


당연하겠죠..

onDraw()는 View의 라이프사이클에 따라 한번 호출되거든요

(버튼, 텍스트뷰, 이미지뷰 등등 모든게 뷰입니다. 한번 그려지고 다시 또 반복해서 그려질 필요가없죠 

// 리소스가 변하지 않는이상..)


그래서 화면을 새로고침(?)하기 위한 메소드가 invalidate()입니다.

이 메소드를 호출하게되면 onDraw()가 다시한번 호출되면서 Canvas에 다시 그리게 됩니다.


그 사이에 변한 값들이 있다면 그대로 적용되어 화면에 나타나는거죠


1
2
3
4
5
6
7
8
9
10
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.TRANSPARENT);
 
        paint.setColor(Color.argb(8025500));
 
        canvas.drawCircle(x, y, 100, paint);
 
    }
cs


보통 뭐 이런식으로 작성하기 마련인데요

처음부터 코드를 살펴보면


canvas.drawColor(Color.TRANSPARENT);

캔버스에 색을 칠하는 메소드입니다. 색은 투명을 넣었네요.


paint.setColor(Color.argb(80,255,0,0));

paint는 맨 앞글자가 소문자인걸보니 멤버변수이고 미리 선언해놓았겠군요

그리고 색은 ARGB (알파값(투명도), 빨강, 초록, 파랑)으로 직접 넣었네요

투명도가 80이고 (완전 불투명은 255입니다. 완전투명은 0이구요)

빨강이 255 최대치죠 완전 빨강!

어쨌든 투명도가 들어있고 액티비티의 배경색을 테마를 블랙계열로 하지않는이상 흰색이기때문에

투명도와 어우러져 분홍으로 보이게됩니다.


canvas.drawCircle(x,y,100,paint);

드디어 뭔가를 제대로 그리는 메소드가 나왔네요

원을 그리는 메소드입니다.

x,y는 원을 그릴 좌표구요(원의 정중앙위치입니다.) 3번째파라미터는 radius 반지름입니다. 

4번째파라미터는 색을 칠할도구인 paint객체입니다. (paint.setColor()로 색을 넣을수 있죠)


그렇다면 여기서 변하는값은 (수학에서 말하는 변수!) x,y 변수죠

우리는 이걸 이용해서 원을 움직여볼겁니다.


코드부터 올리겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public class testView extends View {
 
    private static long targetFrameInterval = 1000L / 30L;           // 프레임 속도 30
    private long frame = 30L;
 
    private int x;
    private int y;
 
    private Paint paint = new Paint();
 
    public testView(Context context) {
        super(context);
        init();
    }

 
    private void init() {
 
        new Thread(new Runnable() {
            @Override
            public void run() {
 
                while (true) {
                    //예시
                    //쓰레드 슬립을 통한 프레임맞춤
                    // 프레임 처음 시간 설정
                    long frameStartTime = System.currentTimeMillis();
 
 
                    x++;
                    y++;
                    if (x > 2000) {
                        x = 0;
                    }
                    if (y > 2000) {
                        y = 0;
                    }
                    
                    //invalidate 함수는 ui쓰레드인 main쓰레드에서 작업해야만합니다.
                    //이유는 안드로이드os에서 정한 규칙입니다.
                    //아래코드는 다른쓰레드에서 작업할때에 메인쓰레드로 이벤트를보내어 실행시키는방법입니다.
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            testView.this.invalidate();
                        }
                    });
 
                    Log.v("@@@""@@@@");
 

                    long frameEndTime = System.currentTimeMillis();
                    long delta = frameEndTime - frameStartTime;
                    if (targetFrameInterval - delta > frame) {
                        t
                            Thread.sleep(targetFrameInterval - delta);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
        }).start();
 
    }
 
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.TRANSPARENT);
 
        paint.setColor(Color.argb(8025500));
 
        canvas.drawCircle(x, y, 100, paint);
 
    }
 
}
cs


여기서는 아주 자세히는 설명하지 않겠습니다

전체적인 구조를 말씀드리자면 생성자에서 Thread를 선언과 동시에 실행시킵니다.

그리고 x,y값을 프레임에 맞춰서 1씩 증가시켜주고 있습니다.

대강 상상을 해보자면 대각선으로 분홍색 원이 이동하겠죠?

그냥 말로만하면 서운하니.. 실행동영상입니다.


만약 onDraw()안에서 invalidate()를 호출한다면 엄청 빠른속도로 화면이 새로고침되고

핸드폰의 성능이 더 나빠지겠죠..

물론 요즘핸드폰에서도 몇가지 그린게 없다면 괜찮지만

사진같은 큰 이미지를 엄청 빠른속도로 그리기를 호출하게되면 버벅이다 멈추고

결국 안드로이드OS에서 응답없음으로 종료시키게 됩니다.

지금 보시는 동영상은 노트2로 실행한화면이고

오랜만에 와이파이에 연결했더니 자동업데이트덕에 갑자기 느려져서

프레임이 끊겨서 부드럽게 보이지 않는영상입니다..ㅋㅋ

오히려 공부가 더 되겠네요

게임에서 프레임이 떨어지면 초당 보이는 화면의 이미지수가 적어지고

부드럽게 넘어가지않고 끊기는거라는걸 이제 더 잘아실거라 생각합니다.


현재는 1000/30

1초당 30프레임으로 

화면을 갱신하고 있습니다.

(화면을 1초에 30번 그린다라고 생각하시면 됩니다.)

사실 이것도 좀 부드럽지 않은감이 있네요..


어쨌든 그 속도를 제어하기 위해서 Thread를 사용하였고 Sleep을 이용해서

프레임보다 더 빠르게 반복되지않게 조절하고 있습니다.

(Thread에서 그냥 무한반복문을 작성하게되면.. 많이 빠릅니다...)


Thread안에서 invalidate()를 호출하기위해 main쓰레드로 이벤트를 전달하는방법은

위와같이 Handler()를 통한 방법과 context.runOnUiThread(new Runnable...)) 방법이 있습니다.

Handler()도 쓰레드에서 new로 생성하게되면 메인으로 전달되지않기때문에 getMainLooper를 사용했습니다.

(나중에 심화내용으로 다룰예정입니다.)

반응형
Comments