반응형

이전 포스트(Fragment 만들기Fragment 제어하기) 에서

레이아웃 xml 파일안에 프레그먼트(Fragment)를 만들어서

화면에 보여주고 제어하는 작업을 소개했습니다.


하지만 실제로 Fragment를 사용할 때는

XML에 프레그먼트를 작성하지 않고 자바코드로 추가합니다.

XML에 추가하면 고정적으로 화면에 추가되기 때문에 나중에

삭제가 되지 않습니다.


동적으로 추가/삭제 또는 다른 Fragment로 재배치 등의 작업을 수행하려면

자바코드에서 Fragment를 추가해야만 합니다.


이번 예제는

버튼 3개가 화면 상단에 있습니다.

처음에는 프레그먼트가 한개도 없는 상태입니다.


첫번째 'Analog'버튼을 누르면 AnalogClock를 보여주는 Fragment를 추가합니다.

두번째 'Digital'버튼을 누르면 DigitalClock을 보여주는 Fragment로 바뀝니다.

세번째 'Calendar'버튼을 누르면 CalendarView를 보여주는 Fragment로 바뀝니다.


   

 

실제 이런 동작의 형태를 보신적이 많이 있을 텐데요.

다른점은 버튼3개가 Tab으로 되어 있다는 거죠.

다음 포스트에서 Tab으로 Fragment를 바꾸는 예제도 할 겁니다.


이제 만들어보죠.


먼저 activity_main.xml 파일입니다.

Button 3개와 Fragment가 추가될 LinearLayout 1개로 구성되어 있습니다.

 

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    

    <LinearLayout 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

        

        <Button android:id="@+id/btn01"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="ANALOG"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

        <Button android:id="@+id/btn02"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="DIGITAL"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

        <Button android:id="@+id/btn03"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="CALENDAR"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

    </LinearLayout>

    

    <LinearLayout android:id="@+id/container"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:orientation="vertical"

        android:padding="8dp" >        

        

    </LinearLayout>    


</LinearLayout>


이제 각 버튼을 누를때 마다 추가될 Fragment 객체가 보여줄

View의 설계을 위해 레이아웃 xml파일 3개를 추가하겠습니다.

res폴더>layout폴더> 안에

'fragment_analog', 'fragment_digital', 'fragment_calendar' 로 3개의 xml 파일을 만듭니다.

모두 LinearLayout으로 만들었습니다.




각 layout xml 파일안에 원하는 View들을 추가하면 되겠죠.


이 예제에서는 단순하게 'AnalogClock', 'DigitalClock', 'CalendarView'

각각의 layout xml파일안에 추가하겠습니다.

 

3개의 layout xml 파일입니다. 

 fragment_analog.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    

    <AnalogClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"/>    


</LinearLayout>

 

 fragment_digital.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    

    <DigitalClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"/>    


</LinearLayout>

 

 fragment_calendar.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" 

    android:padding="16dp">

    

    <CalendarView 

        android:layout_width="match_parent"

        android:layout_height="match_parent"/>    


</LinearLayout>


이제

이 3개의 레이아웃 파일을 View로 만들어 보여줄 Fragment 자바파일을 3개 만들어보겠습니다.

자바 파일인 만큼 src폴더의 package에서 new메뉴를 실행시켜서

'AnalogFragment', 'DigitalFragment', 'CalendarFragment'

라는 이름의 새로운 class 자바 파일을 아래와 같이 생성하겠습니다.


당연히 Class를 만들때 Fragment를 상속받아 만드시면 됩니다.

android.app 패키지의 Fragment를 상속받으면 됩니다.


  




만들어진 3개의 Fragment 자바안에 Activity 자바파일처럼

lifecycle 콜백 메소드 중 보여줄 View를 만들어내는 onCreateView메소드를 오버라이드(Override)해서

위에 만든 3개의 레이아웃 xml파일을 View로 설정해 보겠습니다.


주석 빼면 코드가 몇 줄 안됩니다. 

 AnalogFragment.java

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class AnalogFragment extends Fragment {

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null;//Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_analog.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_analog, null);

//생성된 View 객체를 리턴

return view;

}

}

 

 DigitalFragment.java

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class DigitalFragment extends Fragment {

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null;//Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_digital.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_digital, null);

//생성된 View 객체를 리턴

return view;

}


}

 

 CalendarFragment.java

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class CalendarFragment extends Fragment {

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null;//Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_calendar.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_calendar, null);

//생성된 View 객체를 리턴

return view;

}


}


 이제

Fragment를 다 설계했으니 버튼을 누를때마다 Fragment가 추가되도록

MainActivity.java 파일을 작성해 보겠습니다.


주석을 참조하시기 바랍니다.

 

MainActivity.java

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.View;


public class MainActivity extends Activity {

FragmentManager manager;  //Fragment를 관리하는 클래스의 참조변수

FragmentTransaction tran;  //실제로 Fragment를 추가/삭제/재배치 하는 클래스의 참조변수

Fragment frag1, frag2, frag3; //3개의 Fragment 참조변수


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Fragment를 관리하는 FragmentManager 객체 덩어오기

manager= (FragmentManager)getFragmentManager();

//미리 3개의 Fragment 객체 생성

frag1= new AnalogFragment();

frag2= new DigitalFragment();

frag3= new CalendarFragment();

}

//Activity가 보여주는 activity_main.xml 파일에 태그문으로

//onClick속성이 설정된 View를 클릭했을 때 자동으로 호출되는 콜백 메소드

public void mOnClick(View v){

switch( v.getId() ){

case R.id.btn01:

//fragment_analog.xml을 보여주는 AnalogFragment 객체로 추가(add)

//Fragment 추가 작업을 수행하기 위해 먼저 FragmentManager에게 작업시작(beginTransaction) 요청

//Fragment를 자바에서 동적으로 추가/삭제/재배치 하려면 반드시 Transaction으로 실행

//FragmentTransction 클래스 객체를 생성 및 작업 시작

tran= manager.beginTransaction();

//Fragment 추가(add)

//첫번째 파라미터 : Fragment를 추가시킬 ViewGroup의 ID

  //                   (activity_main.xml안에 보면 버튼 아래에 있는 LinearLayout의 Id가 R.id.container)

//                  이 LinearLayout에 frag1(AnalogFragment) 객체 추가

//두번째 파라미터 : 추가시킬 Fragment 객체

tran.add(R.id.container, frag1); 

//Fragment 변경 작업을 완료했다는 메소드 호출

tran.commit();

break;

case R.id.btn02:

//fragment_digital.xml을 보여주는 DigitalFragment 객체로 추가(add)

//FragmentTransction 클래스 객체를 생성 및 작업 시작

tran= manager.beginTransaction();

//DigitalFragment 추가(add)

tran.add(R.id.container, frag2); 

//Fragment 변경 작업을 완료했다는 메소드 호출

tran.commit();

break;

case R.id.btn03:

//fragment_calendar.xml을 보여주는 CalendarFragment 객체로 추가(add)

//FragmentTransction 클래스 객체를 생성 및 작업 시작

tran= manager.beginTransaction();

//CalendarFragment 추가(add)

tran.add(R.id.container, frag3); 

//Fragment 변경 작업을 완료했다는 메소드 호출

tran.commit();

break;

}

}

}

 

위 코드에서 가장 눈여겨 볼 부분이 Fragment를 add 하는 부분이겠죠.

FragmentManager는 이전 포스트에서도 소개했으니 어떤 클래스인지는 알것이라고 간주하고

처음 보는 것이 FragmentTransaction 이라는 것이겠죠.


안드로이드는 Fragment를 추가/삭제/재배치 등의 작업을 수행하기 위해

FragmentTransaction을 사용합니다.


트랜잭션(Trasaction)이라는 용어를 알면 좀더 이해하기 쉬울텐데요.


짧게 설명하면

Transaction은 하나의 작업 흐름을 말합니다. 비슷하게 인지되는 프로세스(Process)가 있는데

다른 점은

트랜잭션은 프로세스와 다르게 롤백 기능이 있다는 것이죠.


이 롤백이 뭐냐?

예를 들어보겠습니다.

여러분이 인터넷 계좌이체 프로그램을 하나 만들고 있다고 생각해보죠.


A라는 계좌에서 100만원을 B계좌로 옮기는 부분을 눈여겨 보겠습니다.

다른 건 빼고

100만원을 이체하려면

A계좌의 잔액에서 100만원을 차감하도록 할 것이고

바로 이어서 B계좌의 잔액에 100만원을 증감하도록 해야겠죠.


물론 그런 식으로 코드를 작성했을 것이며 잘 된다고 가정해 보죠.

뺄셈 덧셈만 하면 되니 어려운 프로세스는 아니죠.


중요한 건 이 프로세스가 실행되는 도중 발생하는 예외상황에 대한 문제입니다.

무슨말이냐??


프로세서가 여러분이 작성한 코드를 한줄씩 읽어가며 실행하는 것을 아실테고

위 작업을 보면

프로세서가 먼저 A계좌에서 100만원을 빼는 코드를 실행하겠죠.

바로 이어서 B계좌에 100만원을 더하는 코드를 실행하려하겠죠.


바로 이때!!!

갑자기 퍽!! 에러가 나서 프로세서가 종료되었다면!!!!!!!!!


B계좌에 100만원 덧셈 작업은 실행이 안되고

A계좌만 100만원이 뺄셈된 상태겠죠...


그럼 이 100만원은????????

컴퓨터가 먹은(?) 것이죠.


이러면 안되잖아요. 그래서 이런 경우 하나의 작업( A에서 100빼고 B에 100추가 )이

종료되었다고 확인(commit)하기 전에 어떤 문제로 종료되면

처음 상태로 자동으로 되돌아 가도록 하는 기술이 있습니다.

이를 롤백이라고 합니다.


아주 기가막힌 기법이죠. 반드시 필요하기도 하고요.

이 롤백기능을 가진 것이 트랜잭션(Transaction) 입니다.


그래서 작업을 시작한다고 알리기 위해 begineTransaction을 실행하고 

원하는 작업을 수행한 후

마지막에 commit하면 적용되는 문법을 사용합니다.


안드로이드의 Fragment 추가/삭제/재배치는 이런 트랜잭션으로 수행하도록 설계되었습니다.

코드를 보시면 어렵지 않게 이해할 수 있을 것이라 기대합니다.


자 이제 완성했으니 실행해 볼까요?


실행결과는 아래와 같습니다.

각 버튼을 하나씩 눌러서 Fragment를 추가하는 겁니다.


  

 


버튼을 누를때 마다 Fragment가 추가(add)되는 것을 볼수 있습니다.


어! 근데 이건 제가 원하는 결과가 아니죠.

전 add가 아니라 버튼을 누를때마다 다른 Fragment를 보고 싶은 것이죠.

이럴 땐 새로운 Fragment를 추가(add)할 때 

기존 Fragment를 제거(remove)하고 추가 작업을 해야겠죠.

위 코드에서 추가 할때 tran.add() 메소드를 했듯이

제거할때는 tran.remove()메소드를 호출하면 됩니다.


생각해 보면 이 작업은 결국 재배치(replace)라는 용어로 부를 수도 있겠군요.

그래 FragmentTransaction 재배치(replace) 메소드도 가지고 있습니다.

재배치(replace)는 만약 기존에 Fragment가 없다면 자동으로 추가합니다.

편하네요.


위의 add() 코드를 replace() 코드로 변경하겠습니다.

주석은 빼고

변경된 부분은 굵은 글씨로 표시했습니다.

MainActivity.java

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.View;


public class MainActivity extends Activity {

FragmentManager manager;  //Fragment를 관리하는 클래스의 참조변수

FragmentTransaction tran;  //실제로 Fragment를 추가/삭제/재배치 하는 클래스의 참조변수

Fragment frag1, frag2, frag3; //3개의 Fragment 참조변수


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Fragment를 관리하는 FragmentManager 객체 덩어오기

manager= (FragmentManager)getFragmentManager();

//미리 3개의 Fragment 객체 생성

frag1= new AnalogFragment();

frag2= new DigitalFragment();

frag3= new CalendarFragment();

}

//Activity가 보여주는 activity_main.xml 파일에 태그문으로

//onClick속성이 설정된 View를 클릭했을 때 자동으로 호출되는 콜백 메소드

public void mOnClick(View v){

switch( v.getId() ){

case R.id.btn01:

//fragment_analog.xml을 보여주는 AnalogFragment 객체로 재배치(replace)

tran= manager.beginTransaction();

tran.replace(R.id.container, frag1); 

tran.commit();

break;

case R.id.btn02:

//fragment_digital.xml을 보여주는 DigitalFragment 객체로 재배치(replace)

tran= manager.beginTransaction();

tran.replace(R.id.container, frag2); 

tran.commit();

break;

case R.id.btn03:

//fragment_calendar.xml을 보여주는 CalendarFragment 객체로 재배치(replace)

tran= manager.beginTransaction();

tran.replace(R.id.container, frag3); 

tran.commit();

break;

}

}

}

 

동작 결과는 아래와 같습니다.

   

처음 소개했듯이 Fragment가 바뀌는 것을 확인할 수 있습니다.

마지막 CalendarView가 제대로 보이지 않으시면 CalendarView의 높이(layout_height)값을 '300dp'정도로 줄이세요.



참고로

Fragment는 Activity처럼 백스택(back stack)를 가지고 있지 않아서

현재 Fragment를 종료한다고 이전 Fragment가 보이지 않습니다.


하지만 그렇게 하고 싶을 수도 있겠죠.

만약 이전에 보여줬던 Fragment가 보이게 하려면 backstack에 Fragment를 추가해야 합니다.

코드만 한줄 더 쓰면 됩니다.

 tran= manager.beginTransaction();

 tran.replace(R.id.container, frag1);

 tran.addToBackStack(null); // 백스택에 지금 재배치한 Fragment를 추가

 tran.commit();

이렇게 하고 앱의 '뒤로가기' 버튼을 누르면

이전에 보여줬던 Fragment가 보이게 될 겁니다.



여기까지만 하고

다음 포스트에서는 이 예제를 기반으로 해서

Button이 아니라 Tab으로 Fragment가 변경되게 해보겠습니다.


반응형

+ Recent posts