Android仿微信朋友圈3评论输入框及点击外部和列表滑动时隐藏输入框和键盘解决方法

            接着上一篇的评论点赞弹框之后,这次来说说微信的评论输入框,点击屏幕外部评论框和键盘消失,滑动列表时输入框和键盘也要消失,这里不是说一定要舔微信啥的,只是单纯从技术角度出发,分析原理和实现,解决我们自己的需求.

实现的效果图如下:(软键盘在评论框下面) 

1.布局文件main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/ll_scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    <TextView
        android:id="@+id/tv_city"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:text="城市名称"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:gravity="center"/>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/ll_comment"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_gravity="bottom"
        android:background="#f6f6f6"
        android:elevation="3dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:visibility="gone">

        <EditText
            android:id="@+id/et_comment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="6dp"
            android:layout_marginRight="7dp"
            android:layout_marginBottom="6dp"
            android:layout_weight="1"
            android:background="@drawable/shape_comment_bg"
            android:ellipsize="end"
            android:hint="说点什么"
            android:maxLength="300"
            android:paddingLeft="10dp"
            android:singleLine="true"
            android:textColorHint="#a2a2a2"
            android:textSize="13sp" />

        <TextView
            android:id="@+id/tv_send_comment"
            android:layout_width="60dp"
            android:layout_height="30dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:background="@drawable/shape_comment"
            android:gravity="center"
            android:paddingLeft="10dp"
            android:paddingTop="5dp"
            android:paddingRight="10dp"
            android:paddingBottom="5dp"
            android:text="发送"
            android:textColor="#fff"
            android:textSize="14sp" />
    </LinearLayout>
</FrameLayout>

2.MainActivity代码:

/**
 * @作者: njb
 * @时间: 2019/7/22 10:53
 * @描述: 仿微信朋友圈文本显示全文与收起
 */
public class MainActivity extends AppCompatActivity implements CircleAdapter.MyClickListener, View.OnClickListener {
    private RecyclerView recyclerView;
    private CircleAdapter circleAdapter;
    private String content = "茫茫的长白大山,浩瀚的原始森林,大山脚下,原始森林环抱中散落着几十户人家的" +
            "一个小山村,茅草房,对面炕,烟筒立在屋后边。在村东头有一个独立的房子,那就是青年点," +
            "窗前有一道小溪流过。学子在这里吃饭,由这里出发每天随社员去地里干活。干的活要么上山伐" +
            "树,抬树,要么砍柳树毛子开荒种地。在山里,可听那吆呵声:“顺山倒了!”放树谨防回头棒!" +
            "树上的枯枝打到别的树上再蹦回来,这回头棒打人最厉害。";
    private List<String> strings;
    private LikePopupWindow likePopupWindow;
    private int page = 1;
    private EditText etComment;
    private LinearLayout llComment;
    private TextView tvSend;
    private LinearLayout llScroll;
    private int screenHeight;
    private int editTextBodyHeight;
    private int currentKeyboardH;
    private int selectCommentItemOffset;
    private int commentPosition;
    protected final String TAG = this.getClass().getSimpleName();
    CompositeDisposable compositeDisposable;
    private TextView tvCity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        initData();
        initAdapter();
        setListener();
        initRxBus();
    }

    private void initRxBus() {
        compositeDisposable = new CompositeDisposable();
        RxBus.getInstance().toObservable(WeatherEvent.class)
                .subscribe(new Observer<WeatherEvent>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        compositeDisposable.add(d);
                    }

                    @Override
                    public void onNext(WeatherEvent weatherEvent) {
                        Log.e("weather", weatherEvent.getTemperature()+"-**-"+weatherEvent.getCityName());
                        tvCity.setText(String.format("%s %s", weatherEvent.getCityName(),weatherEvent.getTemperature()));
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    private void setListener() {
        tvSend.setOnClickListener(this);
    }


    private void setViewTreeObserver() {
        final ViewTreeObserver swipeRefreshLayoutVTO = llScroll.getViewTreeObserver();
        swipeRefreshLayoutVTO.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                llScroll.getWindowVisibleDisplayFrame(r);
                int statusBarH = Utils.getStatusBarHeight();//状态栏高度
                int screenH = llScroll.getRootView().getHeight();
                if (r.top != statusBarH) {
                    //在这个demo中r.top代表的是状态栏高度,在沉浸式状态栏时r.top=0,通过getStatusBarHeight获取状态栏高度
                    r.top = statusBarH;
                }
                int keyboardH = screenH - (r.bottom - r.top);
                Log.d(TAG, "screenH= " + screenH + " &keyboardH = " + keyboardH + " &r.bottom=" + r.bottom + " &top=" + r.top + " &statusBarH=" + statusBarH);

                if (keyboardH == currentKeyboardH) {//有变化时才处理,否则会陷入死循环
                    return;
                }
                currentKeyboardH = keyboardH;
                screenHeight = screenH;//应用屏幕的高度
                editTextBodyHeight = llComment.getHeight();
                if (keyboardH < 150) {//说明是隐藏键盘的情况
                    MainActivity.this.updateEditTextBodyVisible(View.GONE);
                    return;
                }
                //偏移listview
            }
        });
    }

    /**
     * 初始化控件
     */
    private void initViews() {
        recyclerView = findViewById(R.id.recyclerView);
        llComment = findViewById(R.id.ll_comment);
        etComment = findViewById(R.id.et_comment);
        tvSend = findViewById(R.id.tv_send_comment);
        llScroll = findViewById(R.id.ll_scroll);
        tvCity = findViewById(R.id.tv_city);
    }

    /**
     * 初始化数据
     *
     * @param
     */
    private void initData() {
        strings = new ArrayList<>();
        for (int i = 0; i < 14; i++) {
            strings.add(content);
        }
    }

    /**
     * 设置adapter
     */
    private void initAdapter() {
        circleAdapter = new CircleAdapter(this, strings, this);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.addItemDecoration(new SpaceDecoration(this));
        recyclerView.setAdapter(circleAdapter);
        recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (llComment.getVisibility() == View.VISIBLE) {
                    updateEditTextBodyVisible(View.GONE);
                    return true;
                }
                return false;
            }
        });
    }

    @Override
    public void onClick(int position, View v) {
        if (likePopupWindow == null) {
            likePopupWindow = new LikePopupWindow(this, 0);
        }
        likePopupWindow.setOnPraiseOrCommentClickListener(new OnPraiseOrCommentClickListener() {
            @Override
            public void onPraiseClick(int position) {
                Toast.makeText(MainActivity.this, "点赞成功", Toast.LENGTH_SHORT).show();
                likePopupWindow.dismiss();
                Intent intent = new Intent(MainActivity.this, AddCityActivity.class);
                startActivity(intent);
            }

            @Override
            public void onCommentClick(int position) {
                llComment.setVisibility(View.VISIBLE);
                etComment.requestFocus();
                CommonUtils.showSoftInput(MainActivity.this, llComment);
                likePopupWindow.dismiss();

            }

            @Override
            public void onClickFrendCircleTopBg() {

            }

            @Override
            public void onDeleteItem(String id, int position) {

            }
        }).setTextView(0).setCurrentPosition(position);
        if (likePopupWindow.isShowing()) {
            likePopupWindow.dismiss();
        } else {
            likePopupWindow.showPopupWindow(v);
        }
    }

    public void updateEditTextBodyVisible(int visibility) {
        llComment.setVisibility(visibility);
        if (View.VISIBLE == visibility) {
            llComment.requestFocus();
            //弹出键盘
            CommonUtils.showSoftInput(etComment.getContext(), etComment);

        } else if (View.GONE == visibility) {
            //隐藏键盘
            CommonUtils.hideSoftInput(etComment.getContext(), etComment);
        }
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.tv_send_comment:
                if (TextUtils.isEmpty(etComment.getText().toString())) {
                    Toast.makeText(MainActivity.this, "请输入评论内容", Toast.LENGTH_SHORT).show();
                    return;
                }
                setViewTreeObserver();

                break;
            default:
                break;
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消订阅
        RxBus.rxBusUnbund(compositeDisposable);
    }
}

3.刚开始使用的是dispatchTouchEvent拦截事件

打断点调试发现点击屏幕外部时确实键盘和输入框都消失了,但是发现一个问题,输入文本内容后,只要一点击发送按钮,输入框和键盘就消失了,发送评论的接口都没有调用,以下是日志和断点截图:

4.dispatchTouchEvent事件分发原理:

dispatchTouchEvent函数在Activity,View 和ViewGroup中都有定义,并且处理的逻辑也是不同的。

Activity:
当发生点击事件后,最先响应的是Activity的dispatchTouchEvent()函数,Activity会把TouchEvent传给自身绑定的rootView(一般就是DecorView),由rootView进行处理。
如果TouchEvent一直没有消费掉,最后会调用Activity的onTouchEvent()函数来处理事件。

ViewGroup:
ViewGroup运行dispatchTouchEvent()函数时,首先会调用onInterceptTouchEvent()函数,这是个拦截函数,如果需要拦截子View的点击事件,可以在这里添加一些逻辑;默认返回值是false,此时会按照添加子View时的顺序将事件分发给各个子View,由各个子View处理事件;如果添加了拦截的逻辑,返回值为true的话,会给各个子View发送Action_Cancel指令,并且所有事件都变为已处理。
子View处理事件时逻辑类似,如果是ViewGroup则继续分发,如果是View的话,则执行View的逻辑。

View:
View处理事件时先看是否有onTouchListener,如果有的话,优先执行onTouchListener的onTouch函数,如果没有处理,则执行onTouchEvent函数。
ViewGroup本质也是View,子view都没消费事件的话,也会走View的逻辑。

当子view不想被拦截时可以通过requestDisallowInterceptTouchEvent(true)函数来阻止拦截。

由于Activity没有写onTouchListener事件,所以按照上面的事件分发原理,Activity不会拦截,点击屏幕外部时被消费了,输入评论内容后,点击发送按钮时也被消费了,所以dispatchTouchEvent拦截点击事件,这样肯定不行,于是看了下微信的效果,回想起事件分发原理,尝试了一下解决方法:

5.在recyclerview的触摸事件中处理onTouch事件,代码如下:

这时调试运行发现点击外部输入框和软键盘都消失了,发现这种方法是可行的。还有一个问题recyclerview滑动时也要消失,这又是一个问题,和同事讨论监听输入框的高度,参考资料后给出以下解决方法:

private void setViewTreeObserver() {
    final ViewTreeObserver swipeRefreshLayoutVTO = llScroll.getViewTreeObserver();
    swipeRefreshLayoutVTO.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            Rect r = new Rect();
            llScroll.getWindowVisibleDisplayFrame(r);
            int statusBarH = Utils.getStatusBarHeight();//状态栏高度
            int screenH = llScroll.getRootView().getHeight();
            if (r.top != statusBarH) {
                //在这个demo中r.top代表的是状态栏高度,在沉浸式状态栏时r.top=0,通过getStatusBarHeight获取状态栏高度
                r.top = statusBarH;
            }
            int keyboardH = screenH - (r.bottom - r.top);
            Log.d(TAG, "screenH= " + screenH + " &keyboardH = " + keyboardH + " &r.bottom=" + r.bottom + " &top=" + r.top + " &statusBarH=" + statusBarH);

            if (keyboardH == currentKeyboardH) {//有变化时才处理,否则会陷入死循环
                return;
            }
            currentKeyboardH = keyboardH;
            screenHeight = screenH;//应用屏幕的高度
            editTextBodyHeight = llComment.getHeight();
            if (keyboardH < 150) {//说明是隐藏键盘的情况
                MainActivity.this.updateEditTextBodyVisible(View.GONE);
                return;
            }
        }
    });
}

以上就是解决点击屏幕外部和列表滑动时隐藏输入框和键盘的解决方法,如果小伙伴们有更好的方法可以留言一起交流成长.

项目的完整地址如下:

ExpandTextView: 实现仿微信朋友圈列表多类型布局,图片点击放大、保存,包含点赞、评论、消息提醒、视频播放等功能

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Ajax模仿google提示输入框 使用说明 v1.8 本程序由飞飞asp乐园编写 ************************************************* 特别感谢: 一杯白开水 网友 的帮助 版本所提的修改建议 ************************************************* 飞飞Asp乐园 www.ffasp.com QQ:276230416 QQ:72840059 ********************************************* 程序介绍: 本程序官网演示地址:http://www.ffasp.com/content.asp?newsid=1376 转载请注明出处 此小程序是模仿Google(sgguest)输入提示框所编写 亦可用于用户注册邮箱的提示 此程序使用方便简单、易用灵活 且不限Asp、php、net 更新说明: 1.支持xhtml 2.兼容IE6,IE7,FF 3.支持input显示与value分离【以隐藏域方式提交数据】 4.支持下拉框按钮 5.支持显示结果数量 6.可以一个页面多次使用 7.模拟下拉框不会被select遮挡 8.调用更方便(内核已改为jq插件) 9.皮肤修改方便 ,外置的css样式文件,带有注释说明,任意修改 10.支持键盘方向键按住不放选择 11.解决了延卡死的现象 12.解决了在有滚动条,ie6下被表单遮挡的问题 13.支持回调函数。当该插件执行完毕,执行另外一个指定函数 ######V1.7解决问题############ 14.可以自己设置提示框的长度 15.解决了无法获取用户填写默认值的问题 16.当输入框失去焦点,提示框会自动隐藏 ######V1.8解决问题############ 17.解决了鼠标无法选择的问题 ****************************************************************** 文件使用说明: 在需要使用的页面首先要引用2个js文件 <script src="jquery1.2.6.min.js"></script> <script src="search_suggest.js"></script> 此二文件必须放于调用函数之前 *****************************以下是调用示例*************************: var option = { inputName : "test2", inputText : "测试字符串4", inputValue: "我是值", inputlength:1, url : "search.asp", arrow :1, arrowUrl :"search.asp?aa=nowitest", zIndex:7, fns:function(){alert(3)} } $("#test").suggestShow(option) +++++++解释说明+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ option:为参数设置对象。option内的所有参数可以省略 inputName 表单input的name.即 提交获取input的name。也为search.asp页面获取input的name inputText inputName 所显示的数据 inputValue inputName 所提交的数据,当inputValue省略,inputValue则与inputText相等 url 远程执行获取数据的url arrow 模仿select,使用下拉按钮。固定值。为1则显示下拉箭头 为0则不显示 arrowUrl 当arrow为1 ,默认情况下点击下拉按钮所执行的远程搜索页面 zIndex z-index值 inputlength inputName中输入数据的最小长度 suggestWidth 提示框的长度 fns 当执行完该插件,执行的回调函数 ************************************************************************ search.asp页面介绍: 该页面返回数据必须是xml格式,输出数据数据必须符合以下格式 <response value="1" result ="共2个结果">显示信息</response> 属性介绍: 1.value aa的value值,可以不填。不填value值则取"显示信息" 2.result 显示远程获取的统计结果,可以不填。 3.显示信息 即aa的显示值text值 ************************************************************************
更新说明: 1.支持xhtml 2.兼容IE6,IE7,FF 3.支持input显示与value分离【以隐藏域方式提交数据】 4.支持下拉框按钮 5.支持显示结果数量 6.可以一个页面多次使用 7.模拟下拉框不会被select遮挡 8.调用更方便(内核已改为jq插件) 9.皮肤修改方便 ,外置的css样式文件,带有注释说明,任意修改 10.支持键盘方向键按住不放选择 11.解决了延卡死的现象 12.解决了在有滚动条,ie6下被表单遮挡的问题 13.支持回调函数。当该插件执行完毕,执行另外一个指定函数 ######V1.7解决问题############ 14.可以自己设置提示框的长度 15.解决了无法获取用户填写默认值的问题 16.当输入框失去焦点,提示框会自动隐藏 ######V1.8解决问题############ 17.解决了鼠标无法选择的问题 ****************************************************************** 文件使用说明: 在需要使用的页面首先要引用2个js文件 <script src="jquery1.2.6.min.js"></script> <script src="search_suggest.js"></script> 此二文件必须放于调用函数之前 *****************************以下是调用示例*************************: var option = { inputName : "test2", inputText : "测试字符串4", inputValue: "我是值", inputlength:1, url : "search.asp", arrow :1, arrowUrl :"search.asp?aa=nowitest", zIndex:7, fns:function(){alert(3)} } $("#test").suggestShow(option) +++++++解释说明+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ option:为参数设置对象。option内的所有参数可以省略 inputName 表单input的name.即 提交获取input的name。飞!飞Asp技术乐园也为search.asp页面获取input的name inputText inputName 所显示的数据 inputValue inputName 所提交的数据,当inputValue省略,inputValue则与inputText相等 url 远程执行获取数据的url arrow 模仿select,使用下拉按钮。飞飞A~sp技术乐园固定值。为1则显示下拉箭头 为0则不显示 arrowUrl 当arrow为1 ,默认情况下点击下拉按钮所执行的远程搜索页面 zIndex z-index值 inputlength inputName中输入数据的最小长度 suggestWidth 提示框的长度 fns 当执行完该插件,执行的回调函数 ************************************************************************ search.asp页面介绍: 该页面返回数据必须是xml格式,输出数据数据必须符合以下格式 <response value="1" result ="共2个结果">显示信息</response> 属性介绍: 1.value aa的value值,可以不填。不填value值则取"显示信息" 2.result 显示远程获取的统计结果,可以不填。 3.显示信息 即aa的显示值text值 ************************************************************************
Android中实现仿微信朋友圈底部的拍照和拍摄功能,可以通过使用系统自带的相机应用或者自定义相机来进行实现。 首先,可以使用系统自带的相机应用来实现拍照和拍摄的功能。可以通过调用Intent,使用ACTION_IMAGE_CAPTURE和ACTION_VIDEO_CAPTURE来启动相机应用,并通过设置Uri来指定图片或视频的保存路径。在拍摄完成后,可以在onActivityResult()方法中获取拍摄结果进行处理,比如保存图片或视频的Uri,显示预览等。 其次,也可以自定义相机来实现拍照和拍摄的功能。可以通过使用Camera和Camera2 API来实现相机的预览和拍摄功能。首先,需要使用Camera或Camera2 API来启动相机预览,并通过设置SurfaceHolder或SurfaceTexture来显示预览画面。同,可以监听相机的拍照或拍摄按钮点击事件,在点击进行拍照或拍摄操作,并通过设置相机的PictureCallback或CaptureCallback来处理拍摄结果。 无论是使用系统相机应用还是自定义相机,还可以添加一些额外的功能来实现更好的用户体验。比如,可以添加美颜滤镜、闪光灯控制、切换前后摄像头等功能。 总而言之,在Android中实现仿微信朋友圈底部的拍照和拍摄功能可以通过使用系统自带相机应用或自定义相机来实现,为用户提供类似微信朋友圈的相机功能,并可以根据需求添加额外的功能来增强用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值