精华内容
下载资源
问答
  • android相机
    千次阅读
    2018-04-14 19:42:52


    版权声明:本文使用https://creativecommons.org/licenses/by-nc-nd/4.0/规定的《署名-非商业性使用-禁止演绎 4.0 国际》协议 https://blog.csdn.net/bluewindtalker/article/details/54563910

    相机开发现在有2个类,分别为android.hardware.camera2和android.hardware.Camera,其中Camera类官方已经不推荐,不过鉴于有前人踩坑了,为了快速开发也就直接拿来用了

    This class was deprecated in API level 21.
    We recommend using the new android.hardware.camera2 API for new applications.

    关于旧版的Camera类,google官方给了下面的指导步骤,https://developer.android.com/reference/android/hardware/Camera.html

    To take pictures with this class, use the following steps:


    看了这些后我们可以简单的进行实战,首先是初始化camera的过程。

        /**
         * 初始化照片
         */
        private void initCamera() {
            if (camera != null) {
                camera.startPreview();
            }
            Log.e(TAG, "initCamera");
            //1. Obtain an instance of Camera from open(int).
            //这里可以根据前后摄像头设置
            camera = openCamera(currentCameraType);
            if (camera == null) {
                return;
            }
            //2. Get existing (default) settings with getParameters().
            //获得存在的默认配置属性
            Camera.Parameters parameters = camera.getParameters();
    
            //3. If necessary, modify the returned Camera.Parameters object and call setParameters(Camera.Parameters).
            //可以根据需要修改属性,这些属性包括是否自动持续对焦、拍摄的gps信息、图片视频格式及大小、预览的fps、
            // 白平衡和自动曝光补偿、自动对焦区域、闪光灯状态等。
            //具体可以参阅https://developer.android.com/reference/android/hardware/Camera.Parameters.html
            if (parameters.getSupportedFocusModes().contains(Camera.Parameters
                    .FOCUS_MODE_CONTINUOUS_PICTURE)) {
                //自动持续对焦
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            }
            //在设置图片和预览的大小时要注意当前摄像头支持的大小,不同手机支持的大小不同,如果你的SurfaceView不是全屏,有可能被拉伸。
            // parameters.getSupportedPreviewSizes(),parameters.getSupportedPictureSizes()
            List<Camera.Size> picSizes = parameters.getSupportedPictureSizes();
            Resources resources = this.getResources();
            DisplayMetrics dm = resources.getDisplayMetrics();
            float density = dm.density;
            int width = dm.widthPixels;
            int height = dm.heightPixels;
            Camera.Size picSize = getPictureSize(picSizes, width, height);
            parameters.setPictureSize(picSize.width, picSize.height);
            camera.setParameters(parameters);
            //4. Call setDisplayOrientation(int) to ensure correct orientation of preview.
            //你可能会遇到画面方向和手机的方向不一致的问题,竖向手机的时候,但是画面是横的,这是由于摄像头默认捕获的画面横向的
            // 通过调用setDisplayOrientation来设置PreviewDisplay的方向,可以解决这个问题。
            setCameraDisplayOrientation(this, currentCameraType, camera);
    
            //5. Important: Pass a fully initialized SurfaceHolder to setPreviewDisplay(SurfaceHolder).
            // Without a surface, the camera will be unable to start the preview.
            //camera必须绑定一个surfaceview才可以正常显示。
            try {
                camera.setPreviewDisplay(displaySfv.getHolder());
            } catch (IOException e) {
                e.printStackTrace();
            }
            //6. Important: Call startPreview() to start updating the preview surface.
            // Preview must be started before you can take a picture.
            //在调用拍照之前必须调用startPreview()方法,但是在此时有可能surface还未创建成功。
            // 所以加上SurfaceHolder.Callback(),在回调再次初始化下。
            camera.startPreview();
            //7. When you want, call
            // takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)
            // to capture a photo. Wait for the callbacks to provide the actual image data.
            //当如果想要拍照的时候,调用takePicture方法,这个下面我们会讲到。
    
            //8. After taking a picture, preview display will have stopped. To take more photos, call startPreview() again first.
            //在拍照结束后相机预览将会关闭,如果要再次拍照需要再次调用startPreview()
    
            //9. Call stopPreview() to stop updating the preview surface.
            //通过调用stopPreview方法可以结束预览
            //10. Important: Call release() to release the camera for use by other applications.
            // Applications should release the camera immediately in onPause()(and re-open() it in onResume()).
            //建议在onResume调用open的方法,在onPause的时候执行release方法
        }
    根据上文提到的第9、10步骤我们在onResume与onPause做处理。
    @Override
        protected void onResume() {
            super.onResume();
            Log.e(TAG, "onResume");
            if (!isRequestPermission) {
                checkAndInitCamera();
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            Log.e(TAG, "onPause");
            releaseCamera();
    
        }
    
        private void releaseCamera() {
            if (camera != null) {
                camera.stopPreview();
                camera.release();
                camera = null;
            }
        }

    其中checkAndInitCamera()为权限处理的方法

    private void checkAndInitCamera() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // 检查该权限是否已经获取
                int i = ContextCompat.checkSelfPermission(this, permissions[0]);
                // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝
                if (i != PackageManager.PERMISSION_GRANTED) {
                    // 如果没有授予该权限,就去提示用户请求
                    isRequestPermission = true;
                    ActivityCompat.requestPermissions(this, permissions, CAMERA_PERMISSION_CODE);
                } else {
                    initCamera();
                }
            } else {
                initCamera();
            }
        }
    这里有些细节需要注意,在 parameters.setPictureSize(int  width , int  height ); 这个方法的时候
    不能将宽高随意写,必须从 parameters.getSupportedPictureSizes(); 中选择最合适的宽高,否则会出现setParameters failed的运行时错误。

    而系统提供的宽高是根据摄像头的参数定的,这个导致需要根据手机和surfaceview宽高来动态适配,否则可能会出现图像失真拉伸压缩的情况,在本文中将直接使用最接近的摄像头像素的算法

    /**
         * 获得最合是的宽高size
         */
        private Camera.Size getPictureSize(List<Camera.Size> picSizes, int width, int height) {
            Camera.Size betterSize = null;
            int diff = Integer.MAX_VALUE;
            if (picSizes != null && picSizes.size() > 0) {
                for (Camera.Size size : picSizes) {
                    int newDiff = Math.abs(size.width - width) + Math.abs(size.height - height);
                    if(newDiff == 0){
                        return size;
                    }
                    if (newDiff < diff) {
                        betterSize = size;
                        diff = newDiff;
                    }
                }
            }
            return betterSize;
        }
    还有一个细节是摄像头并不是正的,调用的方法
    setCameraDisplayOrientation
    //设置相机的方向
        public int setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
            android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
            android.hardware.Camera.getCameraInfo(cameraId, info);
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            int degrees = 0;
            switch (rotation) {
                case Surface.ROTATION_0:
                    degrees = 0;
                    break;
                case Surface.ROTATION_90:
                    degrees = 90;
                    break;
                case Surface.ROTATION_180:
                    degrees = 180;
                    break;
                case Surface.ROTATION_270:
                    degrees = 270;
                    break;
                default:
                    degrees = 0;
                    break;
            }
            int result;
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                result = (info.orientation + degrees) % 360;
                result = (360 - result) % 360;   // compensate the mirror
            } else {
                // back-facing
                result = (info.orientation - degrees + 360) % 360;
            }
            camera.setDisplayOrientation(result);
            return degrees;
        }
    https://blog.csdn.net/bluewindtalker/article/details/54563910

    这么做运行的时候,我们发现预览图并不能正常显示出来,这是因为surface还没有正常创建出来,这时候我们可以在initCamera方法中加入如下代码,坚挺SurfaceHolder的事件回调

    SurfaceHolder holder = displaySfv.getHolder();
            if (holder != null) {
                holder.addCallback(new SurfaceHolder.Callback() {
                    @Override
                    public void surfaceCreated(SurfaceHolder holder) {
                        Log.e(TAG, "surfaceCreated" + holder);
                        checkAndInitCamera();
                    }
    
                    @Override
                    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                        Log.e(TAG, "surfaceChanged" + holder);
                    }
    
                    @Override
                    public void surfaceDestroyed(SurfaceHolder holder) {
                        Log.e(TAG, "surfaceDestroyed" + holder);
                    }
                });
            }

    以下就是重点了,拍照,拍照就是触发一个回调事件方法。

        /**
         * 拍摄照片
         */
        private void takePicture() {
            picIV.setImageBitmap(null);
            if (camera == null) {
                return;
            }
            //如果不加第一个回调,手机会没有拍照音效,第二个回调是返回raw格式图片,
            // 了解过相机的人可能知道这是原图的意思,这个我们不处理,我们处理第三个回调,jpg格式的数据
            // 拍摄照片
            camera.takePicture(new Camera.ShutterCallback() {
                @Override
                public void onShutter() {
                }
            }, null, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    // 将拍照数据data数组转化为Bitmap,这里应该放到线程执行了,这里为了简单处理直接放UI线程了
                    Bitmap imageBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                    //一般手机需要旋转90度来适应方向,如果setCameraDisplayOrientation得到的结果不是90度,一般还需要再次旋转180
                    picIV.setImageBitmap(rotate(imageBitmap, 90));
                    picFl.setVisibility(View.VISIBLE);
                }
            });
        }
    
        public Bitmap rotate(Bitmap bitmap, int degree) {
            Matrix matrix = new Matrix();
            matrix.postRotate(degree);
            return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
        }
    

     以下是布局文件内容

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout 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="com.bluewindtalker.camera.demo.CameraActivity">
    
        <SurfaceView
            android:id="@+id/sf_display_demo_camera"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <Button
            android:id="@+id/btn_take_picture_demo_camera"
            android:layout_width="150dp"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal|bottom"
            android:layout_marginBottom="70dp"
            android:background="@android:color/black"
            android:gravity="center"
            android:text="拍照"
            android:textColor="@android:color/white"
            android:textSize="20sp" />
    
        <FrameLayout
            android:id="@+id/fl_picture_demo_camera"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black"
            android:visibility="gone">
    
            <ImageView
                android:id="@+id/iv_picture_demo_camera"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="fitCenter" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:background="#66666666"
                android:padding="20dp"
                android:text="点击任意区域关闭照片"
                android:textColor="@android:color/white"
                android:textSize="18sp" />
        </FrameLayout>
    </FrameLayout>

    下篇将讲解如何通过摄像头识别周围环境光亮强度

    [Android相机]光线传感器识别环境光亮强度

    [Android相机]通过手机摄像头识别环境亮度


    更多相关内容
  • 本章节主要讲述的Android相机Camera的相关知识点,主要包含三个方面,Android启动系统相机生成缩略图、Android启动系统相机生成原图以及Android自定义相机等。 二、Android启动系统相机 核心代码: package ...
  • Android上实现一个简单能用的相机其实挺容易。谷歌随便搜一搜就有很多能用的Sample。当然就像谷歌能搜到的其他代码一样,这些Sample虽然能用但离好用还很远。 这篇文章就只说说从用户点击启动按钮到用户能看到实时...
  • 我的博客里面有博文详细讲解,并且关于android中图片加载的讲解也写得很详细,欢迎大家可以来看!多提意见 我的博客里面有博文详细讲解,并且关于android中图片加载的讲解也写得很详细,欢迎大家可以来看!多提意见
  • 今天小编就为大家分享一篇Android 相机相册权限设置,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • Android相机开发详解(一)Android相机开发详解(一)请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客)Android相机开发能够实现打开相机,前后摄像头切换,摄像预览...

    Android相机开发详解(一)

    Android相机开发详解(一)

    请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客)

    Android相机开发能够实现打开相机,前后摄像头切换,摄像预览,保存图片,浏览已拍照图片等相机功能。

    Android相机开发详解(一)主要实现打开相机,摄像预览,前后置摄像头切换,保存图片等四个功能。

    Android相机开发详解(二)主要实现翻页浏览相片,触控缩放浏览图片,删除图片,发送图片等四个功能。

    Android相机开发详解(三)主要实现录像,视频保存,自动对焦,闪光灯控制等四个功能

    效果图:

    2311251019.png

    1、CameraActivity的布局文件,使用FrameLayout布局(activity_camera.xml)

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context=".CameraActivity" >

    android:id="@+id/fragmentContainer_camera"

    android:layout_width="match_parent"

    android:layout_height="match_parent" />

    2、CameraActivity相机不存在的布局文件(activity_no_camera.xml)

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context=".CameraActivity" >

    android:gravity="center"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:text="未检查到本地有摄像头" />

    3、CameraActivity类(CameraActivity.java)

    a)请求窗口特性:无标题

    b)添加窗口特性:全屏

    c)检查摄像头是否存在。根据检查结果进行布局

    package com.example.camerademo;

    import android.annotation.SuppressLint;

    import android.app.Activity;

    import android.content.Intent;

    import android.content.pm.PackageManager;

    import android.hardware.Camera;

    import android.os.Build;

    import android.os.Bundle;

    import android.support.v4.app.FragmentActivity;

    import android.view.Window;

    import android.view.WindowManager;

    import android.widget.Toast;

    public class CameraActivity extends FragmentActivity {

    private final static int REQUEST_DELETE_PHOTO = 1;

    @SuppressLint("NewApi")

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    // TODO Auto-generated method stub

    //请求窗口特性:无标题

    requestWindowFeature(Window.FEATURE_NO_TITLE);

    //添加窗口特性:全屏

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

    super.onCreate(savedInstanceState);

    //检查摄像头是否存在。

    PackageManager pm = getPackageManager();

    boolean hasCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) ||

    pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) ||

    Build.VERSION.SDK_INT>Build.VERSION_CODES.GINGERBREAD ||

    Camera.getNumberOfCameras()>0;

    //根据检查结果进行布局

    if (!hasCamera) {

    setContentView(R.layout.activity_no_camera);

    return;

    }

    setContentView(R.layout.activity_camera);

    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();

    android.support.v4.app.Fragment fragment = fm.findFragmentById(R.id.fragmentContainer_camera);

    if (fragment==null) {

    fragment = new CameraFragment();

    fm.beginTransaction().add(R.id.fragmentContainer_camera, fragment).commit();

    }

    }

    }

    4、CameraFragment的布局文件(fragment_camera.xml)

    a)使用FrameLayout布局,双层布局

    b)顶层是一个SurfaceView,用来拍照预览

    c)底层是FrameLayout布局,包含两个ImageButton,一个ProgressBar进度条控件

    xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    android:id="@+id/camera_surfaceView"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:layout_weight="1"/>

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:clickable="true">

    android:id="@+id/camera_rotationview_button"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="left|top"

    android:layout_marginTop="16dp"

    android:layout_marginLeft="16dp"

    android:background="@drawable/button_camera_rotationview"

    />

    android:id="@+id/camera_takepicture_button"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="right|top"

    android:layout_marginTop="16dp"

    android:layout_marginRight="16dp"

    android:background="@drawable/button_camera_takepicture"

    />

    android:id="@+id/camera_view_button"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="right|bottom"

    android:layout_marginBottom="16dp"

    android:layout_marginRight="16dp"

    android:background="@drawable/button_camera_view"

    />

    android:id="@+id/camera_progressContainer"

    style="@android:style/Widget.ProgressBar.Large"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center"

    />

    5、CameraFragment类(CameraFragment.java)

    a)相机是一种系统级别的重要资源,因此,很重要一点:需要时使用,用完及时释放。如果忘记释放,除非重启设备,否则其他应用将无法使用相机。

    b)保险起见,我们在onResume()方法中打开相机,在onPause()方法中释放相机。Camera类中打开相机的方法有两个。

    i.         open()方法在API第9级以下的级别使用。

    ii.         open(int)方法在第9级及第9级以上的级别使用,传入参数0开打设备可用的第一相机(通常指的是后置相机),传入参数1开打设备可用的第二相机(通常指的是前置相机)

    c)使用SurfaceView类配合相机来实现摄像预览。需要实现SurfaceHolder接口,并设置Surface类型:setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)。但该方法和该常量在API第11级别就已经被弃用。我们使用@SuppressWarnings("deprecation")注解来消除弃用代码的相关警告。

    d)SurfaceHolder是我们与Surface对象联系的纽带。Surface对象代表着原始像素数据的缓冲区。SurfaceView出现在屏幕上时,会创建Surface,这时我们需要把Camera连接到SurfaceHolder上;SurfaceView从屏幕上消失时,Surface随机被销毁,我们再将Camera从SurfaceHolder断开。注意:Surface不存在时,必须保证没有任何内容要在它上面绘制。理想工作状态如图:

    2311251020.png

    e)为了完成以上任务,SurfaceHolder提供了另一个接口:SurfaceHolder.Callback。该接口有三个方法用来监听Surface生命周期中的事件。

    i.         包含SurfaceView的视图层级结构被放到屏幕上时调用该方法。也是与Camera进行关联的地方。

    public voidsurfaceCreated(SurfaceHolder surfaceHolder)

    ii.         Surface首次显示在屏幕上时调用该方法。通过传入的参数,可以知道Surface的像素格式以及它的宽度和高度。

    public voidsurfaceChanged(SurfaceHolder holder, int format, int w, int h)

    iii.         SurfaceView从屏幕上移除时,Surface也随即被销毁。也是Camera与其断开关联的地方

    public voidsurfaceDestroyed(SurfaceHolder holder)

    f)为了能与SurfaceView无缝贴合,Camera也提供了不同的方法。

    i.         为了连接Camera和Surface,设置Surface被实时预览使用setPreviewDisplay(SurfaceHolderholder)

    ii.         开始捕捉和绘制预览帧到屏幕上

    startPreview()

    iii.         停止捕捉和绘制预览帧到屏幕上

    stopPreview()

    g)使用Camera的内部类Camera.Parameters来确定预览图片和保存图片的大小

    i.         getParameters()返回这个相机的当前参数设置。

    ii.             setParameters(Camera.Parametersparams)改变这个相机的当前参数设置。

    iii.         然后自定义一个找出设备支持的最佳尺寸的方法getBestSupportedSize(List sizes,int width,int height),接受一组预览尺寸,然后穷举法找出具有最大数目像素的尺寸。

    iv.         调用Camera.Parameters类的方法。

    1.        getSupportedPictureSizes()得到图片支持的尺寸

    2.        getSupportedPreviewSizes()得到预览图片支持的尺寸

    3.        setPictureSize(int

    width, int height)设置图片的大小尺寸

    4.setPreviewSize(int

    width, int height)设置预览图片的大小尺寸

    h)使用Camera类见名知意的拍照方法takePicture(Camera.ShutterCallbackshutter,Camera.PictureCallbackraw,Camera.PictureCallbackpostview,Camera.PictureCallbackjpeg)方法

    i.         该方法会触发一个异步的图像捕捉,触发相机初始化一系列的回调机制应用于图像捕捉进程中

    ii.         该方法有四个回调方法。Camera.ShutterCallbackshutter在图像被捕捉时刻触发,它会触发一个快门声音告知用户;PictureCallbackraw在未加工图像有效时触发;Camera.PictureCallbackpostview在被按比例缩放图像有效时触发;Camera.PictureCallbackjpeg在压缩图像有效时触发。

    iii.         该方法只有在startPreview()方法调用之后才有效。该方法调用之后预览效果将会停止,如果想要再次预览或者拍摄更多相片,需要再次调用startPreview()方法。

    package com.example.camerademo;

    import java.io.FileOutputStream;

    import java.io.IOException;

    import java.util.List;

    import android.annotation.SuppressLint;

    import android.app.Activity;

    import android.content.Context;

    import android.content.Intent;

    import android.hardware.Camera;

    import android.hardware.Camera.CameraInfo;

    import android.hardware.Camera.PictureCallback;

    import android.hardware.Camera.ShutterCallback;

    import android.hardware.Camera.Size;

    import android.os.Build;

    import android.os.Bundle;

    import android.support.v4.app.Fragment;

    import android.view.LayoutInflater;

    import android.view.SurfaceHolder;

    import android.view.SurfaceHolder.Callback;

    import android.view.SurfaceView;

    import android.view.View;

    import android.view.View.OnClickListener;

    import android.view.ViewGroup;

    import android.widget.ImageButton;

    public class CameraFragment extends Fragment{

    //startActivityForResult的请求常量

    private final static int REQUEST_DELETE_PHOTO = 1;

    //自定义时间类

    private MyTime mTime=new MyTime();

    //相机类

    private Camera mCamera;

    //预览视图的接口

    private SurfaceHolder mSurfaceHolder;

    //进度条控件

    private View mProgressContainer;

    //当前打开的是哪一个摄像头

    private int switchCamera=0;

    @Override

    public void onCreate(Bundle savedInstanceState) {

    // TODO Auto-generated method stub

    super.onCreate(savedInstanceState);

    }

    @SuppressWarnings("deprecation")

    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    // TODO Auto-generated method stub

    //生成fragment视图

    View v = inflater.inflate(R.layout.fragment_camera, container,false);

    //隐藏进度条控件

    mProgressContainer = v.findViewById(R.id.camera_progressContainer);

    mProgressContainer.setVisibility(View.INVISIBLE);

    //显示最新照片的缩略图的按钮实例化

    ImageButton viewButton = (ImageButton) v.findViewById(R.id.camera_view_button);

    //最新照片的缩略图的按钮监听器

    viewButton.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View arg0) {

    // TODO Auto-generated method stub

    // 跳转ViewPagerActivity,请求ViewPagerActivity执行删除图片操作

    Intent i = new Intent();

    i.setClass(getActivity(), ViewPagerActivity.class);

    startActivityForResult(i, REQUEST_DELETE_PHOTO);

    }

    });

    //切换镜头按钮实例化

    ImageButton rotationViewButton = (ImageButton) v.findViewById(R.id.camera_rotationview_button);

    //切换镜头按钮监听器

    rotationViewButton.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View arg0) {

    // TODO Auto-generated method stub

    //如果摄像头数目小于等于1,该按钮无效,返回

    if (Camera.getNumberOfCameras() <= 1) {

    return ;

    }

    if(switchCamera == 1) {

    //停掉原来摄像头的预览,并释放原来摄像头

    mCamera.stopPreview();

    mCamera.release();

    mCamera = null;

    //打开当前选中的摄像头

    switchCamera = 0;

    mCamera = Camera.open(switchCamera);

    try {

    //通过surfaceview显示取景画面

    mCamera.setPreviewDisplay(mSurfaceHolder);

    } catch (IOException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    //开始预览

    mCamera.startPreview();

    }else {

    //停掉原来摄像头的预览,并释放原来摄像头

    mCamera.stopPreview();

    mCamera.release();

    mCamera = null;

    //打开当前选中的摄像头

    switchCamera = 1;

    mCamera = Camera.open(switchCamera);

    try {

    //通过surfaceview显示取景画面

    mCamera.setPreviewDisplay(mSurfaceHolder);

    } catch (IOException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    //开始预览

    mCamera.startPreview();

    }

    }

    });

    //照相按钮实例化

    ImageButton takePictureButton = (ImageButton) v.findViewById(R.id.camera_takepicture_button);

    //照相按钮监听器

    takePictureButton.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View arg0) {

    // TODO Auto-generated method stub

    if (mCamera!=null) {

    //相机的拍照方法

    mCamera.takePicture(

    //第一个回调方法,快门回调方法

    new ShutterCallback() {

    @Override

    public void onShutter() {

    // TODO Auto-generated method stub

    //该方法回触发快门声音告知用户,并设置进度条显示

    mProgressContainer.setVisibility(View.VISIBLE);

    }

    }

    //第二个,第三个回调方法为空

    , null,null,

    //最后一个回调方法,jpg图像回调方法

    new PictureCallback() {

    @Override

    public void onPictureTaken(byte[] date, Camera camera) {

    // TODO Auto-generated method stub

    //根据当前时间自定义格式生成文件名

    String filename = mTime.getYMDHMS()+".jpg";

    //文件输出流

    FileOutputStream os = null;

    //默认文件保存成功

    boolean success = true;

    try {

    //私有打开应用沙盒文件夹下文件

    os = getActivity().openFileOutput(filename, Context.MODE_PRIVATE);

    //写文件

    os.write(date);

    } catch (Exception e) {

    // TODO: handle exception

    success = false;

    }finally{

    try {

    if (os != null) {

    os.close();

    }

    } catch (Exception e) {

    // TODO: handle exception

    success = false;

    }

    }

    if (success) {

    //如果文件保存成功,进度条隐藏

    mProgressContainer.setVisibility(View.INVISIBLE);

    //再次预览

    try {

    mCamera.startPreview();

    } catch (Exception e) {

    // TODO: handle exception

    mCamera.release();

    mCamera=null;

    }

    }

    }

    });

    }

    }

    });

    //预览视图实例化

    SurfaceView mSurfaceView = (SurfaceView) v.findViewById(R.id.camera_surfaceView);

    //得到预览视图接口

    mSurfaceHolder = mSurfaceView.getHolder();

    //设置预览视图接口类型

    mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    //添加预览视图接口的回调程序,监听视图的生命周期

    mSurfaceHolder.addCallback(new Callback() {

    @Override

    public void surfaceCreated(SurfaceHolder surfaceHolder) {

    // TODO Auto-generated method stub

    //当SurfaceView的视图层级结构被放到屏幕上时候,连接Camera和Surface

    try {

    if (mCamera!=null) {

    mCamera.setPreviewDisplay(surfaceHolder);

    }

    } catch (Exception e) {

    // TODO: handle exception

    }

    }

    @Override

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    // TODO Auto-generated method stub

    //当Surface首次显示在屏幕上时候,设置好相机参数,开始预览

    if (mCamera==null) {

    return;

    }

    Camera.Parameters parameters = mCamera.getParameters();

    Size s = getBestSupportedSize(parameters.getSupportedPreviewSizes(), w, h);

    parameters.setPreviewSize(s.width, s.height);

    s = getBestSupportedSize(parameters.getSupportedPictureSizes(), w, h);

    parameters.setPictureSize(s.width, s.height);

    mCamera.setParameters(parameters);

    try {

    mCamera.startPreview();

    } catch (Exception e) {

    // TODO: handle exception

    mCamera.release();

    mCamera=null;

    }

    }

    @Override

    public void surfaceDestroyed(SurfaceHolder holder) {

    // TODO Auto-generated method stub

    //当视图从屏幕上移除的时候,停止预览

    if (mCamera!=null) {

    mCamera.stopPreview();

    }

    }

    });

    return v;

    }

    /******************************************]

    *

    * 穷举法找出具有最大数目像素的尺寸

    *

    * @param sizes

    * @param width

    * @param height

    * @return

    */

    public Size getBestSupportedSize(List sizes,int width,int height) {

    Size bestSize = sizes.get(0);

    int largestArea = bestSize.width*bestSize.height;

    for (Size s :sizes) {

    int area =s.width*s.height;

    if (area>largestArea) {

    bestSize=s;

    largestArea = area;

    }

    }

    return bestSize;

    }

    //接收活动结果,响应startActivityForResult()

    @Override

    public void onActivityResult(int request, int result, Intent mIntent) {

    // TODO Auto-generated method stub

    if (request == REQUEST_DELETE_PHOTO) {

    if (result == Activity.RESULT_OK) {

    // 跳转ViewPagerActivity,请求ViewPagerActivity执行删除图片操作

    int requestCode = 1;

    Intent i = new Intent();

    i.setClass(getActivity(), ViewPagerActivity.class);

    startActivityForResult(i, requestCode);

    }

    }

    }

    @Override

    public void onPause() {

    // TODO Auto-generated method stub

    super.onPause();

    System.out.println("onPause");

    //程序中止暂停时,释放Camera

    if (mCamera!=null) {

    mCamera.release();

    mCamera=null;

    }

    }

    @Override

    public void onDestroy() {

    // TODO Auto-generated method stub

    super.onDestroy();

    System.out.println("onDestroy");

    }

    @Override

    public void onStop() {

    // TODO Auto-generated method stub

    super.onStop();

    System.out.println("onStop");

    }

    @SuppressLint("NewApi")

    @Override

    public void onResume() {

    // TODO Auto-generated method stub

    super.onResume();

    //程序运行时,打开Camera

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {

    mCamera = Camera.open(switchCamera);

    }else {

    mCamera = Camera.open();

    }

    }

    }

    6、添加权限和Activity特性(AndroidMainfest.xml)

    a)权限:

    b)     Activity特性:

    android:screenOrientation="landscape"

    请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客)

    学习《Android编程权威指南》心得与笔记                    by2015.2.10早

    1楼cheyiliu3天前 20:57markRe: kangweijian前天 13:47回复cheyiliunwhat?

    展开全文
  • android相机预览拍照功能实现

    千次阅读 2021-11-23 15:02:12
    代码目录结构 LAUNCHER activity:CameraActivity : onCreate()方法中创建Camera工具类CameraHelper的实例并初始化: mCameraHelper = new ...CameraHelper类中实现相机的预览与拍照,流程如下: 大致流程如下:

    代码目录结构

    在这里插入图片描述

    LAUNCHER activity:CameraActivity :

    onCreate()方法中创建Camera工具类CameraHelper的实例并初始化:

    mCameraHelper = new CameraHelper(mActivity, textureView, btnImagePreview);
    

    重写onClick方法监听拍照点击事件:

    mCameraHelper.takePicture();
    

    CameraHelper类中实现相机的预览与拍照,流程如下:

    在这里插入图片描述

    大致流程如下:

      1. 用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)方法打开指定摄像头。该方法的第一个参数代表要打开的摄像头ID;第二个参数用于监听摄像头的状态;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
      1. 当摄像头被打开之后会回调接口mStateCallback.onOpened,程序即可获取CameraDevice —— 即根据摄像头ID获取了指定摄像头设备,然后调用CameraDevice的createCaptureSession(List outputs, CameraCaptureSession. StateCallback callback,Handler handler)方法来创建CameraCaptureSession。该方法的第一个参数是一个List集合,封装了所有需要从该摄像头获取图片的Surface,第二个参数用于监听CameraCaptureSession的创建过程;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
      1. 不管预览还是拍照,程序都调用CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder,该方法支持TEMPLATE_PREVIEW(预览)、TEMPLATE_RECORD(拍摄视频)、TEMPLATE_STILL_CAPTURE(拍照)等参数。
      1. 通过第3步所调用方法返回的CaptureRequest.Builder设置拍照的各种参数,比如对焦模式、曝光模式等。
      1. 调用CaptureRequest.Builder的build()方法即可得到CaptureRequest对象,接下来程序可通过CameraCaptureSession的setRepeatingRequest()方法开始预览,或调用capture()方法拍照。
        相机的预览与拍照流程我们基本了解了。
      1. 预览时,是将mSurfaceHolder.getSurface()作为目标,使用setRepeatingRequest()方法,
        显示拍照结果时,是将mImageReader.getSurface()作为目标,使用capture()方法

    layout布局如下:

    <?xml version="1.0" encoding="utf-8"?>
    <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="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/relativeLayout"
        android:orientation="vertical"
        android:background="#000">
    
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="112dp"
            android:background="@color/black"/>
    
        <LinearLayout
            android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="208dp"
            android:layout_alignParentBottom="true"
            android:orientation="vertical">
            
            <com.bilibili.camera2.view.SideBar
                android:id="@+id/sideBar"
                android:layout_width="300dp"
                android:layout_height="60dp"
                android:visibility="gone"
                android:layout_marginTop="10dp"
                android:layout_gravity="center_horizontal"/>
    
            <com.bilibili.camera2.view.HorizontalSelectedView
                android:id="@+id/selectedView"
                android:layout_width="match_parent"
                android:layout_marginTop="10dp"
                android:layout_height="60dp"
                android:layout_gravity="center_horizontal"
                app:HorizontalSelectedViewSeeSize="5"
                android:visibility="visible"
                app:HorizontalSelectedViewSelectedTextColor="@color/white"
                app:HorizontalSelectedViewSelectedTextSize="40"
                app:HorizontalSelectedViewTextColor="@color/gray"
                app:HorizontalSelectedViewTextSize="30" />
    
            <RelativeLayout
    
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    
                <!-- 拍照按钮 -->
                <ImageButton
                    android:id="@+id/btnTakePicture"
                    android:layout_width="80dp"
                    android:layout_height="80dp"
                    android:layout_centerHorizontal="true"
                    android:background="@drawable/ic_camera_main_btn_01_auto"
                    android:layout_gravity="center"
                    tools:ignore="ContentDescription" />
     
                <ImageView
                    android:id="@+id/faceBackCameraChange"
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:layout_marginStart="280dp"
                    android:layout_centerVertical="true"
                    android:background="@drawable/ic_camera_main_btn_02_switch"
    
                    android:clickable="true"
                    tools:ignore="ContentDescription" />
            </RelativeLayout>
    
        </LinearLayout>
        <LinearLayout
            android:id="@+id/linearLayoutBottom"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:background="@color/black"
            android:layout_alignParentBottom="true"
            android:orientation="horizontal" />
    
    </RelativeLayout>
    

    CameraHelper主要逻辑代码如下:

    class CameraHelper {
    
        private final Activity mActivity;
        private final TextureView mTextureView;
        private static final String TAG = "CameraHelper";
    
        private static final int PREVIEW_WIDTH = 1080;       //预览的宽度
        private static final int PREVIEW_HEIGHT = 2340;     //预览的高度
        private static final int SAVE_WIDTH = 1080;          //保存图片的宽度
        private static final int SAVE_HEIGHT = 23400;        //保存图片的高度
    
        private static final int PREVIEW_FIRST_GET_IMAGE = 0;
    
        private CameraManager mCameraManager;
        private ImageReader mImageReader;
        private CameraDevice mCameraDevice;
        private CameraCaptureSession mCameraCaptureSession;
        private CircleImageView imageButton;
    
        private String mCameraId = "0";
        private CameraCharacteristics mCameraCharacteristics;
    
        private int mCameraSensorOrientation = 0;           //摄像头方向
        private int mCameraFacing = CameraCharacteristics.LENS_FACING_BACK;      //默认使用后置摄像头
        //private int mDisplayRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();      //手机方向
    
        private boolean canTakePicture = true;              //是否可以拍照
        private boolean canExchangeCamera = false;          //是否可以切换摄像头
    
        private Handler mCameraHandler;
        private HandlerThread handlerThread = new HandlerThread("CameraThread");
    
        private Size mPreviewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT);                      //预览大小
        private Size mSavePicSize = new Size(SAVE_WIDTH, SAVE_HEIGHT);                       //保存图片大小
        private CoordinateTransformer mTransformer;
    
        private int i = 0;
    
        public CameraHelper(Activity mActivity, TextureView mTextureView, CircleImageView imageButton) {
            this.mActivity = mActivity;
            this.mTextureView = mTextureView;
            this.imageButton = imageButton;
    
            init();
        }
    
        private void init() {
            handlerThread.start();
            mCameraHandler = new Handler(Looper.myLooper());
            mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
                @Override
                public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
                    initCameraInfo();
                }
    
                @Override
                public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
                    releaseCamera();
                    return true;
                }
    
                @Override
                public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
    
                }
    
                @Override
                public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
    
                }
            });
        }
    
        public CameraDevice getCameraDevice(){
            Log.d(TAG, "getCameraDevice: mCameraDevice : " + mCameraDevice);
            return mCameraDevice;
        }
    
        public void releaseCamera() {
            if (mCameraCaptureSession != null) {
                mCameraCaptureSession.close();
                mCameraCaptureSession = null;
            }
            if (mCameraDevice != null){
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (mImageReader != null){
                mImageReader.close();
                mImageReader = null;
            }
            canExchangeCamera = false;
    
        }
    
        public void releaseThread() {
            handlerThread.quitSafely();
            mCameraHandler = null;
        }
    
        /**
         * 拍照
         */
        public void takePicture(){
            Log.i(TAG, "takePicture: 进行拍照  1");
            if (mCameraDevice == null || !canTakePicture || ! mTextureView.isAvailable()) {
                Log.d(TAG, "takePicture: 进行拍照 error 退出");
                return;
            }
            try {
                CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                captureBuilder.addTarget(mImageReader.getSurface());
                captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mCameraSensorOrientation);
                mCameraCaptureSession.capture(captureBuilder.build(),null, mCameraHandler);
                Thread.sleep(200);
                Log.i(TAG, "takePicture: picture capture finish");
            } catch (CameraAccessException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 切换摄像头
         */
        public void exchangeCamera(){
            if (mCameraDevice == null || !canExchangeCamera || !mTextureView.isAvailable()) return;
    
            mCameraFacing = (mCameraFacing == CameraCharacteristics.LENS_FACING_FRONT)
                    ? CameraCharacteristics.LENS_FACING_BACK : CameraCharacteristics.LENS_FACING_FRONT;
    
            mPreviewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT);     //重置预览大小
            releaseCamera();
            initCameraInfo();
        }
    
        /**
         * 初始化
         */
        @SuppressLint("ShowToast")
        private void initCameraInfo() {
            try {
                mCameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
                String[] cameraIdList = mCameraManager.getCameraIdList();
                if (cameraIdList == null) {
                    Toast.makeText(mActivity, "没有相机可用", Toast.LENGTH_SHORT);
                    return;
                }
    
                for (String id : cameraIdList) {
                    CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(id);
                    //+Type add focus
                    RectF mPreviewRect = new RectF(0,0,1080, 1440);
                    mTransformer = new CoordinateTransformer(cameraCharacteristics, mPreviewRect);
                    Rect mFocusRect = new Rect();
                    RectF rectF = mTransformer.toCameraSpace(mPreviewRect);
                    mFocusRect.left = Math.round(rectF.left);
                    mFocusRect.top = Math.round(rectF.top);
                    mFocusRect.right = Math.round(rectF.right);
                    mFocusRect.bottom = Math.round(rectF.bottom);
                    //MeteringRectangle meteringRectangle = new MeteringRectangle(mFocusRect,1000);
                    //-Type add focus
                    Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
                    if (facing == mCameraFacing) {
                        mCameraId = id;
                        mCameraCharacteristics = cameraCharacteristics;
                    }
                    Log.i(TAG, "initCameraInfo: 设备中的摄像头:   " + id);
                }
    
    
    
                Integer supportLevel = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                if (supportLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
                    Toast.makeText(mActivity, "相机硬件不支持新特性", Toast.LENGTH_SHORT);
                }
    
                //获取摄像头方向
                mCameraSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    
                //获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
                StreamConfigurationMap configurationMap = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    
                Size[] savePicSizes = configurationMap.getOutputSizes(ImageFormat.JPEG);        //保存照片尺寸
                if (savePicSizes == null) {
                    Log.d(TAG, "initCameraInfo: savePicSizes为空");
                    return;
                }
                Size[] previewSizes = configurationMap.getOutputSizes(SurfaceTexture.class);        //预览尺寸
                if (previewSizes == null) {
                    Log.d(TAG, "initCameraInfo: previewSizes为空");
                    return;
                }
    
                int mDisplayRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();      //手机方向
                boolean exchange = exchangeWidthAndHeight(mDisplayRotation, mCameraSensorOrientation);
    
                if (exchange) {
                    mSavePicSize = getBestSize(mSavePicSize.getHeight(),
                            mSavePicSize.getWidth(),
                            mSavePicSize.getHeight(),
                            mSavePicSize.getWidth(),
                            Arrays.asList(savePicSizes));
                } else {
                    mSavePicSize = getBestSize(mSavePicSize.getWidth(),
                            mSavePicSize.getHeight(),
                            mSavePicSize.getWidth(),
                            mSavePicSize.getHeight(),
                            Arrays.asList(savePicSizes));
                }
    
                if (exchange) {
                    mPreviewSize = getBestSize(mPreviewSize.getHeight(),
                            mPreviewSize.getWidth(),
                            mTextureView.getHeight(),
                            mTextureView.getWidth(),
                            Arrays.asList(previewSizes));
                } else {
                    mPreviewSize = getBestSize(mPreviewSize.getWidth(),
                            mPreviewSize.getHeight(),
                            mTextureView.getWidth(),
                            mTextureView.getHeight(),
                            Arrays.asList(previewSizes));
                }
    
                mPreviewSize = new Size(1080,1440);
    
    
                if (mTextureView.getSurfaceTexture() == null){
                    Log.i(TAG, "initCameraInfo: mTextureView.getSurfaceTexture()  执行了*******************");
    
                    SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
                    surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                    mTextureView.setSurfaceTexture(surfaceTexture);
    
                }
    
    
                Log.d(TAG, "预览最优尺寸: " + mPreviewSize.getWidth() + "   " + mPreviewSize.getHeight() +
                        "比例  " + mPreviewSize.getWidth() / mPreviewSize.getHeight());
                Log.d(TAG, "保存图片最优尺寸: " + mSavePicSize.getWidth() + "   " + mSavePicSize.getHeight() +
                        "比例  " + mSavePicSize.getWidth() / mSavePicSize.getHeight());
    
    
                mImageReader = ImageReader.newInstance(mSavePicSize.getWidth(), mSavePicSize.getHeight(), ImageFormat.JPEG, 1);
                mImageReader.setOnImageAvailableListener(onImageAvailableListener, mCameraHandler);
                Log.d(TAG, "initCameraInfo: mTextureView-----------5");
    
                openCamera();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    
    
    
        /**
         * 打开相机
         */
        private void openCamera() {
            if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                Log.d(TAG, "initCameraInfo: mTextureView-----------没有相机权限");
                List<String> permissionList = new ArrayList<>();
                permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
                permissionList.add(Manifest.permission.CAMERA);
                permissionList.add(Manifest.permission.CALL_PHONE);
                permissionList.add(Manifest.permission.MODIFY_AUDIO_SETTINGS);
                permissionList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
                permissionList.add(Manifest.permission.READ_PHONE_STATE);
                permissionList.add(Manifest.permission.INTERNET);
                permissionList.add(Manifest.permission.VIBRATE);
                permissionList.add(Manifest.permission.ACCESS_WIFI_STATE);
                ActivityCompat.requestPermissions(mActivity, permissionList.toArray(new String[permissionList.size()]),1002);
    
            }else {
                try {
                    Log.d(TAG, "initCameraInfo: mTextureView-----------6准备打开相机");
                    mCameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);
                    Log.d(TAG, "openCamera: 打开相机");
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
        private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                Log.i(TAG, "onOpened: ");
                mCameraDevice = camera;
                createCaptureSession(mCameraDevice);
            }
    
            @Override
            public void onDisconnected(@NonNull CameraDevice camera) {
                Log.i(TAG, "onDisconnected: ");
            }
    
            @Override
            public void onError(@NonNull CameraDevice camera, int error) {
                Log.i(TAG, "onError: ");
                Toast.makeText(mActivity, "打开相机失败!error: " + error, Toast.LENGTH_SHORT);
            }
        };
    
        private void createCaptureSession(CameraDevice cameraDevice){
            try {
                Log.d(TAG, "createCaptureSession的CameraDevice :" + cameraDevice);
                CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                @SuppressLint("Recycle")
                Surface surface = new Surface(mTextureView.getSurfaceTexture());
                builder.addTarget(surface);
                builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    
                List<Surface> list = new ArrayList<>();
                list.add(surface);
                list.add(mImageReader.getSurface());
                cameraDevice.createCaptureSession(list, new CameraCaptureSession.StateCallback() {
                    @Override
                    public void onConfigured(@NonNull CameraCaptureSession session) {
                        mCameraCaptureSession = session;
                        try {
                            session.setRepeatingRequest(builder.build(), mCaptureCallback, mCameraHandler );
                        } catch (CameraAccessException e) {
                            e.printStackTrace();
                        }
                    }
    
                    @Override
                    public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                        Log.d(TAG, "onConfigureFailed: 开启预览会话失败");
                    }
                }, mCameraHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    
        private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                canExchangeCamera = true;
                canTakePicture = true;
            }
    
            @SuppressLint("ShowToast")
            @Override
            public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                super.onCaptureFailed(session, request, failure);
                Log.d(TAG, "onCaptureFailed");
                Toast.makeText(mActivity,"开启预览失败",Toast.LENGTH_SHORT);
            }
        };
    
    
        private final ImageReader.OnImageAvailableListener onImageAvailableListener = reader -> {
            Image image = reader.acquireLatestImage();
            ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
            byte[] bytesArray = new byte[byteBuffer.remaining()];
            byteBuffer.get(bytesArray);
    
            image.close();
    
            String localPicturePath = BitmapUtils.savePicture(bytesArray, "Camera");
    
            if (localPicturePath.equals("")){
                Log.d(TAG, "没有获取到最后一张图片的路径!!!!!: " + localPicturePath);
                imageButton.setImageResource(PREVIEW_FIRST_GET_IMAGE);
            }else {
                Bitmap bitmap = BitmapFactory.decodeFile(localPicturePath);
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                float w = (float) 256 / width;
                //float h = (float) 256 / height;
                Matrix matrix = new Matrix();
                matrix.postScale(w, w);
    
                Bitmap newBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
                Matrix matrix1 = new Matrix();
                matrix1.setRotate(90f);
                Bitmap resultBitmap = Bitmap.createBitmap(newBitmap1, 0, 0, newBitmap1.getWidth(), newBitmap1.getHeight(), matrix1, true);
                imageButton.setImageBitmap(resultBitmap);
                Log.d(TAG, "onImageAvailable: ------------------------------------------------------------图片保存");
            }
        };
    
        private class CompareSizeByArea implements Comparator<Size> {
            @Override
            public int compare(Size size1, Size size2) {
                return (int) Math.signum( size1.getWidth() * size1.getHeight() - size2.getWidth() * size2.getHeight());
            }
        }
    
        /**
         * 根据提供的屏幕方向 [displayRotation] 和相机方向 [sensorOrientation] 返回是否需要交换宽高
         */
        private boolean exchangeWidthAndHeight(int displayRotation, int sensorOrientation){
            boolean exchange = false;
            switch (displayRotation){
                case Surface.ROTATION_0:
    
                case Surface.ROTATION_180:
                    if (sensorOrientation == 90 || sensorOrientation == 270){
                        exchange = true;
                    }
                    break;
                case Surface.ROTATION_90:
    
                case Surface.ROTATION_270:
                    if (sensorOrientation == 0 || sensorOrientation == 180){
                        exchange = true;
                    }
                    break;
            }
            Log.i(TAG, "屏幕方向: " + displayRotation);
            Log.i(TAG, "相机方向: " + sensorOrientation);
            return exchange;
        }
    }
    

    TION_180:
    if (sensorOrientation == 90 || sensorOrientation == 270){
    exchange = true;
    }
    break;
    case Surface.ROTATION_90:

            case Surface.ROTATION_270:
                if (sensorOrientation == 0 || sensorOrientation == 180){
                    exchange = true;
                }
                break;
        }
        Log.i(TAG, "屏幕方向: " + displayRotation);
        Log.i(TAG, "相机方向: " + sensorOrientation);
        return exchange;
    }
    

    }

    
    
    展开全文
  • android自定义相机连拍

    2019-02-20 02:00:30
    android 自定义 相机 连拍,效果实现,简单实用,适合初学者.
  • Android相机、图册demo

    2021-01-05 04:58:32
    本文为大家分享了Android相机、图册基本demo,供大家参考,具体内容如下 package com.example.democamera; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import ...
  • 自己封装的一个工具jar包,可以打开Android相机进行预览,并且展示预览的控件也自定义了,api调用规则非常简单,有个缺点不能更换前后摄像头,不过可以自行的选择打开前置还是后置。
  • Android相机开发

    千次阅读 2019-03-21 00:29:02
    文章目录Android相机开发申请权限创建一个可以预览的界面1.创建一个新工程2.在新创建的工程中activity中布局文件3.创建一个相机预览的view 继承SurfaceView4.在activity中添加添加偏好设置预览分辨率预览格式照片...

    Android相机开发

    申请权限

    <uses-permission android:name="android.permission.CAMERA" />
    <!--可以防止APP被安装到没有相机的Android设备上(目前仅Google Play支持)-->
    <uses-feature android:name="android.hardware.camera" />
    

    动态权限不要忘了

    创建一个可以预览的界面

    1.创建一个新工程

    2.在新创建的工程中activity中布局文件

    <FrameLayout
        android:id="@+id/preview_f"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    

    3.创建一个相机预览的view 继承SurfaceView

    最新都开始用TextureView,关于SurfaceView/TextureView

    • SurfaceView是一个有自己Surface的View。界面渲染可以放在单独线程而不是主线程中。它更像是一个Window,自身不能做变形和动画。
    • TextureView同样也有自己的Surface。但是它只能在拥有硬件加速层层的Window中绘制,它更像是一个普通View,可以做变形和动画。

    更多关于SurfaceView与TextureView区别的内容可以参考这篇文章Android 5.0(Lollipop)中的SurfaceTexture,TextureView, SurfaceView和GLSurfaceView.

    官方给出的方案,图片来自于Android平台Camera开发实践指南:

    官方给出的方案

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    
        private final SurfaceHolder mHolder;
        private Camera mCamera;
    
        public CameraPreview(Context context) {
            super(context);
            mHolder = getHolder();
            mHolder.addCallback(this);
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        	//surface第一次创建时回调
            //打开相机
            mCamera = Camera.open();
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    		//surface变化的时候回调(格式/大小)
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        	//surface销毁的时候回调
            mHolder.removeCallback(this);
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
    

    4.在activity中添加

    public class MainActivity extends AppCompatActivity {
    
        private FrameLayout mFrameLayout;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            initCamera();
        }
    
        private void initCamera() {
            CameraPreview preview = new CameraPreview(this);
            mFrameLayout.addView(preview);
        }
    
        private void initView() {
            mFrameLayout = (FrameLayout) findViewById(R.id.preview_f);
        }
    }
    

    至此可以显示一个相机界面,并有图像显示。

    添加偏好设置

    如分辨率、闪光灯、对焦等。

    通过当前界面的相机camera对象获取起设置的参数getParameters()

    预览分辨率

    • parameters.getSupportedPreviewSizes()获取相机支持的所有预览分辨率

    预览格式

    具体参照ImageFormat或者自己Google

    • parameters.getSupportedPreviewFormats()获取相机支持的所有预览格式

    照片分辨率

    • parameters.getSupportedPictureSizes()获取相机支持的所有图片分辨率

    图片格式

    具体参照ImageFormat或者自己Google

    • parameters.getSupportedPictureFormats()获取相机支持的所有图片格式

    视频分辨率

    parameters.getSupportedVideoSizes()获取相机支持的所有视频分辨率

    对焦模式

    • parameters.getSupportedFocusModes()获取相机支持的所有对焦模式

    曝光补偿

    • parameters.getMinExposureCompensation()获取相机支持的最低曝光补偿
    • parameters.getMaxExposureCompensation()获取相机支持的最高曝光补偿

    闪光灯模式

    • parameters.getSupportedFlashModes()获取相机支持的闪光灯模式

    白平衡

    • parameters.getSupportedWhiteBalance()获取相机支持的白平衡

    场景

    parameters.getSupportedSceneModes()获取相机支持的场景

    声明GPS权限

    想要拍到的照片中包含GPS信息

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    

    代码如下

    int numberOfCameras = Camera.getNumberOfCameras();
    Log.d("123===", "相机个数===" + numberOfCameras);
    Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
    for (int i = 0; i < numberOfCameras; i++) {
        Camera.getCameraInfo(i, cameraInfo);
        //后置:0 CAMERA_FACING_BACK; 前置:1 CAMERA_FACING_FRONT
        Log.d("123===", "当前相机信息=" + cameraInfo.facing);
    }
    
    
    Camera camera = mPreview.getCamera();
    Camera.Parameters parameters = camera.getParameters();
    
    Log.d("123===", "预览分辨率-----------");
    List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
    for (Camera.Size previewSize : supportedPreviewSizes) {
        Log.d("123===", previewSize.width + "---" + previewSize.height);
    }
    Log.d("123===", "预览分辨率-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "获得相机支持的图片预览格式-----------ImageFormat");
    List<Integer> supportedPreviewFormats = parameters.getSupportedPreviewFormats();
    for (Integer supportedPreviewFormat : supportedPreviewFormats) {
        Log.d("123===", supportedPreviewFormat + "");
    }
    Log.d("123===", "获得相机支持的图片预览格式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "照片分辨率-----------");
    List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
    for (Camera.Size pictureSize : supportedPictureSizes) {
        Log.d("123===", pictureSize.width + "---" + pictureSize.height);
    }
    Log.d("123===", "照片分辨率-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "获得相机支持的图片格式-----------ImageFormat");
    List<Integer> supportedPictureFormats = parameters.getSupportedPictureFormats();
    for (Integer supportedPreviewFormat : supportedPictureFormats) {
        Log.d("123===", supportedPreviewFormat + "");
    }
    Log.d("123===", "获得相机支持的图片格式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "视频分辨率-----------");
    List<Camera.Size> supportedVideoSizes = parameters.getSupportedVideoSizes();
    for (Camera.Size supportedVideoSize : supportedVideoSizes) {
        Log.d("123===", supportedVideoSize.width + "---" + supportedVideoSize.height);
    
    }
    Log.d("123===", "视频分辨率-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    Log.d("123===", "对焦模式-----------ImageFormat");
    List<String> supportedFocusModes = parameters.getSupportedFocusModes();
    for (String supportedFocusMode : supportedFocusModes) {
        Log.d("123===", supportedFocusMode + "");
    }
    Log.d("123===", "对焦模式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    Log.d("123===", "曝光补偿-----------");
    int minExposureCompensation = parameters.getMinExposureCompensation();
    int maxExposureCompensation = parameters.getMaxExposureCompensation();
    Log.d("123===", "最高=" + maxExposureCompensation);
    Log.d("123===", "最低=" + minExposureCompensation);
    Log.d("123===", "曝光补偿-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "支持闪光灯模式-----------");
    List<String> supportedFlashModes = parameters.getSupportedFlashModes();
    for (String supportedFlashMode : supportedFlashModes) {
        Log.d("123===", "闪光模式--->" + supportedFlashMode);
    }
    Log.d("123===", "支持闪光灯模式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "支持白平衡-----------");
    List<String> supportedWhiteBalance = parameters.getSupportedWhiteBalance();
    for (String s : supportedWhiteBalance) {
        Log.d("123===", "--->" + s);
    }
    Log.d("123===", "支持白平衡-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    Log.d("123===", "场景-----------");
    List<String> supportedSceneModes = parameters.getSupportedSceneModes();
    for (String s : supportedSceneModes) {
        Log.d("123===", "--->" + s);
    }
    Log.d("123===", "场景-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    

    拍照,视频,对焦

    假如需要使用原生拍照功能的话,Camera#takePicture()

    拍照和视频都是网上那一套流程,具体的因为时间原因暂时先这样了。

    相机预览和保存注意事项

    下面三张图片引用自:Android: Camera相机开发详解(中) ——实现预览、拍照、保存照片等功能

    • 相机预览方向

    相机预览方向

    • 采集的图像方向

    采集的图像方向

    • 前置摄像头预览与保存一致

    前置摄像头预览与保存一致

    参考

    展开全文
  • 浅谈Android相机演变

    千次阅读 2021-11-21 10:48:45
    Android相机版本更迭主要表现为Camera HAL版本更新(HAL1 -> HAL2 -> HAL3), Camera API版本更新(Camera API1 -> Camera API2) 相机应用camera1 一般对应着api1,camera2对应api2,发展过程中由于各家厂家...
  • Android相机支持的预览格式详解

    千次阅读 2022-04-24 16:52:40
    随便找了一台Android手机,通过log输出,看个究竟。 先打印相机支持的预览格式: Log.d(TAG, "SupportedPreviewFormats:"+mCamera.getParameters().getSupportedPreviewFormats().toString()); 输出的结果是:...
  • 相机简史 https://blog.csdn.net/u012596975/article/details/107136261 安卓相机架构概览 https://blog.csdn.net/u012596975/article/details/107136568 应用层 ...
  • android相机自定义圆形框拍照,保留圆形照片。
  • Android相机屏幕适配

    千次阅读 2018-07-11 14:17:56
    本文默认你已经会的Android相机开发,但是苦恼于相机屏幕适配 如果不会相机开发,可以参考以下作者的文章: Tong ZHAN Android相机开发(一):最简单的相机 Android相机开发(二): 给相机加上偏好设置 Android...
  • Android 相机预览图像进行YUV分量图解 旨在让学习中的朋友理解YUV分量的原理 而不是搬运代码。 如想搬运有很多博客中都有代码亲们可自行搬运
  • 效果图如下: 一、为预览控件设置圆角 为控件设置ViewOutlineProvider public RoundTextureView(Context context, AttributeSet attrs) { super(context, attrs); setOutlineProvider(new ViewOutlineProvider...
  • 深入理解Android相机体系结构之二

    万次阅读 多人点赞 2020-07-05 11:33:11
    安卓相机架构概览 Android系统利用分层思想,将各层的接口定义与实现分离开来,以接口作为各层的脉络连接整体框架,将具体实现的主导权交由各自有具体实现需求的平台厂商或者Android 开发者,这样既做到把控全局,也...
  • 深入理解Android相机体系结构

    万次阅读 多人点赞 2020-07-05 11:49:24
    Android系统自2007年底被Google推出问世以来,已经走过13个春夏秋冬,历经多次的大大小小的迭代重构、架构调整,虽然时代年轮依旧滚滚,虽然每年技术依然在不断地推陈出新,但是到目前为止,依然可以窥见其接口与...
  • Android相机、相册图片上传程序(客户端和服务器端)。 直接解压可以运行,之前下载别人的好多都跑步起来,这个经过加工、优化,可以正常的运行,希望能帮到你!
  • Android相机 解决三星bug版本

    热门讨论 2014-07-19 17:40:21
    最近在Android项目中使用拍照功能 , 其它型号的手机运行成功了 唯独在三星的相机上遇到了bug . BUG具体体现为 : (1) 摄像头拍照后图片数据不一定能返回 ; onActivityResult的data为空 (2) 三星的camera强制切换到...
  • Android Face人脸识别检测,圆形相机预览框,截取图片(宽高自定义),圆形图片显示
  • android 自定义相机apk 是的安装包,请结合我的博客:http://blog.csdn.net/qq_38416326/article/details/71124946
  • 在使用android相机接口进行预览时,大部分的前置摄像头的预览图都没有做镜像处理,因此需要手写处理方案。领导告诉我说, Camera 的 Parameters 中除了提供标准的相机参数,还有一系列的 key-value 参数来控制相机,...
  • 资源中的打开相机和相册均是原生Java代码实现,在我自己手机和安卓7.0版本上面稳定可靠,测试在魅族5.0版本左右打开相机会死掉,由Qt调用,demo可学习Qt如何与原生Java的交互通信,代码均有注释,里面还有微信等接口...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 67,001
精华内容 26,800
关键字:

android相机