Camera使用-实现简单的自定义相机

Android拍照的方式:调用系统相机、自定义 Camera 相机、相册、拍照 阅读详情

这篇文章总结使用Camera API实现自定义相机

之前文章 Android 拍照功能详解介绍了调用系统相机应用完成拍照功能。

但很多时候,应用需要自定义实现相机拍照功能,Android5.0以后推出了Camera2.0来替代之前的Camera API,但是很多时候我们还是需要使用Camera API来实现相机的自定义。


这里写图片描述

关键知识点

  1. SurfaceView的使用,相机预览界面是通过SurfaceView实现的;
  2. Camera API的使用;
  3. 拍照生成的文件的保存与读取。

权限申请
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera"/>

相机权限,文件存储权限。

相机界面
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/surfaceview"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拍照"
        android:layout_gravity="center|bottom"
        android:onClick="takePhoto"/>

</FrameLayout>

这里为什么要使用SurfaceView,因为普通的View无法实现高效的拍照页面视图渲染。

Camera API使用
Camera初始化
 /**
     * 初始化相机对象
     * @return
     */
    private Camera initCamera(){

        Camera camera;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            camera = null;
            e.printStackTrace();
        }

        return camera;
    }

通过Camera.open()方法得到Camera对象。

SurfaceHolder 初始化
 @Override
    protected void preInitData() {

        mFile = new File(getIntent().getStringExtra("file"));

        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
    }

SurfaceHolder是SurfaceView的关键方法,通过SurfaceView获取到SurfaceHolder,然后实现其回调方法。

SurfaceHolder的回调方法实现
 @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //先关闭,再开启
        mCamera.stopPreview();
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }

其中startPreview(mCamera,holder)是我实现的预览相机内容的方法,releaseCamera()方法是释放相机资源的方法。

预览相机内容
 /**
     * 开始预览相机内容
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //将Camera预览角度进行调整
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

通过Camera跟SurfaceHolder产生关联,就可以将Camera的预览内容展现在SurfaceView中。

释放相机资源
/**
     * 释放相机资源
     */
    private void releaseCamera(){
        if(mCamera != null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
获得拍照生成的照片
/**
     * 拍照按钮点击事件
     * @param view
     */
    public void takePhoto(View view){
        setParameters();
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(success){
                    //前两个参数可以为空,第三个方法是照片的回调
                    mCamera.takePicture(null,null,mPictureCallback);
                }
            }
        });
    }

我们这里拿到回调的字节流,然后生成文件保存,最后将文件数据放入Intent中,关闭当前页面。
其中mPictureCallback方法是相机拍照结果的回调方法:

private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            try {
                FileOutputStream fos = new FileOutputStream(mFile);
                fos.write(data);
                fos.close();

                Intent intent = new Intent();
                intent.putExtra("file",mFile.getAbsolutePath());
                setResult(0,intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

setParameters()方法是我们对Camera一些参数的设置:

/**
     * 开始预览相机内容
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //将Camera预览角度进行调整
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

关键的方法基本上都介绍完了,下面我将CameraActivity界面的所有代码都放出来,方便大家的动手操作。

public class Camera1Activity extends BaseActivity implements SurfaceHolder.Callback{

    @BindView(R.id.surfaceview)SurfaceView mPreview;

    private File mFile;
    private Camera mCamera;
    private SurfaceHolder mHolder;

    private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            try {
                FileOutputStream fos = new FileOutputStream(mFile);
                fos.write(data);
                fos.close();

                Intent intent = new Intent();
                intent.putExtra("file",mFile.getAbsolutePath());
                setResult(0,intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };


    @Override
    protected int getContentViewResId() {
        return R.layout.camera1;
    }

    @Override
    protected void onResume() {
        super.onResume();

        if(mCamera == null){
            mCamera = initCamera();
            if(mHolder != null){
                startPreview(mCamera,mHolder);
            }
        }

    }

    @Override
    protected void onPause() {
        super.onPause();

        releaseCamera();
    }

    @Override
    protected void preInitData() {

        mFile = new File(getIntent().getStringExtra("file"));

        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
    }

    @OnClick(R.id.surfaceview)
    public void onSurfaceviewClick(){
        mCamera.autoFocus(null);
    }

    /**
     * 拍照按钮点击事件
     * @param view
     */
    public void takePhoto(View view){
        setParameters();
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(success){
                    //前两个参数可以为空,第三个方法是照片的回调
                    mCamera.takePicture(null,null,mPictureCallback);
                }
            }
        });
    }

    /**
     * 初始化相机对象
     * @return
     */
    private Camera initCamera(){

        Camera camera;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            camera = null;
            e.printStackTrace();
        }

        return camera;
    }

    /**
     * 开始预览相机内容
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //将Camera预览角度进行调整
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放相机资源
     */
    private void releaseCamera(){
        if(mCamera != null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * 设置相机参数
     */
    private void setParameters(){
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPictureFormat(ImageFormat.JPEG);
        parameters.setPreviewSize(800,400);
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //先关闭,再开启
        mCamera.stopPreview();
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }
}

拍照功能实现了,接下来我们处理拍照后相片的显示操作。仔细阅读文章的同学会发现,我们在跳转至CameraActivity中的时候传入了照片文件的路径。

 public void photo(View view){
        Intent intent = new Intent(this,Camera1Activity.class);
        intent.putExtra("file",createImageFile("mFile").toString());
        startActivityForResult(intent,TAKE_PHOTO_CUSTOM);
    }

    private File createImageFile(String fileName){
        File dir = new File(getCacheDir(),"images");
        if(!dir.exists())
            dir.mkdirs();
        return new File(dir,fileName);
    }

同时,我们将拍照后的相片路径放入了Intent中。

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == TAKE_PHOTO_CUSTOM){
            if(data != null){
//                Bitmap resultBitmap = BitmapFactory.decodeFile(filePath);
//                mImageView.setImageBitmap(resultBitmap);
                try {
                    String filePath = data.getStringExtra("file");
                    FileInputStream fis = new FileInputStream(filePath);
                    Bitmap bitmap = BitmapFactory.decodeStream(fis);
                    Matrix matrix = new Matrix();
                    matrix.setRotate(90);
                    bitmap = Bitmap.createBitmap(bitmap,0,0,
                            bitmap.getWidth(),bitmap.getHeight(),matrix,true);
                    mImageView.setImageBitmap(bitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

因为我直接将相片拿出来显示的话,照片显示效果是旋转了90度,至于为什么如此,仔细的同学可能会发现在相机Camera API使用的时候我们对Camera进行了90度的旋转,因为Camera默认相片是横屏显示的。因此我在OnActivityResult中对照片显示的imageview进行了90度的旋转,这样照片就可以正确显示在ImageView中。

Camera的使用简答介绍到这里,实现了简单拍照功能。至于项目中对自定义拍照功能的应用,相信在此基础上进行一定的完善就可以满足一般的功能需求。

感觉有帮助的同学欢迎留言交流,点赞关注!谢谢。

Android实现自定义相机系列(2)—自定义view图片缩放控件 自定义view图片缩放控件 ImageView控件只能展示图片,不能对图片进行手势缩放,此篇文章主要实现对图片的展示和缩放,技能点主要有以下几个方面: 继承View,实现自定义View功能 绘制bitmap,显示图片 实现对图片拖动功能 实现对图片的放大缩小功能 实现图片的裁剪功能 演示效果图 直接看代码会枯燥,这里先看下演示效果图 代码实现 1、自定义view类CropVi... 阅读详情

相关推荐

简单谈谈android自定义相机的实现(上 android.hardware.Camera)

通常情况下,调用android系统相机基本上可以满足拍照的需求,而自定义相机的需求一般来自于开发自己的相机应用,今天我们来简单聊聊android自定义相机的实现,限于篇幅,我们上篇只讨论android.hardware.Camera,下篇我会和大家一起讨论一下android.hardware.Camera2 android.hardware.Camera android 5.0 之前控制相机硬件是通

lovefish2的专栏 1万+

Android自定义camera相机 系列(一)

该文章 主要使用 自定义 surfaceview 及 camera 知识点,来实现一个自定义的拍照 、切换闪光灯 和 前后摄像头的功能。阅读需要消耗时间 :15分钟+ GitHub Demo 地址 :yangmingchuan / SunCamera 1. 调用原生相机 在记录自定义camera相机前,先简单提一下调用系统相机和获取图片返回值的方式。 Intent intent = new I...

魔都大白 9953

android 系统相机调用和自定义相继两个 SurfaceView Camera

android 系统相机调用和自定义相继两个 SurfaceView Camera

随意而然的专栏 3553

Camera开发—如何自定义相机(1)

光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想。

2401_84023482的博客 822

Android使用Camera2打造自定义相机

从5.0开始(API Level 21),可以完全控制Android设备相机的新api Camera2(android.hardware.Camera2)被引入了进来。在以前的Camera api(android.hardware.Camera)中,对相机的手动控制需要更改系统才能实现,而且api也不友好。不过老的Camera API在5.0上已经过时,在未来的app开发中推荐的是Camera2 A

HardWorkingAnt的博客 1万+

Android自定义Camera相机

Android自定义Camera相机 写在前面 Android Framework层为各种不同的Camera和Camera的特色功能提供了支持,使得你可以很方便的在应用使用拍照和录像功能。如果希望快速实现拍照与录制视频的方法是使用Intent方式调用系统提供的相机功能;当然,如果系统提供的方式不足以满足项目的需求,你就需要自定义Camera相机。本篇博客会通过Intent方式和自定义Camera两部分介绍如何使用相机功能。 转载请表明出处,谢谢~ 传送门:https://github.com/zhijunh

ZhijunHong的博客 2354

android 面试算法题 实现单链表反转(1),面试官问技术问题不太会怎么办

T行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!(5)反转后得到的链表最后一个结点是否为null?考虑完以上问题后,就能够写出正确的代码了。(2)假设链表为单节点链表,该怎样处理?(3)怎样防止在反转过程中断链?(4)反转后head是否更新?实现自定义链表反转类。

2401_84103675的博客 646

【Camera相机开发】实现开启相机

上一篇文章:Android: Camera相机开发知识点介绍了进行Camera开发需要了解的知识点 本篇文章会带着小伙伴们实现自己的Camera,并在实现的过程中验证上一篇中所讲解的结论 实现思路: 在xml布局中定义一个SurfaceView,用于预览相机采集的数据 给SurfaceHolder添加回调,在surfaceCreated(holder: SurfaceHolder?)回调中打开相...

Errol's Blog 6764

基于Android TextureView与SurfaceTexture实现相机Camera拍照预览与保存照片

基于Android TextureView与SurfaceTexture实现相机Camera拍照预览与保存照片写一个简单的例子,实现一个常见的开发功能:拍照功能。技术路线:通过TextureView的通道获取SurfaceTexture作为相机的预览,然后通过Camera的take方法把相机抓取的byte字节数据转换为Bitmap,然后存放到手机的存储器上,至此,一个最简单的拍照功能完成。注意,本

Zhang Phil 6436

Android实现自定义的相机

使用系统相机android中使用系统相机是很方便的,单这仅仅是简单的使用而已,并不能获得什么特殊的效果。要想让应用有相机的action,咱们就必须在清单文件中做一些声明,好让系统知道,如下 <intent-filter> <action android:name="android.intent.action.IMAGE_CAPTURE" />

更上一层楼! 8630

自定义相机

Android自定义相机界面,SurfaceView的使用,并添加阴影效果,点击拍照返回图片展示

android 自定义相机,Camera,相机遮罩层

实现效果: 实现方法:一个自定义view实现demo 一、自定义相机Camera,无API版本限制 (1)。实现接口 implements SurfaceHolder.Callback private SurfaceView mView; private Camera mCamera; private int cameraId = 0;//前置1、后置0 ...

meixi_android的博客 6221

Camera照相拍照实现

camera实现拍照功能,照片显示,照片保存本地,本地照片获得、点击放大显示在fragment中,fragment传递参数等。

Android自定义相机—Camera篇

这里主要简单介绍如何使用Camera+SurfaceView自定义相机拍照,如果是Camera2或者是TextureView的可以前往主页,后面会陆续推出系列文章。 自定义相机很多人都已经介绍得非常清楚了,这里个人一方面针对自己的实践起一个记录作用,另一方面也分享一下自己自定义相机的过程和总结。

m0_56146626的博客 5004

Camera使用

android.graphics.Camera实现图像的旋转、缩放,配合Matrix... 配套工程

Android Studio 自定义相机+拍照保存到本地.zip

Android Studio自定义相机,拍照保存到本地。Android版本:3.5 。主页面中有两个按钮:拍照和图片编辑,只对拍照添加了监听事件。拍照页面也有两个按钮:常规模式和笑脸模式,也只对常规模式添加了监听事件,调用的是后置摄像头。图片保存在系统根目录下。命名为:MyPhoto_...

Android中自定义相机Camera2的使用

在之前的博客中我介绍了Camera在单拍和连拍上的使用。大家应该有所了解了。但如果你运行了博客中的例子,或者自己根据讲解写了一个Demo,你会发现拍出的照片并不是很清晰,很大的一个原因是我们的照片支持的最大分辨率是1920*1080,所以对于现在动辄2000往上的分辨率来说却是不是很清晰。所以Android 5.0之后Camera2也应用而生。而和Camera2配合的显示控件也变成了TextureView。下面我们分别对这两个内容一一进行讲解。并在最后给出一个利用Camera2制作的自定义相机示例。 1.

水月洞天 4078

Android中关于自定义相机预览界面拉伸问题

主要为大家详细介绍了Android中关于自定义相机预览界面拉伸问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

上一篇: MVP设计模式浅析、实战
下一篇: Android标题栏ToolBar详解
mo_android
博客等级 码龄9年 12粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值