Android 简单沉浸式弹出输入框

前言

最近公司项目在写IM聊天室功能,刚开始使用dialog方式,让dialog居底部显示,但是项目中需要文本和表情切换发送消息,但是因为软键盘本来就是一种特殊的dialog,dialog具有优先级,软键盘的优先级总是高于我们平时用到的dialog,所以出现显示消失的时候,因为软键盘与自定义的输入弹出框消失显示时机问题,导致闪烁问题。网上还有一种方式是在activity的底部直接添加一个view的方式,通过约束其上的布局来解决闪烁问题,但是这种情况输入弹窗会把整个布局顶上去,感觉体验不是特别好。所以决定去看看其他APP的效果,发现头条和简书的弹出输入框都是都不错,如下图所示:但是头条的弹出输入框在弹出时,系统状态栏会变色,感觉影响体验,简书的弹出输入框是在底部直接添加view的方式,只不过是增加了遮罩层。如何做到效果和dialog一致,点击外部和返回键收起,然后还能切换文本和表情输入,另外不会引起系统状态栏变色,。

头条.png

简书输入框.png

布局添加View

因为要实现当输入框弹出的时候一是不能让原来的内容顶上去,二是输入框内容要覆盖在当前内容的上层。所以不能采取直接在activity的布局底部直接填加view的方式,也不能是dialog的形式,所以只能添加到activity的顶层布局,activity的顶层布局是decorView,然后添加到底部,操作软键盘的隐藏显示来调取弹出输入框,显示软键盘的时候添加,弹出框消失的时候移除。

添加到DecorView
/**
* 添加
*/
if (mContext instanceof Activity) {
    Activity activity = (Activity) mContext;
     final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
     final ViewGroup content = (ViewGroup) decorView.findViewById(android.R.id.content);
     if (getParent() == null) {
           content.addView(this);
     }
}
从DecorView移除
//移除掉当前的view
        ViewParent parent = getParent();
        if (parent != null) {
            ((ViewGroup) parent).removeView(this);
        }

注意事项:因为弹出框是在下方显示,所以添加到DecorView的顶级View要位于DecorView底部

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    //添加到底部
    android:layout_gravity="bottom"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:orientation="vertical">
</LinearLayout>

点击外部消失

如果点击输入框外面,取消输出框体验更好。为此我们监听当前弹出框的onTouchEvent事件:

/**
     * 处理点击范围,如果在不在就取消
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Rect rect = new Rect();
        getInputView().getGlobalVisibleRect(rect);
        if (!WindowUtil.touchIsInRect(event.getX(), event.getY(), rect)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downX = event.getX();
                    downY = event.getY();
                    downTime = System.currentTimeMillis();
                    break;
                case MotionEvent.ACTION_UP:
                    float dx = event.getX() - downX;
                    float dy = event.getY() - downY;
                    float distance = (float) Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
                    if (distance < touchSlop && (System.currentTimeMillis() - downTime) < isClickTime) {
                        //点击外部就消失
                        onInputListener.onCanceledOnTouchOutside();
                    }
                    downX = 0;
                    downY = 0;
                    downTime = 0;
                    break;
            }
        }
        return true;
    }

触点说明:
1、WindowUtil.touchIsInRect(event.getX(), event.getY(), rect) 检测当前触点是否在弹出框之中
2、onTouchEvent返回值设置为true,响应事件

点击返回键消失

 getInputView().setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
                    if (onInputListener != null) {
                        onInputListener.onBackPressed();
                    }
                    return true;
                }
                return false;
            }
        });

返回值一定要为true,否则失效

如果你设置了这个方法,但是发现没有响应,是因为其他地方抢占了焦点,可能是edittext或者是软键盘,这里说明一下软键盘也是一种dialog,dialog默认抢占焦点,所有添加如下方法:

View inputView = getInputView();
            inputView.requestFocus();
            inputView.setFocusable(true);
            inputView.setFocusableInTouchMode(true);

当前弹出框抢占焦点之后,点击back键就会取消软键盘,由于软键盘消失有动画,需要时间,所以有两种方式可以取消弹出框,第一种发送延迟消息的方式,这种方式由于软键盘弹出时间不确定并且如果多次主动弹出会造成消息冲突,如果采用这种方式,尽量去使用防止重复点击监听,第二种是定义中间变量,再监听软键盘消失赋值为消失状态,这时候是消失状态但不是销毁状态。

/**
 * 输入框的四种状态
 */
public static final int INITIALIZE = 0;
public static final int SHOWING = 1;
public static final int DISMISSED= 2;
public static final int DISMISS_OUTSIDE = 100;
public static final int DISMISS_BACKKEY = 101;
public static final int DISMISS_KEYBOARD = 103;
private int currentState = INITIALIZE;

说明:因为之前为了响应返回键和touch事件,强制获取焦点,当弹出框消失的时候主动把焦点返回给当前页面,如果不这么做,可能会遇到其他问题,比如RecyclerView滚动事件等。

     clearFocus();
     View contentView = ((Activity) getContext()).findViewById(android.R.id.content);
     contentView.setFocusable(true);
     contentView.setFocusableInTouchMode(true);

模式切换

查看其它APP,有时候会切换到表情页面,有些APP的软键盘表情页面跟软键盘高度不一致导致有错位的感觉,如果表情页面跟软键盘高度一致体验会好很多,所以我在初始化的时候设置表情页面的高度。

        rlSpecialContent.setVisibility(INVISIBLE);
        ViewGroup.LayoutParams linearParams = (ViewGroup.LayoutParams) rlSpecialContent.getLayoutParams();
        int lastHeight = linearParams.height;
        if (bottomHeight != lastHeight) {
            //不重复设置
            linearParams.height = bottomHeight;
            rlSpecialContent.setLayoutParams(linearParams);
        }

说明:布局中关于表情页面设置Gone,减少绘制,但是显示弹出框的时候一定要给表情页面占位rlSpecialContent.setVisibility(INVISIBLE);并且之后切换的过程中也是如此,不能设置为Gone,否则有闪动错位效果,影响体验。

其他

由于本人比较懒,个人觉得如果弹出框加上动画可能更好一些,不过毕竟动画非常消耗资源,暂且就没加,可以自行添加,由于软键盘方式和各个手机厂商对于Rom的修改,不知道在其他型号手机上是否会出现未知问题,大家如果遇到问题,及时反馈给我,在此感谢大家阅读本文。最后附上代码

简单沉浸式弹出输入框

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容