android 使用 surfaceView 获取 camera 预览界面图像数据
在android
中,通过相机获取预览界面的需求似乎很变态,好像也没有什么使用场景。但是,有一个场景需要获取预览界面的图像,就是扫码,比如微信,支付宝的扫一扫,就是需要获取预览界面的图像数据的。
实现逻辑比较简单,不过肯定比打开系统相机要麻烦一点的。
下面简单说一下实现步骤:
- 实例化一个
SurfaceView
。 - 在
surfaceCreated()
回调中去实例化Camera
对象,去自动对焦。 - 在
onAutoFocus()
回调中去调用camera.takePicture(null,null,callback);
。 - 在第
3
步的callback
里面去获取预览图像数据int data[]
。 - (可选)将获取的数据换成成文件。
- (可选)将该文件对象加载成
bitmap
对象。 - 在不使用的使用释放相机资源。
代码细节:
- 清单文件:
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
java
代码:
package com.python.cat.testgradle;import android.Manifest;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.hardware.Camera;import android.os.Bundle;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.widget.Button;import android.widget.FrameLayout;import android.widget.Toast;import com.apkfuns.logutils.LogUtils;import com.google.zxing.BinaryBitmap;import com.google.zxing.ChecksumException;import com.google.zxing.FormatException;import com.google.zxing.NotFoundException;import com.google.zxing.RGBLuminanceSource;import com.google.zxing.Result;import com.google.zxing.common.GlobalHistogramBinarizer;import com.google.zxing.qrcode.QRCodeReader;import com.yanzhenjie.permission.Action;import com.yanzhenjie.permission.AndPermission;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.util.Arrays;import java.util.List;public class UseCameraActivity extends Activity { private Activity get() { return this; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_asome); setTitle(getClass().getSimpleName()); final FrameLayout frameLayout = findViewById(R.id.prev_content_layout); Button btn = findViewById(R.id.start_camera_preview); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { AndPermission.with(get()) .permission(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE ) .onDenied(new Action() { @Override public void onAction(List permissions) { LogUtils.e("error....." + permissions); } }) .onGranted(new Action() { @Override public void onAction(List permissions) { LogUtils.w("you can do.."); ScanView scanView = new ScanView(get()); frameLayout.removeAllViews(); frameLayout.addView(scanView); } }).start(); } }); } static class ScanView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback { private Camera mCamera; private final File fileImg; private ScanView self; public ScanView(Context context) { super(context); fileImg = new File(context.getCacheDir(), "prev_view.jpg"); SurfaceHolder mHolder = getHolder(); self = this; mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override public void onAutoFocus(boolean success, Camera camera) { LogUtils.w("auto focus..." + success); if (mCamera != null) { mCamera.takePicture(null, null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { mCamera.cancelAutoFocus(); mCamera.stopPreview(); // 拿到数据就停止!!! LogUtils.w("========data========"); LogUtils.w("----------data-----------------");// camera.startPreview(); File pictureFile = fileImg; if (pictureFile == null) { LogUtils.e("Error creating media file, check storage permissions: " + null); return; } if (data == null) { return; } try { LogUtils.d(data); FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); LogUtils.e("save preview complete###!!!"); LogUtils.e("save preview complete###!!!" + pictureFile); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pictureFile.getAbsolutePath(), options); options.inJustDecodeBounds = false; int outWidth = options.outWidth; int outHeight = options.outHeight; if (outWidth >= getWidth() * 2) { options.inSampleSize = outWidth / getWidth(); } if (outHeight >= getHeight() * 2) { options.inSampleSize = outHeight / getHeight(); } Bitmap bmp = BitmapFactory.decodeFile(pictureFile.getAbsolutePath(), options); Result result = parseInfoFromBitmap(bmp); if (result != null) { Toast.makeText(getContext(), "INFO:" + result.getText(), Toast.LENGTH_SHORT).show(); LogUtils.w("解析成功:" + result); } else { LogUtils.e("再次尝试中...."); mCamera.startPreview(); mCamera.autoFocus(self); // todo:这里也可以做最大重试次数的限制... } } catch (Exception e) { LogUtils.e("Error accessing file: " + e.getMessage()); } } }); } } public Result parseInfoFromBitmap(Bitmap bitmap) { int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()]; bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); LogUtils.w("### pixels dest==" + Arrays.toString(pixels)); RGBLuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), pixels); GlobalHistogramBinarizer binarizer = new GlobalHistogramBinarizer(source); BinaryBitmap image = new BinaryBitmap(binarizer); Result result = null; try { result = new QRCodeReader().decode(image); return result; } catch (NotFoundException e) { e.printStackTrace(); Toast.makeText(getContext(), "非二维码图片,不能解析", Toast.LENGTH_SHORT).show(); } catch (ChecksumException e) { e.printStackTrace(); } catch (FormatException e) { e.printStackTrace(); } return null; } @Override public void surfaceCreated(SurfaceHolder holder) { LogUtils.e("x surfaceCreated.. #####"); try { mCamera = Camera.open(); mCamera.setPreviewDisplay(holder); mCamera.setDisplayOrientation(90); Camera.Parameters parameters = mCamera.getParameters();// parameters.setPictureSize(1600, 1200);// parameters.setPreviewSize(640, 480); mCamera.setParameters(parameters); mCamera.startPreview(); mCamera.autoFocus(this); LogUtils.e("surfaceCreated.. #####"); } catch (IOException e) { LogUtils.e("Error setting camera preview: " + e.getMessage()); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { LogUtils.w("--change-"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { LogUtils.w("destroy--"); if (mCamera != null) { mCamera.cancelAutoFocus(); mCamera.stopPreview(); mCamera.release(); } } }}
- 布局文件:
<RelativeLayout 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" tools:context=".UseCameraActivity"> <Button android:id="@+id/start_camera_preview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="@string/start_camera_preview" /> <FrameLayout android:id="@+id/prev_content_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/start_camera_preview">FrameLayout>RelativeLayout>
gradle
配置(可选):
implementation 'com.yanzhenjie:permission:2.0.0-rc4' // 运行时权限语法糖implementation 'com.google.zxing:core:3.3.1' // zxing 解析二维码图片
嗯,以上就是获取相机预览图像数据的代码了。不过,这里用的Camera
接口是过时的了,不建议使用。不过可以运行。我目前的compileSdkVersion=26
。
就酱咯。 完整app代码也许有的吧。
=======
update: 关于Camera2
,我选择放弃….
官方的说法是:
The android.hardware.camera2 package provides an interface to individual camera devices connected to an Android device. It replaces the deprecated Camera class.
This package models a camera device as a pipeline, which takes in input requests for capturing a single frame, captures the single image per the request, and then outputs one capture result metadata packet, plus a set of output image buffers for the request. The requests are processed in-order, and multiple requests can be in flight at once. Since the camera device is a pipeline with multiple stages, having multiple requests in flight is required to maintain full framerate on most Android devices.
反正是看懂了一句话,就是用来替代Camera
的。
但是,真的很麻烦。我没有找到中文的,可以直接运行的案例,找到了两个外国人写的博客,里面给了完整的代码。不过估计不能直接访问。我把拷贝到我的项目里面去了。可以直接运行。
完整app代码
因为每一篇代码都很长,我就不贴出来了,说一下路径。
com.python.cat.testgradle.MainActivity.java[由于这个原始代码没有加动态权限申请,我手动添加了一下…]
com.python.cat.testgradle.AndroidCameraApi.java
我给的是我代码的路径,以及原始链接。
不清楚是出于什么考虑,camera2
里面比camera
多了好几类,复杂度也提升了不少。有需要的可以研究一下。上手难度大于camera
。(目前我还是一头雾水,对于camera2
)。
中文的找到一个:这个我并没有去下载运行验证,不过看博客里面写的挺多的。
更多相关文章
- Androidi性能优化之Java代码优化(摘自Android性能优化一书)
- 通过Python 获取Android设备信息的轻量级框架
- 9个非常棒的Android代码编辑器 移动开发者的最爱
- 星云精准测试对安卓底层驱动代码的测试案例分析
- Android(安卓)Studio Jni开发(二)实现Native调用java方法和Native
- android 取消标题栏出错,程序崩溃的问题
- 几行代码,让你的app动感起来--Android(安卓)Design Support Libra
- 初学android-调用百度api显示地图(出现显示网格问题)
- [置顶] android 多媒体和相机详解六