入门级带你实现一个安卓智能家居APP(1)java版本

2023-05-16

话不多说,直接开干!

先来看看效果
本APP是通过tcp/ip协议与连了WiFi的单片机通信。
在这里插入图片描述

第一步

你得会一点点java基础,你可以去百度搜“java菜鸟教程”,学了基本的基础即可,不必太深入。

第二步

建议看一本书,是郭霖大神些的《第一行代码》,入门安卓基础。不想看书就忽略吧,我建议还是看一下最基本的例如:安卓四大组件----活动。
如果你真的一点都不想学。那就先跳过这一步。

第三步:安装Android studio软件。不会安装的小白,看过来!

注意:要安装Android studio,前提一定要安装了jdk环境。

jdk环境怎么安装?如果你电脑已经安装过eclipse并有使用过的话,就不用再安装jdk了。
打开这个链接:https://mp.weixin.qq.com/s/HKdtcd11Zk3HV3fh8_dtcw,跟着步骤做,做到15步即可。当然,如果你想要顺便把eclipse也给安装了,也无所谓。

装完jdk之后,就可以开始装Android studio了。
打开这个链接:https://www.bilibili.com/read/cv8151954
然后跟着步骤做吧。对了,下载的安装包,我建议选3.5.2 的!!!特别是小白,不然我的代码到时候你导进去各种错误可别来抱怨。

都安装完了就继续往前吧!

第四步:新建项目,开始撸代码

默认点击我标记的红色框框。请跟着我的图片操作哦。
在这里插入图片描述
在这里插入图片描述
我们给自己的项目起个名字哈:SmartHome
第二个小框框是包名,我们先不用理它是干什么的
第三个小框框是项目的存储路径
其他的不解析了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
好啦我们直接开始敲代码啦!
我们先来写界面吧!
在这里插入图片描述
然后把我的代码复制粘贴

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >


    <LinearLayout
        android:layout_marginTop="20dp"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        >
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <EditText
                android:layout_marginLeft="10dp"
                android:layout_marginRight="5dp"
                android:layout_weight="1"
                android:id="@+id/IPText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:textSize="20dp"
                android:hint="请输入衣柜服务端的IP地址" />

            <Button
                android:layout_marginRight="10dp"
                android:layout_weight="2"
                android:id="@+id/StartConnect"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="20dp"
                android:text="开始连接" />
        </LinearLayout>


        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <TextView
                android:id="@+id/tv1"
                android:layout_marginLeft="20dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="20dp" />

        </RelativeLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
            <LinearLayout
                android:gravity="center"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <TextView
                    android:layout_marginLeft="10dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="通风"
                    android:textSize="25sp"
                    android:id="@+id/textView" />
                <Switch
                    android:id="@+id/switch_c"
                    android:layout_marginLeft="40dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textOff="OFF"
                    android:textOn="ON"
                    android:thumb="@drawable/thumb"
                    android:track="@drawable/track"

                    />
            </LinearLayout>



            <LinearLayout
                android:gravity="center"
                android:layout_gravity="center"
                android:layout_marginTop="20dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <TextView
                    android:layout_marginLeft="10dp"
                    android:gravity="center"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="抽湿"
                    android:textSize="25sp"
                    android:id="@+id/textView2" />
                <Switch
                    android:id="@+id/switch_t"
                    android:layout_marginLeft="40dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textOff="OFF"
                    android:textOn="ON"
                    android:thumb="@drawable/thumb"
                    android:track="@drawable/track"

                    />
            </LinearLayout>



        </LinearLayout>


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <TextView
                android:layout_marginLeft="20dp"
                android:layout_marginTop="15dp"
                android:textSize="25sp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="温湿度:0℃"
                android:id="@+id/temp_text" />

            <TextView
                android:layout_marginLeft="20dp"
                android:textSize="25sp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="气体浓度:0%"
                android:id="@+id/mq_text" />
        </LinearLayout>
       <TextView
           android:id="@+id/test"
           android:layout_gravity="center"
           android:textSize="20sp"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="文字显示"/>

        <Button
            android:id="@+id/button_test"
            android:text="点击我就会变成别的文字"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>
</RelativeLayout>
    

在这里插入图片描述

在这里插入图片描述
填写名字
在这里插入图片描述
在这里插入图片描述
thumb.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- 设置按钮在不同状态下的时候,按钮不同的颜色 -->
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:state_checked="true" android:drawable="@drawable/green_thumb" />
    <item android:drawable="@drawable/gray_thumb" />

</selector>


是不是又报错啦?如下图?
在这里插入图片描述
以刚才建drawable资源文件的方式,建以下5个drawable资源文件。
在这里插入图片描述
1.gray_thumb.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <!-- 高度40 -->
    <size android:height="40dp" android:width="40dp"/>
    <!-- 圆角弧度 20 -->
    <corners android:radius="20dp"/>

    <!-- 变化率 -->
    <gradient
        android:endColor="#ffffff"
        android:startColor="#ffffff" />

    <stroke android:width="1dp"
        android:color="#9e9e9e"/>

</shape>


2.gray_track.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <!-- 高度40 -->
    <size android:height="40dp" android:width="40dp"/>
    <!-- 圆角弧度 20 -->
    <corners android:radius="20dp"/>

    <!-- 变化率 -->
    <gradient
        android:endColor="#ffffff"
        android:startColor="#ffffff" />

    <stroke android:width="1dp"
        android:color="#9e9e9e"/>

</shape>


3.green_thumb.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <!-- 高度40 -->
    <size android:height="40dp" android:width="40dp"/>
    <!-- 圆角弧度 20 -->
    <corners android:radius="20dp"/>

    <!-- 变化率 -->
    <gradient
        android:endColor="#ffffff"
        android:startColor="#ffffff" />

    <stroke android:width="1dp"
        android:color="#9e9e9e"/>

</shape>


4.green_track.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <!-- 高度40 -->
    <size android:height="40dp" android:width="40dp"/>
    <!-- 圆角弧度 20 -->
    <corners android:radius="20dp"/>

    <!-- 变化率 -->
    <gradient
        android:endColor="#ffffff"
        android:startColor="#ffffff" />

    <stroke android:width="1dp"
        android:color="#9e9e9e"/>

</shape>


5.track.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- 控制Switch在不同状态下,底下下滑条的颜色 -->
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:state_checked="true" android:drawable="@drawable/green_track" />
    <item android:drawable="@drawable/gray_track" />

</selector>


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上是比较重要的UI控件,大家了解一下,其他的一些属性我就不多说了,你们可以自己尝试修改一下参数,就会发现具体的对应的作用是什么了。

我们的ui已经做好了,现在就去写主活动类的逻辑代码!!!

在这里插入图片描述
我主要说说主要逻辑吧。
首先,当然是要把我们前面编写的界面都映射到我们的主活动, setContentView(R.layout.activity_main);//该activity映射的xml界面是activity_main.xml,就是这一句,然后,开启严苛模式,我们使用StrictMode,系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑。

接下来就是,初始化view组件 ` initView();//初始化显示的功能控件
最后对某些组件设置监听事件,以及编写对应的函数

重点来了

我们想要APP和单片机进行tcp通信,要怎么实现呢?
建立socket连接实现tcp/ip协议----这个就是我们的实现方式。

那应该怎么做
1.创建Socket(安卓作为客户端,所以是client,单片机作为server端)。
2.打开连接到Socket的输入/输出流。
3.按照协议对Socket进行读/写操作。
4.关闭输入输出流、关闭Socket。

也许说了你们可能还是不太懂,这个用代码怎么实现啊?
我先说明一下,java socket的实现和安卓平台socket的实现有一点点的区别,*在安卓里面,涉及到网络连接等耗时操作时,不能将其放在UI主线程中,需要添加子线程,在子线程进行网络连接,这就涉及到安卓线程间的通信了,用Handle来实现。这里的子线程也就是 mThreadClient。

下面直接看我的代码----MainActivity.java

package com.example.smarthome;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
/**
 * 建立socket连接实现tcp/ip协议的方式:
 *1.创建Socket(安卓作为客户端,所以是client,单片机作为server端)
 *
 *2.打开连接到Socket的输入/输出流
 *
 *3.按照协议对Socket进行读/写操作
 *
 *4.关闭输入输出流、关闭Socket

 */
public class MainActivity extends AppCompatActivity {
    private Button startButton;//连接按钮
    private EditText IPText;//ip地址输入
    private boolean isConnecting=false;//判断是否连接
    private Thread mThreadClient=null;//子线程
    private Socket mSocketClient=null;//socket实现tcp、ip协议,实现tcp server和tcp client的连接
    private static PrintWriter mPrintWriterClient=null;//PrintWriter是java中很常见的一个类,该类可用来创建一个文件并向文本文件写入数据
    private  String res="";//接收的数据
    private  TextView warning_show, temp, mq;//警告语  温湿度  气体浓度
    private String []send_order={"1\n","2\n","3\n","4\n"};//发送的指令 1开启通风  2 关闭通风 3开启抽湿 4关闭抽湿
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);//该activity映射的xml界面是activity_main.xml
        strictMode();//严苛模式
        initView();//初始化显示的功能组件
    }

    /**
     * 严苛模式
     * StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,
     * 以达到提升应用响应能力的目的。举个例子来说,如果开发者在UI线程中进行了网络操作或者文件系统的操作,
     * 而这些缓慢的操作会严重影响应用的响应能力,甚至出现ANR对话框。为了在开发中发现这些容易忽略的问题,
     * 我们使用StrictMode,系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑。
     */
    private void strictMode(){
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectNetwork()
                .penaltyLog()
                .build()
        );
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects()
                .penaltyLog()
                .penaltyDeath()
                .build());
    }

    /**
     * layout组件初始化
     */
    private void initView(){
        warning_show = findViewById(R.id.tv1);//警告语显示
        temp = findViewById(R.id.temp_text);//温湿度显示
        mq = findViewById(R.id.mq_text);//气体浓度显示

        IPText= findViewById(R.id.IPText);//ip地址和端口号
        IPText.setText("192.168.1.127:8080");//把ip地址和端口号设一个默认值,这个要改成你自己设置的

        startButton= findViewById(R.id.StartConnect);//连接按钮
        //连接事件  其实就是建立socket连接

        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(isConnecting)
                {
                    isConnecting=false;
                    if(mSocketClient!=null)
                    {
                        try{
                            mSocketClient.close();
                            mSocketClient = null;
                            if (mPrintWriterClient!=null){
                                mPrintWriterClient.close();
                                mPrintWriterClient = null;
                            }
                            mThreadClient.interrupt();
                            startButton.setText("开始连接");
                            IPText.setEnabled(true);//可以输入ip和端口号
                            warning_show.setText("断开连接\n");

                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }else
                {
                    mThreadClient = new Thread(mRunnable);
                    mThreadClient.start();
                }
            }
        });

        //通风开关按钮初始化
        final Switch switch_c=findViewById(R.id.switch_c);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            switch_c.setShowText(true);//按钮上默认显示文字
        }
        switch_c.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked)//当isChecked为true时,按钮就打开,并发送开的指令,当isChecked为false时,按钮就关闭,并发送关的指令
                {
                    switch_c.setSwitchTextAppearance(MainActivity.this,R.style.s_true);//开关样式
                    switch_c.setShowText(true);//显示开关为on
                    if (send(send_order[0],-1)){
                        showDialog("开启通风");
                    }else{
                        switch_c.setChecked(false);//当APP没有连接到单片机时,默认此按钮点击无效
                    }
                }else{

                    switch_c.setSwitchTextAppearance(MainActivity.this,R.style.s_false);//开关样式
                    switch_c.setShowText(true);//显示文字on
                    if (send(send_order[1],-1)){
                        showDialog("关闭通风");
                    }else{
                        switch_c.setChecked(false);//当APP没有连接到单片机时,默认此按钮点击无效
                    }
                }
            }
        });

        //抽湿开关按钮
        final Switch switch_t=findViewById(R.id.switch_t);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            switch_t.setShowText(true);//按钮上默认显示文字
        }
        switch_t.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked)
                {
                    switch_t.setSwitchTextAppearance(MainActivity.this,R.style.s_true);//开关样式
                    switch_t.setShowText(true);//显示文字on
                    if (send(send_order[2],-1)){
                        showDialog("开启抽湿");
                    }else{
                        switch_t.setChecked(false);//当APP没有连接到单片机时,默认此按钮点击无效
                    }
                }else{
                    switch_t.setSwitchTextAppearance(MainActivity.this,R.style.s_false);//开关样式
                    switch_t.setShowText(true);
                    if (send(send_order[3],-1)){
                        // flag=false;
                        showDialog("关闭抽湿");
                    }else{
                        switch_t.setChecked(false);//当APP没有连接到单片机时,默认此按钮点击无效
                    }
                }
            }
        });
    }

    //开启子线程
    private Runnable mRunnable = new Runnable() {

        @Override
        public void run() {
            String msgText = IPText.getText().toString();
            if(msgText.length()<=0)//IP和端口号不能为空
            {
                Message msg = new Message();
                msg.what = 5;
                mHandler.sendMessage(msg);
                return;
            }
            int start = msgText.indexOf(":");//IP和端口号格式不正确
            if((start==-1)||(start+1>=msgText.length()))
            {
                Message msg = new Message();
                msg.what = 6;
                mHandler.sendMessage(msg);
                return;
            }
            String sIP= msgText.substring(0,start);
            String sPort = msgText.substring(start+1);
            int port = Integer.parseInt(sPort);

            BufferedReader mBufferedReaderClient;//从字符输入流中读取文本并缓冲字符,以便有效地读取字符,数组和行
            try
            {
                //连接服务器
                mSocketClient = new Socket();//创建Socket
                SocketAddress socAddress = new InetSocketAddress(sIP, port);//设置ip地址和端口号
                mSocketClient.connect(socAddress, 2000);//设置超时时间为2秒
                //取得输入、输出流
                mBufferedReaderClient =new BufferedReader(new InputStreamReader(mSocketClient.getInputStream()));
                mPrintWriterClient=new PrintWriter(mSocketClient.getOutputStream(),true);

                //连接成功,把这个好消息告诉主线程,配合主线程进行更新UI。
                Message msg = new Message();
                msg.what = 1;
                mHandler.sendMessage(msg);

            }catch (Exception e) {
                //如果连接不成功,也要把这个消息告诉主线程,配合主线程进行更新UI。
                Message msg = new Message();
                msg.what = 2;
                mHandler.sendMessage(msg);
                return;
            }
            char[] buffer = new char[256];
            int count ;

            while(true)
            {
                try
                {
                    if((count = mBufferedReaderClient.read(buffer))>0)//当读取服务器发来的数据时
                    {
                        res = getInfoBuff(buffer,count)+"\n";//接收到的内容格式转换成字符串
                        //当读取服务器发来的数据时,也把这个消息告诉主线程,配合主线程进行更新UI。
                        Message msg = new Message();
                        msg.what = 4;
                        mHandler.sendMessage(msg);
                    }
                }catch (Exception e) {
                    // TODO: handle exception
                    //当读取服务器发来的数据错误时,也把这个消息告诉主线程,配合主线程进行更新UI。
                    Message msg = new Message();
                    msg.what = 3;
                    mHandler.sendMessage(msg);
                }
            }
        }
    };

    /**
     * 在安卓里面,涉及到网络连接等耗时操作时,不能将其放在UI主线程中,
     * 需要添加子线程,在子线程进行网络连接,这就涉及到安卓线程间的通信了,
     * 用Handle来实现。这里的子线程也就是 mThreadClient
     *
     *   handle的定义: 主要接受子线程发送的数据, 并用此数据配合主线程更新UI.
     *   解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) ,
     *   主线程为管理界面中的UI控件,进行事件分发, 比如说, 你要是点击一个 Button,
     *   Android会分发事件到Button上,来响应你的操作。  如果此时需要一个耗时的操作,
     *   例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,
     *   如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,
     *   会收到Android系统的一个错误提示  "强制关闭".  这个时候我们需要把这些耗时的操作,
     *   放在一个子线程中,更新UI只能在主线程中更新,子线程中操作是危险的. 这个时候,
     *   Handler就出现了来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),
     *   它与子线程可以通过Message对象来传递数据,这个时候,
     *   Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,
     *   里面包含数据, 把这些消息放入主线程队列中,配合主线程进行更新UI。
     */
    @SuppressLint("HandlerLeak")
    Handler mHandler = new Handler()
    {
        @SuppressLint("SetTextI18n")
        public void handleMessage(Message msg)
        {
            super.handleMessage(msg);
            if(msg.what==4)//当读取到服务器发来的数据时,收到了子线程的消息,而且接收到的字符串我们给它定义的是res
            {
                char []arrs;
                arrs=res.toCharArray();//接收来自服务器的字符串,把字符串转成字符数组
                if (arrs.length>=3) {
                    if (arrs[0]=='T'){//如果字符数组的首位是T,说明接收到的信息是温湿度 T25

                        temp.setText("温湿度:"+arrs[1]+arrs[2]+"℃");
                    }

                    else if (arrs[0]=='M'){//如果字符数组的首位是T,说明接收到的信息是气体浓度M66

                       mq.setText("气体浓度:"+arrs[1]+arrs[2]+"%");
                    }
                }else {
                    showDialog("收到格式错误的数据:"+res);
                }
            }else if (msg.what==2){
                showDialog("连接失败,服务器走丢了");
                startButton.setText("开始连接");


            }else if (msg.what==1){
                showDialog("连接成功!");
                warning_show.setText("已连接智能衣柜\n");
                IPText.setEnabled(false);//锁定ip地址和端口号
                isConnecting = true;
                startButton.setText("停止连接");
            }else if (msg.what==3){
                warning_show.setText("已断开连接\n");


            }else if (msg.what==5){
                warning_show.setText("IP和端口号不能为空\n");
            }
            else if (msg.what==6){
                warning_show.setText("IP地址不合法\n");
            }
        }
    };

    /**
     * 窗口提示
     * @param msg
     */
    private  void showDialog(String msg) {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setIcon(android.R.drawable.ic_dialog_info);
        builder.setTitle(msg);
        builder.setCancelable(false);
        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });
        builder.create().show();
    }

    /**
     * 字符数组转字符串
     * @param buff
     * @param count
     * @return
     */
    private String getInfoBuff(char[] buff,int count)
    {
        char[] temp = new char[count];
        System.arraycopy(buff, 0, temp, 0, count);
        return new String(temp);
    }

    /**
     * 发送函数
     * @param msg
     * @param position
     * @return
     */
    private boolean send(String msg,int position){
        if(isConnecting&&mSocketClient!=null){
            if ((position==-1)){
                try
                {
                    mPrintWriterClient.print(msg);
                    mPrintWriterClient.flush();
                    return true;
                }catch (Exception e) {
                    // TODO: handle exception
                    Toast.makeText(MainActivity.this, "发送异常"+e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            }

        }else{
            showDialog("您还没有连接衣柜呢!");
        }
        return false;
    }


}

这个时候,我们离成功就更近一步了。包名要和自己的包名一致啊。

接下来,看图
在这里插入图片描述

 <uses-permission android:name="android.permission.INTERNET" />

一定要加上这句话哦,不然的话,就联网不成功了。

都加上了吗???
style.xml 这个忘了给,现在补上,在res的value的文件夹里,不然会有部分代码报错哦

<resources>
 
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>
<style name="s_true" parent="@android:style/TextAppearance.Small">
    <item name="android:textColor">#33da33</item>
</style>
 
<style name="s_false" parent="@android:style/TextAppearance.Small">
    <item name="android:textColor">#ffffff</item>
</style>
 
 
</resources>

我们要准备测试啦!

注意啦,如果你的电脑是运行8G以上的,就可以用模拟器运行,如果是4G的,我建议用真机测试。

4G的看过来,教你怎么快速把APP安装到手机,不用USB连接。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个就是跳出来的文件夹
在这里插入图片描述
在这里插入图片描述
安装好了吗?我们再等等8G的朋友~

8G的朋友们还在吗?来了来了

在这里插入图片描述
在这里插入图片描述

你们一打开是这样的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后就弄好模拟器啦
在这里插入图片描述
在这里插入图片描述
我的模拟器在开机,要等一等,等它开机完再运行。
在这里插入图片描述
这个是开机完的状态
在这里插入图片描述
在这里插入图片描述
要等它安装,我的安装好啦

在这里插入图片描述

测试了

怎么测试?直接拿单片机测试吗?
不是哦,我们可以通过网络调试助手进行测试。
在这里插入图片描述
需要的点网络调试助手
下载之后
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后确认你电脑的ip地址是什么?
win+R
在这里插入图片描述
在这里插入图片描述
ipv4地址就是你本机的地址。要和APP填写的一致。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试开关按钮

在这里插入图片描述

测试数据接收

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
终于都弄好了!!!
累了累了…

后面要是自己加功能或者改功能怎么做?

我就随便讲几个例子,以后大家根据这些例子举一反三吧。

例1:如果我想增加一个文字显示,应该怎么做?

在这里插入图片描述
跟着我一起来动动手吧

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这时候问题又来了,如果我想让它动态显示呢?就是当我点击了按钮它就换成别的文字?

这我们就得在主活动类对它进行绑定了,怎么绑定?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果我们想要在成功连接了单片机之后,显示这个文字内容“已连接单片机”,我们就可以在对应的代码里增加代码textView.setText("已连接单片机");
在这里插入图片描述
然后我们运行一下APP
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
好啦,文字显示的就讲到这里了。

例2:如果我想增加一个按钮控件呢

在这里插入图片描述
在这里插入图片描述
它和文字显示的代码有什么区别呢?基本上是没有啥区别的,用法都差不多。

 <Button
            android:id="@+id/button_test"
            android:text="按钮"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

只不过如果是按钮要多了个监听点击事件。绑定id的方式也是TextView差不多的哦。
在这里插入图片描述
在这里插入图片描述
那么。怎么给这个按钮添加监听事件呢?

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                button.setText("真的变了耶");
            }
        });

在这里插入图片描述
现在运行一下APP看看
在这里插入图片描述
在这里插入图片描述
当然,我不是要你的按钮做这件事,你可以在监听事件的代码加上任何你想做的事。在这里插入图片描述

下一篇出kotlin版本的哦。
也许有小伙伴开始学的不是java版本的安卓,所以,主活动类可能代码看的会很吃力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

入门级带你实现一个安卓智能家居APP(1)java版本 的相关文章

  • 自动化测试工具-Airtest

    目录 一 Airtest介绍与安装二 Airtest基于图像识别自动控制手机App流程三 Airtest基于Poco的UI组件自动化控制App流程四 Airtest实现手机群控操作 一 Airtest介绍与安装 主要介绍区别 xff0c 以
  • Docker桌面版安装与使用(windows)

    目录 一 Docker概念二 下载安装三 docker镜像安装与操作四 制作自己的python镜像容器五 目录挂载六 多容器通信七 Docker Compose管理多个容器运行八 发布和部署九 备份数据迁移 一 Docker概念 1 Doc
  • Gradio 机器学习和数据科学开源 Python 库

    Gradio是一个开源的 Python 库 xff0c MIT的开源项目 xff0c 用于构建机器学习和数据科学演示和 Web 应用 Gradio的定位类似于Streamlit xff0c 但是更轻量 xff0c 因为它推荐的应用场景都是对
  • k8s安装

    目录 一 K8s概念二 minikube安装三 部署应用到集群中四 Service五 k8s部署爬虫1 意义 一 K8s概念 参考文章 xff0c 参考视频它是一个为 容器化 应用提供集群部署和管理的开源工具 xff0c 由 Google
  • 编程工具-GPT来AI编程代码

    一 安装介绍 1 https www cursor so 下载安装 xff0c 重要的说三遍 xff08 目前免费 xff01 免费 xff01 免费 xff01 xff09 xff0c 支持多平台 Mac Windows Linux xf
  • 某wipo专利_六宫格/cookie/css

    这里写目录标题 一 案例分析二 六宫格验证码三 列表页搜索式302四 详情页css链接cookie刷新 一 案例分析 案例网址 xff1a 案例网址 反爬点 xff1a 六宫格验证码 cookie反爬 时间强制等待 session数据强绑定
  • Python之urlencode()使用

    urlencode 传入参数类型 xff1a 字典功能 xff1a 将存入的字典参数编码为URL查询字符串 xff0c 即转换成以key1 61 value1 amp key2 61 value2的形式导入 xff1a from urlli
  • Python之Md5使用等加密

    目录 一 Python之md5使用二 Python之sha1使用三 Python之base64使用四 Python之rsa使用五 Python之aes des 一 Python之md5使用 功能 xff1a MD5签名是一个哈希函数 xff
  • Python之quote()使用

    quote 传入参数类型 xff1a 字符串功能 xff1a 将单个字符串编码转化为 xx 的形式导入 xff1a from urllib parse import quotePs xff1a url多个字符串编码用urlenocde 函数
  • ( 数组和矩阵) 645. 错误的集合 ——【Leetcode每日一题】

    645 错误的集合 难度 xff1a 简单 集合 s 包含从 1 到 n 的整数 不幸的是 xff0c 因为数据错误 xff0c 导致集合里面某一个数字复制了成了集合里面的另外一个数字的值 xff0c 导致集合 丢失了一个数字 并且 有一个
  • Kalman滤波(Part-1:信号模型基础)

    Kalman Filters Dynamical Signal Models 一阶高斯 马尔可夫过程 first order Gauss Markov process 描述采样点之间 xff08 相邻 xff09 的相关性 xff1a s
  • 【STM32】关于keil5中下载按钮灰色及出现#error “Please select first the target STM32F4xx devic....错误的解决方法

    解决首次运行报错问题 近期在网上购买了一个WIFI模组 xff0c 例程是使用HAL库编写的 xff0c 首次编译的时候出现USER stm32f4xx h 193 error 35 error directive 34 Please se
  • 【STM32】F429单片机的时钟系统浅析

    先把429的时钟树附上 xff1a 乍一看是不是很懵逼 xff0c 我也很懵逼 一堆乱七八糟的玩意 xff0c ST公司是真的狗 本文是基于库函数SetSysClock 展开的 xff0c 配合该函数使用时钟树效果更佳O O 讲解之间说一个
  • x-easypdf 基于pdfbox/fop 封装的框架

    x easypdf 是一个基于 pdfbox fop 二次封装的框架 xff0c 目前拥有两大模块 xff1a pdfbox 模块 与 fop 模块 pdfbox 模块 主打 pdf 编辑功能 xff0c 以组件化的形式进行 pdf 的构建
  • 【STM32】利用定时器实现最基本的定时功能(HAL)

    定时器简介 STM32F429单片机有许多定时器Timer xff0c 主要分成三类 xff0c 基本定时器 xff1b 通用定时器和高级定时器 三种类型的定时器功能由少变多 xff0c 高级定时器包含了低级定时器的功能 基本定时器 xff
  • 【STM32】通俗易懂的讲解回调函数

    转载 xff1a https blog csdn net vincent040 article details 50832955 回调函数在程序开发中是一个非常重要的概念 xff0c 所谓的回调其实就是不同程序模块之间的接口和约定 xff0
  • 【树莓派】设置树莓派开机自动运行python脚本

    近期因科研需要 xff0c 需命令树莓派开机后无需进行任何操作自动运行一个python脚本 xff0c 经查阅部分资料后实现了该功能 xff0c 为方便以后查看特此记录一下 1 打开cmd xff0c 输入 sudo vim span cl
  • 部分机器人领域顶会顶刊官网及22年顶会召开时间

    顶刊 Science Robotics AAAS The International Journal of Robotics Research IJRR Journal of Forestry Research JFR IEEE Trans
  • 服务器非root下安装Python cyglfw3库

    服务器非root下安装Python cyglfw3库 在服务器中跑PVNet的代码时遇到的 xff0c 废了比较大的劲才解决 xff0c 特意记录一下解决过程 如果你有sudo权限直接使用sudo apt get install libgl
  • FFB6D搭建环境

    文章目录 FFB6D搭建环境搭建前的准备 根据需要选择性浏览 安装apex安装normal speed安装opencv3 安装pybind11安装normal speed 安装RandLA运行代码前的准备运行代码有疑问多多交流 xff0c

随机推荐

  • win32Day06:控件

    1 什么是控件 xff1f 控件是具备特殊功能的窗口 xff0c 并且有模板 控件的特性 xff1a 一定是子窗口 和创建主窗口一样 xff0c 都是使用CreateWindow函数来创建 xff08 控件这种 xff09 子窗口和主窗口的
  • SIFT3D点云关键点提取详细介绍

    1 引言 SIFT3D的理论基础完全是从图像特征SIFT2D中迁移类比过来的 xff0c 类似的还有Harris3D和Harris6D的理论也是来源于Harris2D的 xff0c 这些点云特征在PCL库中都有具体的实现 Harris3D和
  • 【视觉注意力机制集锦】引言

    视觉注意力机制集锦之引言 1 注意力机制 1 1 注意力机制简介 卷积神经网络具有很强的拟合数据的能力 xff0c 但是由于优化算法和计算能力的限制 xff0c 在实践中 xff0c 卷积网络很难达到通用近似的能力 特别是在处理规模较大的输
  • 期刊论文发表一定要有老师吗

    期刊论文发表一定要有老师吗 不一定 xff0c 具体要看论文的情况以及各方面的要求 xff0c 一般研究生和博士生论文很多都有老师 xff0c 本科论文很多是毕业生自己完成即可 xff0c 大多也是可以自己完成的 xff0c 如果要求有老师
  • QT菜单样式Ribbon Control for Qt, Office ribbon control

    基于Qt xff08 最低要求Qt5 xff0c 支持C 43 43 11的编译器 xff09 开发的一个轻量级的Ribbon控件 Office样式UI 使用Qt Creator直接打开SARibbon pro xff0c 并编译即可 xf
  • C/C++:基本语法看这一篇就够了

    前言 本文来自菜鸟教程的C语言教程和C 43 43 教程的学习笔记 xff0c 对其中的示例有所删减与变更 xff0c 建议以以下两个链接为准 虽说C 43 43 是C的扩展 xff0c 但貌似二者存在差集 xff0c 而本文只展示了兼容部
  • Qt之简易版网络调试助手

    简述 qt中为网络编程提供了一个QtNetwork类 xff0c 使用QtNetwork可以实现ftp文件传输 xff0c 基于tcp ip协议的客户端和 xff08 服务端 xff09 开发等等 xff0c 本文基于QtNetwork中的
  • ubuntu18.04安装docker以及过程中遇到的问题

    一 在ubuntu18 04上安装 1 更新索引包和安装相关依赖 xff1a sudo apt update sudo apt install apt span class token operator span transport spa
  • # 解决onos localhost 登陆onos后台时 需要输入密码问题

    登陆后台要求输入密码 xff0c 但尝试各种密码均无法登陆 一般从网上拷贝来的onos安装包 xff0c 使用时容易出现此类问题 xff1a 登录后台时会要求输入密码 xff0c 但一般各种密码都无效 问题解决 onos命名其实是一个she
  • visual c++与visual studio有什么区别

  • 【基于pyAudioKits的Python音频信号处理(八)】语音增强:谱减法、维纳滤波和卡尔曼滤波

    pyAudioKits是基于librosa和其他库的强大Python音频工作流支持 API速查手册 通过pip安装 xff1a pip install pyAudioKits 本项目的GitHub地址 xff0c 如果这个项目帮助到了你 x
  • 3.K8S网络之pod中容器到容器之间的通信

    文章目录 1 引入1 1 创建一个网络名称空间1 2 root网络名称空间1 3 docker网络名称空间 4 pod容器到容器之间的通信通信的原理 1 引入 在Linux中 xff0c 网络命名空间提供了一个逻辑的网络堆栈 xff0c 包
  • C++基础(八)--空指针

    在C和C 43 43 中 xff0c 用0和NULL都可以表示空指针 声明指针之后 xff0c 对指针赋值之前 xff0c 让它指空 xff0c 表示没有指向任何地址 使用空指针的后果 xff08 1 xff09 如果对空指针进行解引用 x
  • 【嵌入式Linux】嵌入式Linux驱动开发基础知识之驱动设计的思想:面向对象/分层/分离

    文章目录 前言1 分离设计驱动程序分析 程序分层通用驱动程序 面向对象个性化驱动程序 分离 APP 程序分析 前言 韦东山嵌入式Linux驱动开发基础知识学习笔记 文章中大多内容来自韦东山老师的文档 xff0c 还有部分个人根据自己需求补充
  • 【飞控理论】四旋翼飞行器控制原理

    该篇博客是对锡月科技无人机飞行控制原理教学内容的整理 1 四旋翼飞行器的两种结构 xff1a 字模式 xff1a Pitch和 Roll与1 3 2 4两组电机呈 45 夹角 十 字模式 xff1a Pitch对应 2 4电机的对轴 xff
  • WEBPACK和ROLLUP构建前端工具库

    1 WEBPACK webpack 提供了构建和打包不同模块化规则的库 xff0c 只是需要自己去搭建开发底层架构 vue cli xff0c 基于 webpack xff0c vue cli 脚手架工具可以快速初始化一个 vue 应用 x
  • 小觅相机自带的SDK改成自动保存点云、图片、和深度图代码

    首先 xff0c 我用的是小觅提供的SDK xff0c 我使用的是WindowsEXE版本的 版本号 xff1a mynteye d 1 8 0 win x64 opencv 3 4 3 exe 不同的版本可能工程名字不一样 https m
  • C语言----模拟ATM机存取款操作

    题目要求 xff1a 输入银行卡密码 xff0c 如果密码正确则显示操作界面 xff0c 循环提示 请输入操作选项 xff0c 其中按1键实现 查询余额 功能 xff0c 按2键实现 取款 功能 xff0c 按3键实现 存款 功能 xff0
  • java--swing界面实现注册登录(用文本文件存储数据)

    效果如下图 虽然使用java很久了 xff0c 但是对于swing的图形编程还是个小白 xff0c 还有对于文件io流的操作也几乎是没有接触过 xff0c 所以今天写这篇文章来学习一下 用swing来写桌面软件真的好麻烦 xff0c 主要是
  • 入门级带你实现一个安卓智能家居APP(1)java版本

    话不多说 xff0c 直接开干 xff01 先来看看效果 本APP是通过tcp ip协议与连了WiFi的单片机通信 第一步 xff1a 你得会一点点java基础 xff0c 你可以去百度搜 java菜鸟教程 xff0c 学了基本的基础即可