2023-02-17

How to avoid "jumpy" issue when interacting with soft keyboard visibility

Currently, we have an app with the following requirements

  1. Must use android:windowSoftInputMode="adjustPan"
  2. Use ViewCompat.setWindowInsetsAnimationCallback and ViewCompat.setOnApplyWindowInsetsListener to interact with soft keyboard visibility with smooth animation.

Here is our code, when interacting with soft keyboard visibility. It works pretty well in the case, when our EditText is not scrollable.

The animation went pretty well, when keyboard is showing and hiding.


MainActivity.java

public class MainActivity extends AppCompatActivity {
    EditText editText;
    LinearLayout toolbar;
    FrameLayout keyboardView;

    private int systemBarsHeight = 0;
    private int keyboardHeightWhenVisible = 0;
    private boolean keyboardVisible = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = findViewById(R.id.edit_text);
        toolbar = findViewById(R.id.toolbar);
        keyboardView = findViewById(R.id.keyboard_view);

        final View rootView = getWindow().getDecorView().getRootView();

        ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
            boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());

            systemBarsHeight = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;

            keyboardVisible = imeVisible;

            if (keyboardVisible) {
                keyboardHeightWhenVisible = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
            }

            // https://stackoverflow.com/questions/75325095/how-to-use-windowinsetscompat-correctly-to-listen-to-keyboard-height-change-in-a
            return ViewCompat.onApplyWindowInsets(v, insets);
        });

        WindowInsetsAnimationCompat.Callback callback = new WindowInsetsAnimationCompat.Callback(
                WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP
        ) {
            @NonNull
            @Override
            public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations) {
                // Find an IME animation.
                WindowInsetsAnimationCompat imeAnimation = null;
                for (WindowInsetsAnimationCompat animation : runningAnimations) {
                    if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
                        imeAnimation = animation;
                        break;
                    }
                }
                if (imeAnimation != null) {
                    int keyboardViewHeight;
                    if (keyboardVisible) {
                        keyboardViewHeight = (int) (keyboardHeightWhenVisible * imeAnimation.getInterpolatedFraction()) - systemBarsHeight;
                    } else {
                        keyboardViewHeight = (int) (keyboardHeightWhenVisible * (1.0-imeAnimation.getInterpolatedFraction())) - systemBarsHeight;
                    }

                    keyboardViewHeight = Math.max(0, keyboardViewHeight);

                    ViewGroup.LayoutParams params = keyboardView.getLayoutParams();
                    params.height = keyboardViewHeight;
                    keyboardView.setLayoutParams(params);

                    Log.i("CHEOK", "keyboardVisible = " + keyboardVisible + ", keyboardViewHeight = " + keyboardViewHeight);
                }
                return insets;

            }
        };

        ViewCompat.setWindowInsetsAnimationCallback(rootView, callback);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/edit_text"

        android:padding="16dp"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="top" />

    <LinearLayout
        android:id="@+id/toolbar"

        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:orientation="horizontal"
        android:background="#ffff00" />

    <FrameLayout
        android:id="@+id/keyboard_view"

        android:background="#ff0000"
        android:layout_width="match_parent"
        android:layout_height="0dp" />
</LinearLayout>

Here is the outcome.

When EditText is not scrollable

enter image description here


However, our app becomes "jumpy", when the content of EditText is scrollable.

When EditText is scrollable, our app becomes "jumpy"

enter image description here


Does anyone know what is the root cause of this problem, and how we can resolve such?

A demo to demonstrate such an issue, can be downloaded from https://github.com/yccheok/programming-issue/tree/main/jumpy

Thank you.



No comments:

Post a Comment