用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'
implementation 'org.litepal.guolindev:core:3.1.1'
}
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 {
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";
private int port = 12345;
private boolean IStrue = false;
private Socket socket;
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);
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");
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)
list.remove(0);
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;
try {
temp = new char[200];
len = 0;
while ((c = dis.readChar()) != '\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();
} catch (IOException e) {
e.printStackTrace();
}
String data = getdata();
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;
}
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 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;
}
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);
}
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>