当前位置: 首页 > news >正文

IM即时通讯项目讲解(一) 实现类似qq微信表情面板无缝切换

IM即时通讯项目讲解(一)--实现类似qq微信表情面板无缝切换

标签(空格分隔): 开源项目


###该系列技术课程来源慕课IM实战

  • 带后台的IM即时通讯App 全程MVP手把手打造 #####通过该课程可以学习到以下知识点
  • 1、了解和开发后台项目(这个是需要长期积累的,有了这个可以说入门没问题)
  • 2、学习到IM相关知识点,创建群、添加群、单聊、群聊
  • 3、可以学习到数据库的相关操作(建表、表之间的关联等知识)
  • 4、学习到MVP模式,更加深入了解MVP模式的架构和实现
  • 5、学习到关于IM相关的优化,比如如何快速刷新界面
  • 6、学习到如何进行推送等相关操作(服务器端推送,单推、群推)
  • 7。。。当然还有好多的,大家不妨去了解一下,学习到知识才是最重要的

###首先看下效果图(无图耍流氓)

###前言 这里首先给个完整的项目地址,该项目是一个完整的IM即时通信。涵盖的功能群聊、单聊、创建群组、添加好友等。。。。大家可以下载看下.相关功能点readme.md中都有简介。

这个项目也算是一个完整的项目,并且运用了一些比较不错的知识点,还有就是项目中的一些小功能。在我们以后的开发或者写的项目中可能会有启发和用途,因此在这里通过写文章记录一下,一方面也是对于所学知识的巩固(正所谓码了也不一定会、会了也不一定总结出来、总结出来也不一定讲出来),正是这个道理,因为总结出来和自己会不是一个境界,能讲出来境界又是提高一层。所以大家不要只停留在码代码的一层,要做一名有追求的程序员(虽然我自己在码代码的道路上越走越远,但是始终不会放弃初心。加油---小菜鸟)

好了,说了那么多话,没一句说道本篇文章上,不过还要在说几句,关于这个IM即时通讯demo,我还是会写几篇文章对其进行总结的,也算是对于自己所学的一个考评吧。接下来会一到两周更新一篇。准备利用两个月时间消化吸收。

###此篇文章背景 在自己的项目中,用到了EditText输入框,这个是之前已经开发好的,但是有一个问题,那就是没有解决相应的网络出现的各种冲突(泪奔),然后在自己开发社区的时候,在社区详情,对于评论过多的时候在弹出软键盘的时候(当再次点击取消软键盘消失的时候)下面的评论会进行跳动(也就是说取消之后定位的地方不是刚开始弹出软键盘的地方),当然这个和项目初期的控件,评论机制有关系,现在评论做成了本地评论(有相关的需求的可以进行讨论哈),现在问题解决了,但是还有个体验不好的地方就是软键盘的切换闪屏问题,就是当切换表情的时候屏幕会跳动,因此这个也是之后进行的版本优化任务。现在在这个项目中解决了软键盘的切换问题,基本达到了类似微信、QQ软键盘表情切换效果,让你***体验如丝般顺畅***。好了,有如此好的方法能够解决软键盘、表情切换方法或者代码,那还不赶紧给大家推荐推荐,以方便广大老铁们的开发哈。好了废话不多说。看看怎么集成到项目中吧。 ###项目集成 原项目地址 Airpanel

####第一步 在module的build.gradle中加入(这个库很小,8个类,最大类也就200来行代码,小类几十行代码)

compile 'net.qiujuer.widget:airpanel:1.0.0'

复制代码

####第二步 创建空气面板布局lay_chat_air_panel.xml(这个里面就需要用到库中的自定的一个控件了)关于控件的使用在后面会进行相关讲解,这个地方想说明如何引入到项目中

<?xml version="1.0" encoding="utf-8"?>
<net.qiujuer.widget.airpanel.AirPanelLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/airPanelSubLayout"  
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="1px"
    android:visibility="gone">
<!--airPanelSubLayout这个根布局的id必须是这个-->


<!--这里放入一个fragment,在这个fragment里面实现表情、语音、图片、输入法的相关逻辑切换-->
    <fragment
        android:id="@+id/frag_panel"
        class="com.mingchu.cnim4android.fragment.panel.PanelFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:layout="@layout/fragment_panel" />
</net.qiujuer.widget.airpanel.>AirPanelLinearLayout
复制代码

这里给出这个fragment的相关逻辑实现(当然这个逻辑和软键盘没有任何关系了,只是负责控制哪个面板的显示和隐藏,因为所有的关于软键盘的控制都已经被AirPanelLinearLayout完成了)

public class PanelFragment extends BaseFragment implements FaceListener {

    //表情面板
    private View mFacePanel;
    //图片相册面板
    private View mGalleryPanel;
    //录音面板
    private View mRecordPanel;
    private PanelCallback mCallback;

    public PanelFragment() {
        // Required empty public constructor
    }


    @Override
    protected int getContentLayoutId() {
        return R.layout.fragment_panel;
    }


    @Override
    protected void initView(View view) {
        super.initView(view);
        initRecord(view);
        initFace(view);
        initGallery(view);
    }


    //初始化表情布局
    private void initFace(View root) {

        View facePanel = mFacePanel = root.findViewById(R.id.lay_face_panel);
        //todo  接下来就是相关的表情加载和相关点击的实现  这个有空就为大家在以后的文章中讲解
        //

    }

    /**
     * 初始化录音布局
     *
     * @param root 根布局
     */
    private void initRecord(View root) {
        View recordPanel = mRecordPanel = root.findViewById(R.id.lay_record_panel);
       //todo  接下来就是相关的语音录制相关点击的实现  这个有空就为大家在以后的文章中讲解

    }


    /**
     * 初始化图片画廊
     *
     * @param root 根布局
     */
    private void initGallery(View root) {
        View galleryPanel = mGalleryPanel = root.findViewById(R.id.lay_gallery_panel);
       //todo  接下来就是相关的图片展示和相关点击的实现  这个有空就为大家在以后的文章中讲解
    }


    /**
     * 发送
     *
     * @param galleryView
     * @param paths
     */
    private void onSendGalleryClick(GalleryView galleryView, String[] paths) {
        galleryView.clear();
        PanelCallback callback = mCallback;
        if (callback == null)
            return;
        callback.onSendGalleryClick(paths);
    }


    public void setup(PanelCallback callback) {
        this.mCallback = callback;
    }

    public boolean isOpenFace() {
        return mFacePanel.getVisibility() == View.VISIBLE;
    }


    public boolean isOpenMore() {
        return mGalleryPanel.getVisibility() == View.VISIBLE;
    }

    /**
     * 显示表情界面
     */
    public void showFace() {
        mFacePanel.setVisibility(View.VISIBLE);
        mGalleryPanel.setVisibility(View.GONE);
        mRecordPanel.setVisibility(View.GONE);
    }

    /**
     * 显示录音界面
     */
    public void showRecord() {
        mFacePanel.setVisibility(View.GONE);
        mGalleryPanel.setVisibility(View.GONE);
        mRecordPanel.setVisibility(View.VISIBLE);
    }

    /**
     * 显示图片界面
     */
    public void showGallery() {
        mFacePanel.setVisibility(View.GONE);
        mGalleryPanel.setVisibility(View.VISIBLE);
        mRecordPanel.setVisibility(View.GONE);
        GalleryView view = (GalleryView) mGalleryPanel.findViewById(R.id.view_gallery);
        view.clear();
    }

    public void showMore() {
        showGallery();
    }

    /**
     * 面板变化和相关输入输出的回调
     */
    public interface PanelCallback {

        //获取到输入的EditText的值
        EditText getInputEditText();

        /**
         * 点击发送按钮的时候发送图片地址集合
         *
         * @param paths 本地相册图片路径
         */
        void onSendGalleryClick(String[] paths);

        /**
         * 录音完成的时候回调
         *
         * @param file 录音文件
         * @param time 录音时长
         */
        void onRecordDone(File file, long time);
    }
}

复制代码

这个时候万事俱备,只差我们怎么使用了,不急,一步一步来吗,毕竟心急吃不了热豆腐。刚才创建的那个布局,我们放到我们需要使用的fragment或者activity的布局里面

<!--注意一点,根布局使用的是AirPanelLinearLayout-->

    <include layout="@layout/lay_chat_sub_air_panel"/>
    

复制代码

好了,看下如何在fragment编写相关逻辑吧,当然我这里使用的是fragment,activity里面使用的逻辑也是一样的。

    //第一步

  //刚才我们实现的面板fragment
    private PanelFragment mPanelContent;
    //接口
    private AirPanel.Boss mPanelBoss;
    
    //第二步
     mPanelBoss = (AirPanel.Boss) view.findViewById(R.id.lay_container);  //这个id就是根布局的id  (也就是AirPanelLinearLayout或者是子类的id)
        mPanelBoss.setPanelListener(new AirPanel.Listener() {
            @Override
            public void requestHideSoftKeyboard() {
                // 这里面传递的是EditText控件 也就是我们的输入控件   Util这个方法是集成的库中的工具类
                Util.hideKeyboard(mEtContent);
            }
        });
        
    //第三步 找到我们的fragment 可以操作里面的控制逻辑
     PanelFragment fragment = (PanelFragment) getChildFragmentManager().findFragmentById(R.id.frag_panel);
        //让我们的fragment实现这个方法PanelCallback
        fragment.setup(this);
        mPanelContent = fragment;
        
    //这个时候我们可以操作了   因为实现了PanelCallback这个接口,那么会实现里面的方法,我们看下如何处理吧
    
      @Override
    public EditText getInputEditText() {
    //获取表情输入 当然需要自己进行转换
        return mEtContent;
    }

    @Override
    public void onSendGalleryClick(String[] paths) {
    //图片地址 这个时候我们需要进行上传逻辑
        mPresenter.pushImages(paths);
    }

    @Override
    public void onRecordDone(File file, long time) {
        //语音的发起
        mPresenter.pushAudio(file.getAbsolutePath(), time);
    }
    

复制代码

什么,这TM(提莫必须死)说的啥啊,瓜子毛嗑都准备好了,就给看这个。。。所有的点击事件怎么没有呢,不要急吗,先看一个图片

然后我们看下点击逻辑吧


    //语音切换
    @OnClick(R.id.btn_record)
    void onRecordClick() {
        if (mPanelBoss.isOpen()) {
            Util.showKeyboard(mEtContent);
        } else {
            mPanelContent.showRecord();
            mPanelBoss.openPanel();
        }
    }

    //图片点击
    private void onMoreClick() {
        if (mPanelBoss.isOpen() && mPanelContent.isOpenMore()) {
            Util.showKeyboard(mEtContent);
        } else {
            mPanelContent.showMore();
            mPanelBoss.openPanel();
        }

    }

    //表情点击
    @OnClick(R.id.btn_face)
    void onFaceClick() {
        if (mPanelBoss.isOpen() && mPanelContent.isOpenFace()) {
            //显示输入法
            Util.showKeyboard(mEtContent);
        } else {
            mPanelContent.showFace();
            mPanelBoss.openPanel();
        }
    }


复制代码

好了,这个时候就已经完美的实现了表情、输入法、语音、图片面板的切换了,是不是很简单哈。之前也看过好多的关于介绍输入法、表情切换闪屏问题,但是不知道是不是介绍的不详细还是实现比较困难(自己太菜),然后发现了这样的一个封装比较好的一个库,不过有个问题就是父布局必须是AirPanelLinearLayout。这个就在于大家的取舍了。

###写在最后 关于这个库的介绍就不写了,大致看了下,也是对于一个键盘的初始化,给一个最高和最低的值

 <declare-styleable name="AirPanelLinearLayout"><attr format="dimension" name="airPanelMinHeight"/><attr format="dimension" name="airPanelMaxHeight"/></declare-styleable>
    <dimen name="airPanelMaxHeight">280dp</dimen>
    <dimen name="airPanelMinHeight">86dp</dimen>
    <item name="airPanelSubLayout" type="id"/>
复制代码

然后在Helper这个类中进行拿到这些属性,并进行相应的逻辑,比如里面的计算软键盘高度、修复高度、设置相关监听、检测软键盘的动作等逻辑,大家看下也就明白了。

###参考文章

解决Android软键盘和表情面板切换界面闪动问题 Android键盘面板冲突 布局闪动处理方案 Android高仿微信表情输入与键盘输入详解

相关文章:

  • 24.5. IN / NOT IN
  • 集合学习
  • [SSL: CERTIFICATE_VERIFY_FAILED] Certificate Verif
  • 单调、加班、血汗工厂,被夸大的富士康背后真相到底是什么?
  • MySQL主从配置
  • 属性动画
  • 部署redis cluster
  • 你知道升级 tomcat 8.5.x后cookie有什么变化吗?
  • kaggle之数据分析从业者用户画像分析
  • 如何添加自定义脚本到开机自启动
  • Linux学习之路-Linux-循环 for、while【10】---20171223
  • 函数式 Java 到函数式 Kotlin 的转换
  • 23.2. 基础操作
  • Kubernetes系统架构简介[转]
  • Jenkins+Jmeter持续集成笔记(三:集成到Jenkins)
  • 【Amaple教程】5. 插件
  • 【css3】浏览器内核及其兼容性
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • CEF与代理
  • Iterator 和 for...of 循环
  • Joomla 2.x, 3.x useful code cheatsheet
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • PhantomJS 安装
  • python_bomb----数据类型总结
  • python3 使用 asyncio 代替线程
  • SpiderData 2019年2月25日 DApp数据排行榜
  • Unix命令
  • Vim 折腾记
  • vue2.0项目引入element-ui
  • 笨办法学C 练习34:动态数组
  • 从重复到重用
  • 深入浏览器事件循环的本质
  • 实战|智能家居行业移动应用性能分析
  • 小程序开发之路(一)
  • 学习ES6 变量的解构赋值
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 因为阿里,他们成了“杭漂”
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • 进程与线程(三)——进程/线程间通信
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • (pojstep1.3.1)1017(构造法模拟)
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (黑马C++)L06 重载与继承
  • (十三)Maven插件解析运行机制
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .htaccess 强制https 单独排除某个目录
  • .Mobi域名介绍
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .NET 设计一套高性能的弱事件机制
  • @Transaction注解失效的几种场景(附有示例代码)