프로그래밍 정리/안드로이드

[안드로이드] ActivityGroup, Child Activity(View) - Fragment와 FragmentActivity, FragmentManager 라는 녀석들로

주누다 2012. 7. 12. 14:48
반응형





(쓰다가... 멈췄다가.. 좀 오래된 내용이지만, 다시 정리해서 쓰려고 합니다.)


ActivityGroup(액티비티 그룹)은 액티비티들을 관리할 수 있게 도와주는 클래스입니다.

이런 고민을 할때가 있습니다.
"탭 안에서 액티비티들을 전환하고 싶으면 어떻게 하지?" 같은...

다시 말해보자면, TabActivity가 4개의 Child(Content) Activity를 가지고 있고, 
그 중 첫번째 Tab안에서 액티비티들을 전환하고 싶다. 그런데 탭을 유지하고 싶다. 라는 경우에 쓸 수 있는게, 액티비티 그룹인거죠.
(ActivityGroup의 사용은, 프로그래머 나름이니, 무궁무진할겁니다. 안드로이드는 마음만 먹으면 다 되는것 같아요.
어쨋든, 그 중 일부에 대해서 얘기해볼게요)



저는 액티비티 그룹을 탭 액티비티 안에서 이용해 보았는데요,
간단하게 기본적인걸 말해보자면, 액티비티 그룹에서는 액티비티를 뷰로 만들어서 관리를 합니다.
그래서 ActivityGroup의 child로 생성하는 Activity들의 Activity lifecycle은 완벽하게  동작하지 않습니다.
onCreate -> onResume -> onDestroy 정도가 제대로 동작하는것 같고, 다른 것들은 강제적인 방법으로 하면 동작될듯 하긴 하지만, 그렇게 까지는 해보지 않았습니다.

어쨋든, 액티비티 그룹은 차일드 액티비티들을 계속해서 교체해 주어야 하거나, 하나의 화면에서 여러가지 뷰들을 동시에 보여주려고 하는 경우에 용이합니다.

차일드 액티비티들은 FLAG로 Intent.FLAG_ACTIVITY_CLEAR_TOP을 달고 있어야 하구요.
이렇다 보니 차일드 액티비티들은 액티비티 그룹안에서 하나의 태스크 스택처럼 동작하게 됩니다.
그래서 차일드 액티비티들을 실행할때는 계속해서 start를 하게되는 겁니다.
만약 화면이나 데이터를 유지해야 한다면, child Activity를 View로 만들어서 보관해 두면 됩니다.
편법일수 있지만, View를 보관하는 별도의 스택을 만들어서 관리하는것도 하나의 방법이죠.


실제로 애플리케이션이 가진 태스크 스택에 들어가는 녀석은 ActivityGroup이 되고, 나머지 child들은, ActivityGroup이 가진 View가 된다는게 맞겠죠.
(액티비티에서 사용하는 Intent Flag 관련 글 : 아이군의 블로그 )


   FLAG_ACTIVITY_CLEAR_TOP

   


 만약에 엑티비티스택에 호출하려는 엑티비티의 인스턴스가 이미 존재하고 있을 경우에 새로운 인스턴스를 생성하는 것 대신에 존재하고 있는 엑티비티를 포그라운드로 가져옵니다. 

 그리고 엑티비티스택의 최상단 엑티비티부터 포그라운드로 가져올 엑티비티까지의 모든 엑티비티를 삭제합니다.
 
 예를 들면 현재 A-B-C-D-E 순서로 엑티비티가 스택에 들어있다고 할때 엑티비티 E에서 C를 호출하게 되면 D와 E는 스택에서 삭제되고 A-B-C만이 남아있게 됩니다. 여기서 A-B 역시 남는다는 것을 이해하셔야 합니다.

 


어쨋든 액티비티 그룹에 포함되는 차일드 액티비티들은 액티비티가 아닌 뷰로서 존재하게 됩니다.

액티비티를 실행시켜서 뷰로 만들어 내는 방법은 아래와 같습니다.
 

 우선 로컬액티비티 매니저가 필요합니다. 
 액티비티 그룹에서 getLocalActivityManager() 메소드를 통해 로컬 액티비티 매니저를 get할 수 있습니다.

 로컬액티비티 매니저를 획득했다면 아래의 순서를 통해 액티비티를 실행하고 뷰로만들어 가지고 있을 수 있습니다.

 1. 로컬액티비티 매니저의 startActivity메소드를 통해, 인텐트를 넣어서 액티비티를 시작합니다.
 2. 이때 Flag에 Intent.FLAG_ACTIVITY_CLEAR_TOP을 붙여줍니다.
 3. getDecorView메소드로 액티비티를 뷰로 받아옵니다.
 4. 받아온 뷰를 액티비티그룹에 setContentView를 통해서 붙여줍니다.
 

 getLocalActivityManager()

 1. getLocalActivityManager().startActivity("CHILD_ID", new Intent())
 2. new Intent(this, ChildClassName.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
 3. getDecorView()
 4. setContentView(View) 
 




좀 더 개별적인 관리를 하며, 액티비티들을 운용해보겠다면,

Stack<View> 를 이용하는 것도 한가지 방법입니다. 저는 back key를 이용하기 위해 Stack을 사용했었습니다.

예를들어 child Activity가 3개가 있고, 각각의 액티비티를 받아온 뷰의 이름이 view1, view2, view3 이라고 해봅니다.

액티비티 그룹이 시작되면 첫번째 액티비티를 뷰로 만들어둔 view1을 setContentView(view1);로 화면에 보여줍니다.
이와 동시에 Stack<View>에 view1을 push해 줍니다.

그리고 view1에서 어떤 액션을 통해서 view2를 시작하고 싶다면, 
이때 view1에 해당하는 액티비티에서 getParent()를 통해 ActivityGroup에 접근해 view2를 만들고 실행해 줍니다.
역시 view2를 Stack<View>에 push해 줍니다. 그리고 setContentView(view2)로 화면에 보여주죠.

view2에서도 어떤 액션을 하고 있다고 view3으로 화면을 전환, 이동 하고 싶어질 수 있습니다.
이때도 같은 방법으로 view2에 해당하는 activity에서 getParent()를 통해 ActivityGroup에 접근해서 view3을 만들어주고 실행합니다. 그리고 Stack에 view3을 넣고, setContentView(view3)을 해줍니다.

 로컬액티비티매니저를 통해서 액티비티를 start하고 view를 만들었더라도, setContentView()를 해주지 않으면, 해당 액티비티는 화면에 노출되지 않습니다. 당연한거겠죠? 


이제 우리는 view3에 해당하는 액티비티를 화면에서 보고 있습니다.
이때 back키를 누르면 어떻게 될까요?









!!!! 애플리케이션이 종료됩니다. !!!!





액티비티 그룹에서는 뒤로가기(back key)에 대한 관리가 없습니다.
이건 직접 구현을 해주어야 하는 부분이죠.(onKeyDown() or onBackpress() 등)


이유는 당연하게도, 각 액티비티들은 사실 액티비티라기보다는 화면에 보여지는.. 화면에 데코되는 뷰이기 때문이죠.
뷰입니다. 액티비티지만 뷰입니다. 이녀석들은.

가상키보드로 입력하는 키들에 대해서는 처리되겠지만, 하드웨어에서 명령을 내리는 back key(back button) 같은것에는 반응을 하지 않아버리는거죠.

좀 더 명확히 하자면, 이런 것들에 대한 이벤트는 ActivityGroup이 받습니다.

저같은 경우, 액티비티 그룹에서 현재 어떤 뷰가 보여지고 있는지 체크(이건 위에서 만든 Stack<View>를 통해 할 수 있죠)해서 처리해 보았습니다.

예를들면 아래와 같은 방식입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
    if(event.getKeyCode() == KeyEvent.KEYCODE_BACK)
    {
        // 스택에서 뷰를 꺼낸다.
        mStack.pop();
 
        // 스택이 비어있는지 확인한다.
        if(mStack.isEmpty())
        {
            // 스택이 비어있으면 back key의 원래 기능을 동작시킨다.
            return false;
        }
        else
        {
            // 스택이 비어있지 않다면, top에 있는 view를 화면에 보여준다.(뒤로가기 기능처럼)
            setContentView(mStack.peek());
        }
    }
     
    return true;
}


onKeyDown 메소드를 override해서 구현할때, return 을 boolean 값으로 해주어야 합니다.
true면 처리를 하지 않는 것이고, false면 처리를 하겠다라는거죠.

Stack<View> mStack 을 만들어 두었고, 여기에 화면이 전환될때마다, view를 push해 두었죠.

이제 back key가 눌렸을때, top에 있는 view를 꺼내고(현재 보여지는), peek() 메소드를 통해 화면을 전환시켜 주는 것입니다.

더보기



저도 보통은 onKeyDown()메소드를 WebView 사용시 히스토리가 있는지를 체크, 뒤로 가기 기능을 구현하는데에만 사용했었는데, 그것과 비슷하게 ActivityGroup에도 사용해 본것입니다.
언제나 코딩이 그렇듯 더 좋고 효율적인 방법은 많이 있을겁니다. 제게도 공유를..;



궁금했던 기능들 및 액티비티 라이프 사이클만 확인하려고 만들었던 예제라서 별로 쓸모는 없을 수 있지만 첨부해 봅니다.
해당 프로젝트에서는 따로 뷰스택을 만들어서 차일드 액티비티에 대한 관리를 시도했었습니다.
(여러 자잘한것들을 확인하느라 로그도 많이 찍히게 되있고, 여러모로 정말 테스트 프로젝트군요.)




참고로 안드로이드 API 11 : 허니콤(3.0) 이상에서는 ActivityGroup is deprecated 라고 합니다.
대신 Fragment와 FragmentActivity, FragmentManager 라는 녀석들로 ActivityGroup의 기능을 이용할 수 있죠.

태블릿의 특성상, 한 화면에 여러 뷰를 동시에 보여주어야 하고, 네비게이션이 가능해야겠기에, 액티비티그룹만으로는 부족하다고 판단, 프래그먼트라는걸 만들어낸것 같습니다.

잠시 Fragment들에 대해서 살펴보니, 기본적인 느낌은 ActivityGroup과 비슷하고, 관리적인 측면이나,
Interface의 이용부분에서 더 편리하게 되어있더라구요.

허니콤에서 부터는 안드로이드에서 그동안 사용하던 option menu를 ActionBar라는 녀석을 통해 이용할 수 있구요.
여러가지로, 태블릿에 맞게 발전한게 Fragment라고 생각하시면 될 것 같습니다.

 제 주관적인 생각을 이야기해보면, 아이폰과 비교해서 생각해 보았을때,
 ActivityGroup을 NavigationController 처럼 사용할 수 있다면,
 FragmentManager는 NavigationController + SplitViewController 라고 할 수 있을 것 같습니다. 

반응형