관리 메뉴

막내의 막무가내 프로그래밍 & 일상

[안드로이드] 안드로이드 CollapsingToolbarLayout UI 기록 본문

안드로이드/코틀린 & 아키텍처 & Recent

[안드로이드] 안드로이드 CollapsingToolbarLayout UI 기록

막무가내막내 2020. 9. 19. 00:44
728x90

[2021-05-04 업데이트]

 

이전에 기본적인 CollapsingToolbarLayout  에 대해 구현해보고 알아보는 포스팅을 했었습니다. 

여기에 문서를 보며 정리한 내용들이 있으니 먼저 참고해주시면 좋을 것 같습니다. ㅎㅎ

youngest-programming.tistory.com/353

 

[코틀린] 안드로이드 Collapsing Toolbar Layout with Constraint Layout, NestedScroll 구현

Collapsing Toolbar Layout 를 적용하기전에 샘플로 구현해보았습니다. CoordinatorLayout  -AppBarLayout  --CollapsingToolbarLayout ---ImageView, Toolbar -NestedScrollView --ConstraintLayout -FloatingA..

youngest-programming.tistory.com

 

 

이번에도 저번에 했던거와 비슷하지만 조금 더 응용해서 다음과 같은 영상의 UI를 구현해보고 이에 대해 기록하는 포스팅입니다.

 

사용한 프로젝트 링크는 다음과 같습니다. 

github.com/mtjin/NoMoneyTrip

 

mtjin/NoMoneyTrip

[SKT/한국관광공사] 2020 스마트 관광 앱 개발 공모전 '무전여행' 앱. Contribute to mtjin/NoMoneyTrip development by creating an account on GitHub.

github.com

 

리사이클러뷰와 스크롤뷰 CollapsingToolbarLayout 등을 섞어서 사용해 충돌이 좀 일어나서 이상하게 되고 이런식으로 구현하는건 처음이라 삽질을 좀 했었네요.(원하는 샘플도 못찾았고요 ㅠ 그래서 기록합니다youngest-programming.tistory.com/353 에 설명을 좀 달아놓긴 했습니다.)

 

CollapsingToolbarLayout 을 구현할 때 가장 이해하기 쉽게 설명하려면

CoordinatorLayout

   -AppBarLayout

    [AppBarLayout에 접히는 화면인 CollapsingToolbarLayout와 접히지 않을 Toolbar가 들어갈 것]

       --CollapsingToolbarLayout

          [접히는(스크롤에) 모션에 반응하여 나타나거나 사라질 레이아웃]

            ---Toolbar

                [접히는(스크롤에) 모션에도 계속 남아 있을 툴바, 즉 CollapsingToolbarLayout 안에서 Toolbar안에 있는 것은 접혀도 뷰를 보존시킴]

    -NestedScrollView

        --ConstraintLayout [일반적인 레이아웃]

           

구조라고 보면 된다. 속성값은 매번 찾아가면서 필요한걸 사용하면 된다. ㅇㅇ 외울필요 XX

 

내가 구현한 디자인을 동영상으로 첨부해봤다.

https://tv.kakao.com/v/412543814

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

[UI]

첫화면
스크롤 시

[지역페이지 xml]

구현한 XML을 하나하나 뜯어봐서 분석해보도록 하겠습니다.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="vm"
            type="com.mtjin.nomoneytrip.views.localpage.LocalPageViewModel" />
    </data>

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:fitsSystemWindows="true"
            android:theme="@style/toolbarTheme">

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/transparent"
                android:fitsSystemWindows="true"
                app:contentScrim="@color/colorWhiteFDFD"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/iv_local"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/height_224dp"
                    android:scaleType="fitXY"
                    app:layout_constraintTop_toTopOf="parent"
                    tools:src="@drawable/img_product" />

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/tv_local_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="bottom|start"
                    android:layout_marginStart="@dimen/margin_28dp"
                    android:layout_marginBottom="@dimen/margin_20dp"
                    android:textColor="@color/colorWhiteFDFD"
                    tools:text="지역명입니다." />

                <androidx.appcompat.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/height_80dp"
                    android:layout_gravity="top"
                    android:background="@android:color/transparent"
                    android:contentInsetStart="0dp"
                    android:contentInsetLeft="0dp"
                    android:contentInsetEnd="0dp"
                    android:contentInsetRight="0dp"
                    app:contentInsetEnd="0dp"
                    app:contentInsetLeft="0dp"
                    app:contentInsetRight="0dp"
                    app:contentInsetStart="0dp"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

                    <androidx.constraintlayout.widget.ConstraintLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">

                        <androidx.appcompat.widget.AppCompatImageView
                            android:id="@+id/iv_back"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginStart="@dimen/margin_16dp"
                            android:layout_marginTop="@dimen/margin_32dp"
                            android:onClick="@{()->vm.onBackClick()}"
                            app:layout_constraintStart_toStartOf="parent"
                            app:layout_constraintTop_toTopOf="parent"
                            app:srcCompat="@drawable/ic_back_title" />

                        <androidx.appcompat.widget.AppCompatTextView
                            android:id="@+id/tv_local_toolbar_title"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:textColor="@color/colorBlack2D2D"
                            android:textSize="@dimen/text_size_20sp"
                            android:visibility="gone"
                            app:layout_constraintBottom_toBottomOf="@id/iv_back"
                            app:layout_constraintStart_toEndOf="@id/iv_back"
                            app:layout_constraintTop_toTopOf="@id/iv_back"
                            tools:text="지역명입니다" />

                        <androidx.appcompat.widget.AppCompatImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginEnd="@dimen/margin_20dp"
                            android:onClick="@{()->vm.goLodgeSearch()}"
                            app:layout_constraintBottom_toBottomOf="@id/iv_back"
                            app:layout_constraintEnd_toEndOf="parent"
                            app:layout_constraintTop_toTopOf="@id/iv_back"
                            app:srcCompat="@drawable/ic_search_black" />
                    </androidx.constraintlayout.widget.ConstraintLayout>

                </androidx.appcompat.widget.Toolbar>
            </com.google.android.material.appbar.CollapsingToolbarLayout>

        </com.google.android.material.appbar.AppBarLayout>

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            android:fillViewport="true"
            android:paddingBottom="@dimen/padding_36dp"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true">

                <TextView
                    android:id="@+id/tv_lodgment"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="@dimen/margin_28dp"
                    android:layout_marginTop="@dimen/margin_28dp"
                    android:text="@string/nomoney_tour_local_text"
                    android:textColor="@color/colorBlack2D2D"
                    android:textSize="@dimen/text_size_18sp"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv_products"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_16dp"
                    android:clipToPadding="false"
                    android:nestedScrollingEnabled="false"
                    android:orientation="horizontal"
                    android:paddingStart="@dimen/margin_28dp"
                    android:paddingEnd="0dp"
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                    app:layout_constraintTop_toBottomOf="@id/tv_lodgment"
                    bind:setItems="@{vm.productList}"
                    tools:listitem="@layout/item_local_product" />

                <TextView
                    android:id="@+id/tv_tour_introduce"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_32dp"
                    android:text="@string/healing_tour_local_text"
                    android:textColor="@color/colorBlack2D2D"
                    android:textSize="@dimen/text_size_18sp"
                    app:layout_constraintStart_toStartOf="@id/tv_lodgment"
                    app:layout_constraintTop_toBottomOf="@id/rv_products" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv_tours"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_18dp"
                    android:nestedScrollingEnabled="false"
                    android:orientation="horizontal"
                    android:paddingStart="@dimen/padding_28dp"
                    android:paddingEnd="0dp"
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/tv_tour_introduce"
                    bind:setItems="@{vm.tourIntroduceList}"
                    tools:listitem="@layout/item_tour_introduce" />

                <TextView
                    android:id="@+id/tv_restaurant_introduce"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_32dp"
                    android:text="@string/local_restaurant_text"
                    android:textColor="@color/colorBlack2D2D"
                    android:textSize="@dimen/text_size_18sp"
                    app:layout_constraintStart_toStartOf="@id/tv_lodgment"
                    app:layout_constraintTop_toBottomOf="@id/rv_tours" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv_restaurants"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_16dp"
                    android:nestedScrollingEnabled="false"
                    android:orientation="horizontal"
                    android:paddingStart="@dimen/margin_28dp"
                    android:paddingEnd="0dp"
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                    app:layout_constraintTop_toBottomOf="@id/tv_restaurant_introduce"
                    bind:setItems="@{vm.restaurantIntroduceList}"
                    tools:listitem="@layout/item_tour_introduce" />

                <TextView
                    android:id="@+id/tv_local_popular_tour"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_36dp"
                    android:text="@string/local_popular_review_text"
                    android:textColor="@color/colorBlack2D2D"
                    android:textSize="@dimen/text_size_18sp"
                    android:textStyle="bold"
                    app:layout_constraintStart_toStartOf="@id/tv_lodgment"
                    app:layout_constraintTop_toBottomOf="@id/rv_restaurants" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv_reviews"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_22dp"
                    android:nestedScrollingEnabled="false"
                    android:orientation="vertical"
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                    app:layout_constraintTop_toBottomOf="@id/tv_local_popular_tour"
                    bind:setItems="@{vm.userReviewList}"
                    tools:itemCount="3"
                    tools:listitem="@layout/item_user_review" />

                <androidx.appcompat.widget.AppCompatImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_16dp"
                    android:layout_marginBottom="@dimen/margin_36dp"
                    android:onClick="@{()->vm.requestReviews()}"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/rv_reviews"
                    app:srcCompat="@drawable/every_view" />
            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

 

먼저 AppbarLayout에 대해 살펴보자

<com.google.android.material.appbar.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:fitsSystemWindows="true"
            android:theme="@style/toolbarTheme">

별 특이점은 없다. android:fitsSystemWindows="true" 는  뷰가 차지할 수 있는 영역을 상태바 및 소프트키 영역을 제외한 영역까지 확장해주는 역할을 한다.

AppBarLayout 은 Theme 를  설정하여 툴바의 타이틀 색상 등을 바꿔줄 수 있는데 글자크기와 색상정도만 설정해줬습니다.

 

 

 

다음은 CollapsingToolbarLayout 이다.

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/transparent"
                android:fitsSystemWindows="true"
                app:contentScrim="@color/colorWhiteFDFD"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

여기서는 app:contentScrim특정 기준에 해당하는 만큼 레이아웃의 윗단까지 스크롤이 되면 이 특성이 활성화되어 CollapsingToolbarLayout 에 속성값(drawable)을 적용시켜줍니다.

app:layout_scrollFlags 는  NestedScrollView에서 스크롤 상태라고 CollapsingToolbarLayout 에 알려주고 이거에 반응하여 레이아웃이 나타나고 사라지게 됩니다.

저는 여기서 scroll|exitUntillCollapsed를 사용하였는데요!

scroll | enterAlwaysCollapsed 속성은 아래로 스크롤하는 동안 CollapsingToolbar가 확장되는 시간에 대해 lazy한 접근 방식을 사용합니다. 아래로 스크롤되는 순간, CollapsingToolbar는 NestedScrollView가 콘텐의 맨 위로 스크롤 된 후에만 ​​확장됩니다. 즉 만약 엄청 긴 스크롤이 있고 맨아래까지 스크롤한 후 스크롤다운할 때 툴바위치(꼭대기겠죠) 까지 스크롤을 해야 그떄서야 툴바가 펴지기 시작한다는 뜻입니다.

 

다음 링크에서 정리했던게 있으니 참고하도록 합니다.

https://youngest-programming.tistory.com/353

 

[안드로이드] 안드로이드 Collapsing Toolbar Layout with Constraint Layout, NestedScroll 구현

[2021-04-14 업데이트] Collapsing Toolbar Layout 를 적용하기전에 샘플로 구현해보았습니다. CoordinatorLayout  -AppBarLayout  [이 레이아웃의 바로 밑 계층은 일반적인 레이아웃이랑 비슷하다. Collapsin..

youngest-programming.tistory.com

 

 

ToolbarLayout 에 대해 보겠습니다.

                <androidx.appcompat.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/height_80dp"
                    android:layout_gravity="top"
                    android:background="@android:color/transparent"
                    android:contentInsetStart="0dp"
                    android:contentInsetLeft="0dp"
                    android:contentInsetEnd="0dp"
                    android:contentInsetRight="0dp"
                    app:contentInsetEnd="0dp"
                    app:contentInsetLeft="0dp"
                    app:contentInsetRight="0dp"
                    app:contentInsetStart="0dp"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

큰 특이점은 없고 app:layout_collapseMode 에 대해서만 보면 될 것 같습니다. 이 속성값을 pin으로 할 경우, 스크롤을 할때 Toolbar가 최상단까지 위로 올라가면서 가장 최상단에 올라간 경우 고정 시켜주는 역할을 합니다. 

 

 

 

마지막으로 AppbarLayout 밖의 계층인 NestedScrollView 쪽을 봐보겠습니다.

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            android:fillViewport="true"
            android:paddingBottom="@dimen/padding_36dp"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true">

app:layout_behavior="@string/appbar_scrolling_view_behavior" 속성을 설정함으로써 NestedScrollView의 반응에 따라 AppBarLayout이 반응됩니다.

가장 상위 레이아웃 계층인 CoordinatorLayout자식뷰의 스크롤의 변화 상태를 다른 자식뷰들에게 전달 해주는 역할을 합니다. 좀더 쉽게 말해 NestedScrollView RecyclerView 등에 스크롤의 상태를 판단하여 정의된 반응을 하기위한 View Behavior를 등록하면 됩니다.

저는 여기서 app:layout_behavior="@string/appbar_scrolling_view_behavior" 속성을 적용시켰고 CoordinatorLayout는 NestedScrollView가 스크롤시 layout_behavior에 정의된 레이아웃으로 스크롤 정보를 전달 하는 역할을 하고, 그럼 AppBarLayout의 ScrollingViewBehavior가 정보를 받아서 AppBarLayout 자신을 변형하도록 하는 구조입니다.

(참고 : https://blog.kmshack.kr/CoordinatorLayout%EA%B3%BC-Behavior%EC%9D%98-%EA%B4%80%EA%B3%84/)

 

ConstraintLaout에서 animateLayoutChanges적용시켜줄 View의 부모 레이아웃에 넣어주면 알아서 animation 효과를 넣어줍니다다. 예를 들어, view를 감추고 보여줄때 자연스럽게 위아래로 스르륵한 효과를 넣고 싶을때 사용하면 유용합니다. ㅎㅎ

 

 

 

 

 

[지역페이지 Fragment]

CollapsingToolbarLayout 상태에 따라 뷰 변경

private fun initView() {
        binding.appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
            if (abs(verticalOffset) - appBarLayout.totalScrollRange == 0) { // 접혔을때
                binding.tvLocalToolbarTitle.visibility = View.VISIBLE
            } else {// 펴졌을때
                binding.tvLocalToolbarTitle.visibility = View.GONE
            }
        })
    }

 

 

 

[UI]

처음 화면

 

스크롤 시

 

 

[홈 페이지 xml]

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="vm"
            type="com.mtjin.nomoneytrip.views.home.HomeViewModel" />
    </data>

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:fitsSystemWindows="true"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:elevation="0dp">

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/transparent"
                android:fitsSystemWindows="true"
                app:contentScrim="@color/colorWhiteFDFD"
                app:expandedTitleTextAppearance="@android:color/transparent"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_108dp"
                    android:paddingBottom="@dimen/padding_8dp"
                    app:layout_collapseMode="parallax">

                    <TextView
                        android:id="@+id/tv_where_go"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="@dimen/margin_28dp"
                        android:lineSpacingExtra="@dimen/line_spacing_extra_size_10sp"
                        android:text="@string/where_to_go_no_money_trip_question_text"
                        android:textColor="@color/colorBlack2D2D"
                        android:textSize="@dimen/text_size_22sp"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                    <androidx.appcompat.widget.AppCompatImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginEnd="@dimen/margin_26dp"
                        app:layout_constraintBottom_toBottomOf="@id/tv_where_go"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintTop_toTopOf="@id/tv_where_go"
                        app:srcCompat="@drawable/img_main" />


                    <androidx.appcompat.widget.AppCompatImageView
                        android:id="@+id/iv_search"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="@dimen/margin_28dp"
                        android:layout_marginTop="@dimen/margin_14dp"
                        android:layout_marginEnd="@dimen/margin_28dp"
                        android:background="@drawable/button_search"
                        android:drawableEnd="@drawable/ic_search_gray_24dp"
                        android:onClick="@{()->vm.goSearch()}"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toBottomOf="@id/tv_where_go" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.appcompat.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/height_80dp"
                    android:background="@android:color/transparent"
                    android:contentInsetStart="0dp"
                    android:contentInsetLeft="0dp"
                    android:contentInsetEnd="0dp"
                    android:contentInsetRight="0dp"
                    app:contentInsetEnd="0dp"
                    app:contentInsetLeft="0dp"
                    app:contentInsetRight="0dp"
                    app:contentInsetStart="0dp"
                    app:layout_collapseMode="pin"
                    app:layout_constraintTop_toTopOf="parent"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

                    <androidx.constraintlayout.widget.ConstraintLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">

                        <androidx.appcompat.widget.AppCompatImageView
                            android:id="@+id/iv_app_logo2"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginStart="@dimen/margin_20dp"
                            android:layout_marginTop="@dimen/margin_28dp"
                            app:layout_constraintStart_toStartOf="parent"
                            app:layout_constraintTop_toTopOf="parent"
                            app:srcCompat="@drawable/logo" />

                        <androidx.appcompat.widget.AppCompatImageView
                            android:id="@+id/iv_alarm"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginEnd="@dimen/margin_24dp"
                            android:onClick="@{()->vm.goAlarm()}"
                            android:src="@drawable/ic_alarm_off"
                            app:layout_constraintBottom_toBottomOf="@id/iv_app_logo2"
                            app:layout_constraintEnd_toEndOf="parent"
                            app:layout_constraintTop_toTopOf="@id/iv_app_logo2" />

                        <androidx.appcompat.widget.AppCompatImageView
                            android:id="@+id/iv_toolbar_search"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginEnd="@dimen/margin_8dp"
                            android:visibility="gone"
                            app:layout_constraintBottom_toBottomOf="@id/iv_alarm"
                            app:layout_constraintEnd_toStartOf="@id/iv_alarm"
                            app:layout_constraintTop_toTopOf="@id/iv_alarm"
                            app:srcCompat="@drawable/ic_search_black" />
                    </androidx.constraintlayout.widget.ConstraintLayout>
                </androidx.appcompat.widget.Toolbar>
            </com.google.android.material.appbar.CollapsingToolbarLayout>

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv_hash_tags"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margin_20dp"
                android:orientation="horizontal"
                android:paddingStart="@dimen/padding_28dp"
                android:paddingEnd="0dp"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                app:layout_constraintTop_toTopOf="parent"
                bind:setItems="@{vm.hashTagList}"
                tools:listitem="@layout/item_hash_tag" />
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            android:fillViewport="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true">

                <View
                    android:id="@+id/view_below_hashtag"
                    android:layout_width="match_parent"
                    android:layout_height="1dp"
                    android:layout_marginTop="@dimen/margin_12dp"
                    android:layout_marginBottom="@dimen/margin_12dp"
                    android:background="@color/colorWhiteGrayF4F4"
                    app:layout_constraintBottom_toTopOf="@id/rv_home_products"
                    app:layout_constraintTop_toTopOf="parent" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv_home_products"
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:clipToPadding="false"
                    android:orientation="vertical"
                    android:paddingStart="@dimen/padding_28dp"
                    android:paddingTop="@dimen/padding_12dp"
                    android:paddingEnd="@dimen/padding_28dp"
                    android:paddingBottom="@dimen/padding_36dp"
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/view_below_hashtag"
                    bind:setItems="@{vm.productList}"
                    tools:itemCount="2"
                    tools:listitem="@layout/item_product" />
            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

 

 

[홈 페이지 Fragment]

private fun initView() {
        viewModel.requestProducts()
        binding.appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
            if (abs(verticalOffset) - appBarLayout.totalScrollRange == 0) { // 접혔을때
                binding.ivToolbarSearch.visibility = View.VISIBLE
            } else {// 펴졌을때
                binding.ivToolbarSearch.visibility = View.GONE
            }
        })
    }

 

 

만약 영상과 천천히 CollapsingToolbarLayout 가 사라지는게 아닌 더 빨리 사라지게 하고 싶으면 다음과 같이 애니메이션 시간초를 설정해주면 됩니다.

 app:scrimAnimationDuration="10" (ms)

 <com.google.android.material.appbar.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/transparent"
                android:fitsSystemWindows="true"
                app:contentScrim="@color/colorWhiteFDFD"
                app:expandedTitleTextAppearance="@android:color/transparent"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:scrimAnimationDuration="10">

 

 

[P.S]

app:expandedTitleMarginStart
app:expandedTitleGravity
app:expandedTitleMarginBottom
app:expandedTitleTextAppearance
app:collapsedTitleTextAppearance
app:collapsedTitleGravity
등의 속성을 이용해서 툴바의 텍스트를 펼침, 접힘 상태의 모양을 구현해줄 수 있습니다.
Appearance에는 style로 모듈화해서 세팅하면됩니다. (Ex. testSize, testColor, fontFamily등)
그리고 뭐든 마찬가지지만 자주쓸 가능성이 있는 Collapsing Toolbar 는
AppBarLayout~CollapsingToolbarLayout~Toolbar로 모듈화해서
사용할 XML에 최상단 부모레이아웃을 CoordinatorLayout 로 한후 모듈화 한거를 <include> 해서 사용하면 좋습니다 :)

 

 

 

 

댓글과 공감은 큰 힘이 됩니다. 감사합니다 .!!!

 

 

 

[추가로 구현한 화면]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

 

 

 

.

728x90
Comments