안드로이드 프레임레이아웃 뷰 변경하기 1. [addView(), removeView()] (How to change a View in Android FrameLayout)

2017. 5. 19. 12:57


1. FrameLayout에 표시되는 뷰 변경하는 방법

[안드로이드 프레임레이아웃(Android FrameLayout)]에서 설명했듯이, FrameLayout은 자식(Children)으로 추가된 여러 뷰(View) 위젯들 중 하나를 Layout의 전면에 표시할 때 사용하는 Layout 클래스입니다. 그리고 안드로이드에서는 FrameLayout이 오직 하나의 자식(Child) 뷰 위젯만을 표시하게 만들도록 권고하고 있죠.


FrameLayout에 하나의 뷰(View)만을 표시하는 방법은, addView() 및 removeView()를 사용하여 FrameLayout이 하나의 뷰(View)만을 가지게 만들거나, FrameLayout에 여러 자식(Children) 뷰 위젯을 추가해두고 자식(Child) 뷰 위젯의 visibility 속성을 사용하여 하나의 뷰(View)만 화면에 보이도록 만드는 방법이 있습니다.


여기서는 두 가지 방법 중, addView() 및 removeView() 함수를 사용하여 하나의 뷰(View)만 가지도록 만드는 방법에 대해 살펴보겠습니다.

2. addView() 함수와 removeView()

FrameLayout에 뷰(View)를 추가하는 함수는 addView() 함수입니다. addView() 함수를 사용하여 여러 개의 뷰(View)를 추가하면, 마지막으로 추가된 뷰(View)가 FrameLayout의 전면에 표시됩니다. "액자"의 제일 앞에 사진을 끼우는 것처럼 말이죠. 또한 addView() 함수는 파라미터가 다른 여러 함수가 오버로딩(overloading)되어 있습니다.


함수명 설명
addView(View child,
ViewGroup.LayoutParams params)
params 설정을 가진 child 뷰 추가.
addView(View child, int index) index 위치에 child 뷰 추가.
addView(View child, int index,
ViewGroup.LayoutParams params)
params 설정을 가진 index 위치에 child 뷰 추가.
addView(View child) chlid 뷰 추가.
addView(View child, int width, int height) width x height 크기를 가진 child 뷰 추가.


FrameLayout에서 뷰(View)를 삭제하는 함수는 removeView() 함수입니다. 그런데 removeView() 함수는 파라미터가 다른 동일한 함수가 존재하지 않습니다. 대신, 기능에 따라 구분된 다른 이름의 함수가 정의되어 있습니다.


함수명 설명
removeAllViews() FrameLayout에 포함된 모든 자식(Children) 뷰 제거.
removeAllViewsInLayout() FrameLayout 내부에서 모든 자식(Children) 뷰 제거.
removeView(View view) FrameLayout에서 view 제거.
removeViewAt(int index) FrameLayout의 index 위치 자식(Child) 뷰 제거.
removeViewInLayout(View view)

FrameLayout 내부에서 view 제거.

removeViews(int start, int count) FrameLayout의 start 위치부터 count 갯수만큼 자식(Children) 뷰 제거.
removeViewsInLayout(int start, int count) FrameLayout에서 start 위치부터 count 갯수만큼 자식(Children) 뷰 제거.

3. FrameLayout 뷰(View) 변경하기

그럼 이제 예제를 통해, addView()와 removeView() 함수를 사용하여 FrameLayout에 표시되는 뷰(View)를 변경하는 방법에 대해 살펴보겠습니다.


예제에서 작성하는 화면은 아래와 같은 레이아웃으로 구성됩니다.

FrameLayout 뷰 변경하기 예제 화면 레이아웃



참고로 예제 소스에서는, 코드의 쉬운 이해를 위해 FrameLayout에 표시될 뷰(View)들을 개별적인 변수로 선언하였습니다. 하지만 개별 변수로 선언하는 방법은, 전환될 뷰(View)가 많아지면 구현 및 관리가 용이하지 않습니다. 그래서 개선된 코드를 주석에 같이 기록하였으며, "개선된 코드"라는 주석에서 확인할 수 있습니다.

3.1 MainActivity의 레이아웃 구성.

예제 작성을 위해 가장 먼저 할 일은, 앞서 설계한 화면에 따라 MainActivity에 표시될 레이아웃 리소스 XML을 작성하는 것입니다.

[STEP-1] "activity_main.xml" - MainActivity의 Layout 구성.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.recipes4dev.examples.framelayoutchangingview1.MainActivity"
    tools:showIn="@layout/activity_main">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:id="@+id/linear">

        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Text 1" />

        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Text 2" />

        <Button
            android:id="@+id/button3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Text 3" />

    </LinearLayout>


    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/frame"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/linear"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#4CAF50"
            android:gravity="center"
            android:id="@+id/text1"
            android:text="TEXT 1" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FF9800"
            android:gravity="center"
            android:id="@+id/text2"
            android:text="TEXT 2" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#009688"
            android:gravity="center"
            android:id="@+id/text3"
            android:text="TEXT 3" />

    </FrameLayout>

</android.support.constraint.ConstraintLayout>

3.2 각 버튼 클릭 이벤트 처리.

화면에는 세 개의 버튼이 있고, 각 버튼을 클릭하면 순서에 따른 텍스트뷰가 화면에 표시됩니다. 이를 위해 각 버튼의 클릭 이벤트를 작성합니다. 단, 실질적인 FrameLayout의 뷰(View) 변경은 다음 단계에서 changeView() 라는 함수에 작성하고, 버튼 클릭 이벤트 핸들러에서는 changeView() 함수를 호출하도록 만듭니다.

[STEP-2] "MainActivity.java" - onCreate() 함수에서 각 버튼 클릭 이벤트 처리.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        Button button1 = (Button) findViewById(R.id.button1) ;
            button1.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                changeView(0) ;
            }
        });

        Button button2 = (Button) findViewById(R.id.button2) ;
            button2.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                changeView(1) ;
            }
        });

        Button button3 = (Button) findViewById(R.id.button3) ;
            button3.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                changeView(2) ;
            }
        });

        // 코드 계속 ...
    }
}

3.3 FrameLayout에 표시될 뷰 변경 코드 작성.

이제 FrameLayout에 표시될 뷰(View)를 변경하는 기능을 구현합니다. 이를 위해 changeView()라는 함수를 추가한 다음, removeViewAt() 함수와 addView() 함수를 호출하여 뷰(View)를 제거 및 추가합니다.

[STEP-3] "MainActivity.java" - FrameLayout에 표시될 뷰 변경 코드 작성.
public class MainActivity extends AppCompatActivity {

    // textView 참조를 저장하기 위한 변수.
    TextView textView1 ;
    TextView textView2 ;
    TextView textView3 ;
    // TextView textViews[3] ;  // 개선된 코드 (textView 참조를 저장하기 위한 변수.)

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        // textView에 대한 참조 획득.
        textView1 = (TextView) findViewById(R.id.text1) ;
        textView2 = (TextView) findViewById(R.id.text2) ;
        textView3 = (TextView) findViewById(R.id.text3) ;
        /* // 개선된 코드 (textView에 대한 참조 획득.)
        textViews[0] = (TextView) findViewById(R.id.text1) ;
        textViews[1] = (TextView) findViewById(R.id.text2) ;
        textViews[2] = (TextView) findViewById(R.id.text3) ;
        */

        // 코드 계속 ...
    }

    private void changeView(int index) {

        FrameLayout frame = (FrameLayout) findViewById(R.id.frame) ;

        // 0 번째 뷰 제거. (뷰가 하나이므로, 0 번째 뷰를 제거하면 모든 뷰가 제거됨.)
        frame.removeViewAt(0) ;

        // index에 해당하는 textView 표시
        switch (index) {
            case 0 :
                frame.addView(textView1) ;
                break ;
            case 1 :
                frame.addView(textView2) ;
                break ;
            case 2 :
                frame.addView(textView3) ;
                break ;
        }

        /* // 개선된 코드 (index에 해당하는 textView 표시.)
            if (index < 3) {
                frame.addView(textViews[index]) ;
            } else {
                // TODO : index error.
            }
        */
    }
}

3.4 FrameLayout의 초기 상태 결정.

마지막으로, 앱 시작 시 FrameLayout에 표시될 뷰(View)를 결정하기 위해 textView2와 textView3는 removeView() 함수로 제거합니다.

[STEP-4] "MainActivity.java" - onCreate() 함수에서 화면에 표시될 뷰를 제외한 모든 뷰 제거.
public class MainActivity extends AppCompatActivity {

    // ... 코드 계속

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        FrameLayout frame = (FrameLayout) findViewById(R.id.frame) ;

        // textView1을 제외한 모든 뷰 제거.
        // frame.removeView(textView1) ;
        frame.removeView(textView2) ;
        frame.removeView(textView3) ;
        /* // 개선된 코드 (textView1을 제외한 모든 뷰 제거.)
        frame.removeAllViews() ;
        frame.addView(textView1) ;
        */
    }
}

4. 예제 실행 화면

예제 작성을 완료하고 실행하면 아래와 같은 화면이 표시됩니다.

FrameLayout 뷰 변경하기 예제 실행 화면 1


그리고 버튼을 클릭하면, 클릭된 버튼의 순서에 따른 TextView가 FrameLayout에 번갈아 표시됩니다.

FrameLayout 뷰 변경하기 예제 실행 화면 2


5. 참고.

.END.


ANDROID 프로그래밍/LAYOUT , , , , , , , , , , ,

  1. Blog Icon
    루사인

    removeView( )함수들 중에 비슷한 기능을 하는 함수가 있네요.
    removeAllViews( )함수와 removeAllViewsInLayout( )함수가 굳이 나뉘어진 이유가 있나요?
    언뜻 보기엔 같은 함수인것 같은데 왜 나뉘어진것인지 모르겠네요.

  2. 저도 removeAllViewsInLayout() 함수를 직접 사용해보지 않아서 언제, 어떤 용도로 호출해야 하는지 잘 모르지만, 함수에 대한 설명만으로 비교를 해보자면,

    removeAllViewsInLayout() 함수의 경우는,

    Called by a ViewGroup subclass to remove child views from itself,

    이렇게 설명이 되어 있습니다.
    즉, ViewGroup의 subclass에서 자신의 자식들을 삭제하기 위해 호출한다는 것이죠.
    추가적인 설명을 보면, ViewGroup 내부의 자식들에 대한 화면 렌더링과 관련하여 주의깊게 써야 하는 것으로 나오는데요.
    ViewGroup을 확장하지 않으면 이 함수를 호출하지 말라고 되어 있네요.

    그러므로 아마, 일반적인 경우에서는 removeAllViews() 함수 사용법만 알아두면 될 것 같습니다.

    자세한 도움말은 아래 링크에서 확인하실 수 있습니다.

    https://developer.android.com/reference/android/view/ViewGroup.html#removeAllViewsInLayout()

    도움이 되셨는지 모르겠네요.

    감사합니다.

  3. Blog Icon
    넘무

    궁금한게 있습니다
    위의 예제에서
    textView 에 대한 참조는 onCreate() 에서 한번만 하는데에 비해
    frame 에 대한 참조는 onCreate() 와 changeView() 두곳에서 이뤄지는 이유가 뭔가요?

    그리고 제가 여쭈어본 위와 같은 것을 이해하려면 어떤 파트를 공부해야 될지 알려주시면 감사하겠습니다

  4. 딱히 이유는 없습니다.
    그냥 예제 코드를 작성하다보니, 그렇게 작성된 것이고요.

    일반적인 경우, 큰 의미를 둘 필요는 없습니다.

    다만.. 코드의 실행 효율을 따져보자면, 각 뷰에 대한 참조를 onCreate()에서 한번에 가져오는 게, 매번 참조를 획득하는 방법보다 더 최적화되겠죠.

    똑같은 작업을 매번 하지 않아도 되니까요.

    하지만 이런 경우, 뷰의 참조를 위한 변수를 클래스 변수로 만들어야 한다는 단점이 있습니다. 변수 유지에 따른 여러 단점들도 존재하다는 것이죠.

    이런 내용을 이해하기 위해 공부해야 할 파트는, 제가 콕 찝어드리기는 힘들고요.

    그냥 이렇게 계속 만들어보고, 궁금해하고, 코드를 의심하고, 질문하고, 찾아보다보면.... 자연스럽게 이해하실 내용이라 생각이 듭니다.

    답변이 되었는지 모르겠네요.

    감사합니다.