在开发过程中遇到这样的一个需求
需求如下
这也是在开发过程中遇到的一个需求,一开始以为这样的需求其实挺简单, 我们可以有很多种方式来实现,比如自定义dialog , 在界面添加一个输入框弄一个键盘自适应改变view的高度,使用Fragment 使用Dialog 样式来实现。
这次开发都没有用到上面的几种,由于也是实际开发过程中遇到的需求,时间比较紧,也就不用一一去尝试了。
自定义PopWindow来实现上述需求
大概的思路
自定义PopWindow ,
点击评论输入的位子调出键盘
使用回掉函数监听键盘的打开以及隐藏
在键盘打开的时候穿一个键盘高度的参数设置Pop里面View显示的高度
然后由于中间hint带有键盘,那么中间可以使用RelativeLayout 来实现(具体布局实现就不说明了后面有会将代码贴出来)
主要大概的是这么几步操作。 本以为这样是非常顺利的,很快的就能实现这个功能,没想到后面遇到了很多问题。 后面会将问题一一道来
布局代码--
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/ll_dynamic_commecnt"
android:layout_width="match_parent"
android:layout_height="@dimen/x127"
android:orientation="vertical">
<!-- 显示头像 -->
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/x5"
android:layout_marginEnd="@dimen/x10"
android:layout_marginStart="@dimen/x10"
android:layout_marginTop="@dimen/x15"
android:visibility="gone">
</android.support.v7.widget.RecyclerView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/x60"
android:layout_margin="@dimen/x10"
android:background="@drawable/shape_input_comment_bg">
<!-- 最多输入100个文字 -->
<qudaqiu.shichao.wenle.view.editor.RichEditor
android:id="@+id/et_input"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/x10"
android:background="@null"
android:gravity="left"
android:inputType="textMultiLine"
android:maxLength="100"
android:textSize="14sp" />
<!-- 制作简单 -->
<LinearLayout
android:id="@+id/ll_input_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/x15"
android:layout_marginTop="@dimen/x10"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/input_comments" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/x10"
android:text="期待您的神评论"
android:textColor="@color/gray_need" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/x33"
android:layout_marginBottom="@dimen/x10"
android:layout_marginEnd="@dimen/x10"
android:layout_marginStart="@dimen/x10">
<ImageView
android:id="@+id/iv_add_img"
android:layout_width="@dimen/x33"
android:layout_height="@dimen/x33"
android:src="@mipmap/reply_images" />
<ImageView
android:id="@+id/iv_ait_name"
android:layout_width="@dimen/x33"
android:layout_height="@dimen/x33"
android:layout_marginStart="@dimen/x12"
android:layout_toRightOf="@+id/iv_add_img"
android:src="@mipmap/reply_add" />
<TextView
android:id="@+id/tv_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:background="@drawable/shape_reply_submit_bg"
android:paddingBottom="@dimen/x3"
android:paddingEnd="@dimen/x15"
android:paddingStart="@dimen/x15"
android:paddingTop="@dimen/x3"
android:text="发送"
android:textSize="14sp" />
</RelativeLayout>
</LinearLayout>
</layout>
其中RichEditor 继承 AppCompatEditText 自定义view,@用户显示变色,顺便带有@用户全部删除的功能, 这个开源库的地址: https://github.com/JustYJQ/RichEditor
图片显示使用:RecyclerView --> GridLayoutManager(5) 一行显示5个布局格子
准备工作完成,布局已经完成,现在开始关键性的代码
如何来实现CommectDialog,我们需要考虑那些问题?
针对这些问题我建议根据实际情况来考虑,先将大致与activity交互的位子整理出来,其中它需要activity传递那些参数,这些是需要弄清楚的,只有弄清楚了这些东西才好设置一些接口。(根据实际情况来定,目前所说的情况只是符合我当前的项目,后面我会弱化项目本身,从一个工具来来讲解)
问题
有点击事件 at用户 图片选择
有提交按钮,携带那些参数到上个界面,评论空内部的数据,选择的图片,at的用户。
图片选择有数量限制(图片选择需要用到activity跳转activity一个比较常用的一个方法:sstartActivityForResult(* , *),不是很了解的可以自行百度)选择好了图片携带过来首先接收页面是当前activity 然后 -- > CommentDialog )
问题很清楚了,我们需要一个接口用于回调处理CommentDialog某些点击事件
接口设置: Listener --- >
interface Listener{
onClickCheckUser(); // 选择 用户
onClickCheckPictrue(); // 选择图片
OnClickSubmit(); // 提交评论
}
class CommentDialog{
private List<String> imgs = new ***** ; //选择图片的地址
/**
******
*/
}
处理完上述问题。那么接下来的代码得去实现那些。
- 布局已经搭建完成,那么将布局放到Popwindow中去,得让它显示出来
这是整个项目中关键的代码之一
/**
* show comment popupwindow(弹出评论的popupWindow)
*
* @param type 类型 扩展
* @param height_view 软键盘高度
*/
@SuppressLint("WrongConstant")
public void showPopupCommnet(final int pid, final int type, final int height_view) {// pe表示是评论还是举报1.代表评论。2.代表举报
final View view = LayoutInflater.from(context).inflate(
R.layout.view_dynamic_comment_input, null); //使用LayoutInflater 加载布局
inputComment = (RichEditor) view.findViewById(R.id.et_input); //评论框
ll_input_icon = view.findViewById(R.id.ll_input_icon); //中间的hint
// 点击事件
btn_submit = view.findViewById(R.id.tv_submit);
iv_add_img = view.findViewById(R.id.iv_add_img);
iv_ait_name = view.findViewById(R.id.iv_ait_name);
recyclerView = view.findViewById(R.id.recyclerView); // 图片显示Recyclerview
// btn_submit.setOnClickListener(this);
iv_add_img.setOnClickListener(this);
iv_ait_name.setOnClickListener(this);
// 首先进来设置光标不可见
inputComment.setCursorVisible(false);
// 初始化PopWindow 将布局放入 view
popupWindow = new PopupWindow(view, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT, true);
popupWindow.setTouchable(true); // 设置屏幕点击事件
popupWindow.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) { // 处理屏幕点击事件
return false; // 此处返回false 不然点击屏幕事件会被外部抢夺
}
});
popupWindow.setFocusable(true);
// 设置点击窗口外边窗口消失 下面为绑定代码,点击外边窗口消息主要代码是 setBackgroundDrawable
popupWindow.setOutsideTouchable(true);
popupWindow.setBackgroundDrawable(context.getResources().getDrawable(
R.drawable.popuwindow_white_bg));
// 设置弹出窗体需要软键盘
// popupWindow.setSoftInputMode(PopupWindow.INPUT_METHOD_NEEDED);
// 再设置模式,和Activity的一样,覆盖,调整大小。
popupWindow
.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
// 显示的位子 height_view -- activit
popupWindow.showAtLocation(view, Gravity.BOTTOM, 0, height_view);
ColorDrawable cd = new ColorDrawable(0x333333); // 改变覆盖在界面上面的问题
popupWindow.setBackgroundDrawable(cd);
WindowManager.LayoutParams params = context.getWindow().getAttributes();
params.alpha = 0.4f;
context.getWindow().setAttributes(params);
// 设置popWindow的显示和消失动画
popupWindow.setAnimationStyle(R.style.mypopwindow_anim_style);
popupWindow.update();
// 更新UI-- 开启新的线程更新ui。 这里为什么会有一个handle 后面会讲解其中这和RcycleView 显示图片数据有关系
Message msg = Message.obtain();
myHandler.sendMessage(msg);
popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
// 在dismiss中恢复透明度
public void onDismiss() {
WindowManager.LayoutParams params = context.getWindow().getAttributes();
params.alpha = 1f;
context.getWindow().setAttributes(params);
}
});
btn_submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String comment = inputComment.getText().toString().trim();
if (comment.length() <= 0) {
if (type == 1) {
Toast.makeText(context, "评论内容不能为空",
Toast.LENGTH_SHORT).show();
}
return;
}
boolean b = checkBlackList(comment);
if (!b) {
// 包含非法关键字
Utils.toastMessage(context, "您输入咨询订单含有敏感词汇");
return;
}
// 提交评论
if (onSubmitCommentListener != null) {
onSubmitCommentListener.onSubmitComment(comment, selectPhotos, aitTattooistData);
}
}
});
inputComment.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
// 设置光标显示
if (editable.toString().length() > 0) {
ll_input_icon.setVisibility(View.GONE);
inputComment.setCursorVisible(true);
} else {
ll_input_icon.setVisibility(View.VISIBLE);
inputComment.setCursorVisible(false);
}
}
});
// 删除at数据。。。 开源第三方内部编写好的
inputComment.setOnDeleteAtListener(this);
}
这样整个popwindow布局已经搭建好了, 其中很多地方本该用回调的位子我没用,此处考虑这个只用于目前开发的项目,其中还有写界面是需要复用到这个界面也就没有在使用上面将的那几个接口问题,直接是在内部处理了一些点击事件,然后在另外需要评论的界面调用这个界面,点击事件就不用在去重新写了。
- 这个问题主要是弹出键盘的问题。
/**
* 弹出键盘
* 目前有一个bug。。 小米手机不能弹出 魅族可以弹出
*/
public void popupInputMethodWindow() {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Service.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
}
这个方法是在键盘没有弹出的时候点击,键盘会弹出,键盘弹出的时候点击会隐藏
public class SoftKeyBoardListener {
private View rootView;//activity的根视图
int rootViewVisibleHeight;//纪录根视图的显示高度
private OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener;
public SoftKeyBoardListener(Activity activity) {
//获取activity的根视图
rootView = activity.getWindow().getDecorView();
//监听视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//获取当前根视图在屏幕上显示的大小
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int visibleHeight = r.height();
if (rootViewVisibleHeight == 0) {
rootViewVisibleHeight = visibleHeight;
return;
}
//根视图显示高度没有变化,可以看作软键盘显示/隐藏状态没有改变
if (rootViewVisibleHeight == visibleHeight) {
return;
}
//根视图显示高度变小超过200,可以看作软键盘显示了
if (rootViewVisibleHeight - visibleHeight > 200) {
if (onSoftKeyBoardChangeListener != null) {
onSoftKeyBoardChangeListener.keyBoardShow(rootViewVisibleHeight - visibleHeight);
}
rootViewVisibleHeight = visibleHeight;
return;
}
//根视图显示高度变大超过200,可以看作软键盘隐藏了
if (visibleHeight - rootViewVisibleHeight > 200) {
if (onSoftKeyBoardChangeListener != null) {
onSoftKeyBoardChangeListener.keyBoardHide(visibleHeight - rootViewVisibleHeight);
}
rootViewVisibleHeight = visibleHeight;
return;
}
}
});
}
private void setOnSoftKeyBoardChangeListener(OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
this.onSoftKeyBoardChangeListener = onSoftKeyBoardChangeListener;
}
public interface OnSoftKeyBoardChangeListener {
void keyBoardShow(int height);
void keyBoardHide(int height);
}
public static void setListener(Activity activity, OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
SoftKeyBoardListener softKeyBoardListener = new SoftKeyBoardListener(activity);
softKeyBoardListener.setOnSoftKeyBoardChangeListener(onSoftKeyBoardChangeListener);
}
}
上述类是用来监听键盘弹出或者隐藏
在activity里面调用。 在下面使用的过程中会看到我有用到一些boolean类型的参数来控制,这是由于在开发过程中遇到一些问题,AActivity 跳转 BActivity 的时候在回来的时候键盘是被隐藏了。
/**
* 软键盘弹出来监听
*/
SoftKeyBoardListener.setListener(this, object : SoftKeyBoardListener.OnSoftKeyBoardChangeListener {
override fun keyBoardShow(height: Int) {
if (is_show_keybord) {
if (dialog.isIsShowDialog) {
// 显示键盘
view_height = height
dialog.showPopupCommnet(1, 1, height)
// 判断软键盘是否弹出
}
dialog.isIsShowDialog = true
}
}
override fun keyBoardHide(height: Int) {
is_show_keybord = true
// 隐藏键盘
if (dialog.popupWindow != null) {
if (dialog.isIsShowDialog) { // 这一步操作还是显示的
dialog.setDismiss()
}
}
}
})
处理开发过程中遇到的问题
第一个问题:AActivity 跳转 BActivity的时候键盘隐藏,某些手机不知道原因键盘不能使用上述的方法popupInputMethodWindow调用
关于这个问题,测试手机不多,目前发现在红米手机上是有这个问题的。就是A跳转B之后然后finish回来,A界面的软键盘是被关闭了的,然后使用上述popupInputMethodWindow方法准备调用键盘的时候是没有效果,开始怀疑是A界面的焦点没有获取得到,后面打印日志发现不是这个问题,焦点获取得到键盘还是没有调用起来。
然后针对这个问题,我就想,能不能让popwindow不隐藏,也就出现了上述一个参数is_show_keybord ,当跳转其他键盘的时候设置这个参数为false,然后在回来的时候也就不会隐藏popwindow了,或者不显示dialog。一面多次显示dialog重复出现。
上述处理的是显示问题,另外还有一个问题,也是最根本的问题,键盘显示问题,由于跳转键盘被隐藏了,那么我们需要重新调用起来键盘 ,否者键盘是不可见的
// kotlin 写法
KeybordSUtils.openKeybord(dialog.inputComment, this@WenFastDetailActivity)
/**
* 打开软键盘
*
* @param mEditText
* @param mContext
*/
public static void openKeybord(EditText mEditText, Context mContext) {
InputMethodManager imm = (InputMethodManager) mContext
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
这样上面的问题就解决了。
问题二,RecyclerView的显示问题
在写这些之前是没有想到过会有这么多问题,因为Recycleview做一个显示这样还不简单吗?事实上在这块是有一个问题而且和上述有点关联,就是当recyclerview上面显示数据的时候,会导致软键盘隐藏。具体是什么原因目前还不太清楚,这个问题也是出现在个别手机上。
处理方法:
// 更新UI-- 开启新的线程更新ui。
Message msg = Message.obtain();
myHandler.sendMessage(msg);
/**
* 实现监听
*/
private void notifyChange() {
if (recyclerView == null) {
LogUtils_java.e("recyclerView 不能为空");
return;
}
if (selectPhotos.size() > 0) {
recyclerView.setVisibility(View.VISIBLE);
photoAdapter = new CommentImgAdapter(R.layout.item_comment_img_ad, selectPhotos);
recyclerView.setLayoutManager(new GridLayoutManager(context, 5));
recyclerView.setAdapter(photoAdapter);
} else {
recyclerView.setVisibility(View.GONE);
}
if (photoAdapter != null) {
photoAdapter.setOnItemChildClickListener(this);
}
}
// 使用handler。。 刷新数据 处理由于recyclerview在主线程刷新导致软键盘隐藏问题
@SuppressLint("HandlerLeak")
private Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
notifyChange();
super.handleMessage(msg);
}
};
处理完上述问题,关于软键盘的问题也就处理完了。