AdjustResize with animation

前端 未结 1 1742
暗喜
暗喜 2021-02-19 07:58

I have used android:windowSoftInputMode=\"adjustResize\" in the manifest to prevent keyboard from hiding the activity button. It works, but when keyboard opens,

1条回答
  •  遇见更好的自我
    2021-02-19 08:32

    The method setSoftInputMode(int) cannot be overrided, its implementation is inside Window class and I don't think that's possible to replace the current window by your own. You cannot manage this from WindowManager too.
    You could create a listener on a ViewGroup and catch the modification's layout when the SoftKeyboard is opening and closing. Indeed, when the SKB shows up, the container's layout redraws and changes its height. From this event, you could manage to set an Animation on views child and make a smooth effect.

    Edit: The solution by using a GlobalLayoutListener on the rootview is also possible instead of (the solution presented below:) creating a custom viewgroup class.

    You have to create your own viewgroup and make it as a parent container in the layout. It'll implement an interface which will be handled in the UI class (Activity, Fragment, whatever). I found this blog to detect the events on SKB for (~)all versions. According to it, here's the viewgroup's class to handle the height's changes:

    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);
        }
    }
    

    Now, you've to add this viewgroup in layout (eg ). Then, you must implement the above interface setKeyboardStateListener in the activity, like the following:

    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!");
        }
    });
    

    Thus, you can manage different animations to handle and prevent the button to "jump" directly above the SKB. To test this out, I try to reproduce a bounce effect:

    This how my implementation looks like:

    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);
    }
    

    I begin by setting two animations (a1, a2) in the first callback:

    • a1: start below (behind) the SKB (about 4xbutton's height) and up to 20dp above it,
    • a2: start at 20dp and return to 0dp (normal position).

    And two others (b1, b2) in the second callback:

    • b1: start where the button is up to 30dp at top and go down to 30dp outside the parent container,
    • b2: finaly, from 30dp out to 0dp (the initial position).

    PS: don't forget to use adjustResize in the Manifest and to make the content (eg edittexts in my tests) above the footer button which has alignParentBottom to true.

    0 讨论(0)
提交回复
热议问题