Android聊天室(源码)
16lz
2021-12-04
服务器源码
import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;public class Server { public static void main(String[] args){ //startServer=new startServer(); new startServer().start();//new 一个线程对象开始启动(由于startServer类继承了Thread) } public static ArrayList<UserThread> socketList=new ArrayList<UserThread>();//创建一个泛型是UserThread(UserThread是下面的一个类)的动态数组 public static startServer startServer; static class startServer extends Thread{ public void run(){ try{ ServerSocket serverSocket = new ServerSocket(6666); //创建端口值为:6666的ServerSocket对象 while(true){//死循环 Socket socket = serverSocket.accept();//创建socket对象,用于接受客户端的请求 System.out.println(""+socket);//用于显示客户端的IP地址,客户端的端口号,以及电脑的端口号 UserThread userThread = new UserThread(socket);//通过下面定义的UserTread的有参构造,创建userThread对象 Server.socketList.add(userThread); new Thread(userThread).start();//开启输入输出流线程 } }catch(IOException e){ e.printStackTrace(); } } } static class UserThread implements Runnable{ private Socket skt; private DataOutputStream dos; private DataInputStream dis; public DataOutputStream getDos(){//返回输出流 return dos; } public void setDos(DataOutputStream dos){//给输出流传递参数 this.dos=dos; } public DataInputStream getDis(){//返回输入流 return dis; } public void setDis(DataInputStream dis){//给输入流传递参数 this.dis=dis; } public UserThread(Socket socket){//构造有参构造 skt=socket; } @Override public void run(){ try{ dos= new DataOutputStream(skt.getOutputStream());//获取输出流(准备从服务器给其他的客户端发消息) dis= new DataInputStream(skt.getInputStream());//接收客户端发过来的消息(输入流) String recMsg =""; while(true){//使服务器无限循环 if(!"".equals(recMsg=dis.readUTF())){//读取输入流的消息,并把消息传到recMsg中 System.out.println("收到一条消息"+ recMsg);//显示:收到一条消息+“传入的消息” for(UserThread s:socketList){//增强for循环 if(s.equals(this)){ continue; } try{ s.getDos().writeUTF(recMsg);//将UTF-8的字符串写入字节流 }catch(IOException e){ socketList.remove(s);//将s从动态数组socketList中删除 e.printStackTrace(); } } recMsg="";//recMsg内容重新刷新 } } }catch(IOException e){ e.printStackTrace(); } } }}
客户端源码
MainActivity
package com.example.my_chatroom;import androidx.appcompat.app.AppCompatActivity;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 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.66"; 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 = (Button)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); } }}
activity_main.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="match_parent" android:background="@drawable/picture"> <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:gravity="center" 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:gravity="center" 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.66" /> <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: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:background="#07D0F3" android:textColor="#ffffff" android:text="连接" /></RelativeLayout>
ChatRoom
package com.example.my_chatroom;import androidx.annotation.NonNull;import androidx.appcompat.app.AlertDialog;import androidx.appcompat.app.AppCompatActivity;import androidx.recyclerview.widget.LinearLayoutManager;import androidx.recyclerview.widget.RecyclerView;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 java.io.DataInput;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.66"; 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()){//获取当前进程的Looper对象传给handler @Override public void handleMessage(@NonNull Message msg){//? if(!recMsg.isEmpty()){ addNewMessage(recMsg,Msg.TYPE_RECEIVED);//添加新数据 } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chat_room); 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();//finish()是在程序执行的过程中使用它来将对象销毁,finish()方法用于结束一个Activity的生命周期 } }); 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); adapter.notifyItemInserted(msgList.size()-1); 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(""); } } } }}
activity_chat_room.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" 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="#187C8F"> <Button android:id="@+id/back" android:layout_width="50dp" android:layout_height="wrap_content" 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:orientation="horizontal" 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:background="#ffffff" /> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="50dp" android:background="#07D0F3" android:text="发送" android:textColor="#ffffff" /> </LinearLayout></LinearLayout>
msg_item.xml
<?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="60dp" android:layout_height="60dp" /> <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_2" 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_1" 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="60dp" android:layout_height="60dp" /> </LinearLayout></LinearLayout>
Msg
package com.example.my_chatroom;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; }}
MsgAdapter
package com.example.my_chatroom;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){ //ViewHolder通常出现在适配器里,为的是listview滚动的时候快速设置值,而不必每次都重新创建很多对象,从而提升性能。 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false); //LayoutInflat.from()从一个Context中,获得一个布局填充器,这样你就可以使用这个填充器来把xml布局文件转为View对象了。 //LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false);这样的方法来加载布局msg_item.xml 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; }}
shape.xml
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#07D0F3"/></selector>
previous.xml
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#0fffff"/></selector>
click.xml
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="20dp"/> <item android:drawable="@drawable/shape" android:state_enabled="true" android:state_pressed="true"/> <item android:drawable="@drawable/previous" android:state_enabled="true" android:state_pressed="false"/></selector>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.my_chatroom"> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="聊天室" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".ChatRoom"/> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
另外,在说明一点,如果需要修改App图标的话
路径:
修改位置:
其中theme是我选中的一张图片
更多相关文章
- 类和 Json对象
- Android(安卓)蓝牙(BLE)连接,发送,接收消息
- android 单选,复选按钮,以及toast
- Android库. 1 UDP客户端
- java 后端实现WebSocket学习篇和客户端Android(安卓)通信
- [Mobile Web]Web中如何分辨移动设备?(iPad、iPhone、Android)
- Android作为客户端,采用Netty与服务器通信
- Android(安卓)MQTT
- Android(安卓)4.0 gallery2 生成video thumbnail的过程