AdjustResize с анимацией

Я использовал android: windowSoftInputMode = "adjustResize" в манифесте, чтобы запретить клавиатуре скрывать кнопку активности. Он работает, но когда клавиатура открывается, моя кнопка движется вверх. Это немного нерешительно, я хотел бы знать - могу ли я использовать анимацию для плавного перехода?

<activity
        android:name=".Activity"
        android:screenOrientation="portrait"
        android:windowSoftInputMode="adjustResize">
</activity>

Ответ 1

Метод setSoftInputMode(int) нельзя переопределить, его реализация находится внутри класса Window, и я не думаю, что можно заменить текущее окно вашим собственным. Вы также не можете управлять этим с помощью WindowManager.
Вы можете создать прослушиватель на ViewGroup и поймать макет модификации, когда SoftKeyboard открывается и закрывается. Действительно, когда появляется SKB, макет контейнера перерисовывает и изменяет его высоту. Из этого события вам удалось установить дочерний элемент Animation на представлениях и сделать плавный эффект.

Изменить: решение с помощью GlobalLayoutListener в корневом режиме также возможно вместо (решение, представленное ниже): создание настраиваемого класса группы представлений.

Вам нужно создать свою собственную группу просмотра и сделать ее в качестве родительского контейнера в макете. Он реализует интерфейс, который будет обрабатываться в классе UI (Activity, Fragment, что угодно). Я нашел этот блог, чтобы обнаружить события в SKB для (~) всех версий. В соответствии с этим здесь класс viewgroup для обработки высоты изменяется:

public class ContainerViewHandler extends RelativeLayout {

    private boolean isKeyboardShown;
    private onKeyboardStateChange listener;

    public ContainerViewHandler(Context context) {
        super(context);
    }

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

    public ContainerViewHandler(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setKeyboardStateListener(onKeyboardStateChange listener) {
        this.listener = listener;
    }

    // Callbacks
    public interface onKeyboardStateChange {
        void onKeyboardShow();
        void onKeyboardHide();
    }

    @Override
    public boolean dispatchKeyEventPreIme(@NonNull KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            // Keyboard is hiding
            if (isKeyboardShown) {
                isKeyboardShown = false;
                listener.onKeyboardHide();
            }
        }
        return super.dispatchKeyEventPreIme(event);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedHeight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();
        if (actualHeight > proposedHeight) {
            // Keyboard is showing
            if (!isKeyboardShown) {
                isKeyboardShown = true;
                listener.onKeyboardShow();
            }
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

Теперь вы должны добавить эту группу представлений в макет (например, <com.package.name.ContainerViewHandler .../>). Затем вы должны реализовать указанный выше интерфейс setKeyboardStateListener в действии, например:

ContainerViewHandler containerView = 
          (ContainerViewHandler) findViewById(R.id.container_view);
containerView.setKeyboardStateListener(new ContainerHandler.onKeyboardStateChange() {
    @Override
    public void onKeyboardShow() {
        Log.v("onKeyboardShow()", "SoftKeyboard is showing. Hello!");
    }

    @Override
    public void onKeyboardHide() {
        Log.v("onKeyboardHide()", "SoftKeyboard is hiding, Bye bye!");
    }
});

Таким образом, вы можете управлять различными анимациями для обработки и предотвращения нажатия кнопки непосредственно над SKB. Чтобы проверить это, я пытаюсь воспроизвести эффект отскока:

Образец anitmation на кнопке с помощью AdjustResize

Это как моя реализация выглядит так:

containerView.setKeyboardStateListener(new ContainerViewHandler.onKeyboardStateChange() {
    @Override
    public void onKeyboardShow() {
        setAnimationUp();
    }

    @Override
    public void onKeyboardHide() {
        setAnimationDown();
    }
});

private void setAnimationUp() {
    footerButton.setVisibility(View.GONE);
    float dpY = AppUtils.convertPxToDp(20, this); // custom conversion method

    Animation a1 = new TranslateAnimation(0, 0, footerButton.getHeight() * 4, -(dpY));
    a1.setDuration(250);
    a1.setFillAfter(true);

    final Animation a2 = new TranslateAnimation(0, 0, -(dpY), 0);
    a2.setDuration(320);
    a2.setFillAfter(true);

    a1.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
            footerButton.setVisibility(View.VISIBLE);
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            footerButton.startAnimation(a2);
        }

        @Override
        public void onAnimationRepeat(Animation animation) { }
    });

    footerButton.startAnimation(a1);
}

private void setAnimationDown() {
    float dpY = AppUtils.convertPxToDp(30, this); // custom conversion method
    Animation b1 = new TranslateAnimation(0, 0, -(dpY), dpY);
    b1.setDuration(300);
    b1.setFillAfter(true);

    final Animation b2 = new TranslateAnimation(0, 0, dpY, 0);
    b2.setDuration(320);
    b2.setFillAfter(true);

    b1.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) { }

        @Override
        public void onAnimationEnd(Animation animation) {
            footerButton.startAnimation(b2);
        }

        @Override
        public void onAnimationRepeat(Animation animation) { }
    });

    footerButton.startAnimation(b1);
}

Я начинаю с установки двух анимаций (a1, a2) в первом обратном вызове:

  • a1: запустите (за) SKB (около 4xbutton height) и до 20dp над ним,
  • a2: начните с 20dp и вернитесь к 0dp (нормальное положение).

И еще два (b1, b2) во втором обратном вызове:

  • b1: начните с кнопки вверх до 30dp сверху и спуститесь до 30dp вне родительского контейнера,
  • b2: finaly, от 30dp до 0dp (исходная позиция).

PS: не забудьте использовать adjustResize в манифесте и сделать контент (например, edittexts в моих тестах) above кнопку нижнего колонтитула, которая имеет alignParentBottom в true.