Android 软件盘之使特定布局保持在软键盘之上

1.简述

本文将提及的是软键盘的使用技巧,包括一些初始进入的小细节还有一些常见的应用场景。

应用场景包括 1. 登陆注册界面时由于屏幕原因,弹出的软键盘会遮住登陆注册按钮

                       2.评论时输入文字框居于软件盘上的效果时

2.软件盘细节

      细节描述 

      1. 初次进入含有文本框的界面时,光标闪烁

       2. 软键盘弹出,页面跳转,可能导致抖动

       3. 软键盘将标题toolbar顶出去了

       解决方式       

       让EditText的父亲布局抢占其焦点,可以有效解决进入界面输入框光标闪烁问题

            
               .......
                
            

     延迟退出软键盘抗拒可能会出现的抖动

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            // 隐藏软键盘 imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);
            // 延迟个0.1秒是因为 可能推出太快导致软键盘刚刚被回收,前面的界面出现断层
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
finish();

      规避底部的文本框不太接近弹出框的方法:

    
        
        
    

Android 软件盘之使特定布局保持在软键盘之上_第1张图片

 

3. 通过焦点使布局置于软件盘上

        看下下面的效果,是不是还挺赞的,也是拿出来给大家分享下,也是希望懂的大牛告知为啥这样就行,我也不知道原因。

        Android 软件盘之使特定布局保持在软键盘之上_第2张图片Android 软件盘之使特定布局保持在软键盘之上_第3张图片

具体实现过程:如下一共有3步,设置活动的 windowSoftInputMode属性,然后建立 Scroll布局+EditText焦点的布局[含文本的置于底部],最后是设置默认隐藏软键盘。大家可以试一下。

   

   //主题
    


    
    
        
        
    
    
        
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
        setContentView(R.layout.activity_session);
    }

  当然,我是觉得这里这种方式是更适合运用于聊天消息的设置,使得文本输入框且其父布局能够放置于软键盘之上。当然这里的局是文本框位于底部

4.案例 —— 注册时保持登陆按钮置于软件盘上

     当布局中存在EditText时,又涉及到提交按钮特别是登录注册界面,由于手机屏幕高度不够使得按钮被遮挡的效果会出现。这样会带来比较不好的使用体验效果。下面将对这一问题进行解决

运行效果: 

示意图:

Android 软件盘之使特定布局保持在软键盘之上_第4张图片

解释: 如图所示,不做处理软件盘会遮盖button,而我们需要显示出,那么需要是内容区平滑按钮到软键盘顶部的距离,那么按钮就会显示出来了。所以问题的关键是测量当按钮位于软键盘顶部显示时的高度。

核心代码如下

    /**
     * 功能: 软键盘弹起时让特定页面置于软键盘之上
     * @param rootGroup  外围布局
     * @param bottomView 底部需要显示的view
     */
    private void buttonBeyondKeyboardLayout(final  View rootGroup,final View bottomView) {
        mListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                // 总的布局空间
                View decorView = RegisterActivity.this.getWindow().getDecorView();
                int normalHeight = decorView.getRootView().getHeight();
                // 可视区域
                Rect outRect = new Rect();
                decorView.getWindowVisibleDisplayFrame(outRect);
                // 软键盘弹起
                if (normalHeight - outRect.height() > dpToPx(100)){
                    if (distanceY == -1){
                        int[] bottomArray = new int[2];
                        bottomView.getLocationInWindow(bottomArray);
                        // 底部view的bottom坐标
                        int viewBottomY = bottomArray[1]+bottomView.getHeight();
                        // 判断底部view是否被软键盘盖住
                        if (viewBottomY > outRect.bottom){
                            // 计算偏移的距离 并给出20dp的间隙
                            distanceY = viewBottomY - outRect.bottom + dpToPx(20);
                        }else {
                            distanceY = 0;
                        }
                    }
                    if (distanceY != 0){
                        // 滑动间距
                        //rootGroup.scrollTo(0,distanceY);
                        smoothScroll(0,distanceY,rootGroup);
                    }else {
                        unRegisterTree();
                    }
                }else {
                    // 软键盘收起
                    // 还原...
                    if (distanceY != -1 && distanceY != 0){
                       // rootGroup.scrollTo(0,0); 瞬间移动
                       smoothScroll(distanceY,0,rootGroup);  // 平滑移动
                    }
                }
            }
        };
        mTreeObserver = rootGroup.getViewTreeObserver();
        mTreeObserver.addOnGlobalLayoutListener(mListener);
    }

    private void smoothScroll(int fromY, int toY, final View view){
        mValueAnimator = ValueAnimator.ofInt(fromY,toY);
        mValueAnimator.setInterpolator(new DecelerateInterpolator());
        mValueAnimator.setDuration(200);
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int animatedValue = (int) animation.getAnimatedValue();
                view.scrollTo(0,animatedValue);
            }
        });
        mValueAnimator.start();
    }

    private void unRegisterTree(){
        if (mListener != null && mTreeObserver != null && mTreeObserver.isAlive()) {
            mTreeObserver.removeOnGlobalLayoutListener(mListener);
        }
    }

    private void releaseAnimator(){
        if (mValueAnimator != null && mValueAnimator.isRunning()){
            mValueAnimator.cancel();
        }
    }

    @Override
    protected void onDestroy() {
        unRegisterTree();
        releaseAnimator();
        super.onDestroy();
    }

小结:

        咱们在做开发的时候,肯定会遇到很多困难,但要努力思考将之解决,即便解决不了也要有自己的思路。其实也是很常规,总结别人的思考自己的知识储备,提升自己的能力。

5. 案例2-相对布局底部按钮保持-- 解决布局出现在软件盘之上的问题

 

        mListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect rect = new Rect();
                BasicSoftActivity.this.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
                int screenHeight = BasicSoftActivity.this.getWindow().getDecorView().getRootView().getHeight();
                if (screenHeight - rect.bottom > dp2Px(100)){
                    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
                            0);
                    params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                    btnSubmit.setLayoutParams(params);
                }else {
                    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
                            dp2Px(50));
                    params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                    btnSubmit.setLayoutParams(params);
                }
            }
        };

6. 案例3 评论软键盘

活用BottomSheetDialog 弹出评论的软键盘,并解决了布局被被键盘部分遮挡的问题。

效果展示:

Android 软件盘之使特定布局保持在软键盘之上_第5张图片

如上图所示,当点击评论则会从底部弹出Dialog,该DIalog会手指移动而移动,且由于含文本框故而有必要弹出软键盘,且可以发现该软键盘将整个弹出框挤压了上去。好了,这就是我们的效果。

具体实现:

继承BottomSheetDialog,实现显示和隐藏的状态方法

public class BottomDialog extends BottomSheetDialog {

    private BottomSheetBehavior behavior;

    public BottomDialog(@NonNull Context context, int theme,boolean isTranslucentStatus) {
        super(context, theme);
        Window window = getWindow();
        if (window != null) {
            window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE |
                    WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
            if (isTranslucentStatus) {
                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            }
        }
    }

    @Override
    public void setContentView(View view) {
        super.setContentView(view);
        initialize(view);
        setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                if (behavior != null) {
                    behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }
        });
    }

    @Override
    public void show() {
        super.show();
        behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    }

    private void initialize(final View view) {
        ViewGroup parent = (ViewGroup) view.getParent();
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) parent.getLayoutParams();
        behavior = (BottomSheetBehavior) params.getBehavior();
        if (behavior == null) {
            return;
        }
        behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                    dismiss();
                    behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
            }
        });
    }
}

在values文件夹下的style.xml 引入含底部编辑框的style,说明一下,假若你定义style,那么当你弹出软键盘或者点击EditText时,会被部分遮挡,这是很不好的效果。所以下面的设置记住咯。

    

像一些简单的布局和java代码我就不给出了,大家可以在文后的例子中进行查看的,ok了。

**提醒**

具体使用那种显示方式还是得具体分析应用场景,从我目前的开发经验来看:

询问类弹窗,单行输入,优先使用dialog

底部列表拉伸,优先使用bottomSheetDialog

其他一些显示效果 dialogFragment

带多行输入框,自定义假的dialog,即使用背景进行显示隐藏实现效果展示

7.android:windowSoftInputMode属性

Android 软件盘之使特定布局保持在软键盘之上_第6张图片

Github项目地址:https://github.com/crazyzhangxl/KeyBoardDemo

你可能感兴趣的:(常规案例记录)