Android框架之路——聊天Demo实现

xiaoxiao2021-02-28  70

一、所用技术

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注解生成的;

@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<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语言,一起打基础、用框架、学设计模式,菜鸡变菜鸟,菜鸟再起飞,愿意一起努力的话可以公众号留言,谢谢…

转载请注明原文地址: https://www.6miu.com/read-70589.html

最新回复(0)