一、权限文件
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"><!-- 按钮和灯的布局 --><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"android:layout_centerInParent="true"><!-- 按钮1 --><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"android:gravity="center_vertical"android:layout_marginBottom="16dp"><Viewandroid:id="@+id/light1"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginEnd="10dp"android:background="@android:color/darker_gray" /><Buttonandroid: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 --><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"android:gravity="center_vertical"android:layout_marginBottom="16dp"><Viewandroid:id="@+id/light2"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginEnd="10dp"android:background="@android:color/darker_gray" /><Buttonandroid: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 --><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"android:gravity="center_vertical"android:layout_marginBottom="16dp"><Viewandroid:id="@+id/light3"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginEnd="10dp"android:background="@android:color/darker_gray" /><Buttonandroid: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 --><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"android:gravity="center_vertical"><Viewandroid:id="@+id/light4"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginEnd="10dp"android:background="@android:color/darker_gray" /><Buttonandroid: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><!-- 设置按钮 --><Buttonandroid: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 --><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="目标 IP:" /><EditTextandroid:id="@+id/targetIp"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入目标 IP"android:inputType="text" /><!-- 目标端口 --><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="目标端口:"android:layout_marginTop="8dp" /><EditTextandroid:id="@+id/targetPort"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入目标端口"android:inputType="number" /><!-- 本地端口 --><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="本地端口:"android:layout_marginTop="8dp" /><EditTextandroid: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"; // 默认目标 IPprivate int targetPort = 10000; // 默认目标端口private int localPort = 55555; // 默认本地端口49200-65535private DatagramSocket socket;@Overrideprotected 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());// 保存设置到 SharedPreferencesSharedPreferences.Editor editor = preferences.edit();editor.putString("targetIp", targetIp);editor.putInt("targetPort", targetPort);editor.putInt("localPort", localPort);editor.apply();initializeSocket(); // 更新本地端口后重新初始化 SocketToast.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