본문 바로가기

프로그래밍/libGDX

[libGDX] 안드로이드 네이티브 UI 사용하기

이번 포스팅은 안드로이드에만 적용할 수 있는 내용입니다.

안드로이드 네이티브 UI 사용하기

Libgdx에서 안드로이드 앱을 만들고자 할때 Libgdx만 사용해서는 어려운 작업들이 있습니다. 그럴때는 안드로이드 라이브러리를 사용해서 작업을 해야 합니다. 그래서 이번 포스팅에서는 Libgdx를 사용할때 어떻게 안드로이드 네이티브 UI도 같이 사용할지 알아도보도록 하겠습니다.

사실 Libgdx가 안드로이드에서 동작하는 방법은 화면 전체를 채우는 큰 캔버스를 하나 만들고 그 캔버스에 그림을 그리는 것입니다. 이때 캔버스또한 일종의 안드로이드 위젯입니다. 그래서 언제든 Libgdx와 안드로이드 네이티브 UI를 섞어 쓸 수 있습니다.

먼저 안드로이드 프로젝트를 하나 만듭니다. 그리고 레이아웃을 수정합니다. 저는 아래와 같이 만들었습니다. Libgdx 캔버스가 들어갈 FrameLayout을 하나 만들어 주었고, 그 위에 버튼 두개를 만들었습니다.

먼저 레이아웃을 만들자. Libgdx 캔버스가 들어갈 frame을 포함해서 만들어야 한다.


그리고 나서 메인 액티비티를 손보도록 합시다. Libgdx 안드로이드 프로젝트를 만들때와 마찬가지로 액티비티는 AndroidApplication 클래스를 확장하도록 합니다. AndroidApplication 클래스에는 initialize 메쏘드 말고 initializeForView라는 메쏘드가 있습니다. 이름이 말하는 것 처럼 Libgdx를 View로 만들어 주는 메쏘드입니다. View 객체를 하나 만들어서 만들어진 Libgdx view를 받도록 합니다. 그리고 나서 이 view를 준비해둔 FrameLayout에 집어넣습니다. 

initializeForView를 이용해 Libgdx View를 만들고, FrameLayout에 집어넣는다.


이렇게 하면 Libgdx 캔버스 위에 버튼 두개가 올려져 있는 화면이 나타납니다.

아까 만든 레이아웃 안에 Libgdx 화면이 들어갔다.


화면을 나오게 하는데는 성공했지만, 안드로이드 네이티브 UI와 Libgdx 간의 통신이 필요합니다. 버튼을 만들었으면 누르면 반응을 하도록 해야겠죠? 간단한 방법은 ApplicationListener 클래스를 확장해서(여기서는 MyGame 클래스)  메쏘드들을 만들어 주는 것입니다. 그리고 나서 버튼들에게 해당 메쏘드를 호출하는 리스너를 달아줍니다.

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
	AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
	cfg.useGL20 = true;
	cfg.r = 8;
	cfg.g = 8;
	cfg.b = 8;
	cfg.a = 8;
	
	final MyGame game = new MyGame();
	View view = initializeForView(game, cfg);
	
	FrameLayout layout = (FrameLayout) findViewById(R.id.frame) ;
	layout.addView(view);
	
	Button button1 = (Button) findViewById(R.id.button1);
	button1.setOnClickListener(new OnClickListener() {
		@Override
		public void onClick(View v) {
			game.moveLeft();
		}
	});
	
	Button button2 = (Button) findViewById(R.id.button2);
	button2.setOnClickListener(new OnClickListener() {
		@Override
		public void onClick(View v) {
			game.moveRight();
		}
	});
}

이렇게 해서 만들어본 결과물입니다.

처음 시작했을 때 화면

오른쪽으로! 버튼을 누르니 오른쪽으로 이동했다.


Timer 클래스

하지만 이렇게 메쏘드를 직접 호출하는 방법은 좋은 방법이 아닙니다. 안드로이드 액티비티와 Libgdx는 다른 쓰레드에서 돌아가기 때문에 함부러 메쏘드를 마음대로 호출하는 것은 위험합니다. Libgdx 쓰레드에서 메쏘드가 호출되도록 해야합니다. 다행히 Libgdx는 이를 위한 유용한 기능을 제공하고 있습니다. 바로 Timer 클래스입니다. Timer 클래스를 이용하면 쉽게 Libgdx 쓰레드에서 메쏘드 호출을 하도록 할 수 있습니다. 아래는 Timer 클래스를 활용한 구현입니다.

...
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		Timer.post(new Task() {
			@Override
			public void run() {
				game.moveLeft();
			}
		});
	}
});

Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		Timer.post(new Task() {
			@Override
			public void run() {
				game.moveRight();
			}
		});
	}
});


이와같이 Timer 클래스의 static 메쏘드인 post에 새로운 작업을 넣어주면 Libgdx 다음 루프가 시작될때 해당 작업을 수행합니다. 지금 이 구현에서는 아무런 차이를 느낄 수 없습니다. 하지만 복잡한 어플리케이션을 만들다 보면 직접 메쏘드를 호출하는 방법은 로직이 꼬이기 쉽습니다. 즉 안드로이드 액티비티에서 Libgdx에 메세지를 보낼때는 꼭 Timer 클래스를 이용하는 것이 좋습니다. Timer 클래스는 단순히 작업을 넣어두는 것 말고도 시간이 지난 후에 작업이 수행되도록 하던가, 일정 시간으로 반복시킬 수 있습니다. 유용한 기능이 많은 클래스 이므로 한번 잘 봐두면 좋을것 같습니다.

또한 마찬가지로 Libgdx에서 안드로이드에 메세지를 보내고자 할때는 메쏘드를 직접 호출하면 안되고 AsyncTask를 이용하여 비동기적으로 작업을 수행해야합니다. 애초에 다른 쓰레드에서 안드로이드로 직접 메쏘드 호출을 하면 에러가 나도록 되어있습니다. 로직이 꼬이는 것을 사전에 방지하기 위함입니다.


마치며

이번 포스팅은 비교적 짧았지만 중요한 정보를 담고있습니다. 비록 안드로이드 네이티브 UI를 사용하기 시작하면 크로스 플랫폼 어플리케이션과는 멀어지겠지만 안드로이드에 특화된 앱을 만들고자 할때 유용하게 쓰일것입니다. 그래픽을 표현하고자 할때 안드로이드 기본 기능만으로는 제약이 많아서 Libgdx를 사용하면 굉장히 유용할 때가 많습니다. 앞으로 Libgdx를 활용한 유용한 안드로이드 앱들이 나오길 기대해봅니다.

'프로그래밍 > libGDX' 카테고리의 다른 글

[libGDX] libGDX 시작하기  (4) 2014.05.23
[libGDX] 입력 처리  (1) 2013.12.14
[libGDX] 파티클 에디터  (0) 2013.12.09
[libGDX] 곡선 함수  (0) 2013.12.03
[libGDX] PCM으로 오디오 녹음/재생하기  (2) 2013.11.21