微信朋友圈评论功能的实现步骤

最近公司配套智能自行车的App要做发布骑行活动的功能,这就不可避免的要模仿微信朋友圈的很多功能了,这篇文章主要介绍如何做出和微信一样的列表评论效果。


下面先放出实际运行效果:





首先介绍实现这个功能需要经历以下几步:

   1)封装方法,通过代码控制弹出和隐藏系统输入法;

   2)封装方法,获得手机键盘输入法的高度;

   3)代码控制ListView上下滚动指定的距离;

   4)自定义一个评论的输入Editext框和发送按钮的布局放在activity中,要求高度写死,并且 alignParentButton 设置为true,在系统输入法显示的时候设置为 Visible,平时设置为 Gone(这个太简单,下面就不写了);

   5)在用户点击评论时,计算出ListView需要上下滚动的距离;

   6)封装方法,通过代码模拟点击事件确保评论输入的EditText获得焦点;


下面就按照以上知识点的顺序上代码:

   1)弹出和隐藏系统输入法:

    (1)显示系统输入法的关键代码(如果此时输入法已经显示,调用它就隐藏输入法)

  final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
  imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);

    (2)强制隐藏系统输入法的关键代码

  InputMethodManager imm = (InputMethodManager) mInstance.getSystemService(Context.INPUT_METHOD_SERVICE);
  imm.hideSoftInputFromWindow(windowToken, 0);
 // weindowToken参数可以通过页面上任意控件的 getWindowToken() 方法获得,我一般习惯用listview调用此方法


   2)获得手机键盘输入法的高度

     由于这个高度不是统一的,所以每次显示系统输入法的时候最好都要调用它进行测量,以下为关键代码:

 
  // 当输入法弹出完毕之后,获取当前页面窗口的显示范围,并以此计算输入法的高度
  Rect rect = new Rect();
  getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);  // 获取当前手机屏幕显示内容的范围
  // 以下两行代码用于获取手机屏幕整体高度

  WindowManager windowManager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);
  int screenHeight = windowManager.getDefaultDisplay().getHeight(); // 得到手机屏幕整体高度

  // 屏幕高度 - 当前正在显示内容范围的高度 = 键盘实际高度
 int keyBoardHeight = screenHeight - rect.bottom;
 

     方法调用注意事项:由于系统键盘弹出需要时间,因此需要在调用弹出系统输入法之后的 500毫秒 才能开始获取键盘高度,键盘处于隐藏状态时 keyBoardHeight 为 0

   3)代码控制ListView上下滚动指定的距离

    (这部分代码出自百度经验:http://jingyan.baidu.com/article/e6c8503c0564cbe54f1a18e7.html)

     获得了键盘的高度后,就要让 ListView  被评论的条目在键盘弹出后正好置于评论输入框的上方了。但是ListView并没有暴露此方法,因此我们需要反射ListView的 trackMotionScroll() 方法来控制ListView控件的滚动距离了。

     以下是关键代码:

 /**
  * ListView自动向上移动需调用的方法
  * @param listview 需要自动向上移动的ListView控件
  * @param activity ListView 所在的Activity
  * @param y ListView 需要滚动的像素距离,正数为向上滚动,负数向下滚动
 */
  public static void scrollVertical(final ListView listView, Activity activity, final int y) {
      if (listView == null)
          return;
      activity.runOnUiThread(new Runnable() { //执行自动化测试的时候模拟滑动需要进入UI线程操作
          @Override
          public void run() {
              invokeMethod(listView, "trackMotionScroll", new Object[]{-y, -y}, new Class[]{int.class, int.class});
          }
      });
  }

// 以下是上面代码片段 invokeMethod 方法的实际实现

/**
  * 遍历当前类以及父类去查找方法,例子,写的比较简单 (ListView自动向上移动方法中调用)
  * @param object
  * @param methodName
  * @param params
  * @param paramTypes
  * @return
  */
  public static Object invokeMethod(Object object, String methodName, Object[] params, Class[] paramTypes) {
      Object returnObj = null;
      if (object == null) {
          return null;
      }
      Class cls = object.getClass();
      Method method = null;
      for (; cls != Object.class; cls = cls.getSuperclass()) { //因为取的是父类的默认修饰符的方法,所以需要循环找到该方法
          try {
              method = cls.getDeclaredMethod(methodName, paramTypes);
              break;
          } catch (NoSuchMethodException e) {
             // e.printStackTrace();
          } catch (SecurityException e) {
             // e.printStackTrace();
          }
      }
      if (method != null) {
          method.setAccessible(true);
          try {
              returnObj = method.invoke(object, params);
          } catch (IllegalAccessException e) {
              e.printStackTrace();
          } catch (IllegalArgumentException e) {
              e.printStackTrace();
          } catch (InvocationTargetException e) {
              e.printStackTrace();
          }
      }
      return returnObj;
  }


   5)ListView上下滚动距离的计算     知道如何控制ListView上下滚动之后,我们就可以开始计算ListView在输入法弹出时应当滚动多少距离了。核心思路如下:
      (1)借助评论按钮的点击事件获取评论按钮在屏幕上的大致位置
      (2)通过ListView的条目布局获得评论按钮距离条目底部的像素值,并反推出当前条目底部在手机屏幕上的位置
      (3)把当前条目底部在手机屏幕上的位置和系统输入法的位置坐对比,计算出差值,此差值就是ListView需要移动的距离
        以下为关键代码:

// 注意,以下代码在 ListView 的 Adapter 下的 getView() 方法中!
        // 当回复按钮按下的时候
        viewHolder.ll_replay_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 首先获取这个点击事件发生的位置
                int[] location = new int[2];
                v.getLocationOnScreen(location);
                int onClickY = location[1];
                // onClickY就是这个点击事件在Y轴的发生位置,也就是被点击的评论按钮在屏幕Y轴的大体位置

                /**
                 * 根据自己ListView条目的布局来确定评论按钮距离当前Item底部的距离
                 * 为方便阅读,我统一这个距离为10dp
                 */
                // 根据不同的手机分辨率算出 10dp 所对应的像素值
                float density = context.getResources().getDisplayMetrics().density;
                int px = (int) (10 * density + .5);  // 获得 10dp 所对应的像素值

                // 按钮的Y轴位置 + 评论按钮距离条目底部的像素长度 = 条目底部的Y轴位置
                int itemButtonY = onClickY + px;

                // 调用方法显示系统输入法(第一步已经有关键代码了,省略)
                // 调用方法获得键盘的高度(第二步已经有关键代码了,省略)
                // 开始计算ListView需要上下滚动的距离
                WindowManager windowManager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);
                int screenHeight = windowManager.getDefaultDisplay().getHeight(); // 得到手机屏幕整体高度
                // listView需要上下滚动的距离 = 被评论条目底部的Y轴位置 - (屏幕高度 - 输入法高度 - 自定义评论输入布局高度)
                int listViewScrollDistance = itemButtonY - (screenHeight - keyBoardHeight - replyInputLayoutHeight);

                // 调用方法让ListView滚动到指定的位置(第三步已经有关键代码了,省略)
            }
        });

      注意事项:

      (1)假如当前用户评论的是ListView中最后一个条目,则无需再计算它的滚动位置了,直接调用 scrollToPosition(int position) 方法将条目滚动到最后即可。
      (2)此外,我们还需要在 ListView 的最后加上一个固定的 footerView,这个 view 的高度必须和评论输入布局的高度一样,如果不加这个 footerView 最后一个 item 在被评论的时候会被评论输入布局遮挡住部分内容


   6)代码模拟点击事件使EditText获取焦点
        由于在部分手机上发现EditText并不会自动获取焦点,因此我使用了这个简单暴力的方式 ╮(╯▽╰)╭
        使用很简单,输入想要被点击的 View,然后随意输入这个View的相对坐标,然后它就被点击了,很好用的方法
        上关键代码:

 /**

  * 模拟控件点击事件
  * @param view 控件(这里指的就是评论的EditText)
  * @param x 控件相对x坐标
  * @param y 控件相对y坐标
  */
  public static void setSimulateClick(View view, float x, float y) {
      long downTime = SystemClock.uptimeMillis();
      final MotionEvent downEvent = MotionEvent.obtain(downTime, downTime,
              MotionEvent.ACTION_DOWN, x, y, 0);
      downTime += 1000;
      final MotionEvent upEvent = MotionEvent.obtain(downTime, downTime,
              MotionEvent.ACTION_UP, x, y, 0);
      view.onTouchEvent(downEvent);
      view.onTouchEvent(upEvent);
      downEvent.recycle();
      upEvent.recycle();
  }

      注意事项:为了保证输入框在获得焦点的时候,光标就在所有文字的后面,建议最好把评论EditText的宽度和高度写死。这样,我们就可以获取到评论EditText最右下角的坐标了,点击这个坐标就可以确保光标永远在所有文字之后。


到此为止,所有的主要功能就算完成了。隐藏系统输入法的逻辑各位可以写在 onBackPress()  方法中,以及复写的ListView 的 onTouchListener 方法中。
由于公司代码不便公开,暂时还不能提供Demo,等有时间整理代码的时候我会公布给大家。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
继“Java开发微信朋友圈PC版系统-架构1.0”之后,debug这段时间日撸夜撸,终于赶在春节放假前给诸位带来了这一系统的架构2.0版本,特此分享给诸位进行学习,以掌握、巩固更多的技术栈以及项目和产品开发经验,同时也为即将到来的金三银四跳槽季做准备! 言归正传,下面仍然以问答的方式介绍下本门课程的相关内容! (1)问题一:这是一门什么样的课程? 很明显,本门课程是建立在架构1.0,即 第1门课程 的基础上发布的,包含了架构1.0的内容,即它仍然是一门项目、产品实战课,基于Spring Boot2.X + 分布式中间件开发的一款类似“新浪微博”、“QQ空间”、“微信朋友圈”PC版的互联网社交软件,包含完整的门户网前端 以及 后台系统管理端,可以说是一套相当完整的系统! (2)问题二:架构2.0融入了哪些新技术以及各自有什么作用? 本课程对应着系统架构2.0,即第2阶段,主要目标:基于架构1.0,优化系统的整体性能,实现一个真正的互联网社交产品;其中,可以学习到的技术干货非常多,包括:系统架构设计、Spring Boot2.X、缓存Redis、多线程并发编程、消息中间件RabbitMQ、全文搜索引擎Elastic Search、前后端消息实时通知WebSocket、分布式任务调度中间件Elastic Job、Http Restful编程、Http通信OKHttp3、分布式全局唯一ID、雪花算法SnowFlake、注册中心ZooKeeper、Shiro+Redis 集群Session共享、敏感词自动过滤、Java8 等等; A.  基于Elastic Search实现首页列表数据的初始化加载、首页全文检索;B.  基于缓存Redis缓存首页朋友圈“是否已点赞、收藏、关注、评论、转发”等统计数据;整合Shiro实现集群部署模式下Session共享;C.  多线程并发编程并发处理系统产生的废弃图片、文件数据;D.  基于Elastic Job切片作业调度分布式多线程清理系统产生的废弃图片;E.  基于RabbitMQ解耦同步调用的服务模块,实现服务模块之间异步通信;F.  基于WebSocket实现系统后端 与 首页前端 当前登录用户实时消息通知;G.  基于OKHttp3、Restful风格的Rest API实现ES文档、分词数据存储与检索;H.  分布式全局唯一ID 雪花算法SnowFlake实现朋友圈图片的唯一命名;I.  ZooKeeper充当Elastic Job创建的系统作业的注册中心;J.  为塑造一个健康的网络环境,对用户发的朋友圈评论、回复内容进行敏感词过滤;K.  大量优雅的Java8  Lambda编程、Stream编程;  (3)问题三:系统运行起来有效果图看吗?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值