1. 프래그먼트란?
아래 사진은 휴대용 단말과 태블릿의 화면 크기 차이로부터 오는 UI 구성의 차이를 보여준다.
위 사진에서 휴대용 단말은 크기가 작아 글 목록에서 하나를 터치하여 내용을 볼 때마다 액티비티간의 전환이 이루어져야하지만, 태블릿의 경우는 글 목록은 놔두고 오른쪽의 부분화면만 전환 시켜 사용자 편의성이 증대되었다.
이러한 일을 하기위한 다양한 방법들이 있다. 예를들어 액티비티 그룹(ActivityGroup)을 사용하면 하나의 레이아웃 안에 두 개의 액티비티를 동시에 보여줄 수도 있다. 그러나 액티비티가 많아지면 시스템에서 관리해야하는 요소가 많아지므로 리소스를 낭비하게 된다. 아니면 레이아웃 안에 여러개의 레이아웃을 넣어 각각의 레잉웃마다 다른 뷰를 설정하여 각 뷰를 전환하는 것도 방법이다. 그러나 이 방법은 분할화면의 수가 증가될수록 코드가 복잡해진다. 그리고 다른 액티비티에서도 이 분할화면을 보여야 할 경우 같은 코드를 두 번 작성해주어야한다.
위 방법들이 가지는 문제를 해결하기 위해 나온것이 프래그먼트(Fragment)이다. 프래그먼트는 하나의 액티비티(Activity)에 포함시킬 수 있는 분할화면이다. 처음 태블릿이 나왔을 당시 큰 화면을 분할하여 사용할 수 있도록하여 사용자 편의성을 증가시키기 위해 사용했지만, 현재는 일반적으로 널리 쓰일만큼 중요한 기능이 되었다.
프래그먼트는 다음의 중요한 특성을 가진다.
- 액티비티에 올려야만 사용할 수 있다.
- 액티비티와 같이 레이아웃, 생명주기, 동작 처리부를 가지는 독립적인 객체이다.
- 여러 액티비티에서 동시에 사용할 수 있어 재사용성이 뛰어나다.
2번 특성을 보면 프래그먼트가 액티비티와 상당히 비슷하다는 느낌을 받는다. 그 이유는 프래그먼트의 동작방식을 액티비티의 동작방식을 본따 만들었기 때문이다. 액티비티는 앱 구성요소이므로 안드로이드 시스템에서 관리한다. 구체적으로는 액티비티 매니저(Activity Manager)에 의해 독립적으로 동작할 수 있다. 프래그먼트 입장에서는 액티비티가 시스템 역할을 한다. 액티비티에서 프래그먼트 매니저(Fragment Manager)라는 것을 만들어 프래그먼트를 독립적으로 관리한다. 따라서 프래그먼트를 액티비티에 올려야 사용할 수 있는 것이다.
액티비티간의 명령이나 데이터는 시스템이 이해할 수 있는 인텐트 객체를 통해 전달되었다. 하지만 프래그먼트에서는 인텐트를 사용할 수 없으므로 데이터 전달을 할 때는 메소드를 만들어 사용하는 방식을 취한다.
이제 프래그먼트를 사용하면 액티비티 전환을 하지 않고도 가볍게 화면전환 효과를 낸다던지 화면의 일부분만 전환하는 등의 일을 할 수 있다.
2. 프래그먼트 사용법
하나의 액티비티는 XML 레이아웃으로 화면을 구성하고 Java 소스코드로 뷰 객체로 메모리에 로딩하고 추가적인 기능을 만든 것처럼, 프래그먼트 역시 XML 레이아웃 하나와 Java 소스파일 하나로 구성한다.
프래그먼트는 Fragment 클래스를 상속하여 만들 수 있다. 프래그먼트 클래스를 만들었으면 프래그먼트 XML 레이아웃을 인플레이션하여 클래스에서 사용할 수 있다. 인플레이션하여 메모리에 로딩하는 부분의 코드는 onCreateView()
메소드 안에 정의해야한다. 이 메소드는 인플레이션이 필요한 시점에 자동으로 호출된다. 인플레이션 되면 프래그먼트는 하나의 뷰 객체로서 동작한다.
프래그먼트는 액티비티의 XML 레이아웃 코드에 <Fragment\> 태그로 추가할 수도 있고, 액티비티의 Java 소스코드에서 프래그먼트 인스턴스 객체를 만들어 프래그먼트 매니저의 add()
메소드를 사용해 추가할 수도 있다.
다음은 프래그먼트먼트 클래스의 주요 메소드들이다.
public final Activity getActivity() //프래그먼트가 올려진 액티비티를 리턴
public final FragmentManager getFragmentManager() // 프래그먼트가 올려진 액티비티에서 프래그먼트 매니저 리턴
public final Fragment getParentFragment() // 프래그먼트가 포함된 부모 프래그먼트를 리턴. 액티비티면 null 리턴
public final int getId() // 프래그먼트의 ID를 리턴
프래그먼트 매니저는 액티비티 내에서 getFragmentManager()
메소드를 사용해 참조할 수 있다. 프래그먼트 매니저를 사용하면 프래그먼트를 액티비티에 추가하거나 변경 또는 삭제할 수 있다. 다음은 프래그먼트 매니저의 주요 메소드들이다.
public abstract FragmentTransaction beginTransaction() //프래그먼트 화면을 변경하기 위한 트랜젝션
public abstract Fragment findFragmentById(int Id) // ID를 이용해 프래그먼트 객체를 찾음
public abstract Fragment findFragmentByTag(String tag) // 태그를 이용해 프래그먼트 객체를 찾음
public abstract boolean executePendingTransactions() // 트랜젝션을 즉시 시작하고 싶을 때 commit() 메소드 대신 사용
프래그먼트는 액티비티처럼 하나의 화면을 구성하기도 하고, 뷰 객체처럼 액티비티 화면에 추가될 수도 있다. 이는 프래그먼트가 액티비티와 뷰의 특성을 모두 가지고 있다는 의미이다. 아래 표는 프래그먼트의 이러한 특성을 정리한 것이다.
특성 | 설명 |
---|---|
뷰 특성 | 뷰그룹이나 레이아웃에 추가할 수 있음(뷰를 상속하지는 않음) |
액티비티 특성 | 액티비티처럼 동작하며 수명주기도 가짐(컨텍스트 객체는 아님) |
3. 실습
한식, 중식, 일식 세 개의 메뉴 중 하나를 클릭하면 부분화면으로 세부 메뉴를 보여주도록 하는 앱을 만든다.
먼저 한식의 세부 메뉴를 보여줄 프래그먼트의 화면은 아래의 XML 코드로 작성한다.
<fragment_korean.xml\>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/koreanFragment"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal|center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="김치찌개\n\n된장찌개\n\n삼계탕\n\n육개장\n\n불고기"
android:textSize="50dp"
android:layout_margin="20dp"
android:textStyle="bold|italic" />
</LinearLayout>
텍스트뷰 하나에 5개의 메뉴를 텍스트로 보여주는 아주 단순한 구성이다. 그리고 이 xml을 인플레이션하는 자바 코드는 아래와 같다.
<KoreanFragment.java\>
package io.swnomad.samplefragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class KoreanFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_korean, container, false);
return rootView;
}
}
onCreateView()
메소드는 프래그먼트를 설정할 때 자동으로 호출된다. 여기에는 프래그먼트 화면을 구성하는 XML 레이아웃을 인플레이션하는 코드를 넣어주면 된다.
중식, 일식의 경우도 위와 같으므로 생략하겠다.
이제 메인 액티비티를 통해서 화면을 전환해보도록 하자.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1"
android:background="#330000ff">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="메뉴"
android:textSize="30dp"
android:textStyle="bold"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"/>
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="한식"
android:textSize="20dp"
android:layout_marginTop="50dp"/>
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="일식"
android:textSize="20dp"
android:layout_marginTop="20dp"/>
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="중식"
android:textSize="20dp"
android:layout_marginTop="20dp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="2">
</LinearLayout>
</LinearLayout>
최상위 레이아웃 안에 2개의 레이아웃을 추가하고 화면 넓이 비율을 1:2로 지정한다. 왼쪽의 레이아웃은 메뉴 버튼, 오른쪽은 세부메뉴를 보여줄 프래그먼트를 구성할 것이다. 이제 자바 소스코드를 보자.
package io.swnomad.samplefragment;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
Button button1;
Button button2;
Button button3;
KoreanFragment koFrag;
ChineseFragment chFrag;
JapaneseFragment jaFrag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* 메뉴별 프래그먼트 객체 생성 */
koFrag = new KoreanFragment();
chFrag = new ChineseFragment();
jaFrag = new JapaneseFragment();
// 앱이 실행되자마자 처음으로 화면에 보일 프래그먼트 설정
getSupportFragmentManager().beginTransaction().replace(R.id.container, koFrag).commit();
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.container, koFrag).commit();
//클릭된 버튼의 배경색은 빨강, 나머지 버튼의 배경색은 회색으로 설정
button1.setBackgroundColor(Color.RED);
button2.setBackgroundColor(Color.GRAY);
button3.setBackgroundColor(Color.GRAY);
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.container, chFrag).commit();
button1.setBackgroundColor(Color.GRAY);
button2.setBackgroundColor(Color.RED);
button3.setBackgroundColor(Color.GRAY);
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.container, jaFrag).commit();
button1.setBackgroundColor(Color.GRAY);
button2.setBackgroundColor(Color.GRAY);
button3.setBackgroundColor(Color.RED);
}
});
}
}
가장 먼저 각각의 프래그먼트에 대해 new 연산자를 사용해서 객체를 만든다. 그리고 앱을 실행했을 때 보일 프래그먼트를 설정하기 위해 다음 코드를 사용했다.
getSupportFragmentManager().beginTransaction().replace(R.id.container, koFrag).commit();
getSupportFragmentManager()
메소드를 통해 프래그먼트 매니저를 참조하였다. replace()
메소드의 첫 번째 파라미터로는 프래그먼트를 담을 레이아웃의 id를 넣어주어야하므로 레이아웃의 오른쪽 레이아웃의 id인 "container"를 넣어주었다. 두 번째 전달인자로는 설정할 프래그먼트 객체를 넣어주면 된다. 마지막으로 commit()
메소드를 이어서 호출하면 container에 프래그먼트의 설정이 끝난다.
그리고 주의해야할 건 각 버튼의 onClick()
메소드 안에서 프래그먼트 매니저를 참조할 때 MainActivity.this
를 앞에 붙였다는 것이다. 지금 코드가 작성되는 곳은 OnClickListener
익명객체이므로 MainActivity 클래스 외부이다. 그런데 프래그먼트 매니저는 액티비티에서 제공하므로 MainActivity.this
를 참조해야 하는것이다.
앱을 실행하면 다음과 같은 화면이 나온다.
'안드로이드' 카테고리의 다른 글
(안드로이드) 32 - 액션바(ActionBar) (0) | 2020.05.18 |
---|---|
(안드로이드) 31 - 프래그먼트 수명주기(Fragment LifeCycle) (0) | 2020.05.18 |
(안드로이드) 29 - 페이지 슬라이딩(Page Sliding) (0) | 2020.05.18 |
(안드로이드) 28 - 애니메이션(Animation) (0) | 2020.05.18 |
(안드로이드) 27 - 시크바(SeekBar) (0) | 2020.05.18 |