天天看点

仿微信朋友圈点击评论自动定位到对应位置

参考文章:仿微信朋友圈点击评论自动定位到相关行

一、思路:

1、在点击【评论】控件的时候弹出一个 dialog,dialog 上面为 ScrollView,下面为输入框,实现键盘弹出时把输入框顶上去的效果;

2.计算 RecyclerView 应该滑动的距离;

3.监听键盘的显示和隐藏,隐藏时将 dialog dismiss;

在写的过程中做了一点修改:

1.在为列表中【评论】控件设置点击事件回调时将整个 itemView 作为参数传递给回调方法,觉得这样会容易理解一些,也能直接涵盖【评论】控件设置了 margin 的情况,即输入框上部是和列表对应 item 底部对齐的;

2.添加点击 dialog 中 scrollView,dialog 消失的事件;

二、实现步骤及代码片段

1.列表的显示不用说,看下【评论】点击事件代码:

//评论按钮点击事件
itemView.findViewById(R.id.tv_comment).setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
   //这里将整个 itemView 传递过去,position 没用到,留着没什么影响 :)
  ((MainActivity) mContext).showInputDialog(itemView, getAdapterPosition());
                }
            });
           

showInputDialog 方法在 MainActivity 中:

/**
     * 显示评论输入 dialog
     *
     * @param itemView
     * @param position
     */
    public void showInputDialog(View itemView, int position) {
        final int itemBottomY = getCoordinateY(itemView) + itemView.getHeight();//item 底部y坐标
        dialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar);
        View view = LayoutInflater.from(this).inflate(R.layout.dialog_input, null);
        dialog.setContentView(view);
        //scrollView 点击事件,点击时将 dialog dismiss,设置 onClick 监听无效
        dialog.findViewById(R.id.scrollView).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP)
                    dialog.dismiss();
                return true;
            }
        });
        dialog.show();
        itemView.postDelayed(new Runnable() {
            @Override
            public void run() {
                LinearLayout llCommentInput = dialog.findViewById(R.id.ll_comment_input);
                int y = getCoordinateY(llCommentInput);
                Log.i("display", "itemBottomY = " + itemBottomY + "  input text y = " + y);
                //滑动 RecyclerView,是对应 item 底部和输入框顶部对齐
                rv_content.smoothScrollBy(, itemBottomY - y);
            }
        }, );
    }
           

获取屏幕中控件左上顶点y坐标方法

/**
     * 获取控件左上顶点 y 坐标
     *
     * @param view
     * @return
     */
    private int getCoordinateY(View view) {
        int[] coordinate = new int[];
        view.getLocationOnScreen(coordinate);
        return coordinate[];
    }
           

2.监听键盘的显示和隐藏做相关操作

有两种方法:

(1)

getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                handleWindowChange();
            }
        });
           

(2)

getWindow().getDecorView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                handleWindowChange();
            }
        });
           

对应的 handleWindowChange() 方法:

/**
     * 监听键盘的显示和隐藏
     */
    private void handleWindowChange() {
        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);//获取当前界面显示范围
        Log.i("display  ", "top = " + rect.top);
        Log.i("display  ", "bottom = " + rect.bottom);
        int displayHeight = rect.bottom - rect.top;//app内容显示高度,即屏幕高度-状态栏高度-键盘高度
        int totalHeight = getWindow().getDecorView().getHeight();
        //显示内容的高度和屏幕高度比大于 0.8 时,dismiss dialog
        if ((float) displayHeight / totalHeight > )//0.8只是一个大致的比例,可以修改
            if (null != dialog && dialog.isShowing())
                dialog.dismiss();
    }
           

3.点击最后一项 item 的时候特殊处理:

原本键盘弹出时,会自动把 recyclerview 往上顶(当然,这跟当前 activity 设置的 windowSoftInputMode 有关),但是,现在键盘弹出时上面多了一个输入的布局,会导致在点击最后一个 item 的时候被遮挡一部分,所以,最后一个 item 点击时要做特殊处理,即给 recyclerView 添加一个和输入布局等高的 item。方法如下:

输入框弹出时候的处理:

/**
     * 显示评论输入 dialog
     *
     * @param itemView
     * @param position
     */
    public void showInputDialog(View itemView, final int position) {
        final int itemBottomY = getCoordinateY(itemView) + itemView.getHeight();//item 底部y坐标
        dialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar);
        View view = LayoutInflater.from(this).inflate(R.layout.dialog_input, null);
        dialog.setContentView(view);
        //scrollView 点击事件,点击时将 dialog dismiss,设置 onClick 监听无效
        dialog.findViewById(R.id.scrollView).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP)
                    dialog.dismiss();
                return true;
            }
        });
        dialog.show();
        itemView.postDelayed(new Runnable() {
            @Override
            public void run() {
                LinearLayout llCommentInput = dialog.findViewById(R.id.ll_comment_input);
                int y = getCoordinateY(llCommentInput);
                Log.i("display", "itemBottomY = " + itemBottomY + "  input text y = " + y);
                //最后一个 item 特殊处理,添加一个和输入框等高的 item,使 RecyclerView 有足够的空间滑动
                if (position == adapter.data.size() - ) {
                    adapter.data.add(new BottomBean());
                    adapter.bottomHeight = llCommentInput.getHeight();
                    adapter.notifyDataSetChanged();
                }
                //滑动 RecyclerView,是对应 item 底部和输入框顶部对齐
                rv_content.smoothScrollBy(, itemBottomY - y);
            }
        }, );
    }
           

隐藏输入框时的处理:

/**
     * 监听键盘的显示和隐藏
     */
    private void handleWindowChange() {
        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);//获取当前界面显示范围
        Log.i("display  ", "top = " + rect.top);
        Log.i("display  ", "bottom = " + rect.bottom);
        int displayHeight = rect.bottom - rect.top;//app内容显示高度,即屏幕高度-状态栏高度-键盘高度
        int totalHeight = getWindow().getDecorView().getHeight();
        //显示内容的高度和屏幕高度比大于 0.8 时,dismiss dialog
        if ((float) displayHeight / totalHeight > )//0.8只是一个大致的比例,可以修改
            if (null != dialog && dialog.isShowing()) {
                dialog.dismiss();
                //如果添加了空白 item ,移除空白 item
                if (adapter.data.get(adapter.data.size() - ) instanceof BottomBean) {
                    adapter.data.remove(adapter.data.size() - );
                    adapter.notifyDataSetChanged();
                }
            }
    }
           

GitHub 地址:https://github.com/YINQIEIE/weixinCommnetDemo