android camera预览,com.android.camera2,Android实现Camera2预览和拍照效果

android camera预览,com.android.camera2,Android实现Camera2预览和拍照效果

这篇文章主要为大家详细介绍了机器人开发之一个类实现照相机2预览和拍照效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

简介

网上对于照相机2的介绍有很多,在开源代码库上也有很多关于照相机2的封装库,但是对于那些库,封装性太强,有时候我们仅仅是需要个简简单单的拍照功能而已,因此,自定义一个照相机使之变得轻量级那是非常重要的了。(本文并非重复造轮子,而是在于学习Camera2API的基本功能,笔记之。)

学习要点:

使用Android Camera2 API的基本功能。

迭代连接到设备的所有相机的特征。

显示相机预览和拍摄照片。

照相机2 API为连接到机器人设备的各个相机设备提供了一个界面。它替代了已弃用的照相机类。

使用getCameraIdList获取所有可用摄像机的列表。然后,您可以使用getCameraCharacteristics,并找到适合您需要的最佳相机(前/后面,分辨率等)。创建一个摄像设备。状态回调的实例并打开相机。当相机打开时,准备开始相机预览。使用TextureView显示相机预览。创建一个照相机拍摄并设置一个重复的捕获请求。静像拍摄需要几个步骤。首先,需要通过更新相机预览的捕获请求来锁定相机的焦点。然后,以类似的方式,需要运行一个预捕获序列。之后,它准备拍摄一张照片。创建一个新的捕获请求并调用[捕获].

完成后,别忘了解锁焦点。

实现效果

环境

SDK21

Camera2 类图

代码实现

CameraPreview.java

/**

*神华创作于2017-10-20-0020。

*给shenhuanet@126.com发电子邮件

*/

公共类照相机预览扩展TextureView {

private static final String TAG=' camera preview ';

private static final SparseIntArray ORIENTATIONS=new SparseIntArray();//从屏幕旋转转换为联合图像专家组方向

private static final int MAX _ PREVIEW _ WIDTH=1920;//Camera2 API保证的最大预览宽高

private static final int MAX _ PREVIEW _ HEIGHT=1080;

private static final int STATE _ PREVIEW=0;//显示相机预览

private static final int STATE _ WAITING _ LOCK=1;//焦点锁定中

私有静态最终int STATE _ WAITING _ PRE _ CAPTURE=2;//拍照中

private static final int STATE _ WAITING _ NON _ PRE _ CAPTURE=3;//其它状态

private static final int STATE _ PICTURE _ TAKEN=4;//拍照完毕

private int mState=STATE _ PREVIEW;

private int mRatioWidth=0,mration height=0;

私有int mSensorOrientation

私有布尔mflash支持

私有信号量mCameraOpenCloseLock=新信号量(1);//使用信号量旗语进行多线程任务调度

私人活动活动;

私有文件mFile

私有处理程序线程mBackgroundThread

私有处理程序mBackgroundHandler

私有大小mPreviewSize

私有字符串麦卡梅雷德

私人摄像设备;

私有捕获请求构建器mPreviewRequestBuilder

private CaptureRequest mPreviewRequest;

私人摄像师;

私有ImageReader mImageReader

静态{

ORIENTATIONS.append(表面.旋转_0,90);

ORIENTATIONS.append(表面.旋转_90,0);

ORIENTATIONS.append(表面.旋转_180,270);

ORIENTATIONS.append(表面.旋转_270,180);

}

公共摄像机预览(上下文上下文){

这(上下文,空);

}

公共相机预览(上下文上下文,属性集属性){

this(context,attrs,0);

}

公共摄像机预览(Context Context,AttributeSet attrs,int defStyleAttr) {

super(context,attrs,defStyleAttr);

mFile=新文件(getContext().getExternalFilesDir(null),' pic。jpg’);

}

@覆盖

受保护的测量时无效(int width measurespec,int heightMeasureSpec) {

超级棒。on measure(widthMeasureSpec,heightsmeasurespec);

int width=测量规格。getsize(widthMeasureSpec);

int height=测量规格。getsize(heightsmeasurespec);

if(0==mrationwidth | | 0==mrationheight){

setMeasuredDimension(宽度,高度);

}否则{

如果(宽度高度* mrationwidth/mrationheight){

setMeasuredDimension(width,width * mrationheight/mrationwidth);

}否则{

setMeasuredDimension(height * m ration width/m ration height,height);

}

}

}

公共简历无效(活动活动){

this.activity=活动

startBackgroundThread();

//当活动或简历上的片段()时,可以冲洗打开一个相机并开始预览,否则,这个表面已经准备就绪

if (this.isAvailable()) {

openCamera(this.getWidth()、this。getheight());

}否则{

这个。setsurfaceetexturelistener(msurfaceetexturelistener);

}

}

public void onPause() {

关闭摄像头();

stopBackgroundThread();

}

public void setOutPutDir(File File){

this.mFile=file

}

public void setaspectation(int width,int height) {

如果(宽度0 ||高度0) {

抛出新的IllegalArgumentException(' Size不能为负');

}

比率宽度=宽度;

mRatioHeight=高度;

请求布局();

}

public void setAutoFlash(捕获请求.构建器请求构建器){

if (mFlashSupported) {

请求生成器。设置(捕获请求.控制_ AE _模式,

捕获请求. CONTROL _ AE _ MODE _ ON _ AUTO _ FLASH);

}

}

公共空的拍照(){

锁定焦点();

}

private void startBackgroundThread(){

mBackgroundThread=新的处理程序线程('相机背景');

mbackgroundthread。start();

mBackgroundHandler=新处理程序(mbackgroundthread。get looper());

}

private void stopBackgroundThread(){

mbackgroundthread。安全退出();

尝试{

mbackgroundthread。join();

mBackgroundThread=null

mBackgroundHandler=null

} catch (InterruptedException e) {

e。printstacktrace();

}

}

/**

* 处理生命周期内的回调事件

*/

私有最终纹理视图表面纹理监听器msurfaceetexture监听器=新纹理视图.SurfaceTextureListener() {

@覆盖

public void onsurfaceetextureavailable(表面纹理纹理,整数宽度,整数高度){

开放式摄像机(宽度、高度);

}

@覆盖

public void onSurfaceTextureSizeChanged(表面纹理纹理,整数宽度,整数高度){

配置转换(宽度、高度);

}

@覆盖

public boolean onSurfaceTextureDestroyed(表面纹理纹理){

返回真实的

}

@覆盖

公共void onSurfaceTextureUpdated(表面纹理纹理){

}

};

/**

* 相机状态改变回调

*/

私人最终摄像设备状态回调mStateCallback=新的摄像机设备。StateCallback() {

@覆盖

打开的(@非空相机设备相机设备)上的公共void {

mcameraopencloselock。发布();

Log.d(标签,'相机已打开');

mCameraDevice=cameraDevice

createCameraPreviewSession();

}

@覆盖

不连贯的上的公共void(@非空相机设备相机设备){

mcameraopencloselock。发布();

摄像设备。close();

mCameraDevice=null

}

@覆盖

public void on error(@ NonNull相机设备相机设备,int error) {

mcameraopencloselock。发布();

摄像设备。close();

mCameraDevice=null

if (null!=活动){

活动。finish();

}

}

};

/**

* 处理与照片捕获相关的事件

*/

私人摄影师CaptureCallback mCaptureCallback=新摄像机捕获会话.CaptureCallback() {

私有空的流程(捕获结果结果){

开关(状态){

案例状态_预览:{

打破;

}

案例状态_等待_锁定:{

整数af状态=结果。获取(捕获结果.控制_ AF _状态);

if (afState==null) {

captureStillPicture();

} else if (CaptureResult .控制_自动对焦_状态_聚焦_锁定==自动对焦状态| |

捕获结果. CONTROL _ AF _ STATE _ NOT _ FOCUSED _ LOCKED==AF状态){

整数AE状态=结果。获取(捕获结果.CONTROL _ AE _ STATE);

if(ea state==null | | ea state==捕获结果.控制_ AE _状态_收敛){

mState=STATE _ PICTURE _ TAKEN

captureStillPicture();

}否则{

runPreCaptureSequence();

}

}

打破;

}

案例状态_等待_预捕获:{

整数AE状态=结果。获取(捕获结果.CONTROL _ AE _ STATE);

if (aeState==null ||

aeState==CaptureResult .控制_ AE _状态_预先捕获||

aeState==CaptureRequest .控制_ AE _状态_闪存_必需){

mState=STATE _ WAITING _ NON _ PRE _ CAPTURE;

}

打破;

}

案例状态_等待_非_预捕获:{

整数AE状态=结果。获取(捕获结果.CONTROL _ AE _ STATE);

if (aeState==null || aeState!=捕获结果。控制_ AE _状态_预捕获){

mState=STATE _ PICTURE _ TAKEN

captureStillPicture();

}

打破;

}

}

}

@覆盖

public void on captureprogressed(@ NonNull camera capturesession会话,

@NonNull CaptureRequest请求,

@非空捕获结果部分结果){

过程(部分结果);

}

@覆盖

捕获时的公共void已完成(@ NonNull CameraCaptureSession会话,

@NonNull CaptureRequest请求,

@NonNull TotalCaptureResult结果){

过程(结果);

}

};

/**

* 在确定相机预览大小后应调用此方法

*

* @param viewWidth宽

* @param viewHeight高

*/

私有void配置转换(int view width,int viewHeight) {

if(null==mPreviewSize | | null==activity){

返回;

}

int旋转=活动。getwindowmanager().getDefaultDisplay().get rotation();

Matrix Matrix=new Matrix();

RectF viewRect=new RectF(0,0,viewWidth,view height);

RectF bufferRect=new RectF(0,0,mPreviewSize.getHeight()、mPreviewSize。getwidth());

float centex=view rect。centex();

float centey=view rect。centerY();

如果(表面10 .旋转_90==旋转||曲面. ROTATION_270==rotation) {

缓冲矩形。偏移量(百分比-缓冲矩形。centex()、centey-缓冲区矩形。centerY());

matrix.setRectToRect(viewRect,bufferRect,matrix .ScaleToFit。填充);

float scale=Math.max(

(浮点型)视图高度/mpreviewsize。获取高度(),

(浮点型)视图宽度/mpreviewsize。getwidth());

matrix.postScale(Scale,Scale,centerX,centerY);

matrix.postRotate(90 *(旋转- 2)、centex、centey);

} else if(表面. ROTATION_180==rotation) {

matrix.postRotate(180,centex,centey);

}

this.setTransform(矩阵);

}

/**

* 根据麦卡梅雷德打开相机

*/

private void openCamera(int width,int height) {

设置照相机输出(宽度、高度);

配置转换(宽度、高度);

camera manager manager=(camera manager)获取上下文().获取系统服务(上下文。相机_服务);

尝试{

如果(!mcameraopencloselock。尝试获取(2500,时间单位。毫秒)){

抛出新的RuntimeException('等待锁定相机打开超时');

}

如果(活动兼容。checkselpermission(activity,Manifest.permission.CAMERA)!=包管理器.PERMISSION_GRANTED) {

//TODO:考虑调用

//活动比较#请求权限

//此处请求缺少的权限,然后重写

//public void onrequestpermissions结果(int请求代码,String[]权限,

//int[] grantResults)

//处理用户授予权限的情况。参见文档

//对于活动compat #请求权限了解更多详细信息。

返回;

}

manager.openCamera(mCameraId,mStateCallback,mBackgroundHandler);

} catch(CameraAccessException e){

e。printstacktrace();

} catch (InterruptedException e) {

抛出新的RuntimeException('在试图锁定摄像机打开时被中断. e);

}

}

/**

* 关闭相机

*/

私有void closeCamera() {

尝试{

mcameraopencloselock。获取();

if (null!=mCaptureSession) {

mcapturesession。close();

mCaptureSession=null

}

if (null!=mCameraDevice) {

mcameradevice。close();

mCameraDevice=null

}

if (null!=mImageReader) {

mimage阅读器。close();

mImageReader=null

}

} catch (InterruptedException e) {

抛出新的RuntimeException('在尝试锁定摄像机关闭时被中断. e);

}最后{

mcameraopencloselock。发布();

}

}

/**

* 设置相机相关的属性或变量

*

* @param宽度相机预览的可用尺寸的宽度

* @param高度相机预览的可用尺寸的高度

*/

@隐藏警告('可疑的组合')

private void setUpCameraOutputs(int width,int height) {

camera manager manager=(camera manager)获取上下文().获取系统服务(上下文。相机_服务);

尝试{

for(字符串相机id:manager。getcameraidlist()){

camera characteristics characters=经理。getcameracharacteristics(cameraId);

//在这个例子中不使用前置摄像头

integer facing=字符。获取(CameraCharacteristics .镜头_朝向);

如果(面对!=零朝向==摄像机特性.LENS_FACING_FRONT) {

继续;

}

StreamConfigurationMap map=characters。获取(CameraCharacteristics .缩放器_流_配置_映射);

if (map==null) {

继续;

}

最大尺寸=集合。最大(数组。作为列表(地图。获取输出尺寸(图像格式.JPEG)),

new CompareSizesByArea());

mimage reader=imagereader。新实例(最大。getwidth()、largest.getHeight(),

图像格式。JPEG,/* max images */2);

mimagereader。setonimageavailablelistener(

mOnImageAvailableListener,mBackgroundHandler);

int显示旋转=活动。getwindowmanager().getDefaultDisplay().get rotation();

//无检查常数条件

m传感器方向=字符。获取(CameraCharacteristics .传感器_方位);

布尔交换尺寸=假;

开关(显示旋转){

表壳表面。旋转_0:

表壳表面。旋转_180:

如果(m传感器方向==90 | | m传感器方向==270){

swappedDimensions=true

}

打破;

表壳表面。旋转_90度:

表壳表面。旋转_270:

如果(m传感器方向==0 | | m传感器方向==180){

swappedDimensions=true

}

打破;

默认值:

Log.e(标签,'显示旋转无效: '显示旋转');

}

点显示大小=新点();

activity.getWindowManager().getDefaultDisplay().getSize(显示大小);

int rotatedPreviewWidth=宽度

int rotatedPreviewHeight=height;

int maxPreviewWidth=displaysize。x;

int maxPreviewHeight=displaysize。y;

if (swappedDimensions) {

rotatedPreviewWidth=height

rotatedPreviewHeight=宽度

maxPreviewWidth=displaySize.y

maxPreviewHeight=displaysize。x;

}

if(maxPreviewWidth MAX _ PREVIEW _ WIDTH){

MAX PREVIEW WIDTH=MAX _ PREVIEW _ WIDTH;

}

if(maxPreviewHeight MAX _ PREVIEW _ HEIGHT){

MAX PREVIEW HEIGHT=MAX _ PREVIEW _ HEIGHT;

}

mPreviewSize=chooseOptimalSize(map。获取输出尺寸(表面纹理。类),

rotatedPreviewWidth,rotatedPreviewHeight,maxPreviewWidth,

maxPreviewHeight,最大);

int orientation=getResources().获取配置().定向;

如果(方向==配置。方向_横向){

setAspectRatio(mpreviewsize。getwidth()、mpreviewsize。getheight());

}否则{

setAspectRatio(mpreviewsize。getheight()、mpreviewsize。getwidth());

}

布尔可用=字符。获取(CameraCharacteristics .FLASH _ INFO _ AVAILABLE);

mFlashSupported=available==null?假:可用;

mCameraId=cameraId

返回;

}

} catch(CameraAccessException e){

e。printstacktrace();

} catch (NullPointerException e) {

Log.e(标签,'设备不支持摄像机2’);

}

}

/**

* 获取一个合适的相机预览尺寸

*

* @param选择支持的预览尺寸列表

* @param textureViewWidth相对宽度

* @param textureViewHeight相对高度

* @param maxWidth可以选择的最大宽度

* @param maxHeight可以选择的最大高度

* @ param aspectRatio宽高比

* @返回最佳预览尺寸

*/

私有静态大小chooseOptimalSize(Size[]choices,int textureViewWidth,int textureViewHeight,

int maxWidth,int maxHeight,Size as spec ratio){

列表大小足够=new ArrayList();

列表大小不大足够=new ArrayList();

int w=aspectratio。getwidth();

int h=aspectratio。获取height();

对于(尺寸选项:选择){

if(选项。getwidth()=maxWidth选项。getheight()=maxHeight

选项。getheight()==选项。getwidth()*高/宽){

if(选项。getwidth()=纹理视图宽度

选项。getheight()=纹理视图高度){

足够大. add(选项);

}否则{

notBigEnough.add(选项);

}

}

}

如果(bigovernment。size()0){

返回收藏。min(big ough,new CompareSizesByArea());

} else if (notBigEnough.size() 0) {

返回收藏。max(不够大,new CompareSizesByArea());

}否则{

Log.e(标签,'找不到任何合适的预览大小');

返回选择[0];

}

}

/**

* 为相机预览创建新的照相机拍摄

*/

私有void createCameraPreviewSession(){

尝试{

表面纹理纹理=这个。get surface texture();

断言纹理!=空

//将默认缓冲区的大小配置为想要的相机预览的大小

质感。setdefaultbuffersize(mpreviewsize。getwidth()、mpreviewsize。getheight());

表面=新表面(纹理);

mPreviewRequestBuilder=mcameradevice。createcapturerequest(摄像机设备.模板_预览);

mpreviewrequestbuilder。添加目标(表面);

//我们创建一个照相机拍摄来进行相机预览

mcameradevice。createcapturesession(数组。as list(surface,mImageReader.getSurface()),

新的照相机拍摄.StateCallback() {

@覆盖

已配置的公共void(@ NonNull CameraCaptureSession CameraCaptureSession){

if (null==mCameraDevice) {

返回;

}

//会话准备好后,我们开始显示预览

mCaptureSession=camera capture session;

尝试{

mpreviewrequestbuilder。set(CaptureRequest .控制_自动对焦_模式,

捕获请求。控制_ AF _模式_连续_图片);

setAutoFlash(mPreviewRequestBuilder);

mPreviewRequest=mpreviewrequestbuilder。build();

mcapturesession。set repeating request(mPreviewRequest,mCaptureCallback,mBackgroundHandler);

} catch(CameraAccessException e){

e。printstacktrace();

}

}

@覆盖

配置时公共void失败(@ NonNull CameraCaptureSession CameraCaptureSession){

}

},null);

} catch(CameraAccessException e){

e。printstacktrace();

}

}

/**

* 从指定的屏幕旋转中检索照片方向

*

* @param旋转屏幕方向

* @返回照片方向(0,90,270,360)

*/

私有int get orientation(int rotation){

返回(方向。get(旋转)m传感器方位270)% 360;

}

/**

* 锁定焦点

*/

私有void lockFocus() {

尝试{

//如何通知相机锁定焦点

mpreviewrequestbuilder。set(CaptureRequest .控制_自动对焦_触发器,照相机元数据.控制_自动对焦_触发_启动);

//通知mCaptureCallback等待锁定

mState=状态_等待_锁定

mcapturesession。捕获(mpreviewrequestbuilder。build()、mCaptureCallback、mBackgroundHandler);

} catch(CameraAccessException e){

e。printstacktrace();

}

}

/**

* 解锁焦点

*/

私有void unlockFocus() {

尝试{

mpreviewrequestbuilder。set(CaptureRequest .控制_自动对焦_触发,

照相机数据.控制_ AF _触发_取消);

setAutoFlash(mPreviewRequestBuilder);

mcapturesession。捕获(mpreviewrequestbuilder。build()、mCaptureCallback、

mBackgroundHandler);

mState=STATE _ PREVIEW

mcapturesession。set repeating request(mPreviewRequest,mCaptureCallback,

mBackgroundHandler);

} catch(CameraAccessException e){

e。printstacktrace();

}

}

/**

* 拍摄静态图片

*/

私有void捕捉静态图片(){

尝试{

if(null==activity | | null==mCameraDevice){

返回;

}

最终捕获请求。生成器捕获生成器=

mCameraDevice.createCapture

Request(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mImageReader.getSurface()); captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); setAutoFlash(captureBuilder); // 方向 int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { Toast.makeText(getContext(), "Saved: " + mFile, Toast.LENGTH_SHORT).show(); Log.d(TAG, mFile.toString()); unlockFocus(); } }; mCaptureSession.stopRepeating(); mCaptureSession.abortCaptures(); mCaptureSession.capture(captureBuilder.build(), captureCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 运行preCapture序列来捕获静止图像 */ private void runPreCaptureSequence() { try { // 设置拍照参数请求 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); mState = STATE_WAITING_PRE_CAPTURE; mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 比较两者大小 */ private static class CompareSizesByArea implements Comparator<Size> { @Override public int compare(Size lhs, Size rhs) { return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } } /** * ImageReader的回调对象 */ private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); } }; /** * 将捕获到的图像保存到指定的文件中 */ private static class ImageSaver implements Runnable { private final Image mImage; private final File mFile; ImageSaver(Image image, File file) { mImage = image; mFile = file; } @Override public void run() { ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); FileOutputStream output = null; try { output = new FileOutputStream(mFile); output.write(bytes); } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); if (null != output) { try { output.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }

MainActivity.java

public class MainActivity extends AppCompatActivity { CameraPreview cameraView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); cameraView = (CameraPreview) findViewById(R.id.cameraView); } @Override protected void onResume() { super.onResume(); cameraView.onResume(this); } @Override protected void onPause() { cameraView.onPause(); super.onPause(); } public void takePic(View view) { cameraView.takePicture(); } }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" tools:context="com.shenhua.ocr.activity.Main2Activity"> <com.shenhua.ocr.widget.CameraPreview android:id="@+id/cameraView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:layout_width="70dp" android:layout_height="70dp" android:layout_gravity="center" android:background="@drawable/ic_capture_200px" android:onClick="takePic" android:text="TAKE" app:layout_constraintBottom_toBottomOf="@id/constraintLayout" app:layout_constraintEnd_toEndOf="@id/constraintLayout" app:layout_constraintStart_toStartOf="@id/constraintLayout" app:layout_constraintTop_toTopOf="@id/cameraView" app:layout_constraintVertical_bias="0.97" /> </android.support.constraint.ConstraintLayout>

资源文件 ic_capture_200px.xml

<vector android:height="24dp" android:viewportHeight="1024.0" android:viewportWidth="1024.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="#03A9F4" android:pathData="M512,512m-393.8,0a393.8,393.8 0,1 0,787.7 0,393.8 393.8,0 1,0 -787.7,0Z"/> <path android:fillColor="#03A9F4" android:pathData="M512,1024C229.2,1024 0,794.8 0,512S229.2,0 512,0s512,229.2 512,512 -229.2,512 -512,512zM512,984.6c261,0 472.6,-211.6 472.6,-472.6S773,39.4 512,39.4 39.4,251 39.4,512s211.6,472.6 472.6,472.6z"/> </vector>

其它

Manifest 权限:

<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" />

Android6.0 运行时权限未贴出。(注意:为了方便读者手机端阅读,本文代码部分的成员变量使用了行尾注释,在正常编程习惯中,请使用 /* / 注释。)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: