정상에서 IT를 외치다

[Android, Behavior] CoordinatorLayout 에서 Behavior 사용해 보기 본문

안드로이드

[Android, Behavior] CoordinatorLayout 에서 Behavior 사용해 보기

Black-Jin 2019. 5. 13. 11:28
반응형

안녕하세요. 블랙진입니다.

CoordinatLayout을 사용해 애니메이션을 구현해야 되는 미션을 받아 공부한 내용을 간략히 정리해 보았습니다.

 

개요

 

CoordinatorLayout

 

CoordinatorLayout 은 강력한 FrameLayout 으로 2가지 주요 행동이 있습니다.

 

1. 애플리케이션에서 최상위 장식(decor) 뷰로써 사용

2. 자식 뷰들간의 특정한 인터렉션을 위한 컨테이너로써 사용

 

CoordinatorLayout의 자식뷰에 Behaviors를 지정하는 방식으로 하나의 부모뷰 안에서 다양한 인터렉션을 지원할 수 있고 자식뷰들 간의 인터렉션을 할 수 있습니다. 또한 CoordinatorLayout의 자식뷰는 anchorinsetEdge 속성을 가질 수 있습니다. anchor는 CoordinatorLayout의 자식뷰 중 하나이어야 하며 다른 뷰에 맞물러 상대적인 위치에 뷰를 띄우는 용도로 사용됩니다. insetEdge 속성은 자식뷰들이 어떻게 배치될지 지정할 수 있으며 만약 자식뷰가 겹칠 것을 대비해 dodgeInsetEdges 속성을 주어 적절하게 뷰가 겹치지 않도록 배치할 수 있습니다. 

 

Behavior

 

자식뷰들간의 하나 혹은 그 이상의 인터렉션을 구현하기 위해 사용합니다. 인터렉션에는 드래그, 스와이드, 플링 또는 제스처가 포함됩니다.

 

이번 장에서는 Behavior에 대해서만 알아보고 이를 커스텀 마이징을 해보겠습니다. 그러기에 있어 간단한 예제를 통해  Behavior 사용법을 알아보겠습니다.

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="256dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:contentScrim="?attr/colorPrimaryDark"
app:expandedTitleMarginStart="24dp"
app:expandedTitleMarginEnd="64dp">

...

</android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="24dp">

...

</LinearLayout>
</android.support.v4.widget.NestedScrollView>

우리는 일반적으로 AppBarLayout 하단에 위치하게 될 위젯에 layout_behavior="@string/appber_scrolling_view_behavior" 속성을 추가하여 사용합니다.

@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
public class AppBarLayout extends LinearLayout 

 

AppBarLayout에는 보시는 바와 같이 DefaultBehavior이 선언되어 있습니다. CoordinatorLayout은 자식뷰의 변화 상태를 다른 자식뷰들에게 전달해주는 역활을 합니다. 

 

NestedScrollVIew에 layout_behavio 속성을 추가해 인터렉션을 CoordinatorLayout이 받아 AppBarLayout 에게 알려줍니다. 그렇기 때문에 AppbarLayout 과 NestedScrollView 는 상호작용할 수 있습니다.

 

그러면 Behavior 대한 간단한 예제를 살펴보겠습니다.

 

참고한 예제 사이트

 

xml

<android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent" android:layout_height="match_parent">

    <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_margin="16dp"
            android:layout_gravity="end|bottom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
</android.support.design.widget.CoordinatorLayout>

activity

fab.setOnClickListener {
    Snackbar.make(it, "Hello black jin", Snackbar.LENGTH_LONG).show()
}

간단한 예제 입니다. 플로팅 액션 버튼을 누르면 스낵바가 올라옵니다. 스낵바가 올라오면서 플로팅액션버튼도 함께 올라옵니다.

 

 

 

플로팅액션버튼이 스낵바와 인터렉션 할 수 있는 것은 이미 Behavior 이 구현되어 있기 떄문입니다.

 

@DefaultBehavior(FloatingActionButton.Behavior.class)
public class FloatingActionButton extends VisibilityAwareImageButton

플로팅액션버튼의 클래스를 보면 DefultBehavior이 구현되어 있습니다. 그렇기 때문에 CoordinatorLayout 안에서의 변화를 받아 인터렉션 할 수 있는 것입니다. 만약 아무 기능이 없는 Behavior 을 적용하면 어떻게 될까요?

public class ExampleBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {

    public Example01Behavior() {
    }

    public Example01Behavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 레이아웃에 트리거가 발생하면 불러집니다.
     * true 를 리턴하면 onDependentViewChanged 를 부릅니다.
     */
    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent,
                                   @NonNull FloatingActionButton child,
                                   @NonNull View dependency) {

	//Log.d("MyTag","dependency : " + dependency.getClass());
        return dependency instanceof Snackbar.SnackbarLayout;
    }

    /**
     * dependency 의 View 의 변화가 있을때 이벤트가 들어옵니다.
     */
    @Override
    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent,
                                          @NonNull FloatingActionButton child,
                                          @NonNull View dependency) {

    //아무 기능이 없습니다.

        return false;
    }
}

 

ExampleBehavior 을 생성한 후 적용해 보겠습니다.

<android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_margin="16dp"
        android:layout_gravity="end|bottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_behavior=".ExampleBehavior"/>

layout_behavior에 ExampleBehavior을 적용한 후 실행해 보겠습니다.

 

 

 

보시는 바와 같이 아무 인터렉션도 이뤄 지지 않습니다. 아까와 같은 인터렉션을 할 수 있게 코드를 추가해 보겠습니다.

 

 

스낵바 움직임에 따라 함께 움직이는 프로팅액션버튼

@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent,
                                      @NonNull FloatingActionButton child,
                                      @NonNull View dependency) {
    
    float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
    child.setTranslationY(translationY);

    return false;
}

translationY 함수를 사용해 받은 인터렉션값에 따라 Y 좌표를 이동시켜 줍니다. 이렇게 하면 기존 처럼 스낵바가 올라오면 플로팅액션버튼도 따라 올라가게 됩니다. 여기서 더 나아가 다른 액션을 추가해 보겠습니다.

 

 

스낵바 움직임에 따라 크기가 변하는 프로팅액션버튼

@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent,
                                      @NonNull FloatingActionButton child,
                                      @NonNull View dependency) {
    
    float translationY =  Math.min(0, dependency.getTranslationY() - dependency.getHeight());
    float percentComplete = -translationY / dependency.getHeight();
    float scaleFactor = 1 - percentComplete;

    child.setScaleX(scaleFactor);
    child.setScaleY(scaleFactor);

    return false;
}

받아온 Y 좌표를 변형해 뷰의 스케일을 변화시켜 줬습니다.

 

 

이번에는 스낵바가 올라오면서 플로팅액션버튼이 작아지고 스낵바가 내려가면 플로팅액션버튼이 커지게 됩니다. 이렇게 CoordinatorLayout 안에서 Behavior 을 사용해 뷰간의 인터렉션을 하는 기본 예제를 살펴 보았습니다. 필자는 이 예제를 활용해서 아래와 같은 예제를 만들어 보았습니다.

 

 

 

스크롤 변화를 감지하여 위와 같은 인터렉션이 나오게 구성해 보았습니다. 보시는 바와 같이 Behavior를 사용하면 얼마든지 사용자 반응에 따라 멋진 애니메이션을 구현하실 수 있을 겁니다.

 

위에서 작업한 예제 파일은 깃허브를 통해 공유하겠습니다. 많은 도움이 되셨으면 좋겠습니다 :)

BehaviorSample

 

<참고사이트>

CoordinatorLayout과 Behavior의 관계

AppBarLayout에 대한 고찰

 

 

반응형
Comments