original source : https://ranyalbegwein.silvrback.com/transforming-image-behavior

참고자료 ) https://www.androidauthority.com/using-coordinatorlayout-android-apps-703720/

coordinatorlayout을 이용해서 구현가능한 작업 예시

1. Intro

A CoordinatorLayout is this thing:

public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent {
    ... 
}

from the support library ( compile 'com.android.support:design:25.3.1' ), which seems at first like an innocent old FrameLayout, until you discover its real powers – Behaviors!

CoordinatorLayout는 기본적으로 FrameLayout이다. 

A Behavior is an object attached to a child of a CoordinatorLayout, which defines its interaction with another View, ( a dependency) within the same layout.

CoordinatorLayout 아래에 있는 한 element가 다른 View(dependency라고 불리는) 의 변화에 따라 어떻게 변화할것인지를  정의하고 있는 object가 Behavior 이다. 상태변화가 관찰되어지는 View를 dependency라고 한다. 

So, a CoordinatorLayout monitors every movement of its children, and notifies the ones with attached Behaviors for a dependency change.

Something like this:

The SnackBar here is called a dependency – which makes sense, because the child depends on its movement in order to behave in a certain way specified by its attached Behavior.

Following Material Design guidelines, this behavior is the correct interaction between a SnackBar and a FloatingActionButton.

2. Let’s get started!

Everything should run in a context or under the supervision of a CoordinatorLayout, so let’s create a fresh Activity with a CoordinatorLayout as its content-view root element.

Create a new project, and add a new Activity by selecting the “Basic Activity” template:

Take a look at activity_main.xml, it should contain the following:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.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"
    tools:context="com.rany.albeg.wein.behaviors.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

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

    <include layout="@layout/content_main"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_email" />

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

Since the default behavior of FloatingActionButton is to run away from the SnackBar like we wish to implement ourselves, take it off of the XML tree and insert something like an AppCompatButton:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout>

     ...

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/bt_click_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:text="Click me"/>

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

Edit MainActivity accordingly, and modify onCreate():

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        AppCompatButton btn = (AppCompatButton) findViewById(R.id.bt_click_me);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Yeah buddy!", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

3. Implementation

To create a Behavior we’ll need to create a class that extends CoordinatorLayout.Behavior<V>where V is a type of a class which extends View and represents the type of the child – in our case it’ll be AppCompatButton:

public class CustomMoveUpBehavior extends 
                                  CoordinatorLayout.Behavior<AppCompatButton> {
    ...
}

We’re almost done! To complete programming our custom behavior and define the unique interaction between a child and a dependency, we’ll need to:

  • Override layoutDependsOn(...)

This method gets called ( at least once in response to a layout request ) by the parent CoordinatorLayout to determine whether the supplied child view has another specific sibling view as a layout dependency.
In other words, CoordinatorLayout sees the SnackBar and asks us :

“Hey, is this AppCompatButton depends on this SnackBar ?” and we should answer “Yes!” in order to further react in accordance to SnackBar’s movement:

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent,
                                   AppCompatButton child,
                                   View dependency) {

        return dependency instanceof Snackbar.SnackbarLayout;
    }

위의 코드를 설명하면 CoordinatorLayout내의 element 상태가 변화하면 CoordinatorLayout는 layoutDependsOn()를 통하여 View obj인 dependency가 Snackbar.SnackbarLayout instance인지 확인한다. 맞으면 true를 return한다. CoordinatorLayout는 자신의 모든 child element를 이 함수layoutDependsOn()에 넣어 해당 View가 dependency가 맞는지 아닌지 확인한다. 사실 layoutDependsOn()는 상태 변화에도 호출되지만 최초로 화면이 구성될때도 호출된다.

  • Override onDependentViewChanged(...)

This method gets called by the parent CoordinatorLayout after the relevant layoutDependsOn(...)returns true, in which we need to actually move stuff!

In our case, for the button to run away from the SnackBar, we’ll want to translate its Y position to be the difference between SnackBar’s current Y translation and its height!

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent,
                                          AppCompatButton child,
                                          View dependency) {

        float tY = dependency.getTranslationY() - dependency.getHeight();
        child.setTranslationY(tY);
        return true;
    }

위위의 코드 layoutDependsOn 의 과정을 거쳐 dependency의 변화가 관측되었다면 onDependentViewChanged()에 전달된 상태변화가 감지된 dependency와 그에 따라 조정이 필요한 child view를 함수내에서 조정한다.  

We return true to say: “Hey CoordinatorLayout! Our Behavior changed AppCompatButton’s position”

CustomMoveUpBehavior is done and ready to be attached!

There are several ways to attach a Behavior to View, and I’ll choose to go with the common way – via XML.

In order to attach CustomMoveUpBehavior to AppCompatButton via XML, add the following constructor first:

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

and now jump into activity_main.xml and just add the following attribute to AppCompatButton’s XML tag:

    <android.support.v7.widget.AppCompatButton
    ...
        app:layout_behavior="your.package.name.CustomMoveUpBehavior"
    ...
    />

replacing your.package.name as necessary.

Comments are closed.

Post Navigation