一、权限文件
0.gradle切换国内源
#Fri Nov 08 15:46:05 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
1.添加仅测试声明,针对vivo手机安装不上app
android.injected.testOnly=false
2.添加弹窗块的代码
implementation("androidx.appcompat:appcompat:1.6.1")
3.申请使用网络权限
<!-- 申请网络权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
二、前端UI布局
1.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">
<!-- 按钮和灯的布局 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerInParent="true">
<!-- 按钮1 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="16dp">
<View
android:id="@+id/light1"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="10dp"
android:background="@android:color/darker_gray" />
<Button
android:id="@+id/button1"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="1"
android:backgroundTint="@android:color/darker_gray"
android:textColor="@android:color/white" />
</LinearLayout>
<!-- 按钮2 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="16dp">
<View
android:id="@+id/light2"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="10dp"
android:background="@android:color/darker_gray" />
<Button
android:id="@+id/button2"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="2"
android:backgroundTint="@android:color/darker_gray"
android:textColor="@android:color/white" />
</LinearLayout>
<!-- 按钮3 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="16dp">
<View
android:id="@+id/light3"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="10dp"
android:background="@android:color/darker_gray" />
<Button
android:id="@+id/button3"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="3"
android:backgroundTint="@android:color/darker_gray"
android:textColor="@android:color/white" />
</LinearLayout>
<!-- 按钮4 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<View
android:id="@+id/light4"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="10dp"
android:background="@android:color/darker_gray" />
<Button
android:id="@+id/button4"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="4"
android:backgroundTint="@android:color/darker_gray"
android:textColor="@android:color/white" />
</LinearLayout>
</LinearLayout>
<!-- 设置按钮 -->
<Button
android:id="@+id/settingsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设置"
android:backgroundTint="@android:color/darker_gray"
android:textColor="@android:color/white"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp" />
</RelativeLayout>
2.dialog_settings.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="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 目标 IP -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="目标 IP:" />
<EditText
android:id="@+id/targetIp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入目标 IP"
android:inputType="text" />
<!-- 目标端口 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="目标端口:"
android:layout_marginTop="8dp" />
<EditText
android:id="@+id/targetPort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入目标端口"
android:inputType="number" />
<!-- 本地端口 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="本地端口:"
android:layout_marginTop="8dp" />
<EditText
android:id="@+id/localPort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入本地端口"
android:inputType="number" />
</LinearLayout>
三、后端文件
MainActivity.java
package com.example.fl;
import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class MainActivity extends AppCompatActivity {
private String targetIp = "192.168.0.229"; // 默认目标 IP
private int targetPort = 10000; // 默认目标端口
private int localPort = 55555; // 默认本地端口49200-65535
private DatagramSocket socket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 从 SharedPreferences 中读取保存的设置
SharedPreferences preferences = getSharedPreferences("UDPSettings", MODE_PRIVATE);
targetIp = preferences.getString("targetIp", "192.168.0.229"); // 默认值
targetPort = preferences.getInt("targetPort", 10000); // 默认值
localPort = preferences.getInt("localPort", 55555); // 默认值
initializeSocket();
setupButtonAndLight(R.id.button1, R.id.light1, "1");
setupButtonAndLight(R.id.button2, R.id.light2, "2");
setupButtonAndLight(R.id.button3, R.id.light3, "3");
setupButtonAndLight(R.id.button4, R.id.light4, "4");
Button settingsButton = findViewById(R.id.settingsButton);
settingsButton.setOnClickListener(v -> openSettingsDialog());
}
private void initializeSocket() {
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
socket = new DatagramSocket(localPort);
new Thread(this::receiveUDPMessage).start();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "Socket 初始化失败,请检查本地端口设置", Toast.LENGTH_SHORT).show();
}
}
private void setupButtonAndLight(int buttonId, int lightId, String message) {
Button button = findViewById(buttonId);
View light = findViewById(lightId);
button.setOnClickListener(v -> {
sendUDPMessage(message, success -> {
if (success) {
runOnUiThread(() -> light.setBackgroundColor(getResources().getColor(android.R.color.holo_green_light)));
} else {
runOnUiThread(() -> Toast.makeText(this, "发送失败", Toast.LENGTH_SHORT).show());
}
});
});
}
private void sendUDPMessage(String message, UDPResponseCallback callback) {
new Thread(() -> {
boolean success = false;
try {
InetAddress address = InetAddress.getByName(targetIp);
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), address, targetPort);
socket.send(packet);
success = true;
} catch (Exception e) {
e.printStackTrace();
}
callback.onResponse(success);
}).start();
}
private void receiveUDPMessage() {
byte[] buffer = new byte[1024];
while (true) {
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String received = new String(packet.getData(), 0, packet.getLength());
runOnUiThread(() -> handleReceivedMessage(received));
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void handleReceivedMessage(String message) {
int lightId;
switch (message) {
case "1":
lightId = R.id.light1;
break;
case "2":
lightId = R.id.light2;
break;
case "3":
lightId = R.id.light3;
break;
case "4":
lightId = R.id.light4;
break;
default:
return;
}
View light = findViewById(lightId);
light.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
}
private void openSettingsDialog() {
View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_settings, null);
EditText ipInput = dialogView.findViewById(R.id.targetIp);
EditText portInput = dialogView.findViewById(R.id.targetPort);
EditText localPortInput = dialogView.findViewById(R.id.localPort);
// 获取 SharedPreferences 保存的数据
SharedPreferences preferences = getSharedPreferences("UDPSettings", MODE_PRIVATE);
ipInput.setText(preferences.getString("targetIp", targetIp));
portInput.setText(String.valueOf(preferences.getInt("targetPort", targetPort)));
localPortInput.setText(String.valueOf(preferences.getInt("localPort", localPort)));
new AlertDialog.Builder(this)
.setTitle("设置目标和本地端口")
.setView(dialogView)
.setPositiveButton("保存", (dialog, which) -> {
targetIp = ipInput.getText().toString();
targetPort = Integer.parseInt(portInput.getText().toString());
localPort = Integer.parseInt(localPortInput.getText().toString());
// 保存设置到 SharedPreferences
SharedPreferences.Editor editor = preferences.edit();
editor.putString("targetIp", targetIp);
editor.putInt("targetPort", targetPort);
editor.putInt("localPort", localPort);
editor.apply();
initializeSocket(); // 更新本地端口后重新初始化 Socket
Toast.makeText(this, "设置已保存", Toast.LENGTH_SHORT).show();
})
.setNegativeButton("取消", null)
.show();
}
// 定义回调接口
private interface UDPResponseCallback {
void onResponse(boolean success);
}
}
四、实现效果
app按下1234 绿色led亮 目标设备收到数据
回传本地IP和端口 1
led1 灯灭
链接: https://pan.baidu.com/s/1x46I-dhMG_2mRchokZ0xiA?pwd=ktam 提取码: ktam