参考文章:仿微信朋友圈点击评论自动定位到相关行
一、思路:
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(0, itemBottomY - y);
}
}, 300);
}
获取屏幕中控件左上顶点y坐标方法
/**
* 获取控件左上顶点 y 坐标
*
* @param view
* @return
*/
private int getCoordinateY(View view) {
int[] coordinate = new int[2];
view.getLocationOnScreen(coordinate);
return coordinate[1];
}
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)//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() - 1) {
adapter.data.add(new BottomBean());
adapter.bottomHeight = llCommentInput.getHeight();
adapter.notifyDataSetChanged();
}
//滑动 RecyclerView,是对应 item 底部和输入框顶部对齐
rv_content.smoothScrollBy(0, itemBottomY - y);
}
}, 300);
}
隐藏输入框时的处理:
/**
* 监听键盘的显示和隐藏
*/
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)//0.8只是一个大致的比例,可以修改
if (null != dialog && dialog.isShowing()) {
dialog.dismiss();
//如果添加了空白 item ,移除空白 item
if (adapter.data.get(adapter.data.size() - 1) instanceof BottomBean) {
adapter.data.remove(adapter.data.size() - 1);
adapter.notifyDataSetChanged();
}
}
}
GitHub 地址:https://github.com/YINQIEIE/weixinCommnetDemo