机器人远程控制系统(安卓端)
前言
现在正值春招面试的时间段,笔者并没有投很多简历,因为经历过某大公司的电话面之后发现太多的不足,与其花时间在刷简历上,不如好好总结和复习学过的东西,本篇就作为个人项目总结的开端。
这个项目其实就是学校的一个参赛项目,主要实现的功能是通过发送语音文字识别控制机器人移动,也可以定点或多点导航,实时接收并更新机器人反馈的状态。我主要负责安卓的开发,下面分功能模块讲解:
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)&®_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等等。仔细想想确实知识点不少,但自己掌握的都不是很深入,而现在不少公司往往就是看中我们对知识学习的深度,所以以后学习的方法应该改变,试着认真对待每个知识点!