项目需求:自定义锁屏成功后,输入解锁密码两次失败后要求调用前置摄像头拍照并定位,将结果返回服务器
因为本文主要是讲述如何拍照,拍照的方法只写了一个方法,可以不用管它
代码如下:
如此调用:
mBiz.enterWrongPasswordTwice(mContext, mSurfaceView);
mBiz是一个抽象类,它的实现类里实现了这个方法,参数的意义:1 代表一个上下文对象,可以穿当前Activity的context,我们项目里用得Application里的context:mContext = MyAppContext.getAppInstance();这里的MyAppContext是继承了android Application的,关于android Application的用法大家可以GOOLE或是BAIDU,
2 传进去一个surfaceview用来拍照用的,mSurfaceView = (SurfaceView) mView.findViewById(R.id.surfaceView);是从布局文件里找出来的,这里顺便提一下需要对这个SurfaceView做处理:mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//当SurfaceHolder对象的类型设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS时就只能拍照不能绘制了。
SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS的意思是创建一个"PUSH"surface,这个surface没有自己的缓冲区等待屏幕渲染引擎将内容推送至用户前面 接着往下
在mBizimpl实现类里如此调用的
@Override
public void enterWrongPasswordTwice(Context context, SurfaceView surfaceView) {
if (surfaceView != null) {
Thread.getThreadInstant().locateAndTakePhoto(surfaceView);
//得到Thread的实例调用locateAndTakePhoto方法
}
}
接着要为大家来讲核心代码了,在Thread这个类里分别有个默认构造器
private Thread() {
}
然后有个getThreadInstant方法:public static Thread getThreadInstant() {
if (mThread == null) {
mThread = new Thread();
}
return mThread;
}
接下来是上代码调用的时候了,一层接一层封装的方法调用,大家不要看得很混乱,里面带有注释的
补充:private boolean isTakingPicture = false;这个变量是类takephoto中一个私有变量,代表拍照是否正在进行
默认是false
public void locateAndTakePhoto(SurfaceView surfaceView) {
StringmLocationUrl = "";
StringmPhotoUrl = "";
booleanmIsUpdateError = false;
boolean mIsLocateError = false;
booleanhasSendMessage = false;
//拍照
TakePhoto.getPhotoInstance().takePicture(surfaceView);
//得到TakePhoto的实例,方法同上面的一样
//定位
startLocalLocate();
}
TakePhoto中takePicture方法
public void takePicture(final SurfaceView surfaceView) {
new Thread(new Runnable() {
//此方法就是启动一个线程
public void run() {
removeRing();
【【
//这个方法是用来移除系统铃声的,大家知道手机拍照是有快门声的,此段代码就是可以去除拍照的时候去除声音
神不知鬼不觉的就调用摄像头拍照了
private void removeRing(){
AudioManager audioManager = (AudioManager)MyAppContext.getAppInstance().getSystemService(Context.AUDIO_SERVICE);
mRingMode = audioManager.getRingerMode();//返回系统当前铃声模式
mCurrentVolume = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM);
//返回系统当前的音量
audioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 0,AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
//设置系统静音
}
】】
openCamera(surfaceView);
【【
//打开摄像头
private void openCamera(SurfaceView surfaceView) {
//这里判断当isTakingPicture为true的时候是直接返回的,目的是防止打开摄像头准备拍照的时候再次重新打开摄像头进行拍照(连续点两下拍照按钮,只有第一次是成功的)
if (isTakingPicture) {
Log.d(TAG, "is taking picture,will return");
return;
}
int cameraId = findFirstFrontFacingCamera();
】】
//优先找前置摄像头
private int findFirstFrontFacingCamera() {
int foundId = -1;
//此方法得到设备上实际摄像头的数量
int numCams = Camera.getNumberOfCameras();
//循环得到的每个摄像头得到对应的每个摄像头的详细信息
for (int camId = 0; camId < numCams; camId++) {
CameraInfo info = new CameraInfo();
try {
Camera.getCameraInfo(camId, info);
} catch (Exception e) {
Log.e(TAG, "findFirstFrontFacingCamera getCameraInfo error: " + e.toString());
}
//如果得到的是前置摄像头,那么返回前置摄像头对应的camId,否则就返回-1
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
Log.d(TAG, "Found front facing camera");
foundId = camId;
break;
}
}
return foundId;
}
】】
//如果cameraId == -1:即证明找不到前置摄像头,就找后置摄像头
if(cameraId == -1){
Log.d(TAG, "no front camera, get back camera");
cameraId = findBackFacingCamera();
【【
private int findBackFacingCamera() {
int foundId = -1;
int numCams = Camera.getNumberOfCameras();
for (int camId = 0; camId < numCams; camId++) {
CameraInfo info = new CameraInfo();
try {
Camera.getCameraInfo(camId, info);
} catch (Exception e) {
Log.e(TAG, "findBackFacingCamera getCameraInfo error: " + e.toString());
}
if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
Log.d(TAG, "Found back facing camera");
foundId = camId;
break;
}
}
return foundId;
}
】】
}
//得到Camera的实例
mCamera = getCameraInstance(cameraId);
【【
private Camera getCameraInstance(int cameraId) {
Camera camera = null;
try {
camera = Camera.open(cameraId); // attempt to get a Camera instance
} catch (Exception e) {
Log.e(TAG, " Camera is not available (in use or does not exist):" + e.toString());
}
return camera; // returns null if camera is unavailable
}
】】
if (mCamera == null) {
Log.d(TAG, "can not init, camera may in use");
return;
}
Log.d(TAG, "isTakingPicture:" + isTakingPicture + "->true");
isTakingPicture = true;//表示已经开始拍照
try {
//通过surfaceview显示取景画面
mCamera.setPreviewDisplay(surfaceView.getHolder());
//开始取景
mCamera.startPreview();
Log.d(TAG, "mCamera.setPreviewDisplay startPreview");
} catch (IOException e) {
Log.e(TAG, "mCamera init IOException: " + e.toString());
} catch (Exception e) {
Log.e(TAG, "mCamera init error: " + e.toString());
}
}
】】
//上面两个方法都是为拍照做准备的
if (mCamera != null) {
Log.d(TAG, "mCamera is not null, begin takePicture");
try {
//开始拍照,三个参数的意义:第一个参是Camera.ShutterCallback shutter的callback常常用于触发声音来提示用户景象已经被捕获。第二个参是Camera.PictureCallback raw的callback常常用于原始的景象数据产生的回调方法。第三个参数是 Camera.PictureCallback jpeg的callback,常常用来是照相的JPEG图片已经产生的回调。
mCamera.takePicture(null, null, mPicture);
【【
/**
* 处理照片被拍摄之后的事件:将产生的照片压缩一般以文件的形式保存在SDCARD中
/
private PictureCallback mPicture = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(TAG, "onPictureTaken");
//将产生的元景象数据(是一个字节数组)解码为Bitmap,三个参数分别是元数据,从字节流的第一个位置开始,要解码的字节长度
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
//File mPictureFile
mPictureFile = getOutputMediaFile();
【
//创建文件来保存景象
private File getOutputMediaFile() {
// SDCard没有挂载,则返回NULL
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
Log.d(TAG, "can not find sdcard!");
return null;
}
//创造文件,1参是文件保存的路径,Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)此方法返回一个标准的存储图像和视频位置,其它应用可以轻松发现然后读取,修改以及删除此路径下的文件们,2参是文件名
File mediaStorageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"PhoneFinder");
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
//创建文件夹
Log.e(TAG, "failed to create directory:"
+ mediaStorageDir.getPath());
return null;
}
}
//创建文件
File mediaFile = null;
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+getBackUpFileName() + ".jpg");
return mediaFile;
}
//文件名一日期命名
private String getBackUpFileName() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.US);
return sdf.format(new Date());
}
】
//文件为空,出错
if (mPictureFile == null) {
isTakingPicture = false;
ServiceUtils.sendTakePicError();
Log.e(TAG, "Error creating media file, check storage permissions");
return;
}
// write
try {
//构造文件输出流,以便将Bitmap写入文件
mFos = new FileOutputStream(mPictureFile);
//下面这个方法是将bitmap以50的(压缩一半)比例压缩到mFos文件输出流(摄像头照出的景象文件) press(CompressFormat.JPEG, 50, mFos);
//mFos.write(data);
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found: " + e.getMessage());
} catch (Exception e) {
Log.e(TAG, "mFos error" + e.toString());
}
finally {
try {
mFos.close();
bitmap.recycle();
bitmap = null;
} catch (IOException e) {
Log.e(TAG, "mFos.close error:" + e.toString());
}
}
releaseCamera();
【
//释放摄像头
public void releaseCamera() {
if (mCamera != null) {
if (isTakingPicture) {
mCamera.stopPreview();
isTakingPicture = false;
}
mCamera.release();
mCamera = null;
}
Log.d(TAG, "release camera!");
}
】
//向服务器上传照片文件,此方法不做多的解释,可直接跳过
HttpUploadPhoto.uploudPhoto(mPictureFile);
Log.d(TAG, "HttpUploadPhoto.uploudPhoto(mPictureFile)");
}
};
】】
} catch (Exception e) {
//如果出现异常,也需要释放摄像头,否则其他的应用无法用摄像头
releaseCamera();
ServiceUtils.sendTakePicError();
Log.e(TAG, "Camera takePicture error:" + e.toString());
}
} else {
isTakingPicture = false;
ServiceUtils.sendTakePicError();
Log.d(TAG, "mCamera is null!");
}
rollbackRing();
【【
//照片拍完还原系统音量,
private void rollbackRing(){
AudioManager audioManager = (AudioManager)MyAppContext.getAppInstance().getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, mCurrentVolume, mRingMode);
}
】】
}
}).start();
}
自定义锁屏成功解锁两次失败 拍照(调用前置摄像头 没有前置摄像头就调用后置摄像头)和定位...