安卓多人聊天室客户端
主活动代码
package com.example.chatroom_client;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.OutputStream;
import java.net.Socket;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private OutputStream outputStream = null;
private Socket socket = null;
private String ip = "192.168.1.150";
private Button btn_cnt;
private EditText et_ip;
private EditText et_name;
private EditText et_port;
private TextView myName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_cnt = findViewById(R.id.btn_cnt);
et_ip = findViewById(R.id.et_ip);
et_port = findViewById(R.id.et_port);
et_name = findViewById(R.id.et_name);
myName = findViewById(R.id.my_name);
btn_cnt.setOnClickListener(MainActivity.this);
}
public void onClick(View view) {
String name = et_name.getText().toString();
if ("".equals(name)) {
Toast.makeText(this, "请输入用户名", Toast.LENGTH_SHORT).show();
} else {
Intent intent = new Intent(MainActivity.this, ChatRoom.class);
intent.putExtra("name", et_name.getText().toString());
intent.putExtra("ip",et_ip.toString());
intent.putExtra("port",et_port.toString());
startActivity(intent);
}
}
}
ChatRoom代码
package com.example.chatroom_client;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class ChatRoom extends AppCompatActivity implements View.OnClickListener {
private List<Msg> msgList = new ArrayList<>();
private EditText inputText;
private Button send;
private Button back;
private RecyclerView msgRecyclerView;
private MsgAdapter adapter;
private Socket socketSend;
private String ip = "192.168.1.150";
private String port = "6666";
DataInputStream dis;
DataOutputStream dos;
boolean isRunning = false;
private TextView myName;
private String recMsg;
private boolean isSend = false;
private String name;
private Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
if (!recMsg.isEmpty()) {
addNewMessage(recMsg, Msg.TYPE_RECEIVED);
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat_ui);
Intent intent = getIntent();
name = intent.getStringExtra("name");
inputText = findViewById(R.id.input_text);
send = findViewById(R.id.send);
send.setOnClickListener(this);
back = findViewById(R.id.back);
back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AlertDialog.Builder dialog = new AlertDialog.Builder(ChatRoom.this);
dialog.setTitle("退出");
dialog.setMessage("退出登录?");
dialog.setCancelable(false);
dialog.setPositiveButton("是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
finish();
}
});
dialog.setNegativeButton("否", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
dialog.show();
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
LinearLayoutManager layoutManager = new LinearLayoutManager(ChatRoom.this);
msgRecyclerView = findViewById(R.id.msg_recycler_view);
msgRecyclerView.setLayoutManager(layoutManager);
adapter = new MsgAdapter(msgList);
msgRecyclerView.setAdapter(adapter);
}
});
//连接服务器
new Thread(new Runnable() {
@Override
public void run() {
try {
if ((socketSend = new Socket(ip, Integer.parseInt(port))) == null) {
Log.d("ttw", "发送了一条消息1");
} else {
isRunning = true;
Log.d("ttw", "发送了一条消息2");
dis = new DataInputStream(socketSend.getInputStream());
dos = new DataOutputStream(socketSend.getOutputStream());
new Thread(new receive(), "接收线程").start();
new Thread(new Send(), "发送线程").start();
}
} catch (Exception e) {
isRunning = false;
e.printStackTrace();
Looper.prepare();
Toast.makeText(ChatRoom.this, "连接服务器失败", Toast.LENGTH_SHORT).show();
Looper.loop();
try {
socketSend.close();
} catch (IOException e1) {
e1.printStackTrace();
}
finish();
}
}
}).start();
}
public void addNewMessage(String msg,int type) {
Msg message = new Msg(msg, type);
msgList.add(message);
//每当有新消息时,刷新recycleview中的显示
adapter.notifyItemInserted(msgList.size() - 1);
//将recycleview定位到最后一行
msgRecyclerView.scrollToPosition(msgList.size() - 1);
}
//接收线程
class receive implements Runnable {
public void run() {
recMsg = "";
while (isRunning) {
try {
recMsg = dis.readUTF();
// Log.d("ttw", "收到了一条消息" + "recMsg: " + recMsg);
} catch (Exception e) {
e.printStackTrace();
}
if (!TextUtils.isEmpty(recMsg)) {
// Log.d("ttw","inputstream:" + dis);
Message message = new Message();
message.obj = recMsg;
handler.sendMessage(message);
}
}
}
}
@Override
public void onClick(View view) {
String content = inputText.getText().toString();
@SuppressLint("SimpleDateFormat")
String date = new SimpleDateFormat("hh:mm:ss").format(new Date());
StringBuilder sb = new StringBuilder();
sb.append(content).append("\n\n" + date);
content = sb.toString();
if (!"".equals(content)) {
Msg msg = new Msg(content, Msg.TYPE_SENT);
msgList.add(msg);
adapter.notifyItemInserted(msgList.size() - 1);
msgRecyclerView.scrollToPosition(msgList.size() - 1);
isSend = true;
}
sb.delete(0, sb.length());
}
//发送线程
class Send implements Runnable {
@Override
public void run() {
while (isRunning) {
String content = inputText.getText().toString();
Log.d("ttw", "发送了一条消息");
if(!"".equals(content) && isSend) {
@SuppressLint("SimpleDateFormat")
String date = new SimpleDateFormat("hh:mm:ss").format(new Date());
StringBuilder sb = new StringBuilder();
sb.append(content).append("\n\n来自:").append(name).append("\n" + date);
content = sb.toString();
//向服务端写数据
try {
dos.writeUTF(content);
sb.delete(0, sb.length());
Log.d("ttw", "发送了一条消息");
} catch (IOException e) {
e.printStackTrace();
}
isSend = false;
inputText.setText(""); //在副线程发送完毕后再将待发送信息置空
}
}
}
}
}
Msg类
package com.example.chatroom_client;
public class Msg {
public static final int TYPE_RECEIVED = 0;
public static final int TYPE_SENT = 1;
public String getContent() {
return content;
}
public int getType() {
return type;
}
private String content;
private int type;
public Msg(String content, int type) {
this.content = content;
this.type = type;
}
}
RecyclerView的Adapter: MsgAdapter
package com.example.chatroom_client;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {
private List<Msg> mMsgList;
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate
(R.layout.msg_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Msg msg = mMsgList.get(position);
if(msg.getType() == Msg.TYPE_RECEIVED) {
holder.leftLayout.setVisibility(View.VISIBLE);
holder.rightLayout.setVisibility(View.GONE);
holder.leftMsg.setText(msg.getContent());
}else if(msg.getType() == Msg.TYPE_SENT) {
holder.leftLayout.setVisibility(View.GONE);
holder.rightLayout.setVisibility(View.VISIBLE);
holder.rightMsg.setText(msg.getContent());
}
}
@Override
public int getItemCount() {
return mMsgList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
LinearLayout leftLayout;
LinearLayout rightLayout;
TextView leftMsg;
TextView rightMsg;
public ViewHolder(@NonNull View view) {
super(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);
}
}
public MsgAdapter(List<Msg> msgList) {
mMsgList = msgList;
}
}
主活动布局
<?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="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#3FA2F8">
<TextView
android:id="@+id/tv_room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="登陆聊天室"
android:textColor="#F3F4F5"
android:textSize="20sp" />
</androidx.appcompat.widget.Toolbar>
<!--<View--> //注意大写,否则闪退
<!--android:id="@+id/ver_view"-->
<!--android:layout_toLeftOf="@+id/text_ip"-->
<!--android:layout_width="0dp"-->
<!--android:layout_height="match_parent"-->
<!--/>-->
<TextView
android:id="@+id/tv_name"
android:text="用户名"
android:textSize="20sp"
android:layout_marginBottom="8dp"
android:layout_above="@+id/et_ip"
android:layout_marginLeft="45dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_name"
android:layout_toRightOf="@id/tv_name"
android:layout_above="@id/et_ip"
android:layout_width="150dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/text_ip"
android:text="IP"
android:textSize="20sp"
android:layout_toLeftOf="@+id/et_ip"
android:layout_marginTop="5dp"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_name"
/>
<EditText
android:id="@+id/et_ip"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="192.168.1.150"
/>
<TextView
android:id="@+id/tv_port"
android:layout_below="@+id/text_ip"
android:layout_marginLeft="45dp"
android:text="端口"
android:textSize="20sp"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_port"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:text="6666"
android:gravity="center"
android:editable="false"
android:layout_below="@+id/et_ip"
android:layout_marginLeft="20dp"
android:layout_toRightOf="@id/tv_port"
/>
<Button
android:id="@+id/btn_cnt"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_below="@id/et_port"
android:layout_marginLeft="130dp"
android:layout_marginTop="30dp"
android:text="连接" />
</RelativeLayout>
聊天室布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#d8e0e8"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#3FA2F8">
<Button
android:id="@+id/back"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/back" />
<TextView
android:id="@+id/text_room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="聊天室"
android:textColor="#F3F4F5"
android:textSize="20sp" />
</androidx.appcompat.widget.Toolbar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/msg_recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@drawable/background"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/input_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:background="@drawable/shape"
/>
<Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape2"
android:text="发送" />
</LinearLayout>
</LinearLayout>
聊天室子布局:每行消息的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
>
<LinearLayout
android:id="@+id/left_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
>
<ImageView
android:id="@+id/iv_head_others"
android:background="@drawable/headofothers"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/others_name"
android:layout_gravity="left"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/left_msg"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="@drawable/qipao_l"
android:layout_gravity="center"
android:textColor="#B955B9" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/right_layout"
android:layout_gravity="right"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/my_name"
android:layout_gravity="right"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/right_msg"
android:textStyle="bold"
android:layout_gravity="center"
android:background="@drawable/qipao_r"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView
android:id="@+id/iv_head_my"
android:background="@drawable/headofmy"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
另外
在style.xml中将标题栏隐藏
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
在AndroidManif.xml中加入了网络权限
<uses-permission android:name="android.permission.INTERNET"/>
最后看下效果