简体   繁体   中英

ConstraintLayout wrapped in ScrollView is still resized when keyboard is opened

I'd like to be able to open the keyboard without my layout resizing. I don't want to use adjustNothing , because I need one of my bottom views to raise up with the keyboard. I'd also prefer not to use adjustPan , because that raises the entirety of the layout off of the screen which is kind of ugly.

So, I wrapped my layout ( ConstraintLayout ) in a ScrollView like so:

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

<data>
    <variable
        name="viewModel"
        type="com.mypackage.MyViewModel"/>
</data>

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:scrollbars="none">

    <!-- I don't want anything in this ConstraintLayout to move or resize when I open the keyboard, hence why I wrapped it in a ScrollView. -->
    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/scene_container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toTopOf="@+id/guideline"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <ImageView
                android:id="@+id/scene"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:adjustViewBounds="true"
                android:background="@color/black"
                android:scaleType="fitCenter"
                android:src="@drawable/grumpy"/>
        </FrameLayout>

        <android.support.constraint.Guideline
            android:id="@+id/guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent=".7"/>

        <View
            android:id="@+id/status_bar"
            android:layout_width="0dp"
            android:layout_height="48dp"
            android:background="@color/colorPrimary"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/guideline"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/my_recycler"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="48dp"
            android:clipToPadding="false"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/status_bar"/>

    </android.support.constraint.ConstraintLayout>

    </ScrollView>

    <!-- The bottom sheet contains a 48dp high peekable area (a bar with an EditText) that I need to raise with the keyboard. -->
    <include
        android:id="@+id/bottom_sheet"
        layout="@layout/bottom_sheet"
        app:viewModel="@{viewModel}"/>

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

This works fine for the first two times I bring up the keyboard. Here's the bizarre problem I'm having. Every THIRD time I bring up the keyboard, my layout resizes and squishes together as if the ScrollView isn't even there. This occurs consistently every THIRD time I open the keyboard like so:

1) Open keyboard. Layout remains same size.
   Close keyboard.
2) Open keyboard. Layout remains same size.
   Close keyboard.
3) Open keyboard. *LAYOUT RESIZES/SQUISHES TOGETHER!*
   Close keyboard.

The above cycle repeats as I continue to open/close the keyboard.

Any ideas how to solve this? Why does the layout resize every third time I open the keyboard?

Actually, if you use match_parent as the size of ScrollView child, it will also resize when keyboard will appear and fit the ScrollView size.

To understand how ScrollView work, you can think about a Browser. On every link you go, if the page doesn't fit your browser windows, you will see scrollbar to let you navigate in the page. ScrollView are the same, you just need to give them the size you want to occupy on the screen (like the browser window) and you can put everything in it (like the website page), even if it's bigger than the ScrollView size.

According to this, your ConstraintLayout need a fixed size. It could be wrap_content if you only want the user to access all the component in it. This is not your case. You want it to keep his original size, that is, as I can see in your code, also the size of your first CoordinatorLayout .

The only way I found to fix an inner ConstraintLayout to the original view size is by code.

Let's simplify your xml for the exemple :

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:id="@+id/parentView"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@+id/scrollViewItem"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.constraint.ConstraintLayout
            android:id="@+id/childView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <!--ALL YOUR ITEMS-->

        </android.support.constraint.ConstraintLayout>

    </ScrollView>

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

You need to fix childView size at loading of the screen. On the onCreate , you can add this code :

private int first = 0;

public class ConstaintLayoutCanScroll extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        first = childView.getMeasuredHeight();

        final CoordinatorLayout parentView = findViewById(R.id.parentView);
            parentView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    if (first < parentView.getMeasuredHeight()) {
                        ConstraintLayout childView = findViewById(R.id.childView);
                        childView.setMinHeight(parentView.getMeasuredHeight());
                        first = childView.getMeasuredHeight();
                    }
                    return true;
                }
            });

    }

}

I use a listener because at OnCreate , the size is not already init. first is here because onPreDraw isn't called only once by android. It's an variable of the class that I fix to 0 by default.

Using match_parent as the size of ScrollView child isn't ideal. You should rather use fixed size or wrap_content . In your current setup the ConstraintLayout tries to set its height to the ScrollView's height and this changes according to keyboard being displayed or not.

You could try to set your ConstraintLayout to a fixed height as soon as it is displayed on the screen.

To test if that approach works, you can try to set eg android:layout_height="400dp" on your ConstraintLayout and see if the "cycle" you mentioned will be happening or not. If that works, you can move on to using a real dimension of the ConstraintLayout matching the size of the screen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM