一、所用技术

  • GreenDao存储聊天数据;
  • RecyclerView根据viewtype显示聊天界面;
  • butterknife绑定view;

如果这些你还没有了解,你可以参考这些文章:

  • Android框架之路——GreenDao3.2.2的使用
  • Android框架之路——RecyclerView的使用
  • Android框架之路——ButterKnife的使用

二、实现效果

后台每5s发送数据过来,存储到数据库中,并显示到界面上,用户可以发送文字,保存到数据库并显示。.9的图片比较丑,缺一个美工姑娘,欢迎联系。。。

三、总体思路

通过RecyclerView的viewType来决定加载左右聊天布局,通过greendao来操作数据库。一些细节问题较多,需要逐个解决。

  1. 编写activity_main.xml,主要由一个RecyclerView和下方的EditText输入框以及发送按钮组成。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"android:padding="5dp"tools:context="com.ping.chatdemo.activity.MainActivity"><android.support.v7.widget.RecyclerViewandroid:id="@+id/rv_chatList"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_above="@+id/linearLayout"></android.support.v7.widget.RecyclerView><LinearLayoutandroid:layout_alignParentBottom="true"android:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/linearLayout"><android.support.design.widget.TextInputLayoutandroid:layout_width="0dp"android:layout_weight="6"android:layout_height="50dp"><EditTextandroid:id="@+id/et_content"android:hint="请输入文字..."android:textSize="15dp"android:layout_width="match_parent"android:layout_height="match_parent"/></android.support.design.widget.TextInputLayout><Buttonandroid:id="@+id/bt_send"android:padding="10dp"android:textSize="15dp"android:text="发送"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout></RelativeLayout>
    
  2. 看一下我们的聊天布局,分为左右俩个布局文件,一个TextView显示当前时间,然后就是聊天头像与内容;
    左布局:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/white"android:orientation="vertical"android:paddingBottom="5dp"android:paddingLeft="12dp"android:paddingRight="12dp"android:paddingTop="3dp"><TextViewandroid:id="@+id/tv_left_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="2015-6-6 06:06:06"/><RelativeLayoutandroid:layout_marginTop="2dp"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/img_ble"android:layout_width="40dp"android:layout_height="40dp"android:layout_alignParentLeft="true"android:layout_marginRight="4dp"android:src="@drawable/ic_ble"/><TextViewandroid:id="@+id/tv_msg_left"android:layout_width="wrap_content"android:layout_height="40dp"android:textSize="13dp"android:layout_marginRight="50dp"android:background="@drawable/imageleft"android:layout_toRightOf="@id/img_ble"android:textColor="@android:color/white"/></RelativeLayout>
    </LinearLayout>
    

    右布局:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/white"android:orientation="vertical"android:paddingBottom="5dp"android:paddingLeft="12dp"android:paddingRight="12dp"android:paddingTop="3dp"><TextViewandroid:id="@+id/tv_right_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="2015-6-6 06:06:06"/><RelativeLayoutandroid:layout_marginTop="2dp"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/img_phone"android:layout_width="40dp"android:layout_height="40dp"android:layout_alignParentRight="true"android:layout_marginLeft="4dp"android:src="@drawable/ic_phone"/><TextViewandroid:id="@+id/tv_msg_right"android:layout_width="wrap_content"android:layout_height="40dp"android:textSize="13dp"android:layout_marginLeft="50dp"android:background="@drawable/imageright"android:layout_toLeftOf="@id/img_phone"android:textColor="@android:color/black"/></RelativeLayout></LinearLayout>
    
  3. 看一下我们的Msg.java聊天实体,里面包含了主键_id,聊天时间,聊天内容和聊天布局类型,此类方法是通过greendao注解生成的;

    @Entity
    public class Msg {public static final int TYPE_BLE = 0;public static final int TYPE_PHONE = 1;@Id(autoincrement = true)private Long _id;@NotNullprivate String content;@NotNullprivate int type;@NotNullprivate String time;@Generated(hash = 1787798591)public Msg(Long _id, @NotNull String content, int type, @NotNull String time) {this._id = _id;this.content = content;this.type = type;this.time = time;}@Generated(hash = 23037457)public Msg() {}public Long get_id() {return this._id;}public void set_id(long _id) {this._id = _id;}public String getContent() {return this.content;}public void setContent(String content) {this.content = content;}public int getType() {return this.type;}public void setType(int type) {this.type = type;}public String getTime() {return this.time;}public void setTime(String time) {this.time = time;}@Overridepublic String toString() {return "Msg{" +"_id=" + _id +", content='" + content + '\'' +", type=" + type +", time='" + time + '\'' +'}';}public void set_id(Long _id) {this._id = _id;}
    }
    
  4. 接下来我们要着手编写聊天的Apater了,在Adapter中我们需要根据Msg的viewType来返回不同的holder,即渲染不同的视图。下面就是我们ChatAdapter的具体实现;

    public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {private LayoutInflater mLayoutInflater;private Context mContext;private List<Msg> mDatas;public ChatAdapter(Context context, List<Msg> datas) {mContext = context;mLayoutInflater = LayoutInflater.from(mContext);mDatas = datas;}//添加消息显示在RecyclerView中public void addItem(Msg msg) {mDatas.add(msg);notifyDataSetChanged();}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if (viewType == Msg.TYPE_BLE) {View view = mLayoutInflater.inflate(R.layout.item_chat_left, parent, false);return new ChatLeftViewHolder(view);} else {View view = mLayoutInflater.inflate(R.layout.item_chat_right, parent, false);return new ChatRightViewHolder(view);}}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {Msg msg = mDatas.get(position);String time = msg.getTime();String content = msg.getContent();if(holder instanceof ChatLeftViewHolder){((ChatLeftViewHolder) holder).mTvLeftTime.setText(time);((ChatLeftViewHolder) holder).mTvMsgLeft.setText(content);}else if(holder instanceof ChatRightViewHolder){((ChatRightViewHolder) holder).mTvRightTime.setText(time);((ChatRightViewHolder) holder).mTvMsgRight.setText(content);}}@Overridepublic int getItemViewType(int position) {return mDatas.get(position).getType();}@Overridepublic int getItemCount() {return mDatas.size();}static class ChatLeftViewHolder extends RecyclerView.ViewHolder {@BindView(R.id.tv_left_time)TextView mTvLeftTime;@BindView(R.id.tv_msg_left)TextView mTvMsgLeft;ChatLeftViewHolder(View view) {super(view);ButterKnife.bind(this, view);}}static class ChatRightViewHolder extends RecyclerView.ViewHolder{@BindView(R.id.tv_right_time)TextView mTvRightTime;@BindView(R.id.tv_msg_right)TextView mTvMsgRight;ChatRightViewHolder(View view) {super(view);ButterKnife.bind(this, view);}}
    }
    
  5. 编写完上述的一些关于界面显示的东西后,我们要来继续完成我们的数据库操作方法,和这篇教程一样,我们需要单例模式来封装一个DaoManager类,基本不怎么变化,变化的是我们Util,我们针对这次的Msg实体编写MsgDaoUtil类如下。这里我们还给其注入了一个监听器,用来监听是否数据库进行数据插入操作了;

    public class MsgDaoUtil {private static final String TAG = MsgDaoUtil.class.getSimpleName();private DaoManager mManager;private OnDbUpdateListener mUpdateListener;public void setUpdateListener(OnDbUpdateListener updateListener) {mUpdateListener = updateListener;}public MsgDaoUtil(Context context){mManager = DaoManager.getInstance();mManager.init(context);}/*** 完成msg记录的插入,如果表未创建,先创建Msg表* @param msg* @return*/public boolean insertMsg(Msg msg){boolean flag = false;flag = mManager.getDaoSession().getMsgDao().insert(msg) == -1 ? false : true;if(flag)mUpdateListener.onUpdate(msg);Log.i(TAG, "insert Msg :" + flag + "-->" + msg.toString());return flag;}/*** 查询所有记录* @return*/public List<Msg> queryAllMsg(){return mManager.getDaoSession().loadAll(Msg.class);}
    }
    
  6. 最后就是在MainActivity.java中完成我们的业务逻辑了。这里面有几个细节问题,一个是打开页面加载出数据库里的聊天记录,另一个是当下滑RecyclerView时需要隐藏软键盘,还有一个要监听数据库的插入操作,当然,RecyclerView布满时,来记录后要自动上滑,显示最新消息。

    public class MainActivity extends AppCompatActivity {private List<Msg> mMsgs;private MsgDaoUtil mMsgDaoUtil;private ChatAdapter mAdapter;SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@BindView(R.id.rv_chatList)RecyclerView mRvChatList;@BindView(R.id.et_content)EditText mEtContent;@BindView(R.id.bt_send)Button mBtSend;//后台定时5s发送数据Handler handler = new Handler();Runnable runnable = new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubaddMsg(new Msg(null, "来数据了!", Msg.TYPE_BLE, df.format(new Date())));handler.postDelayed(this, 5000);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);mMsgDaoUtil = new MsgDaoUtil(this);//加载历史聊天记录mMsgs = mMsgDaoUtil.queryAllMsg();LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);mRvChatList.setLayoutManager(linearLayoutManager);mAdapter = new ChatAdapter(this, mMsgs);mRvChatList.setAdapter(mAdapter);//初试加载历史记录呈现最新消息mRvChatList.scrollToPosition(mAdapter.getItemCount() - 1);mMsgDaoUtil.setUpdateListener(new OnDbUpdateListener() {@Overridepublic void onUpdate(Msg msg) {mAdapter.addItem(msg);//铺满屏幕后呈现最新消息mRvChatList.scrollToPosition(mAdapter.getItemCount() - 1);}});//设置下滑隐藏软键盘mRvChatList.addOnScrollListener(new RecyclerView.OnScrollListener() {@Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);if (dy < -10) {InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);imm.hideSoftInputFromWindow(mEtContent.getWindowToken(), 0);}}});handler.postDelayed(runnable, 5000);}private boolean addMsg(Msg msg) {return  mMsgDaoUtil.insertMsg(msg);}@OnClick(R.id.bt_send)public void onViewClicked() {String content = mEtContent.getText().toString();addMsg(new Msg(null, content, Msg.TYPE_PHONE, df.format(new Date())));mEtContent.setText("");}
    }
    

四、Demo下载

源码链接

个人公众号:每日推荐一篇技术博客,坚持每日进步一丢丢…欢迎关注,想建个微信群,主要讨论安卓和Java语言,一起打基础、用框架、学设计模式,菜鸡变菜鸟,菜鸟再起飞,愿意一起努力的话可以公众号留言,谢谢…

Android框架之路——聊天Demo实现相关推荐

  1. Android框架之路——Glide加载图片(结合RecyclerView、CardView)

    Android框架之路--Glide加载图片 一.简介: 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech.这个库被广泛的运用在google的开 ...

  2. Android框架之路——Banner实现轮播图(RecyclerView添加Header)

    一.简介 Banner能实现循环播放多个广告图片和手动滑动循环等功能.因为原生ViewPager并不支持循环翻页, 要实现循环还得需要自己去动手.Banner框架可以进行不同样式.不同动画设置, 以及 ...

  3. Android框架之路——EventBus的使用

    一.简介 EventBus是由greenrobot 组织贡献的一个Android事件发布/订阅轻量级框架.EventBus是一个Android端优化的publish/subscribe消息总线,简化了 ...

  4. Android框架之路——OkGo的使用

    一.简介 该库是封装了okhttp的标准RESTful风格的网络框架,可以与RxJava完美结合,比Retrofit更简单易用.支持大文件上传下载,上传进度回调,下载进度回调,表单上传(多文件和多参数 ...

  5. Android仿微信语音聊天demo

    其实我接触android时间也不是很久,但是发现android远远比我们想象的要有趣并且复杂很多,所以还是要多花点时间来写一写这些demo例子,这个程序是我从慕课网上学来的,因为毕竟要自己手写,才能体 ...

  6. 写了个Android聊天客户端框架,基本聊天功能、数据库、服务器都有。大家可以看一看。已经开源

    写了个Android聊天客户端框架,基本聊天功能.数据库.服务器都有.大家可以看一看.已经开源(希望两个手机通信的话,改一下pushid就可以) 几点说明: 1:包含的基本功能.: 1.1比如gif动 ...

  7. Android基于讯飞AIUI的聊天Demo

    基于讯飞AIUI实现一个AI聊天Demo,首先在AIUI开放平台创建应用,做下简单配置,勾选想要的语义技能,记得保存修改 然后选择点开发工具,选择下载对应的SDK 应用信息里有appid,回头下载de ...

  8. Android框架排行榜,上百项资源汇总不容错过

    1.Retrofit 一句话介绍:Retrofit是一款类型安全的网络框架,基于HTTP协议,服务于Android和java语言 上榜理由:Retrofit以21.8k的stars量雄踞github中 ...

  9. 最新Android框架排行榜,上百项资源汇总不容错过

    Android框架排行榜 1.Retrofit 一句话介绍:Retrofit是一款类型安全的网络框架,基于HTTP协议,服务于Android和java语言 上榜理由:Retrofit以21.8k的st ...

最新文章

  1. 动态引用webservice
  2. Java垃圾回收(3)
  3. Flex里Application和TitleWindow数据交互方法
  4. php中is null,php中empty(), is_null(), isset()函数区别
  5. java框架之SpringBoot(5)-SpringMVC的自动配置
  6. 热水器是长期开着好还是要等到用的时候才开?
  7. LINUX 查看分区UUID的两种方法
  8. 更改网页alert弹出框样式
  9. Zigbee 协议栈网络管理
  10. 易百教程(IT技能学习平台)
  11. ISODATA聚类分析算法原理与C++实现
  12. Visual Studio调用约定 __cdecl、__stdcall和__fastcall
  13. 【Linux】深入解析Linux proc文件系统
  14. h5在线制作平台h5案例分享
  15. 网站seo诊断,网站seo诊断方法
  16. Win10问题篇:解决AMD家CPU机械,固态硬盘混用导致的卡顿(爆音)问题。
  17. 欧拉角,轴角,四元数与旋转矩阵详解
  18. B树和B+树的查找方式及原因
  19. android 视频画面切割,抖音三屏黑白特效在哪里?安卓手机画面分割器将视频画面分割成黑白三屏的方法...
  20. 最大化参数 火车头_火车头采集器教程:使用正则匹配模式采集数据

热门文章

  1. 如何把eclipse的背景色改为豆沙色
  2. 安卓图案解锁NC13585
  3. 为何删除bootmgr文件后系统仍然能启动
  4. 100w氮化镓充电器_拆解报告:HYPER JUICE 100W 2A2C氮化镓充电器
  5. 开发 Windows RT 桌面应用(来自 Surface RT)
  6. Vista BitLocker 驱动器加密原理
  7. 西邮Linux兴趣小组2022纳新面试题题解
  8. 采用itextpdf、xmlworker实现HTML转PDF
  9. 智能手机CPU大揭秘
  10. [附源码]Python计算机毕业设计办公用品管理系统