Android框架之路——聊天Demo实现
来源:互联网 发布:电脑画质优化软件 编辑:程序博客网 时间:2024/06/16 17:07
一、所用技术
- GreenDao存储聊天数据;
- RecyclerView根据viewtype显示聊天界面;
- butterknife绑定view;
如果这些你还没有了解,你可以参考这些文章:
- Android框架之路——GreenDao3.2.2的使用
- Android框架之路——RecyclerView的使用
- Android框架之路——ButterKnife的使用
二、实现效果
后台每5s发送数据过来,存储到数据库中,并显示到界面上,用户可以发送文字,保存到数据库并显示。.9的图片比较丑,缺一个美工姑娘,欢迎联系。。。
三、总体思路
通过RecyclerView的viewType来决定加载左右聊天布局,通过greendao来操作数据库。一些细节问题较多,需要逐个解决。
编写activity_main.xml,主要由一个RecyclerView和下方的EditText输入框以及发送按钮组成。
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns: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.RecyclerView android:id="@+id/rv_chatList" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/linearLayout"> </android.support.v7.widget.RecyclerView> <LinearLayout android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout"> <android.support.design.widget.TextInputLayout android:layout_width="0dp" android:layout_weight="6" android:layout_height="50dp"> <EditText android:id="@+id/et_content" android:hint="请输入文字..." android:textSize="15dp" android:layout_width="match_parent" android:layout_height="match_parent"/> </android.support.design.widget.TextInputLayout> <Button android:id="@+id/bt_send" android:padding="10dp" android:textSize="15dp" android:text="发送" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout></RelativeLayout>
看一下我们的聊天布局,分为左右俩个布局文件,一个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"> <TextView android: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"/> <RelativeLayout android:layout_marginTop="2dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android: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"/> <TextView android: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"> <TextView android: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"/> <RelativeLayout android:layout_marginTop="2dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android: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"/> <TextView android: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>
看一下我们的Msg.java聊天实体,里面包含了主键_id,聊天时间,聊天内容和聊天布局类型,此类方法是通过greendao注解生成的;
@Entitypublic class Msg { public static final int TYPE_BLE = 0; public static final int TYPE_PHONE = 1; @Id(autoincrement = true) private Long _id; @NotNull private String content; @NotNull private int type; @NotNull private 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; } @Override public String toString() { return "Msg{" + "_id=" + _id + ", content='" + content + '\'' + ", type=" + type + ", time='" + time + '\'' + '}'; } public void set_id(Long _id) { this._id = _id; }}
接下来我们要着手编写聊天的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(); } @Override public 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); } } @Override public 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); } } @Override public int getItemViewType(int position) { return mDatas.get(position).getType(); } @Override public 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); } }}
编写完上述的一些关于界面显示的东西后,我们要来继续完成我们的数据库操作方法,和这篇教程一样,我们需要单例模式来封装一个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); }}
最后就是在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() { @Override public void run() { // TODO Auto-generated method stub addMsg(new Msg(null, "来数据了!", Msg.TYPE_BLE, df.format(new Date()))); handler.postDelayed(this, 5000); } }; @Override protected 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() { @Override public void onUpdate(Msg msg) { mAdapter.addItem(msg); //铺满屏幕后呈现最新消息 mRvChatList.scrollToPosition(mAdapter.getItemCount() - 1); } }); //设置下滑隐藏软键盘 mRvChatList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public 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实现
- Android框架_Banner实现轮播图Demo——超简单
- android学习日记——基于UDP的聊天demo
- Android框架之路——Fragmentation的使用(流式交互Demo)
- Android聊天机器人Demo
- Android开发之基于MINA框架的聊天通信功能实现
- APICloud框架——融云+UIChatTools实现即时通讯聊天
- Android仿微信语音聊天demo
- Android之消息推送聊天实现
- Android 多媒体之实现语音聊天界面
- Android之消息推送聊天实现
- Android之QQ聊天气泡对话实现
- Android实现聊天机器人之火影忍者
- 高并发MINA框架,网络编程(SOCKET)实现,简单的网络聊天DEMO
- Solr之——Demo实现
- android IM即时通信之聊天界面UI框架
- android IM即时通信之聊天界面UI框架
- android IM即时通信之聊天界面UI框架
- 算法学习笔记--排序之选择排序
- MapReduce之普通文件转SequenceFile
- Mac 怎样往GitHub上传代码
- Android Material Design之TextInputLayout
- 写给小白看的爬虫系列之爬虫入门爬取妹子图
- Android框架之路——聊天Demo实现
- 静态加载页面的load()
- MapReduce之SequenceFile转普通文件
- sed命令
- DIV居中
- 戏说slub分配器
- OSB12C socket TRANSPORT配置
- 内边距
- Vijos P1304 回文数【回文+进制】