안드로이드/정리(Android)

Android - MotionLayout splash 앱 만들어보기

김염인 2022. 3. 11. 22:13


이번 실습은 MotionLayout과 CoordinateLayout을 적용시켜 보았다.

 

1) 귀여운 케릭터를 이용하여 Splash화면을 구현해 보았다.

Splash화면은 MotionLayout을 통해 케릭터가 역동적으로 움직 일 수 있게 해주 었고

 

2) HomeFragment의 상단 앱바는 CoordinateLayout을 통해 스크롤 시 사라지게 해주었다.

 


1) Splash 화면 구현

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.motionlayout_practice" >
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MotionLayout_practice" >
        <activity
            android:name=".SplashScreen"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity"></activity>
    </application>

</manifest>

먼저 Manifast에 intent-filter를 splashActivity로 바꾸어 준다. 그러면 앱이 시작하면 SplashActivity가 실행이 된다.

 

그리고 splashActivity가 끝이나면 어떻게 MainActivity로 넘어 갈 수 있게 구현해 주어야 한다. 여기서 필요한건 Handler()이다.

package com.example.motionlayout_practice

import android.content.Intent
import android.os.Bundle
import android.os.Handler
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.postDelayed
import com.example.motionlayout_practice.databinding.SplashScreenBinding

class SplashScreen : AppCompatActivity() {
    private val splashDuration = 2500L

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.splash_screen)

        // Handler()를 통해서 UI 쓰레드를 컨트롤 한다.
        // Handler().postDelayed(딜레이 시간){딜레이 이후 동작}
        //      postDelayed()를 통해 일정 시간(딜레이 시간)동안 쓰레드 작업을 멈춘다.
        //      {딜레이 이후 동작}을 통해 딜레이 시간 이후, 동작을 정의해준다.
        Handler().postDelayed(splashDuration) {
            val intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
            finish()
        }

    }
}

        1)  Handler()를 통해서 UI 쓰레드를 컨트롤 한다.
        2) Handler().postDelayed(딜레이 시간){딜레이 이후 동작}
        3) postDelayed()를 통해 일정 시간(딜레이 시간)동안 쓰레드 작업을 멈춘다.
        4) {딜레이 이후 동작}을 통해 딜레이 시간 이후, 동작을 정의해준다.

 

1-2) MotionLayout설정

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/motionLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/splash_screen_scene">


    <ImageView
        android:id="@+id/imgView"
        android:layout_width="250dp"
        android:layout_height="300dp"
        android:contentDescription="TODO"
        android:src="@drawable/cute"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="ImageContrastCheck" />


</androidx.constraintlayout.motion.widget.MotionLayout>

Splash_screent.Xml

MotionLayout을 쉽게 적용하는 방법은 아래와 같다.

우측상단 Design을 눌러 MotionLayout을 설정할 start와 end가운데에 위치한 화살표를 눌러 원하는 이벤트 처리를 해준다.

위와같이 KeyAttribute를 생성해주면 여러 효과를 넣어줄 수 있다.

 

여기서 Position은 0부터 100까지 중 하나의 정수이고 50이면 50%의 지점시 scale또는 X,Y의 위치값을 변경시켜준다. 라는 의미로 생각할 수 있다. 여러 Atrribute를 설정하여 넣어줄 수 있다.

 

나는 케릭터가 작아졌다가 커지는 효과를 만들기 위해 Position 25 50 100 세가지에 Attribute ScaleX, scaleY를 각각 0.7 / 1.2 / 5.0 이런식으로 설정해주었다. 1 일경우는 원래 케릭터 이미지의 크기이다

이렇게 설정해주면 res-xml에 파일이 하나 생성이 된다.

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

    <Transition
        app:constraintSetEnd="@id/end"
        app:constraintSetStart="@+id/start"
        app:autoTransition="animateToEnd"
        app:duration="2500">
        <KeyFrameSet >
            <KeyAttribute
                app:motionTarget="@+id/imgView"
                app:framePosition="20"
                android:scaleX="0.7"
                android:scaleY="0.7" />
            <KeyAttribute
                app:motionTarget="@+id/imgView"
                app:framePosition="50"
                android:scaleX="1.2"
                android:scaleY="1.2" />
            <KeyAttribute
                app:motionTarget="@+id/imgView"
                app:framePosition="100"
                android:scaleX="3"
                android:scaleY="3" />
        </KeyFrameSet>
    </Transition>


    <ConstraintSet android:id="@+id/start">
        <Constraint android:id="@+id/imgView" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint android:id="@+id/imgView" />
    </ConstraintSet>

</MotionScene>

이렇게 간단하게 MotionLayout을 만들어줄 수 있다.

 


2. Coordinatorlayout

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".home_fragment">


        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBar"
            android:fitsSystemWindows="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_green_dark">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolBar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_scrollFlags="scroll|enterAlways">

                <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    >

                    <View
                        android:id="@+id/toolbarBackgroundView"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:background="@android:color/holo_green_dark"
                        />

                    <androidx.constraintlayout.widget.ConstraintLayout
                        android:id="@+id/toolbarContainer"
                        android:layout_width="match_parent"
                        android:layout_height="?attr/actionBarSize"
                        >

                        <TextView
                            android:id="@+id/logoTextView"
                            android:layout_width="120dp"
                            android:layout_height="wrap_content"
                            android:gravity="center"
                            android:scaleType="centerCrop"
                            android:text="Photos"
                            android:textColor="@android:color/holo_orange_dark"
                            android:textSize="24sp"
                            android:textStyle="bold"
                            app:layout_constraintBottom_toBottomOf="parent"
                            app:layout_constraintEnd_toEndOf="parent"
                            app:layout_constraintStart_toStartOf="parent"
                            app:layout_constraintTop_toTopOf="parent" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginEnd="16dp"
                            android:text="로그인"
                            android:textColor="@android:color/white"
                            app:layout_constraintBottom_toBottomOf="parent"
                            app:layout_constraintEnd_toEndOf="parent"
                            app:layout_constraintTop_toTopOf="parent" />

                    </androidx.constraintlayout.widget.ConstraintLayout>

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


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/itemRecyclerView"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="50dp"
        />


</androidx.coordinatorlayout.widget.CoordinatorLayout>

Appbar가 스크롤시 사라지게 되는데 위와같이 간단하게 만들어 줄 수 있다.

ㅁ coordinatorlayout

    ㄴ> appbarLayout

         ㄴ> toolbar

                ㄴ> FrameLayout

가장 중요한 것은

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_scrollFlags="scroll|enterAlways">

toolbar에 scroolFlag부분이다.

 

다음과 같은 종류가 있다

 scroll  스크롤 이벤트에 반응할 모든 view에 반드시 이 플래그를 설정해야 합니다. 그렇지 않으면 화면상단에 고정되어 있게 됩니다.
 enterAlways  아래 방향으로 스크롤 할때마다 이 보기가 표시됩니다. ('quitk return')
 exitUntilCollapsed  해당뷰에 minHeight를 정의하고 있으면, Toolbar가 해당 크기 까지만 축소가 됩니다.
 (가장 많이 사용됩니다.)
 enterAlwaysCollapsed  해당뷰에 minHeight속성의 크기로 시작해 맨위로 스크롤이 될때만 전체 높이로 확장하게 됩니다.

 

다음은 이미지뷰를 스크롤하여 상단까지 올리는 경우를 보았는데 공부하기 좋은 경우라 같이 실습해보았다.


<com.google.android.material.appbar.CollapsingToolbarLayout

android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="100dp"
app:contentScrim="@android:color/transparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">

그리고 CollapsingToolbarLayout에 app:contentScrim 속성을 사용하여 최소화 되었을때 툴바의 색상을 변경 할 수 있다.

 

그 다음으로 살펴봐야하는 속성은 app:layout_collapseMode 속성이다.

이 속성은 Toolbar 안에 설정하며, 스크롤이 발생했을 때 Toolbar의 최종 형태를 결정하는 역할을 한다.

이미지를 사용했을 경우, ImageView에도 속성을 넣어주어야 한다.

 pin  CollapsingToolbarLayout이 완전히 축소되면 툴바는 화면위에 고정된다.
 parallax 툴바가 축소되는 동안 Parallax모드로 동작하도록 한다.
옵션으로 layout_collapseParallaxMultipler 속성을 사용하면 transition의 translation Multiplier를 설정할 수 있다. (예 : app:layout_collapseParallaxMultiplier="0.7")
<ImageView
android:id="@+id/ivToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@drawable/test"
app:layout_collapseMode="parallax"/>

<androidx.appcompat.widget.Toolbar

android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay" />

 

이렇게 하면 스크롤이 발생하며 본문의 내용을 위로 올렸을 때 툴바가 작아지면서 상단에 고정된다.

 

CollapsingToolbarLayout에서 app:contentScrim="@android:color/transparent" 로 지정하였으므로, 툴바가 닫혔을 때 투명색으로 변해 이미지뷰가 툴바에 보이게 된다. (이렇게하지 않으면 기본 툴바 색으로 나온다.)

위 스샷의 왼쪽은 스크롤 안한상태, 아래는 모두 한 상태이다.



출처: https://one-delay.tistory.com/68