SW 공부노트

[Android Basics in Kotlin] Navigation: 적응형 레이아웃(Adaptable Layout) 본문

안드로이드/안드로이드 Kotlin

[Android Basics in Kotlin] Navigation: 적응형 레이아웃(Adaptable Layout)

요빈 2023. 3. 19. 15:52

https://developer.android.com/courses/android-basics-kotlin/unit-3

 

Android Kotlin Basics in Kotlin  |  Android Basics in Kotlin - Navigation  |  Android Developers

Enhance your users’ ability to navigate across, into and back out from the various screens within your app for a consistent and predictable user experience.

developer.android.com

해당 글은 위 사이트의 PATHWAY 5 과정인 적응형 레이아웃을 공부하며 작성한 글입니다.

이 과정에서는 다양한 화면 크기에 맞게 앱을 조정하는 방법을 배웁니다.


적응형 레이아웃

 

Android 기기는 다양한 모양과 크기로 출시되기 때문에 앱을 설계할 때는 소형 화면 기기에서 대형 화면 기기에 이르기까지 다양한 유형의 기기에서 실행되도록 해야 한다. 즉, 앱의 레이아웃은 유연해야 한다는 의미이다.

특정 가로세로 비율과 화면 크기를 고정크기로 정의하는 대신, 다양한 화면 크기와 방향에 적절하게 맞출 수 있는 레이아웃이어야 한다.

 

태블릿과 같이 큰 화면에서는 공간이 더 넓어 콘텐츠를 더 많이 표시할 수 있기 때문에 목록-세부정보가 일반 목록보다 더 효율적이다.

목록-세부정보 패턴은 레이아웃의 한쪽에 항목 목록을 표시한 형식을 말한다.

목록-세부정보는 UI 화면 크기에 따라 다르게 작동해야 할 수 있다.

대형 디스플레이에는 목록창과 세부정보를 나란히 배치할 수 있는 충분한 공간이 있다.

하지만 작은 화면에서는 이들이 복잡하게 보이기 때문에 한 번에 창 하나씩 표시하는 것이 좋다.

현재 화면 크기에 따라 적절한 사용자 환경을 선택하기 위해 SlidingPaneLayout을 사용한다.

즉, SlidingPaneLayout을 사용하면 대형 기기에서는 창 두 개를 나란히 표시하고, 휴대전화와 같은 소형기기에서는 창을 한 번에 한 개만 표시하도록 자동 조절할 수 있다.

 

SlidingPaneLayout

 

라이브러리 종속 항목 추가

implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0-beta01"

 

목록 프래그먼트 레이아웃 수정

 

1. 목록 프래그먼트의 루트 레이아웃을 SlidingPaneLayout으로 변환

 

fragment_sports_list의 루트 레이아웃인 FrameLayoutandroidx.slidingpanelayout.widget.SlidingPaneLayout 으로 변경하면 된다.

 

2. SlidingPaneLayout 하위 요소 추가

 

화면 크기에 따라 SlidingPaneLayout 하위 요소가 한 화면에 1개 또는 2개 출력될 지 결정된다.

예제 앱에서는 스포츠 목록 화면과 세부항목 뷰로 나뉜다.

따라서 SlidingPaneLayout 하위에 목록에 사용될 리사이클러뷰와 프래그먼트를 출력할 FragmentContainerView를 추가한다.

 

* FragmentContainerView

name 속성에 적힌 프래그먼트가 해당 컨테이너 뷰에 출력된다.

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/detail_container"
    android:name="com.example.android.sports.NewsDetailsFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

 3. layout_width, layout_weight 속성 업데이트

 

SlidingPaneLayout은 두 창의 너비를 감안해 창을 나란히 표시할 지 결정한다.

합산 너비가 SlidingPaneLayout에 사용 가능한 너비를 초과하는 경우 하위 뷰가 화면에 하나씩 출력된다.

이 경우 하위 뷰는 SlidingPaneLayout에 사용 가능한 너비를 채우도록 확장된다.

다음 표는 화면 너비 별 중단점 목록이다.

 

layout_weight 속성은 두 번째 창이 남은 공간 전체를 차지하도록 UI를 수정하는 데 사용된다.

SlidingPaneLayout을 사용하면 뷰가 겹치지 않는 경우에는 하위 뷰의 layout_weight를 사용하여 측정 완료 후 남은 공간을 어떻게 분할할지 정의할 수 있으며 이 속성은 너비(width)에만 적용된다.

남은 공간을 전부 채우도록 하기 위해선 layout_weight 값을 1로 지정하면 된다.

android:layout_weight="1"

 

4. 화면 간 이동 수정

 

현재 화면 간 이동은 탐색 작업을 통해 이루어지고 있다.

하지만 지금은 SlidingLayoutPane을 사용하고 있기 때문에 pane을 열고 닫음으로써 화면 이동이 가능하다.

따라서 화면 이동 코드를 아래와 같이 수정하면 된다.

binding.slidingPaneLayout.openPane()

 

맞춤 뒤로 탐색

 

소형 기기의 경우 세부 항목으로 이동 후 뒤로 버튼을 누르면 목록 화면으로 다시 돌아가야 한다.

하지만 현재는 하나의 화면에서 SlidingLayoutPane을 통해 두 화면을 보여주고 있기 때문에 세부 화면에서 뒤로 버튼을 누르면 아예 앱이 종료되기 때문에 맞춤 뒤로 탐색을 통해 올바르게 작동하도록 수정해야 한다. 

 

* 뒤로 탐색: 사용자가 이전에 방문한 화면 기록을 통해 뒤로 이동

* 맞춤 뒤로 탐색

Android는 사용자가 애플리케이션을 탐색할 때 대상의 백 스택을 유지한다.

일반적으로 뒤로 버튼을 누르면 이전 대상으로 적절하게 이동하지만 상황에 따라 뒤로 탐색을 자체적으로 구현해야할 수도 있다.

 

맞춤 뒤로 탐색을 구현하기 위해서는 뒤로 키 누름을 처리하는 맞춤 콜백을 정의해야 한다.

이 콜백이 일반적으로 사용되는 onBackPressedCallback보다 우선 적용된다.

맞춤 콜백 정의 단계는 크게 4단계로 나뉜다.

 

1) onBackPressedCallback 클래스 확장

 

맞춤 콜백은 onBackPressedCallback 클래스를 확장한다.

onBackPressedCallback의 생성자는 초기 사용 설정 상태를 나타내는 인수값을 받는다.

두 번째 창이 슬라이드 가능하고, 단일 창이 표시되는 경우에 isSlidable이 true, 두 번째 창이 완전히 열리는 경우 isOpen이 true이다.

인수가 모두 true이면 콜백의 isEnabled()가 true를 반환하고, handleOnBackPressed()를 호출해 뒤로 버튼 이벤트를 처리할 수 있다.

class SportsListOnBackPressedCallback(
   private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen){
	override fun handleOnBackPressed() {
        slidingPaneLayout.closePane()
    }
}

 

2) Panel 이벤트 모니터링

 

뒤로 버튼 이벤트를 처리하는 것 외에도 슬라이딩 창과 관련된 이벤트를 수신하고 모니터링 해야한다.

이를 위해 PaneSlideListener를 사용해 처리해야 한다.

PaneSlideListener에는 세 가지 추상 메서드(onPanelSlide(), onPanelOpened(), onPanelClosed())가 있으며

이러한 메서드는 세부 정보창을 슬라이드하거나 여닫을 때 호출된다.

 

3) 리스너 클래스 리스너 목록에 추가

init {
   slidingPaneLayout.addPanelSlideListener(this)
}

 

4) 콜백 등록

 

콜백 작동 모습을 보기 위해 디스패처(OnBackPressedDispatcher)를 사용해 콜백을 등록한다.

아래 코드는 프래그먼트 파일의 onViewCreated() 메서드 내에 추가하면 된다.

val slidingPaneLayout = binding.slidingPaneLayout
requireActivity().onBackPressedDispatcher.addCallback(
    viewLifecycleOwner,
    SportsListOnBackPressedCallback(slidingPaneLayout)
)

 

잠금모드

slidingPaneLayout.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED

반응형 레이아웃

 

반응형 UI는 유연성과 연속성의 원칙을 기반으로 한다.

 

유연성은 사용 가능한 공간을 최적으로 하고 사용 가능한 공간이 변경될 때 조정되는 레이아웃을 의미한다.

연속성은 하나의 창 크기에서 다른 창 크기로 전환하는 동안 원활한 사용자 환경이 유지되는 것을 의미한다.

화면 크기 변경에는 전체 뷰 계층 구조의 삭제와 재생성이 수반될 수 있으므로 현재 데이터를 잃지 않는 것이 중요하다.

 

레이아웃을 결정할 때 실제 하드웨어 값을 사용하면 안된다. 고정된 값은 UI가 사용할 수 있는 공간을 결정하는 데 유용하지 않다.

이와 같은 이유로 앱을 특정 방향이나 가로세로 비율로 제한하면 안된다.

또한 앱이 사용될 기기를 휴대전화 또는 태블릿으로 특정지으면 안된다. 무엇을 태블릿으로 볼 것인지에 대한 기준은 주관적이다.