机器人远程控制系统(安卓端)

时间:2024-03-24 13:54:21

前言

现在正值春招面试的时间段,笔者并没有投很多简历,因为经历过某大公司的电话面之后发现太多的不足,与其花时间在刷简历上,不如好好总结和复习学过的东西,本篇就作为个人项目总结的开端。
这个项目其实就是学校的一个参赛项目,主要实现的功能是通过发送语音文字识别控制机器人移动,也可以定点或多点导航,实时接收并更新机器人反馈的状态。我主要负责安卓的开发,下面分功能模块讲解:

index界面

实现功能:三个页面(控制,对话,基本信息)可以切换。
实现方式:RadioGroup+ViewPager
实现过程:

首先创建好三个fragment并生成对象,加入ArrayList

ControlFrgt controlFrgt=new ControlFrgt();
ChatFrgt chatFrgt=new ChatFrgt();
InfoFrgt infoFrgt=new InfoFrgt();
fragmentlist=new ArrayList<>();
fragmentlist.add(controlFrgt);
fragmentlist.add(chatFrgt);
fragmentlist.add(infoFrgt);

然后写两个监听事件,一个是RadioGroup.OnCheckedChangeListener还有一个是ViewPager.OnPageChangeListener

RadioGroup的切换事件:

private class MyOnCheckListener implements RadioGroup.OnCheckedChangeListener{
    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        changeCheck(controlrbtn);
        changeCheck(chatrbtn);
        changeCheck(infotbtn);//此方法用来改变radiobutton的属性实现切换时的动画效果
        switch (checkedId){
            case R.id.tap_control:viewPager.setCurrentItem(0);break;
            case R.id.tap_chat:viewPager.setCurrentItem(1);break;
            case R.id.tap_info:viewPager.setCurrentItem(2);break;
        }
    }
}

fragment页面切换事件:

private class MyPagerChangeListener implements ViewPager.OnPageChangeListener{

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
	//position:当前页面——点击滑动的页面
	//positionOffset:当前页面偏移的百分比
	//positionOffsetPixels:当前页面偏移的像素位置   
    }

    @Override
    public void onPageSelected(int position) {
        switch(position){
            case 0:radioGroup.check(R.id.tap_control);break;
            case 1:radioGroup.check(R.id.tap_chat);break;
            case 2:radioGroup.check(R.id.tap_info);break;
        }

    }

    @Override
    public void onPageScrollStateChanged(int state) {
	//此方法是在当前页面状态发生改变时调用的
	switch(state){
		case 0:什么都没做;break;
		case 1:正在滑动;break;
		case 2:滑动完成;break;
	}
	//所以页面滑动时,state的应该这样改变:1(正在改变)->2(滑动完成)->0(静止状态)
    }
}

再创建FragmentPagerAdapter

FragmentPagerAdapter pagerAdapter=new FragmentPagerAdapter(getSupportFragmentManager()) {
//由于v4库不能直接获取FragmentManager,因此这里使用getSupportFragmentManager()间接获取
    @Override
    public Fragment getItem(int position) {
        return fragmentlist.get(position);
    }

    @Override
    public int getCount() {
        return fragmentlist.size();
    }
};

最后就是viewpager设置adapter和监听器,还有给radiogroup设置监听事件(当然初始的状态也要设置好):

viewPager.setAdapter(pagerAdapter);
viewPager.setCurrentItem(0);
radioGroup.check(R.id.tap_control);
changeCheck(controlrbtn);
MyPagerChangeListener myPagerChangeListener=new MyPagerChangeListener();
MyOnCheckListener myOnCheckListener=new MyOnCheckListener();
viewPager.addOnPageChangeListener(myPagerChangeListener);
radioGroup.setOnCheckedChangeListener(myOnCheckListener);

基本的控制主页面

实现功能:基本的前后左右控制、加减速,开始或停止,状态数据(速度、温度)的更新,展示机器人的视野(监控直播)。
实现方式:http请求(控制),timer周期任务执行(实时更新),webview(由于笔者比较菜,所以监控直播这块直接通过加载一个老师写好的网页来实现功能)。
实现过程:

使用HttpURLConnection发起网络请求

public void requestUrl(final String url){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String murl="http://"+url;
                    Log.e("当前请求的ip",murl);
                    HttpURLConnection urlConnection= (HttpURLConnection) new URL(murl).openConnection();
                    urlConnection.setRequestMethod("POST");
                    urlConnection.setDoOutput(true);//是否允许向网络读取数据
                    urlConnection.setConnectTimeout(3000);
                    urlConnection.setReadTimeout(5000);//设置连接,读取的超时限制
                    if (urlConnection.getResponseCode()==200) {
                        Log.e("连接服务器","成功");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }) .start();
}

就以前进举例
安卓端这边,requestUrl(ipstr+":8080/forward");
tomcat那边会这样处理:
机器人远程控制系统(安卓端)所以安卓端这边只需要发送类似这样的url请求就可以了,说实话笔者对于java web不是很熟,基本现在的后台都是用php写的,也只是停留在很简单的数据交互,我认为一个好的安卓开发工程师不应该只懂安卓端的开发,所以这块有必要加强。

webview加载视频流的网页

这块是老师直接提供的页面,经过很长时间的尝试都没成功,所以老师没有再为难我哈哈,让我直接用网页代替video,所以这里就写一下加载网页的代码

private void openWeb(String url,WebView webView){
    webView.loadUrl(url);
    webView.setInitialScale(130);//设置缩放比例,这里放大1.3倍
    webView.getSettings().setJavaScriptEnabled(true);
    webView.getSettings().setDomStorageEnabled(true);
    webView.setWebViewClient(new WebViewClient(){
    //如果不设置WebViewClient,请求会跳转系统浏览器
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            return true;
        }

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            handler.proceed();//接受证书
        }
    });
}

放一张运行的图片结束这块的尴尬吧:
机器人远程控制系统(安卓端)

实时更新数据

首先从网络获取json

public static void requestJson(final String url,  final Handler handler){
		………………
		……………………………
		………
        InputStream is=conn.getInputStream();
        BufferedReader reader=new BufferedReader(new InputStreamReader(is));
        String rline="";
        StringBuilder builder_result=new StringBuilder();
        while ((rline=reader.readLine())!=null){
            builder_result.append(rline);
            Log.e("读取到json",rline);
        }
        Message message=new Message();
        message.obj=builder_result.toString();
        handler.sendMessage(message);
        is.close();
    }
    }catch (Exception e){
      e.printStackTrace();
}
}

然后,创建定时器及TimerTask

timer=new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        NetWorkUtil.requestJson("http://" + ipstr + ":8080/app_GetInfo",  handler);
    }
},1000,3*1000);

@Override
public void onStop() {
    super.onStop();
    timer.cancel();//结束任务
}

最后在handler里面解析json并更新ui

Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        String json_data = null;
        json_data = msg.obj.toString();
        try {
            JSONObject object=new JSONObject(json_data);
            String temp=object.getString("temperature");
            String sped=object.getString("speed");
            //String lat=object.getString("lat");
            //String lon=object.getString("lon");
            temTv.setText("当前速度:"+temp+"℃");
            spdTv.setText("当前温度:"+sped+"m/s");
            } catch (JSONException e) {
            e.printStackTrace();
            Toast.makeText(getContext(),"数据解析出错",Toast.LENGTH_SHORT).show();
            temTv.setText("当前速度:--℃");
            spdTv.setText("当前温度:--m/s");
        }

    }
};

聊天对话界面

实现功能:聊天页面,语音识别,发送文字进行控制。
实现方式:重写BaseAdapter+listview,动态设置View的visible属性,集成科大讯飞语音识别功能,http请求。
实现过程:

listview实现聊天页面

1.写好数据源(Bean类)

public class Message {
    String mesg;
    int code;
    public Message(int mcode, String msg){
        code=mcode;
        mesg=msg;
    }
    public void setTypeCode(int code){
        this.code=code;
    }
    public int getTypeCode(){
        return code;
    }
    public void setMsg(String mes){
        mesg=mes;
    }
    public String getMsg(){
        return mesg;
    }
}

2.自定义adapter(使用ViewHolder优化)
(1)重写BaseAdapter的方法

public MesgAdapter(Context context, ArrayList<Message> messageArrayList){
    mcontext=context;
    arrayList= messageArrayList;
    inflater=LayoutInflater.from(context);
}//这是adapter的构造函数

@Override
public int getCount() {
    return arrayList.size();
}

@Override
public Object getItem(int position) {
    return arrayList.get(position);
}

@Override
public long getItemId(int position) {
    return 0;
}

@Override
public int getItemViewType(int position) {
    return arrayList.get(position).getTypeCode();
    //返回 代表某一个样式 的 数值,在getview方法里面进行判断
}

@Override
public int getViewTypeCount() {
    return 2;//表示可以有两种样式item
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
	switch (getItemViewType(position)) {
		case 0:发送消息的item;break;
		case 1:回复消息的item;break;
		……
	}
}

(2)创建ViewHolder类与Item布局形成映射关系

private class ViewHolder{
    TextView voi_msg;
    ImageView mhead;//发送消息
}
private class MyHolder{
    TextView rep_msg;
    ImageView rhead;//回复消息
}

(3)在getView方法中对convertView进行判断

if (convertView == null) {//没有缓存的item
    convertView = inflater.inflate(R.layout.voi_msg_item, parent, false);
    viewHolder = new ViewHolder();
    viewHolder.voi_msg=convertView.findViewById(R.id.tv_voi_msg);
    convertView.setTag(viewHolder);//将viewholder与convertView关联
} else {//如果有缓存的item则直接通过getTag取出缓存的viewholder
    viewHolder = (ViewHolder) convertView.getTag();
}

//设置控件的数据
viewHolder.voi_msg.setText(arrayList.get(position).mesg);

3.给listview设置adapter

listView=view.findViewById(R.id.chatlist);
ArrayList<Message> datas=new ArrayList<>();
MesgAdapter adapter=new MesgAdapter(getActivity(),datas);
listView.setAdapter(adapter);

4.创建http的post请求

public static void postStr(final String s,final String reqstr, final String url){
    new Thread(new Runnable() {
        @SuppressLint("LongLogTag")
        @Override
        public void run() {
            try {
                String str= reqstr+URLEncoder.encode(s,"utf-8");//设置编码
                Log.e("请求url:"+url,"发送"+str+"至服务器");
                HttpURLConnection connection= (HttpURLConnection) new URL(url).openConnection();
                connection.setDoOutput(true);
                connection.setDoInput(true);
                connection.setUseCaches(false);
                connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
                connection.setRequestProperty("Content-Length", String.valueOf(str.length()));//设置请求头的一些属性
                connection.setRequestMethod("POST");
                connection.setConnectTimeout(5 * 1000);
                OutputStream outputStream=connection.getOutputStream();
                outputStream.write(str.getBytes());
                outputStream.flush();
                if (connection.getResponseCode()==connection.HTTP_OK){
                    Log.e("请求结果","成功");
                }
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }).start();
}

5.给发送按钮设置事件:通过增加数据源,动态添加item并提交数据到网络

sendBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        preferences=getActivity().getSharedPreferences("ipstr", Context.MODE_PRIVATE);
        url="http://"+preferences.getString("ip","")+":8080/userquestion";//由于fragment的生命周期问题,此处获取prefrences应该写在方法内部
        String myword=text.getText().toString();
        if (myword!=""&& !TextUtils.isEmpty(myword)){
            Message msg=new Message(1,myword);//添加发送消息item
            datas.add(msg);
            adapter.notifyDataSetChanged();
            NetWorkUtil.postStr(myword,"question=",url);//post数据
            text.setHint("继续输入指令");
            text.setText("");
            datas.add(new Message(2,myword));//添加回复的消息item
            adapter.notifyDataSetChanged();//更新列表
        }
        else {
            text.setHint("发送不能为空!");
        }
    }
});

测试结果截图

机器人远程控制系统(安卓端)

语音识别

1.在讯飞开放平台获取sdk,并根据官方文档集成好
机器人远程控制系统(安卓端)2.创建语音听写对象

SpeechUtility.createUtility(this, SpeechConstant.APPID+"=appid");
//appid需要去讯飞开放平台获取
SpeechRecognizer mIat= SpeechRecognizer.createRecognizer(this,null);
mIat.setParameter(SpeechConstant.ASR_PTT,"0");//去除标点符号

3.解析语音返回并发送结果

public class XfJsonParser {
    public String parseResult(String json) {
        StringBuffer buffer = new StringBuffer();
        try {
            // 转写结果词,默认使用第一个结果
            JSONTokener tokener = new JSONTokener(json);
            JSONObject obj = new JSONObject(tokener);
            JSONArray array = obj.getJSONArray("ws");
            for (int i = 0; i < array.length(); i++) {
                JSONArray items = array.getJSONObject(i).getJSONArray("cw");
                JSONObject object = items.getJSONObject(0);
                buffer.append(object.getString("w"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }
}

private void getResult(RecognizerResult result){//解析语音识别结果
    XfJsonParser jsonParser=new XfJsonParser();
    String ret=jsonParser.parseResult(result.getResultString());
    SpeechRecognizer.getRecognizer().stopListening();
    if(TextUtils.isEmpty(ret)){
        return;
    }
    String sn=null;
    try {
        JSONObject jsonObject=new JSONObject(result.getResultString());
        sn=jsonObject.optString("sn");
    }catch (Exception e){
        e.printStackTrace();
    }
    mIatResults.put(sn,ret);
    StringBuffer retBuffer=new StringBuffer();
    for (String key:mIatResults.keySet()){
        retBuffer.append(mIatResults.get(key));
    }
    String reg_str=retBuffer.toString();
    if (reg_str!=null&&!TextUtils.isEmpty(reg_str)&&reg_str!="") {
        datas.add(new Message(0, reg_str));
        NetWorkUtil.postStr(reg_str,"question=","http://"+preferences.getString("ip","")+":8080/userquestion");
        datas.add(new Message(2,reg_str));
        adapter.notifyDataSetChanged();//更新listview
    }
}

结果截图所示

机器人远程控制系统(安卓端)

导航功能

实现功能:定点导航,多点巡逻,导航记录
实现方式:集成百度地图,http请求,TabHost

定点导航

1.在百度地图开放平台下载SDK并且根据官方的集成手册进行集成
机器人远程控制系统(安卓端)
2.创建tabhost

<?xml version="1.0" encoding="utf-8"?>
<TabHost 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:id="@+id/tabhost"
    tools:context="com.example.qy.robotcontrol.Gps">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@android:id/tabcontent">
            <LinearLayout
            <!-- 定义第一个标签页(定点导航)的内容 -->
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/navigate"
                >
                <com.baidu.mapapi.map.MapView
                    android:id="@+id/mapview"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" >
                </com.baidu.mapapi.map.MapView>
            </LinearLayout>
            <!-- 第二个标签页的内容(多点巡逻 )-->
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/patrolgps"
                >
                <com.baidu.mapapi.map.TextureMapView
                    android:id="@+id/mapview2"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" >
                </com.baidu.mapapi.map.TextureMapView>
            </LinearLayout>
            <LinearLayout
            <!-- 定义第三个标签页的内容 (导航历史) -->
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/gpsdetail">
                <android.support.v4.widget.SwipeRefreshLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/gps_info_refresh">
                <ListView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/gps_info">
                </ListView>
                </android.support.v4.widget.SwipeRefreshLayout>
            </LinearLayout>
        </FrameLayout>
    </LinearLayout>

</TabHost>

private void initTab(){
    TabHost host=findViewById(R.id.tabhost);
    host.setup();
    TabHost.TabSpec nav_tab=host.newTabSpec("tab1").setIndicator("立即导航").setContent(R.id.navigate);
    TabHost.TabSpec pio_tab=host.newTabSpec("tab2").setIndicator("定点巡逻").setContent(R.id.patrolgps);
    TabHost.TabSpec det_tab=host.newTabSpec("tab3").setIndicator("导航信息").setContent(R.id.gpsdetail);
    host.addTab(nav_tab);
    host.addTab(pio_tab);
    host.addTab(det_tab);
}

3.初始化地图

LocationClient locationClient=new LocationClient(getApplicationContext());
MyLocationListener locationListener=new MyLocationListener();
locationClient.registerLocationListener(locationListener);
SDKInitializer.initialize(getApplicationContext());//初始化sdk显示地图
setContentView(R.layout.activity_gps);//注意要在此方法之前初始化地图
locationClient.setLocOption(initLocationOption());
locationClient.start();
baiduMap=mapView.getMap();
baiduMap2=mapView2.getMap();//得到地图
baiduMap.setMyLocationEnabled(true);
baiduMap2.setMyLocationEnabled(true);//允许定位
    private LocationClientOption initLocationOption(){
        LocationClientOption option = new LocationClientOption();
        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
        //设置定位模式,默认高精度
        option.setCoorType("bd09ll");
        //设置返回经纬度坐标类型,默认gcj02,gcj02:国测局坐标;
        option.setScanSpan(6000);
        //设置发起定位请求的间隔,int类型,单位ms
        option.setOpenGps(true);
        //设置是否使用gps,默认false
        option.setLocationNotify(true);
        option.setIgnoreKillProcess(false);
        //设置是否在stop的时候杀死这个进程,默认不杀死
        option.SetIgnoreCacheException(false);
        //设置是否收集Crash信息,默认收集,即参数为false
        option.setWifiCacheTimeOut(5*60*1000);
//如果设置了该接口,首次启动定位时,会先判断当前WiFi是否超出有效期,若超出有效期,会先重新扫描WiFi,然后定位
        option.setEnableSimulateGps(false);
//设置是否需要过滤GPS仿真结果,默认需要,即参数为false
        return option;
    }
private class MyLocationListener extends BDAbstractLocationListener {

    @Override
    public void onReceiveLocation(BDLocation location) {
        double latitude = location.getLatitude();    //获取纬度信息
        double longitude = location.getLongitude();    //获取经度信息
        //float radius = location.getRadius();    //获取定位精度,默认值为0.0f
        //String coorType = location.getCoorType();
        //获取经纬度坐标类型,以LocationClientOption中设置过的坐标类型为准
        //int errorCode = location.getLocType();
        //获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明
        //Log.e("获取定位返回码",errorCode+"");
        MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
        locationBuilder.latitude(latitude);
        locationBuilder.longitude(longitude);
        MyLocationData locationData=locationBuilder.build();
        baiduMap.setMyLocationData(locationData);
        baiduMap2.setMyLocationData(locationData);
        //获取当前位置 并显示到地图上
        if (isFirstloc) {
            changeMapStatus(latitude, longitude);
        }
    }
}

private void changeMapStatus(double latitude,double longitude){
    LatLng center=new LatLng(latitude,longitude);//设定中心点坐标
    MapStatus mapStatus=new MapStatus.Builder().target(center).zoom(18).build();//定义地图状态
    MapStatusUpdate mapStatusUpdate= MapStatusUpdateFactory.newMapStatus(mapStatus);//定义MapStatusUpdate对象,以便描述地图状态将要发生的变化
    baiduMap.setMapStatus(mapStatusUpdate);
    baiduMap2.setMapStatus(mapStatusUpdate);//改变地图状态
    isFirstloc=false;
}

4.设置地图的点击事件:
(1)定点导航

baiduMap.setOnMapClickListener(new BaiduMap.OnMapClickListener() {
    @Override
    public void onMapClick(LatLng latLng) {
        double lat=latLng.latitude;
        double lon=latLng.longitude;
        changeMapStatus(lat,lon);
        showDialog("位置信息","此处坐标:"+"经度:"+lat+",纬度:"+lon+",去这里吗?",lat+","+lon,url+"movetopoint","location=");
        //点击空白地点
    }

    @Override
    public boolean onMapPoiClick(MapPoi mapPoi) {
        String locname=mapPoi.getName();
        double lati=mapPoi.getPosition().latitude;
        double longi=mapPoi.getPosition().longitude;
        changeMapStatus(lati,longi);
        showDialog("位置信息","去"+locname+"吗?",lati+","+longi,url+"movetopoint","location=");
        return false;
        //点击标注点击事件
    }
});

private void showDialog(String title, String mesg, final String laAndLo, final String murl, final String req){
    new AlertDialog.Builder(Gps.this).setTitle(title).setIcon(R.drawable.gps_dialogicon).
            setMessage(mesg).setPositiveButton("Go!", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            NetWorkUtil.postStr(laAndLo,req,murl);
            Toast.makeText(Gps.this,"正在开始导航……",Toast.LENGTH_SHORT).show();
        }
    }).setNegativeButton("不去", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Toast.makeText(Gps.this,"取消导航",Toast.LENGTH_SHORT).show();
        }
    }).show();
}

(2)多点巡逻

baiduMap2.setOnMapClickListener(new BaiduMap.OnMapClickListener() {
    @Override
    public void onMapClick(LatLng latLng) {
        locPopub.popubLocWindow(getLayoutInflater().inflate(R.layout.activity_gps,null),String.valueOf(latLng.latitude),String.valueOf(latLng.longitude),"未标注地点",url+"app_movebypath",mhandler,baiduMap2);
    }

    @Override
    public boolean onMapPoiClick(MapPoi mapPoi) {
        locPopub.popubLocWindow(getLayoutInflater().inflate(R.layout.activity_gps,null),String.valueOf(mapPoi.getPosition().latitude),String.valueOf(mapPoi.getPosition().longitude),mapPoi.getName(),url+"app_movebypath",mhandler,baiduMap2);
        return false;
    }
});


public void popubLocWindow(){

List<Location>locations=new ArrayList<>();
BaiduMapOverlay baiduMapOverlay=new BaiduMapOverlay();
baiduMapOverlay.addMakers(locations,map);
…………在地图上标注几个点,然后放到arraylist中再转换成jsonArray发到服务端处理
……只粘上部分代码
locations.add(new Location(mla,mlo,ln));
if (locations.size()>=2) {
if (locations.size()>=2) {
    cordintArr=new JSONArray();
    for (int i=0;i<locations.size();i++){
        locObj=new JSONObject();
        try {
            locObj.put("coordinate",locations.get(i).getMyPopLat()+","+locations.get(i).getMyPopLon());
            cordintArr.put(locObj);
            locObj=null;
        } catch (Exception e) {
            e.printStackTrace();
        }
NetWorkUtil.requestJson(urlReq,jsonString,handler);
    }

(3)导航记录

1)创建adapter

public class GpsinfoAdapter extends BaseAdapter {
    private Context context ;
    private List<Routes>routesList;
    private View.OnClickListener delClickListener,navaginClickListener;//点击事件接口
    public GpsinfoAdapter(Context context,List<Routes>routesList){
        this.context=context;
        this.routesList=routesList;
    }
    public void setRouteDel(View.OnClickListener delListener){
        delClickListener=delListener;
    }
    public void setNavaAgin(View.OnClickListener navaginListener){
        navaginClickListener=navaginListener;
    }
    @Override
    public int getCount() {
        return routesList.size();
    }

    @Override
    public Object getItem(int position) {
        return routesList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder=null;
        if (convertView==null){
            convertView= LayoutInflater.from(context).inflate(R.layout.gpsdetail_item,parent,false);
            viewHolder=new ViewHolder();
            viewHolder.idtext=convertView.findViewById(R.id.id_tv);
            viewHolder.pathtext=convertView.findViewById(R.id.path_tv);
            viewHolder.timetext=convertView.findViewById(R.id.time_tv);
            viewHolder.again=convertView.findViewById(R.id.navi_again);
            viewHolder.del=convertView.findViewById(R.id.route_del);
            viewHolder.del.setOnClickListener(delClickListener);
            viewHolder.again.setOnClickListener(navaginClickListener);
            //设置接口回调,注意参数不是上下文,它需要ListView所在的Activity或者Fragment处理接口回调方法
            convertView.setTag(viewHolder);
        }
        else {
            viewHolder= (ViewHolder) convertView.getTag();
        }
        Routes routes=routesList.get(position);
        viewHolder.idtext.setText("序号 "+routes.getId());
        viewHolder.pathtext.setText("路径:"+routes.getPath());
        viewHolder.timetext.setText("记录时间:"+routes.getTime());
        viewHolder.del.setTag(position);
        viewHolder.again.setTag(position);
        //设置Tag,用于判断用户当前点击的哪一个列表项的按钮,解决问题:如何知道你点击的按钮是哪一个列表项中的
        return convertView;
    }
    private class ViewHolder{
        TextView idtext,pathtext,timetext;
        ImageButton again,del;
    }

2)创建列表

private void createInfoList(){
    infoData=new ArrayList<>();
    new Thread(new Runnable() {
        @Override
        public void run() {
            NetWorkUtil.requestJson(url+"app_robot_navigator","getJson",mhandler);
        }
    }).start();
    infoData.add(new Routes("1","上海电机学院图书馆-上海电机学院电子信息学院-教学楼c-上海电机学院第一食堂","2018-05-08 23:23:12"));
    infoData.add(new Routes("2","上海电机学院文理楼-上海电机学院机械学院-教学楼d-上海电机学院第三食堂","2018-05-01 12:21:10"));
    gpsinfoAdapter=new GpsinfoAdapter(Gps.this,infoData);
    gpsinfoAdapter.setRouteDel(this);
    gpsinfoAdapter.setNavaAgin(this);
    gpsinfo_list.setAdapter(gpsinfoAdapter);
}

3)设置item中控件的监听事件 
@Override
public void onClick(View v) {
    Object tagObj=v.getTag();
    if (tagObj!=null&&tagObj instanceof Integer) {
        curPos = (Integer) tagObj;
        curPath=infoData.get(curPos).getPath();
        curId=infoData.get(curPos).getId();
    }
    switch (v.getId()) {
        case R.id.route_del:new AlertDialog.Builder(Gps.this).setIcon(R.drawable.delbtn).setTitle("提示").setMessage("确定删除此条记录?").setPositiveButton("是的", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                NetWorkUtil.postStr(curId,"pid=",url+"app_deletepath");
                infoData.remove(curPos);
                Toast.makeText(Gps.this,"删除成功",Toast.LENGTH_SHORT).show();
                gpsinfoAdapter.notifyDataSetChanged();
            }
        }).setNegativeButton("不要",null).show();
        break;
        case R.id.navi_again:
            showDialog("开始导航", "再次按照此路径导航吗?", curPath,url+"movebypath","pathcontent=");break;
    }
}

运行效果如下:
机器人远程控制系统(安卓端)

基本信息

实现功能:设置ip,显示产品信息
实现方式:对话框,SharedPreferences,listview
实现过程:
1.使用正则表达式匹配局域网的ip

//匹配C类地址的IP
public static final String regexCIp = "^192\\.168\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$";
//匹配A类地址
public static final String regexAIp = "^10\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$";
//匹配B类地址
public static final String regexBIp = "^172\\.(1[6-9]|2\\d|3[0-1])\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$";

//利用正则表达式匹配局域网ip
public static String getHostIp(){
    String hostIp;
    Pattern ip = Pattern.compile("(" + regexAIp + ")|" + "(" + regexBIp + ")|" + "(" + regexCIp + ")");
    Enumeration<NetworkInterface> networkInterfaces = null;
    try {
        networkInterfaces = NetworkInterface.getNetworkInterfaces();
    } catch (SocketException e) {
        e.printStackTrace();
    }
    InetAddress address;
    while (networkInterfaces.hasMoreElements()) {
        NetworkInterface networkInterface = networkInterfaces.nextElement();
        Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
        while (inetAddresses.hasMoreElements()) {
            address = inetAddresses.nextElement();
            String hostAddress = address.getHostAddress();
            Matcher matcher = ip.matcher(hostAddress);
            if (matcher.matches()) {
                hostIp = hostAddress;
                return hostIp;
            }
        }
    }
    return null;
}

2.创建自定义对话框对ip进行编辑

public void showDialog(){
    LayoutInflater inflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    RelativeLayout layout= (RelativeLayout)inflater.inflate(R.layout.wifi_info_dialog,null);
    final EditText ip=layout.findViewById(R.id.ipedit);
    sp=context.getSharedPreferences("ipstr",Context.MODE_PRIVATE);
    ipstr=sp.getString("ip","");
    ip.setText(ipstr);
    ip.setSelection(ipstr.length());
    AlertDialog alertDialog=new AlertDialog.Builder(context).setTitle("局域网信息").setIcon(R.drawable.wifiicon).setView(layout).setPositiveButton("完成", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            String ips=ip.getText().toString();
            sp=context.getSharedPreferences("ipstr", Context.MODE_PRIVATE);
            editor=sp.edit();
            editor.putString("ip",ips).commit();//编辑ip存储到SharedPreferences中
            Toast.makeText(context,"修改完成",Toast.LENGTH_SHORT).show();
            InputMethodManager imm = (InputMethodManager)context.getSystemService(
                    Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(ip.getWindowToken(), 0);
        }
    }).setNegativeButton("更新",null).create();
    alertDialog.show();
    alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(context,"局域网信息已更新",Toast.LENGTH_SHORT).show();
            String hip=NetWorkUtil.getHostIp();//匹配到局域网的ip
            ip.setText(hip);
            ip.setSelection(hip.length());
        }
    });
}

3.显示相关信息列表

private String[] info_name=new String[]{"型号","硬件信息","软件版本"};
private String[] info_detail=new String[]{"探索号1.0.0","Ros操作系统","Android "+android.os.Build.VERSION.RELEASE};

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_info_frgt, container, false);
listView=view.findViewById(R.id.info_list);
arrayList=new ArrayList<>();
for (int i=0;i<info_name.length;i++){
    Map<String,Object>map=new HashMap<>();
    map.put("name",info_name[i]);
    map.put("detail",info_detail[i]);
    arrayList.add(map);
}
adapter=new SimpleAdapter(getContext(),arrayList,R.layout.info_item,
        new String[]{"name","detail"},new int[]{R.id.textname,R.id.textdetail});
listView.setAdapter(adapter);
return  view;
}

运行截图:机器人远程控制系统(安卓端)

总结

该项目其实涉及到个人的技术含量较少,因为通过集成科大讯飞和百度地图两个SDK来实现语音识别和导航两大功能,这样确实大大加快了项目开发的进度。而在项目中用到最多的知识点就是activity生命周期的使用,fragment,listview(baseadapter的优化,item中控件的监听事件),http请求(get,post),handler,json的处理(生成和解析json等),以及SharedPreferences等等。仔细想想确实知识点不少,但自己掌握的都不是很深入,而现在不少公司往往就是看中我们对知识学习的深度,所以以后学习的方法应该改变,试着认真对待每个知识点!