如果这些你还没有了解,你可以参考这些文章:
后台每5s发送数据过来,存储到数据库中,并显示到界面上,用户可以发送文字,保存到数据库并显示。.9的图片比较丑,缺一个美工姑娘,欢迎联系。。。
通过RecyclerView的viewType来决定加载左右聊天布局,通过greendao来操作数据库。一些细节问题较多,需要逐个解决。
编写activity_main.xml,主要由一个RecyclerView和下方的EditText输入框以及发送按钮组成。
看一下我们的聊天布局,分为左右俩个布局文件,一个TextView显示当前时间,然后就是聊天头像与内容;
左布局:
右布局:
看一下我们的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;
@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 {
private LayoutInflater mLayoutInflater;
private Context mContext;
private List mDatas;
public ChatAdapter(Context context, List 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 queryAllMsg(){
return mManager.getDaoSession().loadAll(Msg.class);
}
}
最后就是在MainActivity.java中完成我们的业务逻辑了。这里面有几个细节问题,一个是打开页面加载出数据库里的聊天记录,另一个是当下滑RecyclerView时需要隐藏软键盘,还有一个要监听数据库的插入操作,当然,RecyclerView布满时,来记录后要自动上滑,显示最新消息。
public class MainActivity extends AppCompatActivity {
private List 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("");
}
}
源码链接