Android가 프래그먼트를 처음 도입한 것은 Android 3.0(API 레벨 11)부터입니다. 기본적으로 태블릿과 같은 큰 화면에서 보다 역동적이고 유연한 UI 디자인을 지원하는 것이 목적이었습니다.
예를 들어 뉴스 애플리케이션이라면 A프래그먼트는 왼쪽에 기사 목록을 표시하고, B프래그먼트는 오른쪽에 기사 내용을 표시할 수 있습니다. 화면이 큰 태블릿에서는 하나의 Activity에서 목록과 내용을 한번에 나타낼수 있고, 반면 모바일 기기에서는 A, B 프래그먼트를 각각의 Activity에 나타낼 수 있습니다.
Fragment는 앱 UI의 재사용 가능한 부분을 나타냅니다. Fragment는 자체 레이아웃을 정의 및 관리하고, 자체 수명 주기를 가지며, 자체 입력 이벤트를 처리할 수 있다. Fragment는 단독으로 존재할 수 없으며 Activity나 다른 Fragment에 의해 호스팅되어야 합니다. Fragment의 보기 계층은 호스트의 보기 계층 구조의 일부가 되거나 호스트에 연결됩니다.
여기서 중요한 키워드는 재사용
, 자체 수명 주기
, 단독으로 존재 불가
Fragment는 Android Jetpack 라이브러리의 Navigation, BottomNavigationView 및 ViewPager2와 함께 작동하도록 설계되었습니다.
dependencies {
def fragment_version = "1.4.1"
// Java language implementation
implementation "androidx.fragment:fragment:$fragment_version"
// Kotlin
implementation "androidx.fragment:fragment-ktx:$fragment_version"
}
public Fragment() {
initLifecycle();
}
@ContentView
public Fragment(@LayoutRes int contentLayoutId) {
this();
mContentLayoutId = contentLayoutId;
}
Fragment 생성자는 위처럼 2가지가 있습니다.
class AFragment: Fragment() { }
class AFragment: Fragment(R.layout.fr_a) { }
상속으로 간단하게 프래그먼트를 만들 수 있고, 두번째 생성자 레이아웃 id로 생성하면
onCreateView
를 override 하지 않아도 UI를 구성할 수 있습니다.
앞서 소개했듯이 Fragmnet는 단독으로 존재할 수 없습니다. 따라서 Fragment는 FragmentActivity 에 포함되어 있어야 합니다. 기본적으로 프로젝트 생성시 AppCompatActivity로 생성 되어 있고 AppCompatActivity는 FragmentActivity는 상속하고 있어서 바로 사용할 수 있습니다.
AppCompatActivity는 결국 Activity를 상속하고 있고 하위 버전과 호환을 위한 클래스 입니다.
AppCompatActivity에 관한 내용은 여기 를 읽어보셔도 좋을것 같습니다.
Activity xml에 FragmentContainerView를 추가해 줍니다.
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:name="com.example.test.AFragment"
android:tag="AFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
FragmentContainerView
기존에 FrameLayout에 동적으로 프래그먼트를 생성하는 방식에서 FragmentContainerView가 새롭게 도입되었습니다.
FragmentContainerView
는 FrameLayout을 상속하고 있고, 프래그먼트에 대한 z-ordering 처리가 개선되었다는 것입니다.
예를 들어 두 프래그먼트 간의 exit 및 enter 전환이 서로 겹치지 않음을 의미합니다. 대신 이 FragmentContainerView는 종료 애니메이션을 먼저 시작한 다음 시작 애니메이션을 시작합니다.
https://youtu.be/RS1IACnZLy4?t=548
주의점
FragmentContainerView must have an android:id to add Fragment...
android:id 속성 필수
입니다!class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
supportFragmentManager.commit {
setReorderingAllowed(true)
add<AFragment>(R.id.fragment_container_view)
}
}
}
}
Activity의 onCreate에서 중요! savedInstanceState가 null일때
FragmentManager를 가져와 FragmentTransaction의 add로 container id에 프래그먼트를 추가할 수 있습니다.
왜 savedInstanceState가 null일때라는 조건문 안에서 프래그먼트를 추가해줘야 할까❓
Activity가 시스템에 의해 또는 화면 회전으로 Actity가 재생성 될때 savedInstanceState가 not null이기 때문에 자칫 프래그먼트가 중복 생성
이 되기때문입니다.
Activity의 Lifecycle 생명주기에서 onPause() -> onStop() -> onSavedInstanceState() 순서로 호출되고, 다른 Activity로 이동하거나, 백그라운드로 화면 이동, 화면 회전 등이 일어날때 onSavedInstanceState()가 호출되고, 사용자가 명시적으로 뒤로가기 또는 finish() 함수로 Activity를 종료했을때는 호출 되지 않습니다. Activity Lifecycle
간단하고 가벼운 UI 상태 저장 ( 화면이 재생성 될때 보고있던 화면의 데이터를 그대로 유지하기위해 )
onSavedInstanceState()가 데이터를 디스크에 직렬화하기 때문에저장용량 및 속도에 의해 제한됩니다.
직렬화될 객체가 복잡하면 직렬화 시 많은 메모리가 소비될 수 있습니다. 직렬화 프로세스는 구성 변경 시 기본 스레드에서 발생하기 때문에 직렬화가 장기적으로 실행되면 프레임 하락 및 시각적인 끊김 현상이 발생할 수 있습니다.
비트맵과 같은 대량의 데이터, 또는 길이가 긴 직렬화나 역직렬화가 필요한 복잡한 데이터 구조를 저장하는 데 onSavedInstanceState()를 사용해서는 안 됩니다. 대신 기본 유형 및 String 같은 단순하고 작은 객체만 저장해야 합니다.
예를 들면 텍스트필드 값, 체크박스 상태, 리스트 스크롤 위치 등등..
Save UI states