Search code examples
androidandroid-support-libraryandroid-dialogfragmentandroid-keypadandroid-coordinatorlayout

CoordinatorLayout with CollapsingToolbarLayout breaks with Keyboard in Dialog fragment


I recently decided to move my app to using the new Support design library, and recently discovered a very nasty bug.

Say I have a CoordinatorLayout hosting an AppBarLayout and any scrollable view, be it a ViewPager, NestedScrollView, or even a RecyclerView with the required scroll behavior; choosing to display a dialog fragment that displays the keyboard causes the AppBarLayout to disconnect from the scrolling view and they don't scroll together anymore.

Furthermore, The scrolling view is forced to resize to half the screen at the bottom, and the AppBar layout takes the top half, despite it not needing it.

A video of the bug is here: Youtube Link

EDIT:

Setting the the softInputMode of the Keyboard in the Activity to "adjustPan" fixes the issue. Apparently the CoordinatorLayout does not like being resized dynamically by the keyboard.

The offending XML looks like this, but I have multiple XML variations that exhibit this behavior, with the common element among them all being they use a CollapsingToolbarLayout:

<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:orientation="vertical">

<android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:background="@color/transparent">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_width="match_parent"
        android:layout_height="144dp"
        android:elevation="4dp"
        app:contentScrim="?attr/colorPrimary"
        app:expandedTitleMarginBottom="@dimen/quadruple_margin"
        app:layout_collapseParallaxMultiplier="0.7"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:animateLayoutChanges="true"
            android:background="@color/primary"
            android:orientation="vertical">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="bottom"
                android:adjustViewBounds="true"
                android:scaleType="centerCrop"
                android:src="@drawable/ranking_background" />

            <View
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignParentTop="true"
                android:layout_gravity="bottom"
                android:background="@color/black_40" />

            <de.hdodenhof.circleimageview.CircleImageView
                android:id="@+id/profile_picture"
                android:layout_width="@dimen/double_margin"
                android:layout_height="@dimen/double_margin"
                android:layout_alignBottom="@+id/text"
                android:layout_marginLeft="@dimen/double_margin"
                android:layout_marginStart="@dimen/double_margin"
                android:src="@drawable/image_placeholder"
                android:visibility="gone" />

            <TextView
                android:id="@+id/text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_alignWithParentIfMissing="true"
                android:layout_marginLeft="@dimen/single_margin"
                android:layout_marginStart="@dimen/single_margin"
                android:layout_marginTop="@dimen/quadruple_margin"
                android:layout_toRightOf="@+id/profile_picture"
                android:text="@string/my_places_for"
                android:textColor="@color/white"
                android:textSize="20sp"
                android:visibility="gone" />

            <TextView
                android:id="@+id/sub_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/text"
                android:layout_marginLeft="@dimen/single_margin"
                android:layout_marginStart="@dimen/single_margin"
                android:layout_marginTop="@dimen/single_margin"
                android:text="@string/pick_category_or_business"
                android:textColor="@color/white"
                android:textSize="16sp"
                android:visibility="gone" />
        </RelativeLayout>

        <android.support.v7.widget.Toolbar
            android:id="@+id/action_bar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/abc_action_bar_default_height_material"
            app:contentInsetLeft="@dimen/triple_margin"
            app:contentInsetStart="@dimen/triple_margin"
            app:layout_collapseMode="pin"
            app:popupTheme="@style/Theme.AppCompat.NoActionBar"
            app:theme="@style/Theme.AppCompat.NoActionBar" />
    </android.support.design.widget.CollapsingToolbarLayout>

    <android.support.design.widget.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/primary"
        app:layout_scrollFlags="scroll">

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:layout_alignParentBottom="true"
            android:background="@color/primary"
            android:elevation="4dp"
            app:layout_scrollFlags="enterAlways"
            app:tabGravity="fill"
            app:tabMode="fixed"
            app:tabSelectedTextColor="@color/white"
            app:tabTextColor="@color/grey_400" />
    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>


Solution

  • The answer provided by Tunji_D will solve the problem, but not necessarily the underlying issue.

    The scroll behavior on the coordinator layout has an issue right now during its invalidation step on its child views: https://code.google.com/p/android/issues/detail?id=176373#c5

    So in order to fix it use this workaround provided by junya, and set your view's behavior with

    app:layout_behavior="com.app.common.PatchedScrollingViewBehavior"
    

    and use:

    public class PatchedScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {
    
        public PatchedScrollingViewBehavior() {
            super();
        }
    
        public PatchedScrollingViewBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
            if(child.getLayoutParams().height == -1) {
                List dependencies = parent.getDependencies(child);
                if(dependencies.isEmpty()) {
                    return false;
                }
    
                AppBarLayout appBar = findFirstAppBarLayout(dependencies);
                if(appBar != null && ViewCompat.isLaidOut(appBar)) {
                    if(ViewCompat.getFitsSystemWindows(appBar)) {
                        ViewCompat.setFitsSystemWindows(child, true);
                    }
    
                    int scrollRange = appBar.getTotalScrollRange();
    //                int height = parent.getHeight() - appBar.getMeasuredHeight() + scrollRange;
                    int parentHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
                    int height = parentHeight - appBar.getMeasuredHeight() + scrollRange;
                    int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
                    parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
                    return true;
                }
            }
    
            return false;
        }
    
    
        private static AppBarLayout findFirstAppBarLayout(List<View> views) {
            int i = 0;
    
            for(int z = views.size(); i < z; ++i) {
                View view = (View)views.get(i);
                if(view instanceof AppBarLayout) {
                    return (AppBarLayout)view;
                }
            }
    
            return null;
        }
    }