基于Bmob数据服务实现的简单群聊


logo
  • 简介
  • 运行效果
  • 设计思路
  • 代码
  • 参考

一、简介


   该应用主要利用bmob提供的数据实时同步功能来让客户端对云端指定表的监听完成了群聊的基本功能,同时在消息类型上区分了文本消息和图片消息。应用可以判断消息类型并根据对应的消息类型来显示文本/图片。笔者接触Android不久,在性能的优化及程序的设计上如有不足还望指出~ 谢谢~

二、运行效果

---
群聊消息截图

三、设计思路


  即时通讯的实现最大的难点在于双方数据的实时同步,现在Bmob已经帮我们解决了,所以我们只需要将云端的数据加载到ListView中进行显示即可。

  我们要实现两种消息:文本消息 和 图片消息。两种消息在云端存放的本质是相同的,只不过一个存放的内容为发送的消息文本一个存放的是云端图片的URL。两种消息的区分可以另外定义一个变量viewtype(准确来说应该命名msgtype,命名viewtype是因为对应了不同的viewholder,不过这都不重要,不要在意这些细节)来实现,当viewtype为0时,以文本的形式展示,当viewtype为1时,以图片的形式展示。

我设计了如下结构:

类型 变量名
String name
String content
int viewtype

其中name可省,也可以根据自己的需要添加其它值

  之后调用bmob提供的save方法即可完成数据的保存,在BmobRealTimeData类的回调函数中就可以拿到新增的数据了。

本地数据的加载方法主要参考了文章结尾的参考,在原基础上做了一些调整。

四、代码


除了以下代码使用前还要进行Bmob的初始化以及在清单中添加相应的权限build.gradle中的部分依赖

Chat.class

public class Chat extends BmobObject{

    public final static int TEXT_TYPE = 0;
    public final static int IMAGE_TYPE = 1;

    private int viewtype;
    private String name;
    private String content;

    public Chat(String name, String content, int type) {
        this.name = name;
        this.content = content;
        this.viewtype = type;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setViewtype(int viewtype) {
        this.viewtype = viewtype;
    }

    public int getViewtype() {
        return viewtype;
    }
}

MainActivity.class

public class MainActivity extends Activity implements View.OnClickListener {

    ListView mListView;
    String name;
    Button btn_send, btn_pic_send;
    EditText et_content;

    PlayAdapter mPlayAdapter;
    List<Chat> messages = new ArrayList<Chat>();
    BmobRealTimeData data = new BmobRealTimeData();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        name = telephonyManager.getDeviceId().toString();

        initview();
        initdata();

    }

    private void initdata() {


        BmobQuery<Chat> query = new BmobQuery<Chat>();
        query.order("createdAt");
        query.setLimit(500);
        query.findObjects(new FindListener<Chat>() {
            @Override
            public void done(List<Chat> list, BmobException e) {
                if (e == null) {
                    for (Chat chat : list) {
                        messages.add(chat);
                    }
                } else {
                    Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
                }
            }
        });

        data.start(new ValueEventListener() {
            @Override
            public void onConnectCompleted(Exception e) {
                if (data.isConnected()) {
                    data.subTableUpdate("Chat");
                }
            }

            @Override
            public void onDataChange(JSONObject jsonObject) {
                if (BmobRealTimeData.ACTION_UPDATETABLE.equals(jsonObject.optString("action"))) {
                    JSONObject data = jsonObject.optJSONObject("data");

                    Chat chat = new Chat(data.optString("name"), data.optString("content"), Integer.parseInt(data.optString("viewtype")));
                    messages.add(chat);

                    mPlayAdapter.notifyDataSetChanged();
                }
            }
        });
    }

    private void initview() {
        mListView = (ListView) findViewById(R.id.lv_data);
        mPlayAdapter = new PlayAdapter(MainActivity.this, messages, name);
        mListView.setAdapter(mPlayAdapter);
        mListView.setDivider(null);
        mListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

        btn_pic_send = (Button) findViewById(R.id.btn_send_pic);
        btn_send = (Button) findViewById(R.id.btn_send);
        btn_pic_send.setOnClickListener(this);
        btn_send.setOnClickListener(this);
        et_content = (EditText) findViewById(R.id.et_msg);
    }

    private void sendMsg(String name, String msg, int msgType){
        Chat chat = new Chat(name, msg, msgType);
        chat.save(new SaveListener<String>() {
            @Override
            public void done(String s, BmobException e) {
                if (e == null) {
                    et_content.setText("");
                }
            }
        });
    }

    @Override
    public void onClick(View view) {
        if (view == btn_send) {
            String content = et_content.getText().toString();
            if (TextUtils.isEmpty(name) || TextUtils.isEmpty(content)) {
                Toast.makeText(MainActivity.this, "消息不能为空", Toast.LENGTH_SHORT).show();
                return;
            } else {
                sendMsg(name, content, Chat.TEXT_TYPE);
            }
        } else if (view == btn_pic_send) {
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.setType("image/*");
            startActivityForResult(intent, 42);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 42) {
            try {
                Uri uri = data.getData();
                String path = getPath(this, uri);
                final BmobFile pic = new BmobFile(new File(path));
                pic.uploadblock(new UploadFileListener() {
                    @Override
                    public void done(BmobException e) {
                        if (e == null) {
                            sendMsg(name, pic.getFileUrl(), Chat.IMAGE_TYPE);
                        }
                    }
                });
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
        }
    }

    public static String getPath(Context context, Uri uri) throws URISyntaxException {
        if ("content".equalsIgnoreCase(uri.getScheme())) {
            String[] projection = { "_data" };
            Cursor cursor = null;
            try {
                cursor = context.getContentResolver().query(uri, projection, null, null, null);
                int column_index = cursor.getColumnIndexOrThrow("_data");
                if (cursor.moveToFirst()) {
                    return cursor.getString(column_index);
                }
            } catch (Exception e) {
                // Eat it  Or Log it.
            }
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }
}

MsgAdapter.class

public class MsgAdapter extends BaseAdapter {

    public static final int ITEM_TEXT = 0;
    public static final int ITEM_PIC = 1;

    private List<Chat> mList;
    private Context context;
    private String MyName;
    private LayoutInflater inflater;

    class Hodler1 {
        LinearLayout leftLayout;
        LinearLayout rightLayout;
        TextView leftMsg;
        TextView rightMsg;

        Hodler1(View view) {
            leftLayout = view.findViewById(R.id.left_layout);
            rightLayout = view.findViewById(R.id.right_layout);
            leftMsg = view.findViewById(R.id.left_msg);
            rightMsg = view.findViewById(R.id.right_msg);
        }
    }

    class Hodler2 {
        LinearLayout left_pic_layout;
        LinearLayout right_pic_layout;
        ImageView leftPic;
        ImageView rightPic;

        Hodler2(View view) {
            left_pic_layout = view.findViewById(R.id.pic_left_layout);
            right_pic_layout = view.findViewById(R.id.pic_right_layout);
            leftPic = view.findViewById(R.id.iv_picture_left);
            rightPic = view.findViewById(R.id.iv_picture_right);
        }
    }

    public PlayAdapter(Context context, List<Chat> mList, String name) {
        this.context = context;
        this.mList = mList;
        inflater = LayoutInflater.from(context);
        this.MyName = name;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int i) {
        return mList.get(i);
    }

    @Override
    public int getItemViewType(int position) {
        return mList.get(position).getViewtype();
    }

    //图片与文本对应两种不同的viewholder
    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        int type = getItemViewType(i);

        Hodler1 hodler1 = null;
        Hodler2 hodler2 = null;

        if (view == null) {

            switch (type) {
                case ITEM_TEXT:
                    view = inflater.inflate(R.layout.msg_item, null);
                    hodler1 = new Hodler1(view);

                    if (mList.get(i).getName().equals(MyName)) {
                        hodler1.leftLayout.setVisibility(View.GONE);
                        hodler1.rightLayout.setVisibility(View.VISIBLE);
                        hodler1.rightMsg.setText(mList.get(i).getContent());
                    } else {
                        hodler1.leftLayout.setVisibility(View.VISIBLE);
                        hodler1.leftMsg.setText(mList.get(i).getContent());
                        hodler1.rightLayout.setVisibility(View.GONE);
                    }
                    view.setTag(hodler1);
                    break;
                case ITEM_PIC:
                    view = inflater.inflate(R.layout.msg_item_pic, null);
                    hodler2 = new Hodler2(view);

                    if (mList.get(i).getName().equals(MyName)) {
                        hodler2.left_pic_layout.setVisibility(View.GONE);
                        hodler2.right_pic_layout.setVisibility(View.VISIBLE);
                        Picasso.with(context).load(mList.get(i).getContent()).into(hodler2.rightPic);
                    } else {
                        hodler2.left_pic_layout.setVisibility(View.VISIBLE);
                        hodler2.right_pic_layout.setVisibility(View.GONE);
                        Picasso.with(context).load(mList.get(i).getContent()).into(hodler2.leftPic);
                    }
                    view.setTag(hodler2);
                    break;
                default:
                    break;
            }
        } else {
            switch (type) {
                case ITEM_TEXT:
                    hodler1 = (Hodler1) view.getTag();
                    if (mList.get(i).getName().equals(MyName)) {
                        hodler1.leftLayout.setVisibility(View.GONE);
                        hodler1.rightLayout.setVisibility(View.VISIBLE);
                        hodler1.rightMsg.setText(mList.get(i).getContent());
                    } else {
                        hodler1.leftLayout.setVisibility(View.VISIBLE);
                        hodler1.leftMsg.setText(mList.get(i).getContent());
                        hodler1.rightLayout.setVisibility(View.GONE);
                    }
                    break;
                case ITEM_PIC:
                    hodler2 = (Hodler2) view.getTag();
                    if (mList.get(i).getName().equals(MyName)) {
                        hodler2.left_pic_layout.setVisibility(View.GONE);
                        hodler2.right_pic_layout.setVisibility(View.VISIBLE);
                        Picasso.with(context).load(mList.get(i).getContent()).into(hodler2.rightPic);
                    } else {
                        hodler2.left_pic_layout.setVisibility(View.VISIBLE);
                        hodler2.right_pic_layout.setVisibility(View.GONE);
                        Picasso.with(context).load(mList.get(i).getContent()).into(hodler2.leftPic);
                    }
                    break;
                default:
                    break;
            }
        }
        return view;
    }
}

五、参考


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • //我所经历的大数据平台发展史(三):互联网时代 • 上篇http://www.infoq.com/cn/arti...
    葡萄喃喃呓语阅读 51,182评论 10 200
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 今天看文章时无意间看到了一个词,“终生皆苦”。这个充满佛家意味的词,就像深山寺庙里厚重深邃的钟声,穿过我的胸膛,冲...
    十里云月阅读 384评论 0 0
  • 此时此刻,人在武都,我却在想念宕昌,想念宕昌颐馨宾馆的那间房。 在那个里套外的豪华套房里,窗明几净,床足够的宽,水...
    快乐英子阅读 310评论 3 4
  • 今天晚上吃完饭,打开手机看到群里很多同学都在上面读了第二课课文,儿子在那看书,我把手机拿到他面前让他看了看,我说你...
    满一飞阅读 80评论 0 0