Compose를 부분 적용하는 케이스 중 하나는 BottomSheetDialogFragment에서 View만 Compose로 바꾸는 케이스이다.
샘플 코드 : 링크
스크롤 시 어색하지 않다고 보였다면, 작은 차이가 존재한다. 문제는 기본적으로 아래 2가지의 케이스가 있다.
동작 체크에 사용한 코드는 아래와 같다.
class ComposeItemListDialogFragment : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View = content {
SampleTheme {
Greeting((0..100).toList())
}
}
}
@Composable
fun Greeting(list: List<Int>) {
LazyColumn(
contentPadding = PaddingValues(vertical = 8.dp)
) {
items(items = list) {
Text(
text = it.toString(),
fontSize = 20.sp,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
}
전체 코드를 보면 특별한 문제는 없어 보일 수 있다.
해당 동작의 원인은 BottomSheetDialogFragment의 스크롤과 LazyColumn의 스크롤이 중첩으로 사용해서 발생하는 이슈이다. 기본적으로 BottomSheetDialogFragment은 스크롤이 암시적으로 존재한다.
이 문제의 해결 방법은 nestedScroll()에 rememberNestedScrollInteropConnection을 사용하면 된다.
LazyColumn(
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()),
...
) {
...
}
이 문제의 해결법은 이미 Android Develoepr 사이트의 Compose 가이드에도 존재한다.
그중에서 CoordinatorLayout가 이번 이슈의 주요 이슈 항목이다. 그런데 왜 BottomSheetDialogFragment와 무슨 상관이 있는지 이해가 안 될 수 있다.
BottomSheetDialogFragment를 노출한 상태에서 Layout Inspector로 View 구조를 체크하면 아래와 같다.
design_bottom_sheet은 coordinatorLayout 하위에 있다.
BottomSheetDialogFragment는 기본적으로 AppCompatDialogFragment를 상속하지만, Material의 BottomSheetDialog를 사용하고 있다.
public class BottomSheetDialogFragment extends AppCompatDialogFragment {
...
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
return new BottomSheetDialog(getContext(), getTheme());
}
...
}
소스 출처 : BottomSheetDialogFragment.java
BottomSheetDialog는 AppCompatDialog를 상속했으며, design_bottom_sheet_dialog.xml으로 View를 inflate하고 있다.
public class BottomSheetDialog extends AppCompatDialog {
...
@Override
public void setContentView(View view) {
super.setContentView(wrapInBottomSheet(0, view, null));
}
/** Behavior를 찾기 위해 Container layout을 생성 */
private FrameLayout ensureContainerAndBehavior() {
if (container == null) {
container =
(FrameLayout) View.inflate(getContext(), R.layout.design_bottom_sheet_dialog, null);
...
}
return container;
}
private View wrapInBottomSheet(
int layoutResId, @Nullable View view, @Nullable ViewGroup.LayoutParams params) {
ensureContainerAndBehavior();
...
return container;
}
}
소스 출처 : BottomSheetDialog.java
design_bottom_sheet_dialog.xml에는 CoordinatorLayout가 존재하며, 자식 뷰로 design_bottom_sheet id를 가지는 FrameLayout이 존재한다. 해당 ID의 자식 뷰로 실제 사용자가 추가하려는 View가 추가되는 구조이다.
<FrameLayout ...>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
...>
...
<FrameLayout
android:id="@+id/design_bottom_sheet"
.../>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
소스 출처 : design_bottom_sheet_dialog.xml
bottomSheet#addView로 넘겨지는 View가 ComposeView이고 내부 Content는 샘플로 만들 Composable인 것을 볼 수 있다.
아래는 기존 View, 오동작하는 Compose, 스크롤 이슈가 해결된 Compose의 모습이다.
comments powered by Disqus
Subscribe to this blog via RSS.
LazyColumn/Row에서 동일한 Key를 사용하면 크래시가 발생하는 이유
Posted on 30 Nov 2024