一生有你llx 阅读(133) 评论(0)
/*************************************************************************************************
PROJECT:        wifi遥控小车
HARDWARE:    STC89C52RC单机, L239D直流电机驱动器, ESP8266安信可wifi模块
SOFTWARE:    Eclipse安卓开发环境
AUTHOR:          DDDDD
DATE:              2014-12-5
*************************************************************************************************/

这个小项目有软件和硬件两大块,这里主要阐述在开发android客户端中遇到的问题
1、需要几个普通的Button,控制前进后退等。
     findViewById()这个函数将定义的button变量和xml文件中的button控件绑定起来
                upBtn = (Button)findViewById(R.id.upbtn);
     setOnClickListener()为button控件添加监听器,这样button被按下后监听器就会执行里面的函数
                downBtn.setOnClickListener(new runButtonListenner());
     如果为每一个button都绑定一个监听器,程序很不友好。最常用的方法就是为所有同种类型的button设置同一个监听器
                upBtn.setOnClickListener(new runButtonListenner());
                downBtn.setOnClickListener(new runButtonListenner());
                leftBtn.setOnClickListener(new runButtonListenner());
                rightBtn.setOnClickListener(new runButtonListenner());
                stopBtn.setOnClickListener(new runButtonListenner());
     在监听器里,利用switch语句来判断是哪个button被按下,onclick函数喊传入参数v,通过调用参数v的getId方法获取按钮
              class runButtonListenner implements OnClickListener
              {
                    @Override
                    public void onClick(View v)
                    {
                            // TODO Auto-generated method stub
                            switch(v.getId())
                            {
                                case R.id.upbtn:

                                            break;
                                case R.id.upbtn:

                                            break;
                            }
                    }
2、连接服务器的按钮应该是个开关型按钮,按下和松开是两种状态。在android里有ToggleButton类,这就是个按钮控件,但是个人感觉用着
     很不舒服,因此就自定义了一个开关型的button。其实就是一个普通的button,通过判断语句来实现开关的功能,关于这一部分功能可以参
    考我的源代码
3、socket编程,这个项目里TCP服务器已经由硬件实现,android只需要实现TCP客户端
     (1)socket是要访问网络的,因此需要在AndroidManifset中申明网络访问权限
                <uses-permission android:name="android.permission.INTERNET"/>
               这句话代表访问网络权限,加在application标签后  
     (2)在开发时遇到一个问题,android4.0以上的版本不支持在同一个线程里访问网络,因此必须创建一个新线程
                //创建一个线程,将ip和端口号传入,在新的线程里创建socket
                TcpClientThread tcpClientThread = new TcpClientThread(ip, port);
                //启动线程
                tcpClientThread.start();               
               由于创建socket时IP和端口应该由外部输入,不能定死,因此需要在创建新线程时传递参数。这样就需要
               在
新的线程类中有带参数的构造函数
                public class TcpClientThread extends Thread
                {
                    String ip = null;
                    int port = 0;
                    //在构造函数里得到参数ip 和port
                    public TcpClientThread(String Ip, int Port)
                    {
                        // TODO Auto-generated constructor stub
                        ip = Ip;
                        port = Port;
                    }

                    @Override
                    //run方法会自动调用
                    public void run()
                    {
                    
                    }
                }
    (3)创建线程的函数new Socket(dstName, dstPort)是一个阻塞的过程,如果服务器没有启动,那么程序就会一直等待,
            因此用下面这种方法来创建一个socket
                    //创建一个socket
                    socket = new Socket();
                    //获取socket地址
                    SocketAddress socketAddress = new InetSocketAddress(ip, port);
                    //连接socket
                    socket.connect(socketAddress);
4、源代码
     (1)MainActivity.java

点击(此处)折叠或打开

  1. package com.example.tcp_client;

  2. import java.io.IOException;
  3. import java.io.OutputStream;

  4. import android.os.Bundle;
  5. import android.app.Activity;
  6. import android.app.AlertDialog;
  7. import android.content.DialogInterface;
  8. import android.view.Menu;
  9. import android.view.View;
  10. import android.view.View.OnClickListener;
  11. import android.widget.Button;
  12. import android.widget.EditText;
  13. import android.widget.TextView;

  14. public class MainActivity extends Activity
  15. {
  16.     //按钮控件,有前后左右,停车
  17.     private Button upBtn = null;
  18.     private Button downBtn = null;
  19.     private Button leftBtn = null;
  20.     private Button rightBtn = null;
  21.     private Button stopBtn = null;
  22.     //按钮控件,连接或者断开服务器
  23.     private Button tcpBtn = null;
  24.     //EditText 输入服务器ip和端口号
  25.     private EditText ipEdit = null;
  26.     private EditText portEdit = null;
  27.     //TextView 显示信息
  28.     private TextView tcpInfoText = null;
  29.     private TextView carInfoText = null;
  30.     
  31.     private String IP = null;
  32.     private int PORT;
  33.     //如果已经连接到服务器,那么按钮上就应该显示断开服务器
  34.     private String linked = "断开服务器";
  35.     //如果服务器已经断开,那么按钮上就应该显示;连接服务器
  36.     private String unlink = "连接服务器";
  37.     //检测自定义ToggleButton的状态,false代表还没按下,true代表按下
  38.     private boolean isChecked = false;
  39.     //自定义的TcpClient类实体
  40.     private TcpClient tcpClient = null;
  41.     //输出流,用于socket
  42.     static OutputStream outStream = null;
  43.     @Override
  44.     protected void onCreate(Bundle savedInstanceState)
  45.     {
  46.         super.onCreate(savedInstanceState);
  47.         //指定布局文件
  48.         setContentView(R.layout.activity_main);
  49.         //将控件找到
  50.         upBtn = (Button)findViewById(R.id.upbtn);
  51.         downBtn = (Button)findViewById(R.id.downbtn);
  52.         leftBtn = (Button)findViewById(R.id.leftbtn);
  53.         rightBtn = (Button)findViewById(R.id.rightbtn);
  54.         stopBtn = (Button)findViewById(R.id.stopBtn);
  55.         //为控件绑定监听器Listener
  56.         upBtn.setOnClickListener(new runButtonListenner());
  57.         downBtn.setOnClickListener(new runButtonListenner());
  58.         leftBtn.setOnClickListener(new runButtonListenner());
  59.         rightBtn.setOnClickListener(new runButtonListenner());
  60.         stopBtn.setOnClickListener(new runButtonListenner());
  61.         //连接服务器的按钮式一个button控件,自定义为ToggleButton
  62.         tcpBtn = (Button)findViewById(R.id.tcpToggleBtn);
  63.         tcpBtn.setOnClickListener(new switchButtonListener());
  64.         tcpBtn.setText(unlink);
  65.         
  66.         ipEdit = (EditText)findViewById(R.id.ipEdit);
  67.         portEdit = (EditText)findViewById(R.id.portEdit);
  68.         tcpInfoText = (TextView)findViewById(R.id.tcpInfoText);
  69.         carInfoText = (TextView)findViewById(R.id.carInfoText);
  70.         

  71.     }
  72.     //关于小车状态按钮的监听器
  73.     class runButtonListenner implements OnClickListener
  74.     {
  75.         @Override
  76.         public void onClick(View v)
  77.         {
  78.             // TODO Auto-generated method stub
  79.             switch(v.getId())
  80.             {
  81.                 case R.id.upbtn:
  82.                     if(outStream != null)
  83.                     {
  84.                         tcpClient.sendBuffer(outStream, "#up*");
  85.                         carInfoText.setText("小车正在前进");
  86.                     }                    
  87.                     break;
  88.                 case R.id.downbtn:
  89.                     if(outStream != null)
  90.                     {
  91.                         tcpClient.sendBuffer(outStream, "#dn*");
  92.                         carInfoText.setText("小车正在后退");
  93.                     }                
  94.                     break;
  95.                 case R.id.leftbtn:
  96.                     if(outStream != null)
  97.                     {
  98.                         tcpClient.sendBuffer(outStream, "#zz*");
  99.                         carInfoText.setText("小车正在左转");
  100.                     }        
  101.                     break;
  102.                 case R.id.rightbtn:
  103.                     if(outStream != null)
  104.                     {
  105.                         tcpClient.sendBuffer(outStream, "#yz*");
  106.                         carInfoText.setText("小车正在右转");
  107.                     }        
  108.                     break;
  109.                 case R.id.stopBtn:
  110.                     if(outStream != null)
  111.                     {
  112.                         tcpClient.sendBuffer(outStream, "#sp*");
  113.                         carInfoText.setText("小车已经停车");
  114.                     }    
  115.                 default:
  116.                     break;
  117.             
  118.             }
  119.         }
  120.         
  121.     }
  122.     //自定义ToggleButton控件的监听器,按下和松开是两种显示状态
  123.     class switchButtonListener implements OnClickListener
  124.     {
  125.         @Override
  126.         public void onClick(View v)
  127.         {
  128.             // TODO Auto-generated method stub
  129.             //每次点击按钮之后,按钮的状态就应该取反
  130.             isChecked = !isChecked;
  131.             //如果按下按钮,ToggleButton的状态时true
  132.             if(isChecked)
  133.             {
  134.                 //从ip的EditText获取IP
  135.                 IP = ipEdit.getText().toString();
  136.                 //如果没有输入IP,那么获取的字符串长度就是0,按钮的状态还是false
  137.                 if(IP.length() == 0)
  138.                 {
  139.                     //没有IP,ToggleButton的状态还是false
  140.                     isChecked = false;
  141.                     //弹出提示对话框,提示用户输入IP
  142.                     new AlertDialog.Builder(MainActivity.this)
  143.      .setTitle("提示")
  144.      .setIcon(android.R.drawable.ic_dialog_info)
  145.      .setMessage("请输入服务器IP地址")        
  146.      .setPositiveButton("确定", new DialogInterface.OnClickListener(){

  147.                         @Override
  148.                         public void onClick(DialogInterface arg0, int arg1) {
  149.                             // TODO Auto-generated method stub

  150.                         }})     
  151.      .show();
  152.                 }
  153.                 else
  154.                 {
  155.                     //如果没有输入端口号,那么从端口edit获取的字符串长度也是0
  156.                     if(portEdit.getText().toString().length() == 0)
  157.                     {
  158.                         //没有端口号,ToggleButton的状态还是false
  159.                         isChecked = false;
  160.                         //弹出对话框,提示用户输入端口
  161.                         new AlertDialog.Builder(MainActivity.this)
  162.          .setTitle("提示")
  163.          .setIcon(android.R.drawable.ic_dialog_info)
  164.          .setMessage("请输入服务器端口号")        
  165.          .setPositiveButton("确定", new DialogInterface.OnClickListener(){

  166.                             @Override
  167.                             public void onClick(DialogInterface arg0, int arg1) {
  168.                                 // TODO Auto-generated method stub
  169.                                 
  170.                             }})     
  171.          .show();    
  172.                     }
  173.                     try{
  174.                         //将获取的端口号转为一个整数
  175.                         PORT = Integer.parseInt(portEdit.getText().toString());
  176.                         }catch (Exception e){
  177.                             e.printStackTrace();
  178.                         }
  179.                     //创建TcpClient实体
  180.                     tcpClient = new TcpClient();
  181.                     //创建socket,并且获取到一个outputStream
  182.                     tcpClient.createTcpCient(IP, PORT);
  183.                     if(outStream != null)
  184.                     {
  185.                         //如果得到了OutputStream,在信息框提示连接成功自定义ToggleButton就显示
  186.                         tcpInfoText.setText("OK,连接服务器成功");
  187.                         //已经连接到服务器,ToggleButton显示“断开连接”
  188.                         tcpBtn.setText(linked);
  189.                     }
  190.                     else
  191.                     {
  192.                         //没有得到OutputStream,自定义ToggleButton的属性还是false
  193.                         isChecked = false;
  194.                     }
  195.                 }
  196.             }
  197.             else
  198.             {
  199.                 //如果ToggleButton的状态是false,那么就断开socket
  200.                 try
  201.                 {
  202.                     TcpClient.socket.close();
  203.                 } catch (IOException e)
  204.                 {
  205.                     // TODO Auto-generated catch block
  206.                     e.printStackTrace();
  207.                 }
  208.                 //释放资源
  209.                 outStream = null;
  210.                 tcpClient = null;
  211.                 //将ToggleButton的内容设置为“连接服务器”
  212.                 tcpBtn.setText(unlink);
  213.                 //信息edit提示服务器断开
  214.                 tcpInfoText.setText("注意,服务器已断开");
  215.             }
  216.                 
  217.         }
  218.         
  219.     }

  220.     @Override
  221.     public boolean onCreateOptionsMenu(Menu menu)
  222.     {
  223.         // Inflate the menu; this adds items to the action bar if it is present.
  224.         getMenuInflater().inflate(R.menu.main, menu);
  225.         return true;
  226.     }

  227. }
     (2)TcpClient.java

点击(此处)折叠或打开

  1. package com.example.tcp_client;

  2. import java.io.IOException;
  3. import java.io.OutputStream;
  4. import java.io.UnsupportedEncodingException;
  5. import java.net.InetSocketAddress;
  6. import java.net.Socket;
  7. import java.net.SocketAddress;

  8. public class TcpClient
  9. {
  10.     public static Socket socket = null;
  11.     
  12.     public void createTcpCient(String ip, int port)
  13.     {
  14.         //创建一个线程,将ip和端口号传入,在新的线程里创建socket
  15.         TcpClientThread tcpClientThread = new TcpClientThread(ip, port);
  16.         //启动线程
  17.         tcpClientThread.start();
  18.     }
  19.     
  20.     public class TcpClientThread extends Thread
  21.     {
  22.         String ip = null;
  23.         int port = 0;
  24.         //在构造函数里得到参数ip 和port
  25.         public TcpClientThread(String Ip, int Port)
  26.         {
  27.             // TODO Auto-generated constructor stub
  28.             ip = Ip;
  29.             port = Port;
  30.         }

  31.         @Override
  32.         //run方法会自动调用
  33.         public void run()
  34.         {
  35.             // TODO Auto-generated method stub
  36.             super.run();
  37.             try{
  38.                 //创建一个socket
  39.                 socket = new Socket();
  40.                 //获取socket地址
  41.                 SocketAddress socketAddress = new InetSocketAddress(ip, port);
  42.                 //连接socket
  43.                 socket.connect(socketAddress);
  44.                 //从socket获取一个outputstream
  45.                 MainActivity.outStream = socket.getOutputStream();
  46.                 //将buffer的内容写入outputstream
  47.             }catch    (IOException e){
  48.                 e.printStackTrace();
  49.             }
  50.         }
  51.     }
  52.     //通过socket发送数据
  53.     public void sendBuffer(OutputStream outputStream, String buf)
  54.     {
  55.         byte buffer[] = null;
  56.         try
  57.         {
  58.             //将String转为byte类型
  59.             buffer = buf.getBytes("ISO-8859-1");
  60.         } catch (UnsupportedEncodingException e)
  61.         {
  62.             // TODO Auto-generated catch block
  63.             e.printStackTrace();
  64.         }
  65.         try
  66.         {
  67.             //输出数据
  68.             outputStream.write(buffer, 0, buffer.length);
  69.             outputStream.flush();
  70.         } catch (IOException e)
  71.         {
  72.             // TODO Auto-generated catch block
  73.             e.printStackTrace();
  74.         }
  75.         
  76.     }
  77. }
    (3)activity_main.xml

点击(此处)折叠或打开

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     android:paddingBottom="@dimen/activity_vertical_margin"
  6.     android:paddingLeft="@dimen/activity_horizontal_margin"
  7.     android:paddingRight="@dimen/activity_horizontal_margin"
  8.     android:paddingTop="@dimen/activity_vertical_margin"
  9.     tools:context=".MainActivity" >

  10.     <Button
  11.         android:id="@+id/upbtn"
  12.         style="?android:attr/buttonStyleSmall"
  13.         android:layout_width="wrap_content"
  14.         android:layout_height="wrap_content"
  15.         android:layout_alignLeft="@+id/downbtn"
  16.         android:text="前进" />
  17.     <Button
  18.         android:id="@+id/leftbtn"
  19.         style="?android:attr/buttonStyleSmall"
  20.         android:layout_width="wrap_content"
  21.         android:layout_height="wrap_content"
  22.         android:layout_below="@+id/upbtn"
  23.         android:layout_marginRight="28dp"
  24.         android:layout_toLeftOf="@+id/upbtn"
  25.         android:text="左转" />
  26.     <Button
  27.         android:id="@+id/rightbtn"
  28.         style="?android:attr/buttonStyleSmall"
  29.         android:layout_width="wrap_content"
  30.         android:layout_height="wrap_content"
  31.         android:layout_alignBaseline="@+id/stopBtn"
  32.         android:layout_alignBottom="@+id/stopBtn"
  33.         android:layout_marginLeft="24dp"
  34.         android:layout_toRightOf="@+id/stopBtn"
  35.         android:text="右转" />
  36.     
  37.     <Button
  38.         android:id="@+id/stopBtn"
  39.         android:layout_width="wrap_content"
  40.         android:layout_height="wrap_content"
  41.         android:layout_alignBottom="@+id/leftbtn"
  42.         android:layout_alignLeft="@+id/upbtn"
  43.         android:layout_alignRight="@+id/upbtn"
  44.         android:layout_below="@+id/upbtn"
  45.         android:text="停车" />

  46.     <Button
  47.         android:id="@+id/downbtn"
  48.         style="?android:attr/buttonStyleSmall"
  49.         android:layout_width="wrap_content"
  50.         android:layout_height="wrap_content"
  51.         android:layout_below="@+id/stopBtn"
  52.         android:layout_centerHorizontal="true"
  53.         android:text="后退" />
  54.     
  55.     <EditText
  56.         android:id="@+id/ipEdit"
  57.         android:layout_width="fill_parent"
  58.         android:layout_height="wrap_content"
  59.         android:hint = "请输入服务器IP"
  60.         android:inputType="none"
  61.         android:layout_below="@+id/downbtn"
  62.         />
  63.     <EditText
  64.         android:id="@+id/portEdit"
  65.         android:layout_width="fill_parent"
  66.         android:layout_height="wrap_content"
  67.         android:hint = "请输入服务器端口"
  68.         android:inputType="none"
  69.         android:layout_below="@+id/ipEdit"
  70.         />
  71.    <TextView
  72.         android:id="@+id/tcpInfoText"
  73.         android:layout_width="fill_parent"
  74.         android:layout_height="wrap_content"
  75.         android:layout_below="@+id/tcpToggleBtn"
  76.         android:singleLine="false"
  77.         />
  78.    <TextView
  79.         android:id="@+id/carInfoText"
  80.         android:layout_width="fill_parent"
  81.         android:layout_height="wrap_content"
  82.         android:layout_below="@+id/tcpInfoText"
  83.         android:singleLine="false"
  84.         />

  85.     <Button
  86.         android:id="@+id/tcpToggleBtn"
  87.         style="@android:style/MediaButton"
  88.         android:layout_width="wrap_content"
  89.         android:layout_height="wrap_content"
  90.         android:layout_alignLeft="@+id/portEdit"
  91.         android:layout_alignRight="@+id/portEdit"
  92.         android:layout_below="@+id/portEdit"
  93.         android:textOn="断开服务器"
  94.         android:textOff="连接服务器"
  95.         />
  96.     

  97. </RelativeLayout>
    (4)AndroidManifest.xml

点击(此处)折叠或打开

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3.     package="com.example.tcp_client"
  4.     android:versionCode="1"
  5.     android:versionName="1.0" >

  6.     <uses-sdk
  7.         android:minSdkVersion="8"
  8.         android:targetSdkVersion="18" />

  9.     <application
  10.         android:allowBackup="true"
  11.         android:icon="@drawable/ic_launcher"
  12.         android:label="@string/app_name"
  13.         android:theme="@style/AppTheme" >
  14.         <activity
  15.             android:name="com.example.tcp_client.MainActivity"
  16.             android:label="@string/app_name" >
  17.             <intent-filter>
  18.                 <action android:name="android.intent.action.MAIN" />

  19.                 <category android:name="android.intent.category.LAUNCHER" />
  20.             </intent-filter>
  21.         </activity>
  22.     </application>
  23. <uses-permission android:name="android.permission.INTERNET"/>
  24. </manifest>