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

2017. 5. 22. 14:39


1. addView(), removeView() 함수로 FrameLayout 뷰 변경하기.

[안드로이드 프레임레이아웃(Android FrameLayout)]에서 FrameLayout에 대해 설명하고, [안드로이드 프레임레이아웃 뷰 변경하기 1. [addView(), removeView()]]에서는 ViewGroup의 addView(), removeView() 함수를 사용하여 FrameLayout에 표시될 뷰를 변경하는 방법을 살펴보았습니다.


[안드로이드 프레임레이아웃 뷰 변경하기 1. [addView(), removeView()]]에서 작성한 예제에서는, 레이아웃 리소스 XML에서 FrameLayout의 자식(Children) 뷰 위젯들을 미리 추가 해놓고, 앱 시작 시 화면에 표시할 뷰(View)를 제외한 모든 뷰를 removeView() 함수로 제거했습니다. 그리고 나서 버튼 클릭 이벤트에 따라 removeView()와 addView() 함수를 호출하여 뷰(View)를 바꿔 표시했죠.


그런데, 하나의 레이아웃 리소스 XML의 FrameLayout 아래에 모든 자식(Children) 뷰 위젯들을 선언하는 방법은 그리 좋은 방법이 아닙니다. 특히, 자식(Children) 뷰 위젯의 갯수가 많아지고 구조가 복잡해지면, 그에 따라 레이아웃 리소스 XML의 내용도 늘어나므로, 소스를 작성하고 수정하는 작업이 매우 번거로워질 수 있습니다.


이런 경우, 코드 작성 및 관리를 편하게 수행하기 위해 개발자가 취할 수 있는 방법은, FrameLayout에 표시될 자식(Children) 뷰를 각각 별도의 레이아웃 리소스 XML로 분리하여 작성한 다음, LayoutInflater를 사용하여 View로 변환하는 것입니다.

2. LayoutInflater

LayoutInflater리소스 XML 파일에 선언된 레이아웃을 자바 코드 상에서 View 객체로 변환할 때 사용하는 클래스입니다. LayoutInflater를 통해 획득된 View 객체는 리소스 XML에 작성된 모든 뷰 그룹(ViewGroup) 및 뷰(View)를 포함하므로, findViewById() 함수를 통해 뷰에 포함된 위젯에 대한 참조를 획득할 수 있습니다.

FrameLayout 뷰 변경하기 2 LayoutInflater 클래스


LayoutInflater를 사용할 때는 new를 통해 직접적으로 객체를 생성하는 방법을 사용할 수 없습니다. 대신, Context 클래스의 getSystemService()함수를 호출하거나, Activity의 getLayoutInflater() 함수를 사용하여 LayoutInflater의 참조를 획득할 수 있습니다.

    // 방법 1.
    LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    // 방법 2.
    LayoutInflater inflater = getLayoutInflater() ;

LayoutInflater에 대한 참조를 획득하고나면, inflate() 함수를 호출하여 리소스 XML 파일에 선언된 레이아웃을 View 객체로 변환할 수 있습니다.


리턴 타입 함수 설명
View inflate(int resource, ViewGroup root) resource(XML 리소스)로부터 새로운 View를 inflate하여 root 아래에 추가.
View inflate(XmlPullParser parser, ViewGroup root) resource(XML 노드)로부터 새로운 View를 inflate하여 root 아래에 추가.
View inflate(XmlPullParser parser, ViewGroup root,
boolean attachToRoot)
resource(XML 노드)로부터 새로운 View를 inflate. root 아래에 추가할 것인지 여부는 attachToRoot의 값에 따라 결정.
View inflate(int resource, ViewGroup root, boolean attachToRoot) resource(XML 리소스)로부터 새로운 View를 inflate. root 아래에 추가할 것인지 여부는 attachToRoot의 값에 따라 결정.


다음은 inflate() 함수를 사용하여 레이아웃 리소스 XML을 View로 변환하는 예제입니다.

    // "/res/layout/page1.xml" 파일을 inflate. (root = frame, attachToRoot = false)
    View view = inflater.inflate(R.layout.page1, frame, false) ;    

3. FrameLayout 뷰(View) 변경하기

예제를 통해 FrameLayout에 표시되는 뷰(View)를 변경하는 방법에 대해 살펴보겠습니다.


참고로, [안드로이드 프레임레이아웃 뷰 변경하기 1. [addView(), removeView()]]에서 설명한, 뷰(View) 변경 방법은 여기서도 그대로 사용됩니다. 즉, addView(), removeView() 함수가 여기서도 그대로 적용된다는 것입니다.


또한 여기서 사용하는 예제의 화면은 이전 예제와 동일한 레이아웃으로 구성됩니다.

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



3.1 MainActivity의 레이아웃 구성.

가장 먼저 MainActivity에 표시될 레이아웃 리소스 XML을 작성합니다. 이 때 FrameLayout에 표시될 뷰(View)는 FrameLayout의 아래가 아닌, 별도의 XML 파일에 작성됨을 주의하세요.

[STEP-1] "activity_main.xml" - MainActivity의 레이아웃 구성.
<?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.framelayoutchangingview2.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">

    </FrameLayout>

</android.support.constraint.ConstraintLayout>

3.2 FrameLayout에 표시될 뷰(View)에 대한 레이아웃 리소스 XML 추가.

이제 FrameLayout에 표시될 뷰를 위한 레이아웃 리소스 XML 파일을 추가합니다. 뷰 레이아웃 리소스 XML 파일은 별도의 XML 파일로 작성되며, 각각 "page1.xml", "page2.xml", "page3.xml" 이라는 이름으로 작성합니다.

FrameLayout 뷰 변경하기 2 페이지 XML 추가화면


[STEP-2.1] "page1.xml" - 첫 번째 레이아웃 XML 작성.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_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" />

</LinearLayout>
[STEP-2.2] "page2.xml" - 두 번째 레이아웃 XML 작성.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <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" />

</LinearLayout>
[STEP-2.3] "page3.xml" - 세 번째 레이아웃 XML 작성.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <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" />

</LinearLayout>

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

다음 단계로, 화면에 배치된 버튼의 클릭 이벤트를 작성합니다. 버튼 클릭 시 실질적인 뷰(View) 변경은 다음 단계에서 작성되는 changeView() 라는 함수에 구현하고, 여기서는 단지 changeView() 함수만을 호출하도록 만듭니다.

[STEP-3] "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.4 FrameLayout에 표시될 뷰 변경 코드 작성.

이제 FrameLayout에 표시될 뷰(View)를 변경하는 기능을 changeView() 함수에 구현합니다.

[STEP-4] "MainActivity.java" - FrameLayout에 표시될 뷰 변경 코드 작성.

public class MainActivity extends AppCompatActivity { private void changeView(int index) { // LayoutInflater 초기화. LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); FrameLayout frame = (FrameLayout) findViewById(R.id.frame) ; if (frame.getChildCount() > 0) { // FrameLayout에서 뷰 삭제. frame.removeViewAt(0); } // XML에 작성된 레이아웃을 View 객체로 변환. View view = null ; switch (index) { case 0 : view = inflater.inflate(R.layout.page1, frame, false) ; break ; case 1 : view = inflater.inflate(R.layout.page2, frame, false) ; break ; case 2 : view = inflater.inflate(R.layout.page3, frame, false) ; break ; } // FrameLayout에 뷰 추가. if (view != null) { frame.addView(view) ; } } }

3.5 FrameLayout의 초기 상태 결정.

마지막으로, 앱 시작 시 FrameLayout에 첫 번째 뷰(View)가 표시되도록 만들기 위해, MainActivity의 onCreate() 함수에서 changeView(0) 을 호출합니다.

[STEP-5] "MainActivity.java" - onCreate() 함수에서 최초 FrameLayout에 표시될 뷰 결정.
public class MainActivity extends AppCompatActivity {

    // ... 코드 계속

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        // 첫 번째 뷰 표시.
        changeView(0) ;
    }
}

4. 예제 실행 화면

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

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


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

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


5. 참고.

.END.


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

  1. 안녕하세요 개발자님
    정성껏 쓰신 설명덕분에 공부가 재밌습니다 ㅎㅎ

    그런데 질문이있습니다..

    어차피 마지막엔 frame.addView()로 자식으로 넣을건데

    inflater.inflate(R.layout.page2, frame, false) ;

    왜 attachToRoot부분에 false를 주셨나요??



    그리고..혹시 frame의자식으로 들어가지않는다면 결국은
    //
    button1 button2 button3

    프레임부분
    (화면꽉차게 left right bottom을 parent로했으니)

    //여기까지가 폰으로 보이는 화면끝

    이 밑에
    page1 or 2 or 3
    가 추가되는건가요요?

    (page1,2,3들은 width와height를 parent로 가지고있으니 상위 레이어의 constraints레이어를 따라서요,,)

  2. 본문을 작성한지 3년이 넘었다보니, 예제를 작성할 때, attachToRoot 값에 왜 false를 지정했는지는 잘 기억나지 않고요.
    질문글에 올려주신대로, 의미상으로 보면 false를 지정할 필요는 없는 것 같습니다.

    그리고 frame의 자식으로 들어가지 않는 경우에 대해서는 FrameLayout이 아닌 다른 컨테이너를 사용하셔야 하니, 현재에서는 큰 의미가 없는 질문이라 생각됩니다.

    감사합니다.