앱 만들다 보면 한 화면에 여러 종류의 UI를 스크롤 되게 구성해야 할 일이 자주 생기는데,
이런 경우에 사용하는 3가지 방식에 대해 알아보겠습니다
FragmentContainerView 방식 (Fragment 삽입)
특징
- 각 UI 블록을 Fragment로 나눠서 FragmentContainerView에 삽입
- 고정된 레이아웃이나, 특정 위치에 동적으로 fragment를 붙이는 방식
장점
- 각 블록마다 생명주기, ViewModel 분리 가능
- 로직이 복잡하거나 독립된 기능일 경우 구조화에 유리
단점
- fragment가 많아지면 성능 저하 발생
- ScrollView 안에 fragment 여러 개 → 비추천 구조 (nested fragment 문제 등)
[layout]
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/bannerFragmentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/listFragmentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
[activity]
supportFragmentManager.beginTransaction()
.replace(R.id.bannerFragmentContainer, BannerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.listFragmentContainer, ItemListFragment())
.commit()
addLayout()방식
특징
- API 응답을 순회하며 뷰를 직접 생성해서 addView()로 붙이는 방식
- LinearLayout이나 ConstraintLayout 안에서 뷰 조합
장점
- 구조가 단순하고 유연함 (서대로 뷰만 붙이면 됨)
- 빠르게 화면 만들기 좋고, 레이아웃 순서를 동적으로 바꾸기도 쉬움
단점
- 뷰 재사용이 안 되기 때문에 수가 많아지면 성능 저하
- 코드가 복잡해지기 쉬움 (뷰, 데이터, 로직이 뒤엉김)
[layout]
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
[activity]
class MainActivity : AppCompatActivity() {
private lateinit var container: LinearLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
container = findViewById(R.id.container)
val titles = listOf("Title 1", "Title 2", "Title 3")
titles.forEach { title ->
val textView = TextView(this).apply {
text = title
textSize = 20f
setPadding(16, 16, 16, 16)
}
container.addView(textView)
}
}
}
RecyclerView + ViewType 방식
특징
- 다양한 UI 블록을 ViewType으로 분기해서 하나의 RecyclerView로 구성
- ViewHolder 기반으로 뷰 재사용 가능
장점
- 성능 최고 (뷰 재사용, ViewHolder 풀링)
- 각 타입별로 클래스 분리 가능 → 유지보수, 테스트 용이
- 구조화된 패턴으로 확장성 높음
단점
- 초기 설계가 조금 번거롭다 (ViewType 정의, sealed class 설계..)
[layout]
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
[adapter]
sealed class UiModel {
data class Title(val text: String) : UiModel()
data class Image(val url: String) : UiModel()
}
class MultiViewAdapter(private val items: List<UiModel>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
is UiModel.Title -> 0
is UiModel.Image -> 1
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
0 -> TitleViewHolder(LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false))
else -> ImageViewHolder(ImageView(parent.context))
}
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val item = items[position]) {
is UiModel.Title -> (holder as TitleViewHolder).bind(item.text)
is UiModel.Image -> (holder as ImageViewHolder).bind(item.url)
}
}
class TitleViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(text: String) {
(itemView as TextView).text = text
}
}
class ImageViewHolder(private val imageView: ImageView) : RecyclerView.ViewHolder(imageView) {
fun bind(url: String) {
imageView.setImageResource(android.R.drawable.ic_menu_gallery)
}
}
}
[activity]
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
val items = listOf(
UiModel.Title("Hello"),
UiModel.Image("https://example.com/image1.jpg"),
UiModel.Title("World"),
UiModel.Image("https://example.com/image2.jpg")
)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = MultiViewAdapter(items)
각각 3가지 타입이 있는데,
- 기능 단위로 분리하고 싶고, 각각 UI가 독립적이면 → FragmentContainerView
- 뷰 몇 개 순서대로 붙이기만 하면 되면 → addLayout()
- 여러 UI 타입이 섞여 있고 리스트가 길어질 수 있으면 → RecyclerView + ViewType
이 와중에 FragmentContainerView랑 recyclerview가 각각 언제 사용되면 좋은지 좀 헷갈렸는데,
- 이 블록이 미니 화면처럼 동작하고 독립적으로 동작해야 한다? : FragmentContainerView
(ex. 영상 플레이어, 지도 등) - 그냥 여러 UI를 스크롤되게 보여주고 성능도 중요하다? : RecyclerView + ViewType
(ex. 홈피드, 뉴스 피드, 상품 리스트 등)
이렇게 생각하면 이해가 편하더라구요!
'📱 Android' 카테고리의 다른 글
[Android] NetworkInfo Deprecated (0) | 2025.06.06 |
---|---|
[Android] MVI 패턴, 이름을 왜이렇게 헷갈리게 지었어요? (0) | 2025.05.27 |
[Android] WorkManager (0) | 2025.04.18 |
[Android] 클린 아키텍처 적용 시 고민했던 3가지 의문점 (0) | 2025.04.12 |
[Android / RecyclerView] onCreateViewHolder vs onBindViewHolder: 클릭 리스너는 어디에 둘까? (0) | 2025.04.07 |