Android聊天室开发(java写的后端服务)
程序员文章站
2022-05-16 22:26:16
用Android写的客户端,java写的服务端,这里简单的介绍一下我的客户端,想看服务端,源码的都可在我的博客查看效果展示代码展示分析1.添加权限,依赖库权限 uses-permission android:name=“android.permission.INTERNET”
用Android写的客户端,java写的服务端,这里简单的介绍一下我的客户端
服务端链接
源码链接
效果展示
代码展示分析
1.添加权限,依赖库
权限 uses-permission android:name=“android.permission.INTERNET”
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.uichat"> <uses-permission android:name="android.permission.INTERNET" /> <application ... </application> </manifest>
依赖库
dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) ... implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha05' //recyclerview implementation 'org.litepal.guolindev:core:3.1.1' //LitePal }
1.主页面
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout //约束布局 xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/ddd" tools:context=".MainActivity"> <androidx.constraintlayout.widget.Guideline //虚拟线,用于对下面布局进行约束 android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.45" /> <TextView
android:id="@+id/username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="用户名" android:textSize="24sp" app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/login" app:layout_constraintVertical_chainStyle="packed" app:layout_constraintVertical_bias="0.4" android:layout_marginEnd="5dp" android:layout_marginRight="5dp" /> <EditText
android:id="@+id/usernameEdit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:hint="输入用户名" app:layout_constraintBaseline_toBaselineOf="@id/username" app:layout_constraintStart_toEndOf="@id/guideline"/> <Button
android:id="@+id/login" android:text="登录" android:textSize="18sp" android:layout_marginTop="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" app:layout_constraintTop_toBottomOf="@id/username" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
MainActivity
package com.example.uichat; 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.Toast; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ Button login; EditText loginedittext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); login = (Button)findViewById(R.id.login); loginedittext = (EditText)findViewById(R.id.usernameEdit); login.setOnClickListener(this); } @Override public void onClick(View view) { String name = loginedittext.getText().toString(); if("".equals(name)) Toast.makeText(MainActivity.this,"请输入用户名",Toast.LENGTH_SHORT).show(); else { //只要输入了名字就进入ChatUI界面 Intent intent = new Intent(MainActivity.this, ChatUI.class); intent.putExtra("username", name); startActivity(intent); } } }
2.聊天页面
activity_chatui.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="match_parent" android:background="@drawable/eee" > <TextView
android:id="@+id/top_TextView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.25" android:text="聊天室" android:gravity="center"/> <View
android:layout_width="match_parent" android:layout_height="1dp" android:background="#FF909090"/> <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview_ChatUi" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="4" /> <LinearLayout
android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" android:layout_weight="1"> <View
android:layout_width="match_parent" android:layout_height="1dp" android:background="#FF909090"/> <LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_weight="1" > <LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <EditText
android:id="@+id/ip_editText" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="2"/> <Button
android:id="@+id/record" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="消息记录"/> <Button
android:id="@+id/cls" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="清屏"/> </LinearLayout> </LinearLayout> <View
android:layout_width="match_parent" android:layout_height="1dp" android:background="#FF909090"/> <LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_weight="1"> <LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <EditText
android:id="@+id/send_editText" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3"/> <Button
android:id="@+id/send" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="Send"/> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout>
chatui.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"> <TextView
android:id="@+id/item_data" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <LinearLayout
android:id="@+id/item_leftlinearlayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="left"> <ImageView
android:id="@+id/item_leftImageView" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/bbb" android:layout_margin="5dp" /> <LinearLayout
android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <TextView
android:id="@+id/leftname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="5dp" android:layout_marginLeft="5dp" /> <LinearLayout
android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/shadow_2" > <TextView
android:id="@+id/item_lefttextview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="5dp"/> </LinearLayout> </LinearLayout> </LinearLayout> <LinearLayout
android:id="@+id/item_rightlinearlayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="right"> <LinearLayout
android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <TextView
android:id="@+id/rightname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_marginEnd="5dp" android:layout_marginRight="5dp" /> <LinearLayout
android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/shadow_2" > <TextView
android:id="@+id/item_righttextview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="5dp"/> </LinearLayout> </LinearLayout> <ImageView
android:id="@+id/item_rightImageView" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/bbb" android:layout_margin="5dp" /> </LinearLayout> </LinearLayout>
ChatUI
package com.example.uichat; import android.content.Intent; import android.os.Bundle; import android.os.Handler; 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.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.litepal.LitePal; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class ChatUI extends AppCompatActivity implements View.OnClickListener{ private String ip = "192.168.1.2"; //指定服务器ip private int port = 12345; //指定端口号 private boolean IStrue = false; //判断是否连接上服务器 private Socket socket; //我用DataInputStream,DataOutputStream来和服务器交互 private DataInputStream dis; private DataOutputStream dos; private RecyclerView recyclerView; private String myname; private int massage_type; private ChatAdapt adapt; private Button send; private Button record; private Button cls; private EditText ipedit; private EditText sendedit; private TextView toptext; private List<myChat> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chatui); //必须重开一条线程不能放到主线程中,负责出现android.os.NetworkOnMainThreadException new Thread(new Runnable() { @Override public void run() { try { if( (socket = new Socket(ip,port))==null){ Toast.makeText(ChatUI.this,"连接服务器失败",Toast.LENGTH_SHORT).show(); }else{ IStrue = true; dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); //开一条线程接收服务器传来的信息 new Thread(new Revised()).start(); } } catch (IOException e) { e.printStackTrace(); } } }).start(); Intent intent = getIntent(); myname = intent.getStringExtra("username"); //拿到主活动传来的name //用recyclerView来显示信息 recyclerView = (RecyclerView)findViewById(R.id.recyclerview_ChatUi); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); adapt = new ChatAdapt(list); recyclerView.setAdapter(adapt); send = (Button)findViewById(R.id.send); record = (Button)findViewById(R.id.record); cls = (Button)findViewById(R.id.cls); ipedit = (EditText)findViewById(R.id.ip_editText); sendedit = (EditText)findViewById(R.id.send_editText); toptext = (TextView)findViewById(R.id.top_TextView); send.setOnClickListener(this); record.setOnClickListener(this); cls.setOnClickListener(this); } private void AutoDelete() { while (list.size()>20) //最多显示20条消息 list.remove(0); //这里吧刷新操作全放在 runOnUiThread中,不然会出现异常 runOnUiThread(new Runnable() { @Override public void run() { // 刷新操作 adapt.notifyDataSetChanged(); } }); } //接收服务器传来的信息 class Revised implements Runnable{ @Override public void run() { //这里必须无限循环,不然只能接到一条信息 while(true) { String title = null; String name = null; char[] temp = null; int len = 0; int massagetype = 0; char c = 0; //这里全是接收信息的,我接受三项信息title, name,temp try { temp = new char[200]; len = 0; while ((c = dis.readChar()) != '\t') { //用的‘\t’对信息做分割 temp[len] = c; len++; } title = new String(temp, 0, len); len = 0; while ((c = dis.readChar()) != '\t') { temp[len] = c; len++; } name = new String(temp, 0, len); massagetype = dis.readInt(); // Log.d("chatui",title+" 4567 "+name+""+massagetype); } catch (IOException e) { e.printStackTrace(); } String data = getdata(); //吧就收到的信息封装成 myChat对象,添加到集合中 myChat my = new myChat(title, name, data, myChat.TYPE_RESIVE, massagetype); list.add(my); AutoDelete(); runOnUiThread(new Runnable() { @Override public void run() { // 刷新操作 adapt.notifyDataSetChanged(); } }); } } } //按钮点击事件 @Override public void onClick(View view) { switch (view.getId()){ //如果点了发送要把信息显示到页面同时传给服务器 case R.id.send: { final String title = sendedit.getText().toString(); String data = getdata(); final String ip = ipedit.getText().toString().equals("")?"255.255.255.255":ipedit.getText().toString(); Log.d("ChatUI",ip); if(!"".equals(sendedit.getText().toString())){ if(ipedit.getText().toString().equals("")||ipedit.getText().toString().equals("255.255.255.255")) { massage_type = myChat.MESSAGE_PUBLIC; } else { massage_type = myChat.MESSAGE_PRIVATE; } // Log.d("ChatUI",massage_type+""); if(IStrue){ //开一条线程把信息发给服务器,这里在按钮点击时无限循环,所以这里不用无限循环 new Thread(new Runnable() { @Override public void run() { try { dos.writeChars(title); dos.writeChar('\t'); dos.writeChars(myname); dos.writeChar('\t'); dos.writeChars(ip); dos.writeChar('\t'); Log.d("ChatUI","我发了"+title); } catch (IOException e) { e.printStackTrace(); } } }).start(); } else Toast.makeText(ChatUI.this,"发送失败",Toast.LENGTH_SHORT).show(); //把发出的信息封装成 myChat对象,添加到集合中 myChat my = new myChat(title,myname,data,myChat.TYPE_SEND,massage_type); list.add(my); AutoDelete(); runOnUiThread(new Runnable() { @Override public void run() { // 刷新操作 adapt.notifyDataSetChanged(); } }); sendedit.setText(""); addressed(title,myname,data); } break; } //清空页面的信息 case R.id.cls: { list.clear(); runOnUiThread(new Runnable() { @Override public void run() { // 刷新操作 adapt.notifyDataSetChanged(); } }); break; } //点了消息记录进入recordactivity活用于显示记录 case R.id.record:{ Intent intent = new Intent(ChatUI.this,recordactivity.class); startActivity(intent); break; } } } //获取时间 private String getdata() { Date data = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); return sdf.format(data); } //这里用LitPal操作数据库来保存数据,在发消息,收消息时都会用到,用于保存消息记录 private void addressed(String title, String name, String data) { record r = new record(); r.setTitle(title); r.setData(data); r.setName(name); r.save(); } }
myChat
package com.example.uichat; public class myChat { public static final int TYPE_SEND = 1; //信息是发送的还是接收的 public static final int TYPE_RESIVE = 0; public static final int MESSAGE_PRIVATE = 1; //是私发信息还是群聊 public static final int MESSAGE_PUBLIC = 0; private String title; private int type; private int massagetype; public int getMassagetype() { return massagetype; } private String name; private String data; public String getData() { return data; } public myChat(String title, String name, String data,int type,int messagetype) { this.title = title; this.name = name; this.data = data; this.type = type; this.massagetype = messagetype; } public String getTitle() { return title; } public int getType() { return type; } public String getName() { return name; } }
ChatAdapt
package com.example.uichat; import android.util.Log; 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 ChatAdapt extends RecyclerView.Adapter<ChatAdapt.ViewHolder> { private List<myChat> list; private String type_public = "[所有人]"; private String type_private = "[私有消息]"; static class ViewHolder extends RecyclerView.ViewHolder{ LinearLayout leftlinearLayout; LinearLayout rightlinearlayout; TextView item_data; TextView item_leftname; TextView item_rightname; TextView item_lefttitle; TextView item_righttitle; public ViewHolder(@NonNull View itemView) { super(itemView); leftlinearLayout =(LinearLayout)itemView.findViewById(R.id.item_leftlinearlayout); rightlinearlayout = (LinearLayout)itemView.findViewById(R.id.item_rightlinearlayout); item_data = (TextView)itemView.findViewById(R.id.item_data); item_rightname = (TextView)itemView.findViewById(R.id.rightname); item_leftname = (TextView)itemView.findViewById(R.id.leftname); item_lefttitle = (TextView)itemView.findViewById(R.id.item_lefttextview); item_righttitle = (TextView)itemView.findViewById(R.id.item_righttextview); } } public ChatAdapt(List<myChat> l){ list = l; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chatui_item,parent,false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { myChat my = list.get(position); if(my.getType()==myChat.TYPE_RESIVE){ holder.leftlinearLayout.setVisibility(View.VISIBLE); //显示左边的 holder.rightlinearlayout.setVisibility(View.GONE); //右边隐藏 holder.item_data.setText(my.getData()); holder.item_lefttitle.setText(my.getTitle()); if(my.getMassagetype()==myChat.MESSAGE_PUBLIC){ holder.item_leftname.setText(type_public+" "+my.getName()); } else if(my.getMassagetype()==myChat.MESSAGE_PRIVATE){ holder.item_leftname.setText(type_private+" "+my.getName()); } } else if(my.getType()==myChat.TYPE_SEND){ holder.leftlinearLayout.setVisibility(View.GONE); //左边隐藏 holder.rightlinearlayout.setVisibility(View.VISIBLE); //右边显示 holder.item_data.setText(my.getData()); holder.item_righttitle.setText(my.getTitle()); Log.d("ChatUI",my.getMassagetype()+" adapt"); if(my.getMassagetype()==myChat.MESSAGE_PUBLIC){ holder.item_rightname.setText(type_public+" "+my.getName()); } else if(my.getMassagetype()==myChat.MESSAGE_PRIVATE){ holder.item_rightname.setText(type_private+" "+my.getName()); } } } @Override public int getItemCount() { return list.size(); } }
3.消息记录页面
这里用了LitePal操作数据库不懂可点击
activity_recordactivity.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout
android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <TextView
android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="4" android:gravity="center" android:textSize="24sp" android:text="消息记录"/> <Button
android:id="@+id/record_button" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text = "清空记录"/> </LinearLayout> <View
android:layout_width="match_parent" android:layout_height="1dp" android:background="#FF909090"/> <TextView
android:layout_weight="9" android:id="@+id/recordtext" android:layout_margin="10dp" android:layout_width="match_parent" android:layout_height="0dp"/> </LinearLayout>
recordactivity
package com.example.uichat; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import org.litepal.LitePal; import java.util.ArrayList; import java.util.List; public class recordactivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recordactivity); final TextView text = (TextView)findViewById(R.id.recordtext); List<record> list = LitePal.findAll(record.class); String str =""; for(record r : list){ str += r.getData()+" "+r.getName()+": "+r.getTitle() +"\n"; } text.setText(str); Button recorebutton = (Button)findViewById(R.id.record_button); recorebutton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder dialog = new AlertDialog.Builder(recordactivity.this); dialog.setTitle("This is Dialog"); dialog.setMessage("是否确定删除所有记录"); dialog.setCancelable(false); dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { LitePal.deleteAll(record.class); text.setText(""); } }); dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { } }); dialog.show(); } }); } }
recoed
package com.example.uichat; import org.litepal.crud.LitePalSupport; public class record extends LitePalSupport { String title; String name; String data; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getData() { return data; } public void setData(String data) { this.data = data; } }
litepal.xml
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="Record"></dbname> <version value="1"></version> <list> <mapping class="com.example.uichat.record"></mapping> </list> </litepal>
本文地址:https://blog.csdn.net/haazzz/article/details/107755583
上一篇: 这就是我们谈恋爱的最终目的
下一篇: 我希望,你喜欢的样子我都有