本篇仅介绍实现聊天室的前端,也就是安卓端代码,后端的实现看链接说明

链接说明

1.后端使用了spring boot 框架,若不熟悉,有关spring boot 入门教程请戳此链接使用Intellij IDEA开发第一个spring boot项目
2.websocket后端实现细节戳此链接spring boot练习–利用websocket实现QQ聊天室

界面展示

说明

有两个界面,第一个是登陆界面,我借用了我之前实现的登陆界面,并做了一些微调。需要输入ID和名字,测试的时候输入的ID不能重复,第二个是名字用于界面展示,登陆后跳转入第二个界面,就可以在聊天室里聊天了。

下面图模拟了一组场景,大青儿先进入聊天室,然后小明进入,互相发一段消息后,小明退出聊天室。

大青儿界面变化

小明界面变化

代码部分

这里仅展示安卓的代码,后端代码详见上面链接说明。

文件的简单介绍

Activity文件:LoginActivity.java,ChatActivity.java,与其对应布局文件activity_login.xml,activity_chat.xml
模型文件:Msg.java,User.java
适配器:UserAdapter
子布局文件:user_item.xml

第一步修改.gradle文件,添加以下内容
    implementation 'com.squareup.okhttp3:okhttp:3.8.1'implementation 'com.squareup.okhttp3:mockwebserver:3.8.1'implementation 'com.alibaba:fastjson:1.2.10'implementation 'com.android.support:recyclerview-v7:28.0.0'
第二步,修改AndroidManifest.xml文件,添加以下内容
 <activity android:name=".ChatActivity"></activity><activity android:name=".LoginActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity>
以及添加这行,注意添加的位置的区别
<uses-permission android:name="android.permission.INTERNET" />
第三步具体功能代码
LoginActivity.java
public class LoginActivity extends Activity {private EditText mEditTextName,mEditTextId;private Button mButtonLogin;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);init();mButtonLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String userName = mEditTextName.getText().toString();String userId = mEditTextId.getText().toString();User user = new User(userId,userName);Intent intent = ChatActivity.newIntent(LoginActivity.this,user.toString());startActivity(intent);LoginActivity.this.finish();}});}private void init(){mEditTextName = findViewById(R.id.nickname_editText);mEditTextId = findViewById(R.id.id_editText);mEditTextName.setCompoundDrawables(initDrawable(R.drawable.nickname),null,null,null);mEditTextId.setCompoundDrawables(initDrawable(R.drawable.id),null,null,null);mButtonLogin = findViewById(R.id.login);}/*** 设置EditText左边图片的大小* */private Drawable initDrawable(int res){Drawable drawable = getResources().getDrawable(res);//距离左边距离,距离上边距离,长,宽drawable.setBounds(0,0,100,100);return drawable;}
}
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/background"><!--    https://www.runoob.com/w3cnote/android-tutorial-relativelayout.html--><RelativeLayoutandroid:id="@+id/viewTop"android:layout_width="match_parent"android:layout_height="240dp"android:background="@color/mihuang"><ImageViewandroid:layout_width="90dp"android:layout_height="90dp"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="10dp"android:src="@drawable/boy" /></RelativeLayout><RelativeLayoutandroid:id="@+id/viewCenter"android:paddingTop="20dp"android:layout_centerHorizontal="true"android:layout_width="280dp"android:layout_height="wrap_content"android:layout_below="@+id/viewTop"android:paddingBottom="70dp"><EditTextandroid:id="@+id/id_editText"android:layout_width="match_parent"android:layout_height="60dp"android:drawablePadding="10dp"android:drawableLeft="@drawable/id"android:hint="请输入Id"android:textSize="16sp"android:layout_marginLeft="10dp"/><ImageViewandroid:layout_width="40dp"android:layout_height="40dp"android:layout_alignParentRight="true"android:src="@drawable/down"android:paddingRight="10dp"android:paddingTop="20dp"/><EditTextandroid:layout_below="@+id/id_editText"android:id="@+id/nickname_editText"android:layout_width="match_parent"android:layout_height="60dp"android:drawablePadding="10dp"android:drawableLeft="@drawable/nickname"android:hint="请输入昵称"android:layout_marginLeft="10dp"android:textSize="16sp"/></RelativeLayout><Buttonandroid:layout_below="@+id/viewCenter"android:layout_centerHorizontal="true"android:id="@+id/login"android:layout_width="200dp"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:text="登陆"android:textColor="@color/text"android:background="@color/login"android:textSize="25dp" /><RelativeLayoutandroid:layout_alignParentBottom="true"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingBottom="10dp"><TextViewandroid:text="无法登陆?"android:paddingLeft="10dp"android:layout_width="wrap_content"android:layout_height="wrap_content"/><TextViewandroid:text="新用户注册"android:paddingRight="10dp"android:layout_alignParentRight="true"android:layout_width="wrap_content"android:layout_height="wrap_content"/></RelativeLayout></RelativeLayout>
ChatActivity.java
public class ChatActivity extends Activity {private ArrayList<User> mUserArrayList = new ArrayList<>();//定义一个存储信息的列表private EditText mInputText;//输入框private Button mSend;//发送按钮private RecyclerView mRecyclerView;//滑动框private UserAdapter mAdapter;//适配器private boolean backFlag = false;private WebSocket mSocket;private User mUser;;//全局User@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_chat);initView();String data = getIntent().getStringExtra("data");if (!data.equals("")){mUser = JSON.parseObject(data,User.class);}else {mUser = new User("0001","测试名字",R.drawable.boy);}LinearLayoutManager layoutManager = new LinearLayoutManager(this);mRecyclerView.setLayoutManager(layoutManager);mAdapter = new UserAdapter(mUserArrayList);mRecyclerView.setAdapter(mAdapter);//开启连接start(mUser.getUserId());mSend.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String content = mInputText.getText().toString();if (!"".equals(content)) {Msg msg = new Msg(true,content,false);User tempUser = new User(mUser.getUserId(),mUser.getUserName(),R.drawable.boy,msg);mSocket.send(tempUser.toString());mUserArrayList.add(tempUser);updateRecyclerView();//刷新RecyclerView//清空输入栏mInputText.setText("");}}});}/*** 刷新view* */private void updateRecyclerView(){//当有新消息时,刷新RecyclerView中的显示mAdapter.notifyItemInserted(mUserArrayList.size() - 1);//将RecyclerView定位到最后一行mRecyclerView.scrollToPosition(mUserArrayList.size() - 1);}/*** 开启web socket连接* */private void start(String userId) {OkHttpClient mOkHttpClient = new OkHttpClient.Builder().readTimeout(300, TimeUnit.SECONDS)//设置读取超时时间.writeTimeout(300, TimeUnit.SECONDS)//设置写的超时时间.connectTimeout(300, TimeUnit.SECONDS)//设置连接超时时间.build();//定义requestRequest request = new Request.Builder().url("ws://192.168.5.10:8080/test/"+userId).build();//绑定回调接口mOkHttpClient.newWebSocket(request, new EchoWebSocketListener());mOkHttpClient.dispatcher().executorService().shutdown();}/*** 显示内容* */private void output(final User user) {runOnUiThread(new Runnable() {@Overridepublic void run() {mUserArrayList.add(user);updateRecyclerView();}});}/*** 初始化界面* */private void initView(){mInputText = findViewById(R.id.input_text);mSend = findViewById(R.id.send);mRecyclerView = findViewById(R.id.msg_recycler_view);}/*** 静态方法返回一个能启动自己的intent* */public static Intent newIntent(Context context,String data){Intent intent = new Intent(context,ChatActivity.class);intent.putExtra("data",data);return intent;}/*** 对返回键的处理* */@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event){if(keyCode==KeyEvent.KEYCODE_BACK&&!backFlag){Toast.makeText(ChatActivity.this,"再按一次退出程序",Toast.LENGTH_SHORT).show();backFlag = true;return true;}else {return super.onKeyDown(keyCode, event);}}/*** 内部类,监听web socket回调* */private final class EchoWebSocketListener extends WebSocketListener {@Overridepublic void onOpen(WebSocket webSocket, Response response) {super.onOpen(webSocket, response);mSocket = webSocket;    //实例化web socketUser user = new User();user.setUserMsg(new Msg(false,"连接成功",true));output(user);}@Overridepublic void onMessage(WebSocket webSocket, String text) {super.onMessage(webSocket, text);User user = JSON.parseObject(text, User.class);output(user);}@Overridepublic void onClosed(WebSocket webSocket, int code, String reason) {super.onClosed(webSocket, code, reason);User user = new User();user.setUserMsg(new Msg(false,"关闭连接",true));output(user);}@Overridepublic void onFailure(WebSocket webSocket, Throwable t, Response response) {super.onFailure(webSocket, t, response);User user = new User();user.setUserMsg(new Msg(false,"连接失败:"+t.getMessage(),true));output(user);}}}
activity_chat.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#d8e0e8"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/msg_recycler_view"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/input_text"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:maxLines="2"android:hint="请输入……"/><Buttonandroid:id="@+id/send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="发送"/></LinearLayout>
</LinearLayout>
Msg.java
public class Msg {private boolean send;//是否是发送的消息private String content;//发送的内容private boolean system;//是否是系统消息public Msg(boolean send, String content) {this.send = send;this.content = content;this.system = false;}public Msg() {}public Msg(boolean send, String content, boolean system) {this.send = send;this.content = content;this.system = system;}public boolean isSystem() {return system;}public void setSystem(boolean system) {this.system = system;}public boolean isSend() {return send;}public void setSend(boolean send) {this.send = send;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}@Overridepublic String toString() {return JSON.toJSONString(this);}
}
User.java
public class User {private String userId;private String userName;private int userImg;private Msg userMsg;public int getUserImg() {return userImg;}public void setUserImg(int userImg) {this.userImg = userImg;}public User(String userId, String userName, int userImg) {this.userId = userId;this.userName = userName;this.userImg = userImg;}public User() {}public User(String userId, String userName) {this.userId = userId;this.userName = userName;}public User(String userId, String userName, Msg userMsg) {this.userId = userId;this.userName = userName;this.userMsg = userMsg;}public User(String userId, String userName, int userImg, Msg userMsg) {this.userId = userId;this.userName = userName;this.userImg = userImg;this.userMsg = userMsg;}public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public Msg getUserMsg() {return userMsg;}public void setUserMsg(Msg userMsg) {this.userMsg = userMsg;}@Overridepublic String toString() {return JSON.toJSONString(this);}
}
UserAdapter.java
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.ViewHolder>{private ArrayList<User> mUsers;public UserAdapter(ArrayList<User> users) {mUsers = users;}@NonNull@Overridepublic UserAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_item,parent,false);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(@NonNull UserAdapter.ViewHolder holder, int position) {User user = mUsers.get(position);if (user.getUserMsg().isSystem()){//系统消息holder.mLinearLayoutSystem.setVisibility(View.VISIBLE);holder.mRelativeLayoutReceive.setVisibility(View.GONE);holder.mRelativeLayoutSend.setVisibility(View.GONE);holder.mTextViewSystemText.setText(user.getUserMsg().getContent());}else{if (user.getUserMsg().isSend()){//是发送消息holder.mLinearLayoutSystem.setVisibility(View.GONE);holder.mRelativeLayoutReceive.setVisibility(View.GONE);holder.mRelativeLayoutSend.setVisibility(View.VISIBLE);holder.mImageViewSend.setImageResource(user.getUserImg());holder.mTextViewNameSend.setText(user.getUserName());holder.mTextViewContentSend.setText(user.getUserMsg().getContent());}else { //接收的消息holder.mLinearLayoutSystem.setVisibility(View.GONE);holder.mRelativeLayoutReceive.setVisibility(View.VISIBLE);holder.mRelativeLayoutSend.setVisibility(View.GONE);holder.mImageViewReceive.setImageResource(user.getUserImg());holder.mTextViewNameReceive.setText(user.getUserName());holder.mTextViewContentReceive.setText(user.getUserMsg().getContent());}}}@Overridepublic int getItemCount() {return mUsers.size();}public class ViewHolder extends RecyclerView.ViewHolder {//绑定所有视图RelativeLayout mRelativeLayoutSend,mRelativeLayoutReceive;LinearLayout mLinearLayoutSystem;ImageView mImageViewReceive,mImageViewSend;TextView mTextViewNameReceive,mTextViewNameSend,mTextViewContentReceive,mTextViewContentSend,mTextViewSystemText;public ViewHolder(@NonNull View itemView) {super(itemView);mRelativeLayoutReceive = itemView.findViewById(R.id.receive_relative);mRelativeLayoutSend = itemView.findViewById(R.id.send_relative);mLinearLayoutSystem = itemView.findViewById(R.id.system_layout);mImageViewReceive = itemView.findViewById(R.id.image_receive);mImageViewSend = itemView.findViewById(R.id.image_send);mTextViewNameReceive = itemView.findViewById(R.id.text_name_receive);mTextViewNameSend = itemView.findViewById(R.id.text_name_send);mTextViewContentReceive = itemView.findViewById(R.id.text_content_receive);mTextViewContentSend = itemView.findViewById(R.id.text_content_send);mTextViewSystemText = itemView.findViewById(R.id.system_text);}}
}
user_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:layout_height="wrap_content"android:layout_marginBottom="10dp"><RelativeLayoutandroid:id="@+id/receive_relative"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:layout_alignParentLeft="true"android:id="@+id/image_receive"android:layout_width="60dp"android:layout_height="60dp"android:layout_marginTop="20dp"android:layout_marginLeft="10dp"android:src="@drawable/boy"/><LinearLayoutandroid:layout_toRightOf="@id/image_receive"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:id="@+id/text_name_receive"android:layout_width="50dp"android:layout_height="20dp"android:layout_gravity="left"android:text="大青儿"android:textSize="15dp"android:textColor="#131313"android:layout_marginLeft="20dp"/><LinearLayoutandroid:id="@+id/layout_content_receive"android:layout_gravity="right"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/message_left"><TextViewandroid:id="@+id/text_content_receive"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="5dp"android:textSize="20dp"android:textColor="#F5EFEF"android:text="我是一段文字一段文"/></LinearLayout></LinearLayout></RelativeLayout><RelativeLayoutandroid:id="@+id/send_relative"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:layout_alignParentRight="true"android:id="@+id/image_send"android:layout_width="60dp"android:layout_height="60dp"android:layout_marginTop="20dp"android:layout_marginRight="10dp"android:src="@drawable/boy"/><LinearLayoutandroid:layout_toLeftOf="@id/image_send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:id="@+id/text_name_send"android:layout_width="50dp"android:layout_height="20dp"android:layout_gravity="right"android:text="大青儿"android:textSize="15dp"android:textColor="#131313"android:layout_marginRight="20dp"/><LinearLayoutandroid:id="@+id/layout_content_send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right"android:background="@drawable/message_right"><TextViewandroid:id="@+id/text_content_send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="5dp"android:text="我是一段文字一段文"android:textSize="20dp" /></LinearLayout></LinearLayout></RelativeLayout><LinearLayoutandroid:id="@+id/system_layout"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/system_text"android:layout_width="wrap_content"android:layout_height="20dp"android:text="系统消息:"android:layout_gravity="center"android:textColor="#F17070"/></LinearLayout>
</RelativeLayout>

图片资源

应用中大部分图片资源都是只起装饰性作用,可以在阿里巴巴矢量图标库获取,当然包裹聊天文字的图片是.9格式图片,可以参考:Android Studio制作.9图片

提醒一哈

先配置好后端再进行前端测试,不然没连上服务器会报错并闪退。因为当你点发送的时候,有一段代码中websocket并没有被实例化。

安卓android+WebSocket实现简易QQ聊天室相关推荐

  1. 使用Node+websocket实现简易1v1聊天室(前端+服务器)

    使用Node+websocket实现简易1v1聊天室(前端+服务器) 前提: 安装好node环境~~~ 可使用 node -v 和 npm -v 查看是否装好 实现逻辑: 用户A 用户B 服务器 用户 ...

  2. 简易QQ聊天室,socket多线程编程(C语言实现),简单易懂

    简易QQ聊天室 实现本功能,仅需了解socket套接字的使用,我已经将socket套接字的监听接受状态封装在了common.c中,相信你看了会有所收获,socket的连接也封装在common.c中. ...

  3. 简易QQ聊天室(多线程通信)

    基于控制台实现的简易聊天室,详细实现见代码~ main.cpp(主程序) #include <iostream> #include<string>#include " ...

  4. 安卓Android活动社交仿QQ聊天app设计

    根据开发的目的和意义,主要有以下几个需求 用户需要进行身份验证(登录,注册) 用户的个人信息管理(修改头像,昵称,性别,签名,手机号,地区...) 好友管理 查看天气 社交平台(聊天交友,参加活动,查 ...

  5. spring boot练习--利用websocket实现QQ聊天室

    本篇介绍websocket实现QQ聊天室的后端实现,前端的实现看链接说明 链接说明 1.使用了spring boot 框架,有关spring boot 入门请戳此链接使用Intellij IDEA开发 ...

  6. android socket 简易聊天室 java服务器,Android Socket通信实现简单聊天室

    socket通信是基于底层TCP/IP协议实现的.这种服务端不需要任何的配置文件和tomcat就可以完成服务端的发布,使用纯java代码实现通信.socket是对TCP/IP的封装调用,本身并不是一种 ...

  7. 【系统开发】WebSocket + SpringBoot + Vue 搭建简易网页聊天室

    文章目录 一.数据库搭建 二.后端搭建 2.1 引入关键依赖 2.2 WebSocket配置类 2.3 配置跨域 2.4 发送消息的控制类 2.5 R类 三.前端搭建 3.1 自定义文件websock ...

  8. rudesocket如何使用_[WebSocket入门]手把手搭建WebSocket多人在线聊天室(SpringBoot+WebS...

    前言 本文中搭建了一个简易的多人聊天室,使用了WebSocket的基础特性. 源代码来自老外的一篇好文: 本文内容摘要: 初步理解WebSocket的前后端交互逻辑 手把手使用 SpringBoot ...

  9. java qq聊天界面代码,Java简易qq聊天,代码

    Java简易qq聊天,代码 关注:156  答案:2  手机版 解决时间 2021-02-26 11:56 提问者心死旳很干净 2021-02-25 16:17 Java简易qq聊天,代码 最佳答案 ...

最新文章

  1. 各种 Optimizer 梯度下降优化算法总结
  2. python雷达图-Python成绩单雷达图
  3. 计算机网络 实验六 静态路由配置,实验六-静态路由配置.doc
  4. windows php exec()不生效问题
  5. 虚拟局域网VLAN简介
  6. android中button点击两次才响应onclick方法
  7. Flink 1.9报错:No implicits found for parameter evidence$2: TypeInformation[(String, String, String)]
  8. trados 2007 2009 共用一个LICENSE服务器
  9. 3个人的java 实验_20165104-JAVA第三次实验
  10. 使用dd命令制作ISO镜像U盘启动盘
  11. 天锐绿盾解密_天锐绿盾签约兴威 助力食品行业信息防泄漏
  12. 记一次调研DS证据理论中遇到的问题
  13. php mysql计数器代码一例
  14. AlertManager配置参数解析
  15. (六)我的JavaScript系列:更好的JavaScript之CoffeeScript
  16. 电容笔和Apple pencil的区别?适合ipad画画的电容笔推荐
  17. 前后端分离简单项目--蚂蚁博客--后端部分
  18. vxworks6.6 ramdisk的创建
  19. RSA非对称加密和解密(同时生成密钥)
  20. 五十四、HBase的协处理器

热门文章

  1. Java游戏聊斋聂小倩_求手机游戏聊斋聂小倩的攻略。华娱那版。
  2. 基于Neo4j的网络安全知识图谱构建分析
  3. linux查看内存、cpu占用情况
  4. STM32CubeIDE导入机智云生成基于MDK的STM32工程
  5. OpenCV图像处理 空间域图像增强(图像锐化 1 基于拉普拉斯算子)
  6. 【5G网络基础,熟知即可】
  7. linux是专业的gis系统,首款支持红旗Linux操作系统的GIS基础平台问世
  8. linux的wine启动软件报错:err:font:add_replace_font not found suitable family L“MS Shell Dlg“
  9. 自动驾驶仿真时如何采集数据做地图
  10. SegmentFault 社区访谈 | 依云: 一只想依偎在云上的野百合