태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

#08. TabView 및 FrameLayout을 이용한 탭 이용하기

2009.04.08 14:58

이번 강좌에서는 화면 구성에 많이 사용되는 TabView 에 대해서 알아보도록 하겠습니다.
TabView는 윈도우 컨트롤의 Tab 컨트롤과 동일하게 생겼으며, 안드로이드 마켓에서도 사용되는 View이며, 이번에 회색님께서 하신 프로젝트 결과물도 이 TabView를 이용하였습니다.



TabView에 관한 것은 저도 오늘 처음 배운 것이라, 자세한 내용(...)은 다루지 않고, TabView를 생성하고 띄우는 방법까지만 다루도록 하겠습니다. 언제까지나, 이론보다 더 중요한 것은 실습해보고, 직접 결과를 보는 것이니까요.(응?)

TabView를 사용하기 위해서는 몇 가지 조건이 필요한데, 요약하면 다음과 같습니다.

1. Google map을 사용할 때 처럼, 액티비티가 Activity를 상속하지 않고 TabActivity를 상속해야 함
2. 레아아웃 골격 구성에 FrameLayout을 사용해야 함

이번 예제에 사용된 레이아웃의 구성은 다음과 같습니다.


FrameLayout은 TabView처럼 탭의 선택에 따라 여러 개의 View를 보여주어야 할 때 사용되며, FrameLayout 내부에 표시될 View 혹은 Layout이 책처럼 차곡차곡 들어가 있는 형태입니다.

[main.xml]
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent">
          
        <LinearLayout android:layout_height="wrap_content" 
         android:layout_width="fill_parent" 
         android:id="@+id/view1" 
         android:orientation="vertical">
         
         <EditText android:id="@+id/EditText01" 
          android:layout_height="wrap_content" 
          android:hint="첫번째 화면입니다." 
          android:layout_width="fill_parent"></EditText>   

<Button android:id="@+id/Button01" 
    android:layout_height="wrap_content"
    android:layout_width="fill_parent" 
    android:text="버튼!"></Button>
  </LinearLayout>
  
  <LinearLayout android:layout_height="wrap_content" 
   android:layout_width="fill_parent" 
   android:id="@+id/view2">
   
   <ImageView android:id="@+id/ImageView01" 
   android:layout_height="wrap_content" 
   android:layout_width="fill_parent" 
   android:src="@drawable/camera"></ImageView>
  </LinearLayout>
  </FrameLayout>
    </LinearLayout>
</TabHost>



예제에서는 탭 2개를 가지도록 구현하였으며, 하나의 View는 EditText와 버튼을 표시하는 View, 나머지 하나는 ImageView를 표시하도록 하였습니다. 이 예제를 따라하려면 미리 프로젝트의 /res/drawable 폴더에 적절한 그림 파일을 넣어야 하겠죠?
그림 파일의 이름이 그대로 id로 사용되니, 영어로 파일명을 지정해주셔야 합니다. (소문자만 가능)

지금까지 해온 레이아웃 구성과는 다소 다른 모습을 보이는데요, 다음 이미지에 정리해 두었습니다.

그럼, 소스를 한번 볼까요?

[TVExample.java]

package com.androidhuman.TabView;

package com.androidhuman.TabView;

import android.app.TabActivity; // TabActivity
import android.os.Bundle;
import android.widget.TabHost;

public class TVExample extends TabActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        TabHost mTabHost = getTabHost();
        
        mTabHost.addTab(mTabHost.newTabSpec("tab_test1")
        		.setIndicator("위젯")
        		.setContent(R.id.view1)
        		);
        mTabHost.addTab(mTabHost.newTabSpec("tab_test2")
        		.setIndicator("이미지뷰")
        		.setContent(R.id.view2)
        		);
                
        //mTabHost.setCurrentTab(0);
    }

}

소스를 보시면 아까 말씀드렸던 것처럼 TabActivity를 상속하는 것을 볼 수 있습니다.
코드상에서 16번째 줄을 보시면 getTabHost()메소드를 통해 TabHost를 받아오는 것을 확인할 수 있습니다.
이는 실질적으로 우리가 Tab에 내용을 추가하는 등의 작업을 위해 필요합니다.

바로 아래로 내려가보시면, 탭에 탭 항목을 추가하는 것을 확인할 수 있습니다.
탭 항목의 addTab()메소드를 사용합니다.

addTab()메소드 안에서 netTabSpec()메소드를 이용하여 실질적인 Tab 항목별로 설정을 해 주는 것을 확인할 수 있습니다.

public TabHost.TabSpec newTabSpec (String tag)

Get a new TabHost.TabSpec associated with this tab host.


SDK 문서에 나와 있는 내용입니다. 말 그대로 각 탭 항목의 설정 등을 반환하도록 되어있습니다. newTabSpec을 호출할 때, tag는 자세히는 모르겠지만, 아마 나중에 탭을 호출할 때 쓰이는 문자열인듯 합니다.

이어서, setIndicator() 메소드에서는 탭의 이름을 추가해주고 있습니다. 여기에서는 단순히 이름만 추가해 주었는데, 다음과 같이 사용하면 아이콘도 지정 가능합니다.

setIndicator("위젯", getResources().getDrawable(R.drawable.icon))

setContent()메소드에서는 각각의 탭에 표시할 View를 지정해줍니다. 지금까지는 보통 LinearLayout 등 레이아웃 자체에는 id를 지정하지 않았는데, 이번 예제에서는 id를 통해 각각의 View, 혹은 Layout을 불러오므로 ID지정이 필수입니다.

마지막으로, 주석처리된 부분은 보여질 탭을 선택하는 것인데, 따로 지정하지 않을 경우 맨 앞의 탭이 기본으로 보여지게 되고, 다른 탭이 보여지도록 하고 싶다면 보여지게 하고 싶은 탭의 index를 지정해주시면 됩니다.

ps. 탭의 인덱스는 배열과 마찬가지로 0부터 시작합니다!

커니 유저 인터페이스/뷰(View) , , , , , , , ,

  1. 이전 댓글 더보기
  2. 레이아웃에서 탭위젯 때문에 java.lang.NullPointerException이 발생하는데 왜이러는걸까요? 그대로 붙여넣었는데 이러네요;;

  3. 저도 계속 오류가 나고있어요~
    SDK 1.1때는 안그랬는데 1.5로 업데이트 한 이후로 발생하고있습니다. 하지만 미리보기 화면(?)에서만 제데로 안 나올 뿐이지 실제로 코딩을 해보면 돌아가는것은 잘 돌아갑니다. ㅎㅎ

  4. Blog Icon
    한글판

    안녕하세요 강의 보고 탭까지 완벽하게 구현하였습니다.

    그런데 이미지 뷰대신 꼭 웹뷰를 해야 하는 프로젝트를 진행중인데 도대체가 웹뷰가 실행이 되질 않네요

    된다고 하더라도 첫번째 뷰나오다가 바로 웹뷰가 나오는 형식으로만 되네요

    두번째 탭을 눌렀을때 웹뷰가 되도록 하는 방법좀 가르쳐 주시면 안될까요??

  5. 두번째 탭의 내용에 웹뷰를 사용하였는데,
    어플리케이션을 실행하면 첫번째 탭의 내용물이 표시되어있는 상태로 있는 것이 아니라 웹뷰가 바로 실행되어버린다는 말씀이신가요?

  6. Blog Icon
    한글판

    네 말씀하신내용이 맞습니다.

    첫번째 탭에 내용이 표시되어야 하는데 표시되는척하다가 바로 웹뷰가 실형이 되어버리네요ㅠㅠ

    함만 도와주세영~~

  7. mTabHost.setCurrentTab(0);

    요렇게 보여줄 탭을 강제로 지정해주어도 여전한지요?

  8. Blog Icon
    한글판

    네 0번탭을 지정해줘도 같은 현상입니다.. ㅠㅠ

  9. 한번 LogCat에 뜨는 Log를 확인해보시는게 뭐 때문에 그런건지 가장 빨리 확인해볼 수 있는 방법인 듯 하네요.;

    저도 웹뷰는 많이 써보질 않아서..ㅠㅠ

  10. Blog Icon
    김종성

    안녕하세요 강의 보고서 하나하나 따라서 해보고 있는 사람입니다.
    그런데 위의 main.xml에서 tabhost만 추가하게 되면 emulator에서 에러가 나면서 강제 종료 상자가 나옵니다.
    처음에는 위의 그림대로 해서 eclipse에서 layout을 만들었는데 emulator에서 에러가 나서 하나씩 지우면서 실행을 해보니까, TabHost만 넣기만 하면 에러가 납니다.
    이때는 어떻게 해야 되는지요??

    현재 제가 한 main.xml을 넣습니다.
    <?xml version="1.0" encoding="utf-8"?>

    <TabHost android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost"></TabHost>

  11. 현재 알려주신 정보만으로는 어떤 것이 문제가 되는지 잘 모르겠습니다.

    LogCat으로 로그를 한번 확인해보시고... 안드로이드 개발자 사이트의 TabWidget 부분 (http://developer.android.com/guide/tutorials/views/hello-tabwidget.html)도 한번 참고해보세요.

  12. Blog Icon
    neo279

    안드로이드 SDK2.0 기반에서 커니님의 강좌를 죽~~ 연습해오고 있는 연습생입니다.
    다름이 아니오라 이제까지 2.0버전으로 잘 연습해왔는데 여기서 막히는 군요... 주말내내 붙잡고 삽질끝에
    답이 안나와서 이렇게 문의 드리는 바입니다.
    제 에뮬에서 실행했을 경우 발생한 에러 메세지는 다음과 같습니다.

    12-07 01:32:10.501: WARN/dalvikvm(422): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
    12-07 01:32:10.510: ERROR/AndroidRuntime(422): Uncaught handler: thread main exiting due to uncaught exception
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.TabViewTest1/com.android.TabViewTest1.TabViewTest1}: java.lang.RuntimeException: Your content must have a TabHost whose id attribute is 'android.R.id.tabhost'
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2454)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2470)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.ActivityThread.access$2200(ActivityThread.java:119)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1821)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.os.Handler.dispatchMessage(Handler.java:99)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.os.Looper.loop(Looper.java:123)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.ActivityThread.main(ActivityThread.java:4310)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at java.lang.reflect.Method.invokeNative(Native Method)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at java.lang.reflect.Method.invoke(Method.java:521)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at dalvik.system.NativeStart.main(Native Method)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): Caused by: java.lang.RuntimeException: Your content must have a TabHost whose id attribute is 'android.R.id.tabhost'
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.TabActivity.onContentChanged(TabActivity.java:105)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:201)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.Activity.setContentView(Activity.java:1622)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at com.android.TabViewTest1.TabViewTest1.onCreate(TabViewTest1.java:12)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2417)
    12-07 01:32:10.539: ERROR/AndroidRuntime(422): ... 11 more


    그외의 나머지 xml 파일 소스나 java 파일 소스는 위의 내용과 동일한 조건입니다.
    2.0으로 업데이트 되면서 TabHost의 적용 방식이 바뀐건가요?...
    제가 보고 있는 교제가 영 구리구리해서 교제를 바꿔야겠어요...
    이 문제 관련해서 참고해볼만한 교제도 추천 부탁드려요...
    가급적 바이블 형식으로 나온 교제로요...

  13. Your content must have a TabHost whose id attribute is 'android.R.id.tabhost'

    참고하세요. :)

  14. Blog Icon
    ddd

    커니님 제가 지금 탭을 만들고 그중에 하나는 그냥 위 예제처럼 버튼하나만 놓게 하고
    다른 탭에 구글 맵을 띄울려고 하는데요
    구글맵 클래스랑 저 탭뷰 클래스랑 해서 두개가 필요하더라구요
    그래서 두개를 만들고 밑에 main.xml에서 imageview쪽을 <com.android...> 어쩌구 그걸로 바꾸고
    앞에 Linearlayout도 구글맵에 있는걸로 바꾼다음에 아이디 view2로 지정해주고 탭뷰.java에서 setcontent로
    R.id.view2로 불러오려고 했는데 안되네요
    다른 클래스에 있는 내용을 탭 안에 구현시킬려면 어떻게 해야될까요?

  15. 정확히 어떻게 안되는건가요?
    에러 메시지 내용을 봐야 알 수 있을 것 같네요.

  16. Blog Icon
    ddd

    이클립스상에선 에러가 없습니다
    근데 안드로이드를 구동시켜서 보면 얘기치못한 에러발생이라고 하면서 강제종료 y or no창이 뜨더라구요
    어떤게 문제일까요?

  17. Blog Icon
    ddd

    제가 구상하고 있는 어플리케이션이 탭 두개로 동작하고요
    하나는 그냥 텍스트나 이미지몇개 있는 정보창이고 다른 한쪽에 구글맵을 띄우려고 하거든요
    그래서 커니님 소스에서 이미지뷰 대신 구글맵에 있는 소스를 넣고 id를 부여한 다음에
    구글맵 코드를 main말고 id를 보여주는걸로 바꿨구요
    이클립스상에선 문제 없는 코드인데 안드로이드 에뮬레이터 상에선 자꾸 에러가 나더라구요

    이게 가능한 방법인가요?
    만약에 불가능하다면

    구글맵을 백그라운드로 깔고 그 위에 탭이나 버튼 두개를 올려놓고 누를때마다 전환되는 방식으로 해도
    좋을거 같은데
    조언 좀 부탁드립니다

  18. Blog Icon
    ddd

    커니님 제발 조금만 도와주세요 ㅠㅠ

  19. http://androidhuman.tistory.com/entry/%EB%9F%B0%ED%83%80%EC%9E%84-%EC%97%90%EB%9F%ACRun-time-Error%EC%97%90-%EB%8C%80%EC%B2%98%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95

    요거 한번 보시고 에러 내용 확인해보세요~
    에러 내용을 확실히 알아야 도움을 드릴수 있어요 ㅎㅎ

  20. Blog Icon
    ddd

    그대로 적을게요
    caused by : android.content.res.Resources$NotFoundException: Resource ID #0x7f050003 t...

    이렇게 나와요
    무엇이 문제일까요?

    커니님 소스에서 이미지뷰를 구글맵 소스의 <com.android..(이하생략)>
    으로 바꾸고 그 앞에 리니어레이아웃 하나 깔았을 뿐인데..

    그래서 그 구글맵 들어있는 리니어레이아웃에 id부여하고 구글맵 자바파일은 sencontent에 그걸 넣구요
    그렇게 했는데..
    제 방법이 어디가 잘못된걸까요?

    구글사 공모전 50위 수상작중에 CallAcab이라는게 있던데 거기선 탭으로 구글맵 넣어서 구현했더라구요
    그 어플리케이션 소스는 도저히 못찾겠고..
    방법 없을까요?

  21. Blog Icon
    ddd

    찜찜해서 다시 해봤는데 에러메세지가 바뀌었네요
    에뮬레이터가 이상하게 메세징도 에러나고 상태가 안좋군요 ㅠㅠ
    Caused by: java lang Runtimeexception Binary XML file line #2 : You must supply a layout_width attribute
    이렇게 나와요
    아.. 정말 탭 두개 만들어서 그 중 하나에 구글맵 넣을려는 소망이 이렇게 클줄이야 ㅠㅠ

  22. layout_width 속성이 안들어가서 나오는 메시지네요.
    속성 넣어주세요~

  23. Blog Icon
    ddd

    그걸 고치니까 다시
    caused by : android.content.res.Resources$NotFoundException: Resource ID #0x7f050003 t...
    이 에러가 뜨네요

    아........ 방법 없을까요?ㅠㅠ


    자꾸 귀찮게 해드려 정말로 죄송합니다...

  24. 구글맵이 id가 tabcontent인 레이아웃의 하위에 있는지 확인해보시고,
    아니면 프로젝트 설정이 GoogleAPIs로 되어있는지도 한번 확인해보시구요.

    이것도 아니라면 지금으로서는 도움을 드릴 방법이 없네요 ^^;

  25. Blog Icon
    꿈은

    강의 감사드리고요. Tab의 모양을 약간 변경하고 싶을 때는 어떻게 하면 될까요?

    http://postfiles9.naver.net/20100319_40/liesdo_1268988558352koVjz_jpg/2_liesdo.jpg?type=w3]
    --> 이미지 링크

    이렇게 말이죠. 위에 공백에 이미지 같은거 집어넣지 말고 아애 없애게요. 그래야 아래에 공간이 좀더 늘어나거든요.

  26. Foce CLose 가 떠서 LogCat 을 확인해 보니 저도 "Your content must have a TabHost whose id attribute is 'android.R.id.tabhost'" 같은 메세지가 뜨는데요.
    이걸 참고하라고만 답하시는데..이게 무슨뜻인가요?
    뭐가 잘못됐다는건지 도통 모르겠어요..ㅠㅠ

  27. 그 메시지에 뭐가 잘못되어있는지 다 나와있습니다만...;;;
    영어 해석해보면 바로 나오지 않나욤.

    id가 android.R.id.tabhost 인 놈이 없다는 메시지,
    즉 TabHost 위젯의 id가 android:id/tabhost 로 되어있지 않아서 발생하는 문제입니다.

  28. 변 감사합니다~~(달아주시리라곤 생각도 못한!!)ㅎㅎ
    그러니깐효..ㅠㅠ
    제가 해석하기로도 그래서 뜨는 에러같은데,
    TabHost 위젯의 id가 android:id/tabhost 로 이미 되어있거든효..ㅠㅠ
    대체 뭐가 문제인걸까효..ㅠㅠ

  29. ^ㅡ^ 좋은 강좌 잘보고갑니다.!

  30. Blog Icon
    maechinzz

    항상 조은 강좌 감사합니당 ~~!!


    mTabHost.addTab(mTabHost.newTabSpec("tab_test2";)
    .setIndicator("이미지뷰";)
    .setContent(R.id.view2)
    );

    이부분에서 id cannot be resolved or is not a field라는 에러가 뜨는데 왜그런건가요?ㅠ.ㅠ
    다른 실습을 해볼때도 이런 메시지가 자주 뜨더라구요..


    제생각엔 R.id <-이부분이 잘못된 것 같거든요 ㅠ.ㅠ

  31. FrameLayout 내에 들어가는 뷰의 id에 맞추어 지정해주시면 됩니다. 뷰의 id 를 view1, view2로 지정해주지 않아서 생기는 문제 같군요.

    아니면 다른 문제에 의해 R.java 파일이 제데로 업데이트 되지 않아서 생기는 문제일 수도 있구요.

  32. Blog Icon
    비형여자

    개발하면서 도움이 많이 되었습니다. 정말 항상 감사합니다.
    질문 하나 여쭤볼께요ㅡ
    텝을 구현할 때, 탭 호스트 안에 탭의 xml 을 다 넣어야 하나요?
    탭별로 xml을 만들고 메인 xml(tabhost가 포함되어있는)에 넣어서 할 수 있는 방법은 없나요,?

  33. xml로 레이아웃을 모두 짠다면 <include> 속성을 사용하면 외부 xml을 바로 넣어줄 수 있는 것 같은데... 이 기능은 사용해보질 않아서 확실히는 잘 모르겠네요.

    아니면 탭의 내용별로 액티비티를 만들어놓고 인텐트를 통해 각 액티비티를 호출하는 방법도 있습니다~

  34. 좋은 글 잘 보고 갑니다. 다음에도 또 좋은 글 기대 할께요. 퍼가도 되죠?

  35. Blog Icon
    초보

    커니의 안드로이드 2판책 사서 따라하면서 공부중입니다...
    제가 아직 초보라서 그런지 몰라도 탭 이용하기에서 extends TabActicity 가 되질 않네요..
    찾아보니 호환 안한다고 FragmentActivity 를 사용하라고 하길래 사용하는 방법을 찾아봤는데
    어떻게 하는지 잘 모르겠더라구요...

    혹시 FramentActicity를 사용해서 탭 이용하는 방법은 없을까요...?