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

一、所用技术

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

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

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

二、实现效果

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

Android框架之路——聊天Demo实现_第1张图片

三、总体思路

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

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

    
    
    
        
    
        
    
        
            
                
            
    
            
  2. 看一下我们的聊天布局,分为左右俩个布局文件,一个TextView显示当前时间,然后就是聊天头像与内容;
    左布局:

    
    
    
        
    
        
    
            
    
            
        
    
    

    右布局:

    
    
    
        
    
        
    
            
    
            
    
        
    
    
    
    
  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;
        @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;
        }
    }
    
  4. 接下来我们要着手编写聊天的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);
            }
        }
    }
    
  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 queryAllMsg(){
            return mManager.getDaoSession().loadAll(Msg.class);
        }
    }
    
  6. 最后就是在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("");
        }
    }
    

四、Demo下载

   源码链接



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

Android框架之路——聊天Demo实现_第2张图片

你可能感兴趣的:(Android,Android框架之路)