关于地图拍照上传项目的一些总结
2011-12-22 14:59
162 查看
这个项目是为某个运动品牌商店定做,一开始就是十分吸引我的。因为它的功能点十分普遍,所以如果我完成了这个项目,自然会沉淀下来一些功能代码,项目框架,和相关的经验,以方便日后使用 。首先它是款地图应用,可以获得所有商店,在地图上以小图钉的方式呈现。然后要支持查找用户当前的位置,进入某个商店,查看里面的商店信息和店内的视频和照片,支持拍照,录制视频并上传,也可以进行评论。用户也可自己添加,编辑,删除商店。
功能点就是这些,算是个小项目,为期也就3周时间,但是由于一些新东西没有接触过,所以还是需要总结一下这次遇到的问题。
1.注册MAPKEY这个是众所周知的,可以理解为不同的开发电脑有不同的debug.keystore文件,所以需要对应不同的MAPKEY。 这只是限于开发,发布APK不会影响。
keytool -list -alias androiddebugkey -keystore C:\Documents and Settings\user\.android\debug.keystore
这个keytool 是java/bin环境下的
然后得到MD5值后再去http://code.google.com/android/maps-api-signup.html 点击打开链接验证获得KEY就行了
2.这次项目结构很清晰,吸取以往的经验,告别application这个类.之前的项目就是把很多需要传递的数据装在application里面,包括activity之间的数据传递,我都完全没有使用intent传递(觉得序列化很麻烦) 然后getApplication满天飞,数据管理起来非常混乱,各种未知的数据不同步的bug.这次发现Parcelable序列化接口蛮好用的
只需要实现以下几个方法就好,逻辑很清晰
3.既然用到了intent传递数据,这次也用到一个十分方便的方法startActivityForResult().这样activity之间的数据交互就更方便了,比如A提交数据给B, 需要B做一些处理然后再告知A,以往我的做法不是发广播就是传handler回调,这些的可读性和管理都很糟糕 .而使用startActivityForResult启动B之后, 当B结束之后,只需要setResult 返回成功或者失败,并可将数据返回,当然记得调用finish().A这边才会从onActivityResult()方法中得到回调并获取数据.这样使你的代码结构非常清晰.
4.碰到这样一个需求 有A,B两个线程,A是获取所有的商店的一个网络请求,B是获得当前的经纬度 然后传给服务器得到附近最近的一家商店. 这个B请求一定是建立在A执行完毕之后才执行, 也许我们以往的写法就是A线程执行完毕后 然后在A线程里面发送message到handler(post到UI线程) 然后再调用B线程启动. 这样扩展性很糟糕,比如我有个刷新商店的功能,自然是启动A线程执行完毕就OK, 而现在还会导致B线程也启动. 所以我最后用到一个很不错的java的方法 就是Thread的join()方法.
这个join方法很绕不是很好理解.于是写了一个demo
所以你想B线程排在A线程后面执行,就要在B线程里面调用A.join(); 如果A没执行完,B会一直sleep等待
5.关于GPS定位, 我们都知道在室内GPS几乎是无法定位,这个时候我们就需要依赖基站定位. 所以室内是基站定位,室外是GPS定位.
这次定位我采用的是google现有的API, MyLocationOverlay这个类.你只需要
就可以在你的地图里面显示一个蓝色的亮点,并且一直闪烁.这个MyLocationOverlay可以自动选择合适的定位方式 包括GPS定位和基站定位,当他定位成功你也可以通过他得到经度维度.你可以通过下面的方法,当他成功获得当前经纬度后,自动回调该方法,做一些你后续的工作.
不过MyLocationOverlay有个唯一的缺点,他会频繁的调用你所有的Overlay对象的onDraw()方法,我猜想是它需要一直更新当前的位置并刷新那个蓝点,这个问题我还无法解决,不过对我的功能不会有影响。
6.关于照片裁剪,用户拍得照片大小各异,然后可能是横着竖着的,所以需要裁剪,统一大小放在一个照片列表里面.我没有重新BitmapFactory.decode进行裁剪
而是直接
采用android:scaleType="centerCrop"的办法 他会根据我们设置的宽高选择适中的区域裁剪。比较简单
7.关于post上传照片,然后照片名和评论是中文时候是乱码的情况。这个问题我困扰了我一天,最后还是顺利解决了(见注释部分)。代码发一下,方便给需要用到的朋友
8.关于地图上的小图钉OverlayItem的问题, google提供的OverlayItem很薄弱,仅仅只有getTittle 和getSnippet这些方法
连set方法都不提供。所以建议大家继承OverlayItem,给他包装上你需要的一些业务逻辑和属性。
9.关于拍照和录制视频的问题
拍照和录像我都是采用调用内置实现的,只是由于android的系统的平台定制的多样化,碰到一些问题.比如录制视频我就没有采用给他分配路径的形式,因为在三星的galaxy平板上会出现相机无法退出的情况.所以改成了
然后在onAcitivityResult里面:
而拍照如果不指定路径,我发现在MIUI系统上生成的图片会找不到.(真的想对android的开源性吐槽,屏幕UI设计繁琐我都不说了,结果连一些基本的性质都不一致了)
所以图片采用的是分配地址:
然后在onAcitivityResult里面:
先暂时写这么多...如果还有想到什么再补充
功能点就是这些,算是个小项目,为期也就3周时间,但是由于一些新东西没有接触过,所以还是需要总结一下这次遇到的问题。
1.注册MAPKEY这个是众所周知的,可以理解为不同的开发电脑有不同的debug.keystore文件,所以需要对应不同的MAPKEY。 这只是限于开发,发布APK不会影响。
keytool -list -alias androiddebugkey -keystore C:\Documents and Settings\user\.android\debug.keystore
这个keytool 是java/bin环境下的
然后得到MD5值后再去http://code.google.com/android/maps-api-signup.html 点击打开链接验证获得KEY就行了
2.这次项目结构很清晰,吸取以往的经验,告别application这个类.之前的项目就是把很多需要传递的数据装在application里面,包括activity之间的数据传递,我都完全没有使用intent传递(觉得序列化很麻烦) 然后getApplication满天飞,数据管理起来非常混乱,各种未知的数据不同步的bug.这次发现Parcelable序列化接口蛮好用的
只需要实现以下几个方法就好,逻辑很清晰
@Override public int describeContents() { // TODO Auto-generated method stub return 0; } @Override public void writeToParcel(Parcel dest, int flags) { //把你想序列化的数据都写进去 dest.writeDouble(gp.getLatitudeE6()); dest.writeDouble(gp.getLongitudeE6()); dest.writeInt(isTemp ? 1 : 0); dest.writeInt(isEdit ? 1 : 0); dest.writeSerializable(storeBean); } public static final Parcelable.Creator<StoreOverlay> CREATOR = new Parcelable.Creator<StoreOverlay>() { public StoreOverlay createFromParcel(Parcel in) { //根据你上面写的数据,再读出来重新生成,注意是新的对象生成,类似深度clone double mLat = in.readDouble(); double mLon = in.readDouble(); boolean isTemp = in.readInt() == 1 ? true : false; boolean isEdit = in.readInt() == 1 ? true : false; StoreBean storeBean = (StoreBean) in.readSerializable(); GeoPoint gp = new GeoPoint((int) mLat, (int) mLon); StoreOverlay storeOverlay = new StoreOverlay(gp, storeBean, isTemp); storeOverlay.setEdit(isEdit); return storeOverlay; } public StoreOverlay[] newArray(int size) { return new StoreOverlay[size]; } };
3.既然用到了intent传递数据,这次也用到一个十分方便的方法startActivityForResult().这样activity之间的数据交互就更方便了,比如A提交数据给B, 需要B做一些处理然后再告知A,以往我的做法不是发广播就是传handler回调,这些的可读性和管理都很糟糕 .而使用startActivityForResult启动B之后, 当B结束之后,只需要setResult 返回成功或者失败,并可将数据返回,当然记得调用finish().A这边才会从onActivityResult()方法中得到回调并获取数据.这样使你的代码结构非常清晰.
4.碰到这样一个需求 有A,B两个线程,A是获取所有的商店的一个网络请求,B是获得当前的经纬度 然后传给服务器得到附近最近的一家商店. 这个B请求一定是建立在A执行完毕之后才执行, 也许我们以往的写法就是A线程执行完毕后 然后在A线程里面发送message到handler(post到UI线程) 然后再调用B线程启动. 这样扩展性很糟糕,比如我有个刷新商店的功能,自然是启动A线程执行完毕就OK, 而现在还会导致B线程也启动. 所以我最后用到一个很不错的java的方法 就是Thread的join()方法.
这个join方法很绕不是很好理解.于是写了一个demo
CustomThread1 = new Thread(new Runnable() { @Override public void run() { System.out.println("CustomThread1" + " start."); try { for (int i = 0; i < 4; i++) { System.out.println("CustomThread1" + " loop at " + i); Thread.sleep(1000); } System.out.println("CustomThread1" + " end."); } catch (Exception e) { e.printStackTrace(); } } }); CustomThread2 = new Thread(new Runnable() { @Override public void run() { try { CustomThread1.join();//这里线程2会一直等待线程1结束后才会执行 } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } System.out.println("CustomThread2" + " start."); try { for (int i = 0; i < 4; i++) { System.out.println("CustomThread2" + " loop at " + i); Thread.sleep(1000); } System.out.println("CustomThread2" + " end."); } catch (Exception e) { e.printStackTrace(); } } }); CustomThread1.start(); CustomThread2.start();
所以你想B线程排在A线程后面执行,就要在B线程里面调用A.join(); 如果A没执行完,B会一直sleep等待
5.关于GPS定位, 我们都知道在室内GPS几乎是无法定位,这个时候我们就需要依赖基站定位. 所以室内是基站定位,室外是GPS定位.
这次定位我采用的是google现有的API, MyLocationOverlay这个类.你只需要
mylocOverlay = new MyLocationOverlay(this, mapView); mylocOverlay.enableMyLocation();//允许定位 mylocOverlay.enableCompass();//打开指南针 mapListOverlay.add(mylocOverlay);//添加到mapview
就可以在你的地图里面显示一个蓝色的亮点,并且一直闪烁.这个MyLocationOverlay可以自动选择合适的定位方式 包括GPS定位和基站定位,当他定位成功你也可以通过他得到经度维度.你可以通过下面的方法,当他成功获得当前经纬度后,自动回调该方法,做一些你后续的工作.
mylocOverlay.runOnFirstFix(new Runnable() { @Override public void run() { System.out.println("get my location !"); GeoPoint gp = mylocOverlay.getMyLocation(); updateMapView(gp); b_mylocation.post(new Runnable() { @Override public void run() { b_mylocation.setBackgroundResource(R.drawable.selector_aim); } }); } });
不过MyLocationOverlay有个唯一的缺点,他会频繁的调用你所有的Overlay对象的onDraw()方法,我猜想是它需要一直更新当前的位置并刷新那个蓝点,这个问题我还无法解决,不过对我的功能不会有影响。
6.关于照片裁剪,用户拍得照片大小各异,然后可能是横着竖着的,所以需要裁剪,统一大小放在一个照片列表里面.我没有重新BitmapFactory.decode进行裁剪
而是直接
<ImageView android:id="@+id/iv_photoalbum_photo" android:layout_width="200px" android:layout_height="150px" android:layout_centerInParent="true" android:scaleType="centerCrop" android:src="@null" />
采用android:scaleType="centerCrop"的办法 他会根据我们设置的宽高选择适中的区域裁剪。比较简单
7.关于post上传照片,然后照片名和评论是中文时候是乱码的情况。这个问题我困扰了我一天,最后还是顺利解决了(见注释部分)。代码发一下,方便给需要用到的朋友
public static String postFile(String actionUrl, Map<String, String> params, String filePath, String encoding) { String newName = ""; if (params.get("type").equals("1")) { newName = "image.jpg"; } else if (params.get("type").equals("2")) { newName = "video.mp4"; } else { newName = "image.jpg"; } // String uploadFile = "/sdcard/image.JPG"; String LINEND = "\r\n"; String PREFIX = "--"; String BOUNDARY = "*****"; String MULTIPART_FROM_DATA = "multipart/form-data"; String CHARSET = encoding; try { URL url = new URL(actionUrl); HttpURLConnection con = (HttpURLConnection) url.openConnection(); // con.setReadTimeout(DEF.TIMEOUT_POST_CONNECTION); // con.setConnectTimeout(timeout) con.setDoInput(true); con.setDoOutput(true); con.setUseCaches(false); con.setRequestMethod("POST"); con.setRequestProperty("connection", "keep-alive"); con.setRequestProperty("Charset", CHARSET); con.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY); StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : params.entrySet()) { sb.append(PREFIX); sb.append(BOUNDARY); sb.append(LINEND); sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND); sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND); // sb.append("Content-Transfer-Encoding: 8bit" + LINEND); sb.append(LINEND); // sb.append(URLEncoder.encode(entry.getValue(), encoding)); sb.append(entry.getValue()); sb.append(LINEND); } DataOutputStream outStream = new DataOutputStream(con.getOutputStream()); //这句是关键的关键,加了这句才帮我解决中文乱码的问题 outStream.write(EncodingUtils.getBytes(sb.toString(), CHARSET));// encode StringBuilder sb1 = new StringBuilder(); sb1.append(PREFIX); sb1.append(BOUNDARY); sb1.append(LINEND); sb1.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + newName + "\"" + LINEND); sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND); sb1.append(LINEND); outStream.writeBytes(sb1.toString()); FileInputStream fStream = new FileInputStream(filePath); int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; int length = -1; while ((length = fStream.read(buffer)) != -1) { outStream.write(buffer, 0, length); } fStream.close(); outStream.writeBytes(LINEND); outStream.writeBytes(PREFIX + BOUNDARY + PREFIX + LINEND); outStream.flush(); outStream.close(); String response = read(con.getInputStream()); con.disconnect(); return response; } catch (Exception e) { e.printStackTrace(); } return ""; }
8.关于地图上的小图钉OverlayItem的问题, google提供的OverlayItem很薄弱,仅仅只有getTittle 和getSnippet这些方法
连set方法都不提供。所以建议大家继承OverlayItem,给他包装上你需要的一些业务逻辑和属性。
9.关于拍照和录制视频的问题
拍照和录像我都是采用调用内置实现的,只是由于android的系统的平台定制的多样化,碰到一些问题.比如录制视频我就没有采用给他分配路径的形式,因为在三星的galaxy平板上会出现相机无法退出的情况.所以改成了
private void recordVideo() { Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); startActivityForResult(intent, MENU_RECORD_VIDEO_ID); }
然后在onAcitivityResult里面:
case MENU_RECORD_VIDEO_ID: Uri uriVideo = data.getData(); Cursor cursorVideo = this.getContentResolver().query(uriVideo, null, null, null, null); if (cursorVideo.moveToNext()) { String s[] = cursorVideo.getColumnNames(); String videoPath = cursorVideo.getString(cursorVideo.getColumnIndex("_data")); Toast.makeText(this, videoPath, Toast.LENGTH_LONG).show(); System.out.println("videoPath :" + videoPath); } break;
而拍照如果不指定路径,我发现在MIUI系统上生成的图片会找不到.(真的想对android的开源性吐槽,屏幕UI设计繁琐我都不说了,结果连一些基本的性质都不一致了)
所以图片采用的是分配地址:
private void takePhotoToMyFolder() { String sdcardState = Environment.getExternalStorageState(); if (sdcardState.equals(Environment.MEDIA_MOUNTED)) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); String photoDirPath = Environment.getExternalStorageDirectory() + File.separator + NIKEPHOTO_FOLDER + File.separator; String fileName = "IMG_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".jpg"; File out = new File(photoDirPath); if (!out.exists()) { out.mkdirs(); } out = new File(photoDirPath, fileName); photoPath = photoDirPath + fileName; Uri uri = Uri.fromFile(out); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, MENU_TAKE_PHOTO_ID); } else { Util.show1BtnDialog(this, 0, 0, R.string.hint_nosdcard_disconnect, R.string.OK, null); } }
然后在onAcitivityResult里面:
case MENU_TAKE_PHOTO_ID: Toast.makeText(this, photoPath, Toast.LENGTH_LONG).show(); Intent intentUpload = new Intent(); intentUpload.putExtra(UploadActivity.KEY_DATAPATH, photoPath); intentUpload.setClass(this, UploadActivity.class); startActivity(intentUpload); break;
先暂时写这么多...如果还有想到什么再补充
相关文章推荐
- 关于地图拍照上传项目的一些总结
- 项目总结之关于JQuery一些常用的函数
- 项目q总结:关于Linux性能问题的一些思考
- 关于iphone http上传请求协议的一些总结
- 最近做项目的一些关于重构方面的总结
- 关于项目中异常处理的一些总结
- 关于乱码的一些总结--项目过程的点点滴滴
- 安卓中关于图片从网络获取,压缩,上传,下载,缩略图,缓存的一些处理总结(四)
- 安卓中关于图片从网络获取,压缩,上传,下载,缩略图,缓存的一些处理总结(三)
- 最近做项目的一些关于重构方面的总结
- 关于项目中异常处理的一些总结
- 关于ionic开发的一些总结(项目启动设置,app图标名称更改)
- 关于**活动项目上线之后的一些总结
- 关于项目中异常处理的一些总结(转)
- 关于JSP Commons FileUpload 组件上传文件的一些总结
- Spring MVC学习总结(5)——SpringMVC项目关于安全的一些配置与实现方式
- 安卓中关于图片从网络获取,压缩,上传,下载,缩略图,缓存的一些处理总结(一)
- [置顶] 【Android开发技巧】 关于Webview拍照或从相册上传图片处理总结
- 关于项目中异常处理的一些总结
- Spring MVC学习总结(5)——SpringMVC项目关于安全的一些配置与实现方式