Coordinator layout custom layout behavior never getting called

16,858

Solution 1

The reason why it is not working is that view with Behavior must be a direct child of CoordinatorLayout. In your case, the hierarchy is: CoordinatorLayout -> RelativeLayout -> LinearLayout (with Behavior).

Solution 2

I have a layout such as this. There are a few things located in the upper regions, but the bottom contains only a FAB which is deeply nested.

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/your_coordinator_id">

    <android.support.constraint.ConstraintLayout
        app:layout_behavior="com.yourpackage.YourBehavior">

        <ScrollView>
            ...
        </ScrollView>

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

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

        <!--
            Everything "around" the FAB needs to be moved.
        -->
        <RelativeLayout
            android:id="@+id/your_view_id">

           <com.github.jorgecastilloprz.FABProgressCircle>

                <!--
                   This is the actual FAB.
                -->
                <android.support.design.widget.FloatingActionButton/>
            </com.github.jorgecastilloprz.FABProgressCircle>
        </RelativeLayout>
    </android.support.constraint.ConstraintLayout>
</android.support.design.widget.CoordinatorLayout>

The FAB is deeply nested.

CoordinatorLayout > ConstraintLayout > RelativeLayout > FABProgressCircle > FAB

However the RelativeLayout needs to be pushed up by the CoordinatorLayout when the Snackbar is shown.

The behavior which will do this is as simple as follows.

package com.yourpackage;

...

public class YourBehavior extends CoordinatorLayout.Behavior<ConstraintLayout> {

    public YourBehavior(Context context, AttributeSet attrs) {
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, ConstraintLayout child, View dependency) {
        float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
        // Note that the RelativeLayout gets translated.
        child.findViewById(R.id.your_view_id).setTranslationY(translationY);
        return true;
    }

    @Override
    public void onDependentViewRemoved(CoordinatorLayout parent, ConstraintLayout child, View dependency) {
        child.findViewById(R.id.your_view_id).setTranslationY(0.0f);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, ConstraintLayout child, View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }
}

Show the Snackbar like this.

Snackbar.make(findViewById(R.id.your_coordinator_id), "Message", Snackbar.LENGTH_SHORT).show();

onDependentViewRemoved needs to be overridden, because when manually dismissing a Snackbar the CoordinatorLayout won't trigger moving the translated View (the FloatingActionButton and its RelativeLayout) back to it's original place. Overriding the method we can translate it back to where it was.

Share:
16,858

Related videos on Youtube

cj1098
Author by

cj1098

Professional computer science major SOreadytohelp

Updated on September 15, 2022

Comments

  • cj1098
    cj1098 over 1 year

    Firstly, I'd like to preface this with my lack of knowledge about the coordinator layout. I'm merely following tutorials I found online and am curious why my behavior isn't working.

    Does the child view inside of coordinator layout have to be app bar layout? Or are you able to put any view inside there.

    Also, when I define the res-auto namespace it doesn't give me the option for layout_behavior. Usually android studio will auto-complete if a function is available and it didn't. Although, if I type out layout_behavior it doesn't complain. So maybe it's working...?

    Regardless, I've defined my own custom layout behavior and am trying to apply it but it doesn't seem to be working. Any insight would be greatly appreciated.

    Here is the layout. I'm trying to apply my custom behavior to the first LinearLayout (search_polls_toolbar) and have it scroll up when the vertical recyclerview scrolls up. (Like the toolbar currently does.) I should also mention, this xml is for a fragment in a viewpager. And the hosting activity has a coordinator layout attached to it that does make the toolbar scroll up. (Could it be conflicted because of that?)

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/root"
        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">
    <RelativeLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >
        <LinearLayout
            android:id="@+id/search_polls_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?android:actionBarSize"
            android:background="@color/icitizen_toolbar_orange"
            android:weightSum="1"
          app:layout_behavior="com.example.chrisjohnson.icitizenv2.CustomBehaviors.ToolbarBehavior"
            >
    
            <EditText
                android:id="@+id/search_polls"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:hint="@string/search_polls"
                android:gravity="center_horizontal"
                android:layout_weight=".5"
                android:drawableLeft="@drawable/magnifying_glass"
                android:drawableStart="@drawable/magnifying_glass"
                android:layout_marginTop="5dp"
                android:layout_marginLeft="15dp"
                android:drawablePadding="-50dp"
                android:paddingLeft="5dp"
                android:paddingTop="5dp"
                android:paddingBottom="10dp"
                android:cursorVisible="false"
                android:textSize="20sp"
                android:background="@color/icitizen_light_orange"
                />
    
        </LinearLayout>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/poll_horizontal_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="75dp"
            android:layout_below="@id/search_polls_toolbar"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="5dp"
            android:scrollbars="none"
            >
    
        </android.support.v7.widget.RecyclerView>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/poll_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            android:layout_below="@id/poll_horizontal_recycler_view"
            app:layout_scrollFlags="scroll|enterAlways"
            android:scrollbars="vertical" />
    
    </RelativeLayout>
    
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/polls_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/white_plus_icon"
        android:layout_marginBottom="70dp"
        app:backgroundTint="@color/icitizen_orange"
        app:layout_anchor="@id/container"
        app:layout_anchorGravity="bottom|right|end"
        app:borderWidth="0dp"
        android:layout_marginRight="15dp"
        android:layout_marginEnd="15dp"/>
    

    And here's the custom behavior:

    public class ToolbarBehavior extends CoordinatorLayout.Behavior<Toolbar> {
        public ToolbarBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
            Toast.makeText(context, "AJSJA", Toast.LENGTH_LONG).show();
        }
    
        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, Toolbar child, View dependency) {
            return dependency instanceof RecyclerView;
        }
    
        @Override
        public boolean onDependentViewChanged(CoordinatorLayout parent, Toolbar child, View dependency) {
            child.setTranslationY(child.getY());
            return true;
        }
    }
    

    And here's the hosting activies' layout.

        <?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"
        android:id="@+id/coordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.design.widget.AppBarLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?android:attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways"
                />
        </android.support.design.widget.AppBarLayout>
        <android.support.v4.view.ViewPager
            android:id="@+id/home_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            />
    </android.support.design.widget.CoordinatorLayout>
    

    If anyone would like me to post anymore of my code, please let me know! Thanks :)

  • Konstantin Konopko
    Konstantin Konopko about 8 years
    Sometimes Google makes me cry
  • Ibrahim Disouki
    Ibrahim Disouki over 7 years
    Hi, do you know any workaround to solve this problem ?
  • Blablablabli
    Blablablabli almost 7 years
    Works perfectly, you just saved my day :D
  • Kohányi Róbert
    Kohányi Róbert almost 7 years
    @Blablablabli I've updated my Java code snippet, you might be interested in the change.
  • Blablablabli
    Blablablabli almost 7 years
    Indeed, I did not know this case had to be handled, you're the MVP :)
  • Kohányi Róbert
    Kohányi Róbert almost 7 years
    @Blablablabli Haha, thanks man. One thing bothers me tho'. If the Snackbar is automatically dismissed (that is the user doesn't swipe it away) the "slide down" animation is fluent, smooth and there's no delay. However if I use the "onDependentViewRemoved" fix I mentioned then the "animation" is rigid and there is a delay between the Snackbars removal and the actual start of the FAB's movement. Let me know if you can figure out a fix for the latter. The "rigid" animation, or lack of it can be fixed by applying an animation by using view.animator().
  • rgv
    rgv almost 7 years
    Point very well explained!