码迷,mamicode.com
首页 > 移动开发 > 详细

东拼西凑写的android 相机例子,包含一些遇到的坑

时间:2016-01-10 17:16:44      阅读:356      评论:0      收藏:0      [点我收藏+]

标签:android   自定义相机   照片模糊   照片质量差   

闲扯:最近开始学android开发,还好有些java基础,直接找了个android教程的视频,边学边写。本来我是很懒惰的。不打算写博客,但是在写这个自定义相机的时候,坑还真是不少。容我吐槽下,那些没事儿转载的,搜来搜去都是一样的内容,真是给跪了。

话入正题,本代码是跟着视频里写的,然后又完善的。首先说的一点,就是真的不难,但是很坑:相机写好后,调试了下,有个问题,就是相片很模糊。大小只有200kb,直接说原因,就是没有设置,parameters.setPictureSize(picSize.width, picSize.height);  parameters.setPreviewSize(preSize.width, preSize.height);这两个参数,当然一定要 myCamera.setParameters(parameters);不然没用哦,当然并不是所有手机都会这个问题。还有就是保存目录的问题,这个好解决。其他问题,我都写在注释里了,自己看吧

package com.example.ggpla.myapplication;

import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ToggleButton;

import com.example.ggpla.utils.AndroidSystemUtils;
import com.example.ggpla.utils.FileOptionUtils;

import java.io.File;
import java.io.IOException;

public class MyCamera extends AppCompatActivity implements SurfaceHolder.Callback{

    private Camera myCamera;

    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;

    //硬件相机所支持的尺寸
    private Camera.Size picSize;
    private Camera.Size preSize;

    private byte[] buffer = null;

    /*为了实现拍照的快门声音及拍照保存照片需要下面三个回调方法之一,可以为空*/
    private Camera.ShutterCallback shutter = new Camera.ShutterCallback()
    //快门按下的回调,在这里我们可以设置类似播放“咔嚓”声之类的操作。默认的就是咔嚓。
    {
        public void onShutter() {
        }
    };

    /**
     * 拍照完成后后回调,重写onPictureTaken方法
     */
    private Camera.PictureCallback mPic = new Camera.PictureCallback(){
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            //保存数据到文件中去,这个已经被我写成一个静态方法,方便其他场合调用
            FileOptionUtils.saveImageToFile(data,FileOptionUtils.NAMED_BY_TYPE_AND_DATE,"","JPEG","IMAGE");
            //需要继续拍照,需要重新开启preview
            myCamera.startPreview();
        }
    };

    private ToggleButton toggleButton;

    @Override
    protected void onResume() {
        super.onResume();
        if (myCamera == null){
            myCamera = getCamera();
            if(surfaceHolder != null){
                Camera.Parameters parameters = myCamera.getParameters();
                parameters.setPictureFormat(ImageFormat.JPEG);
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                //获取最大的支持的尺寸,这关乎你最终拍照的相片质量,一开始我也是被折磨的够呛
                picSize = AndroidSystemUtils.getCameraSupportPicMaxSize(myCamera);
                preSize = AndroidSystemUtils.getCameraSupportPreMaxSize(myCamera);
                parameters.setPictureSize(picSize.width, picSize.height);
                parameters.setPreviewSize(preSize.width, preSize.height);
                //设置尺寸后必须将参数设置到硬件相机对象中,才能被执行
                myCamera.setParameters(parameters);
                //启动preview
                setStartPreview(surfaceHolder, myCamera);
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
//      webSocketStart();

        surfaceView = (SurfaceView)findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();
        //需要添加回调,这里由于已经实现了 SurfaceHolder.Callback的三个方法,所以就传this就可以了
        surfaceHolder.addCallback(this);

//        SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface
//        SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface
//        SURFACE_TYPE_GPU:适用于GPU加速的Surface
//        SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,
// 在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。
// 如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//貌似用来向下兼容的一个方法

        //togglebutton 作为拍照按钮,当从关闭状态切换到 开启状态时执行拍照方法
        toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
        toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    //自定义的拍照方法
                    capture();
                }
            }
        });

        //这里给surfaceView 定义了一个点击事件,当点击surfaceview时,执行自动对焦方法
        surfaceView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //相机自动对焦,null表示除了对焦外,我什么也不敢。
                myCamera.autoFocus(null);
            }
        });
    }


    /**
     * 初始化硬件摄像头
     * @return
     */
    private Camera getCamera(){
        Camera camera = Camera.open();
        //启动人脸识别功能,需要添加监听事件,如下所示
//        camera.setFaceDetectionListener(new Camera.FaceDetectionListener() {
//            @Override
//            public void onFaceDetection(Camera.Face[] faces, Camera camera) {
//                Log.i("main","人脸数量:"+faces.length);
//            }
//        });
//        打开人脸识别方法
//        camera.startFaceDetection();
        return  camera;
    }

    /**
     *自定义 拍照功能
     */
    public void capture(){
        //可以在拍照前重新定义一些硬件相机的设置,这里只是简单说明
        //为相机添加自动对焦行为,重写onAutoFocus方法,在对焦完成后执行
        myCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                //success表示对焦是否完成。
                if (success) {
                    //相机拍照方法。shutter为快门声音。mPic为回调放拍照完成后后回调
                    camera.takePicture(shutter, null, mPic);
                }
            }
        });
    }

    //开始
    private void setStartPreview(SurfaceHolder surfaceHolder,Camera camera){
        try {
            camera.setPreviewDisplay(surfaceHolder);
            //将系统预览角度调整90度,不然你看到的内容是倒得
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //释放相机资源,终止preview等操作
    private void releseCamera(){
        if(myCamera != null){
            myCamera.setPreviewCallback(null);
            myCamera.stopPreview();
            myCamera.release();
            myCamera = null;
        }
    }


    /**
     * 重写该方法,并调用释放相机资源方法
     * 释放相机的目的,因为硬件相机属于独占资源,只能一个Activity用,所以,在暂停当前任务前必须释放相机资源
     */
    @Override
    protected void onPause() {
        super.onPause();
        releseCamera();
    }


    //surfaceview.callback相关内容我也看不懂,可以参考http://www.android100.org/html/201406/10/23235.html
    //能看懂的可以给我教教我 O(∩_∩)O~
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //在创建surfaceview时执行,开启
        setStartPreview(surfaceHolder, myCamera);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        myCamera.stopPreview();
        //在改变surface时执行,开启
        setStartPreview(surfaceHolder, myCamera);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //在销毁surface时执行,开启
        releseCamera();
    }


}


/**
这个是我自己造的自定义保存图片的静态方法
*/
package com.example.ggpla.utils;

import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.Buffer;
import java.security.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by ggpla on 2016/1/9.
 */
public class FileOptionUtils {

    /**
     * 命名规则常量
     */
    public static int NAMED_BY_TYPE_AND_STAMPTIME = 1;//时间戳命名规则,如IMAGE_20424533214
    public static int NAMED_BY_TYPE_AND_DATE = 2;//根据时间和类型,如VIDEO_20160110_100427,即2016_1_10拍摄的视频文件
    public static int NAMED_BY_SELF = 0;//自定义文件名

    /**
     * 文件命名基础参数
     */
    public static String VIDEO_BASE_FILE = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath();
    //文件类型分类
    public static Map<String, String> FILE_CONTENT_MAP = new HashMap<String, String>() {
        {
            put("VIDEO", "MyVideo");
            put("IMAGE", "MyPictures");
        }
    };
    //文件扩展名
    public static Map<String, String> FILE_EXT_MAP = new HashMap<String, String>() {
        {
            put("3GP", ".3gp");
            put("MP3", ".mp3");
            put("MP4", ".mp4");
            put("AVI", ".avi");
            put("JPEG", ".jpg");
            put("PNG", ".png");
            put("JPEG1", ".jpeg");
        }
    };

    /**
     * 获取完整文件目录和名称
     * @param rule 命名规则,默认为 1,为时间戳命名规则
     * @param name 自定义名称,
     * @param ext  文件扩展类型名称
     * @param type 文件类型,VIDEO 或 IMAGE
     * @return
     */
    public static String getSavePath(int rule, String name, String ext, String type) {
        String path = VIDEO_BASE_FILE + File.separator + FILE_CONTENT_MAP.get(type) + File.separator;

        File mediaStorageDir = new File(path);
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                return null;
            }
        }
        if (rule == NAMED_BY_SELF) {
            path = path + name + FILE_EXT_MAP.get(ext);
        } else if (rule == NAMED_BY_TYPE_AND_DATE) {
            String currDate = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            path = path + type + "_" + currDate + FILE_EXT_MAP.get(ext);
        } else {
            long timestamp = System.currentTimeMillis();
            path = path + type + "_" + timestamp + FILE_EXT_MAP.get(ext);
        }
        System.out.println(path);
        return path;
    }


    public static void saveImageToFile(byte[] buffer, int rule, String name,String ext,String type) {
        String path = getSavePath(rule,name,ext,type);
        File file = new File(path);
        if (file == null) {
            return;
        }
        if (buffer != null) {
            try {
                FileOutputStream fos = new FileOutputStream(file);
                fos.write(buffer);
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //得到输出文件的URI
    public static Uri getOutFileUri(int rule, String name, String ext, String type) {
        String path = getSavePath(rule, name, FILE_EXT_MAP.get("JPG"), FILE_CONTENT_MAP.get("IMAGE"));
        return Uri.fromFile(new File(path));
    }

}


/**
这是获取相机支持图像大小的工具类,同样是一些静态方法,直接调用
*/
package com.example.ggpla.utils;

import android.hardware.Camera;
import android.util.Log;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
 * Created by ggpla on 2016/1/10.
 */
public class AndroidSystemUtils {



    public  static  List<Camera.Size> getCameraSupportSizeList(Camera myCamera){
        Camera.Parameters parameters = myCamera.getParameters();
        List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
        return supportedPictureSizes;
    }


    public  static  Camera.Size getCameraSupportPicMaxSize(Camera myCamera){
        AndroidSystemUtils androidSystemUtils = new AndroidSystemUtils();
        Camera.Parameters parameters = myCamera.getParameters();
        List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
        Camera.Size size = androidSystemUtils.getMaxSupportSize(supportedPictureSizes, 0)
        return size;
    }

    public  static  Camera.Size getCameraSupportPreMaxSize(Camera myCamera){
        AndroidSystemUtils androidSystemUtils = new AndroidSystemUtils();
        Camera.Parameters parameters = myCamera.getParameters();
        List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
        Camera.Size size = androidSystemUtils.getMaxSupportSize(supportedPreviewSizes,0);
        return size;
    }

    private Camera.Size getMaxSupportSize(List<Camera.Size> size,int th){
        Collections.sort(size,new CameraSizeComparator());
        int i = 0;
        for(Camera.Size s:size){
            if((s.width > th) && equalRate(s, 1.33f)){
                Log.i("main", "最终设置图片尺寸:w = " + s.width + "h = " + s.height);
                break;
            }
            i++;
        }

        return size.get(i);
    }

    public boolean equalRate(Camera.Size s, float rate){
        float r = (float)(s.width)/(float)(s.height);
        if(Math.abs(r - rate) <= 0.2)
        {
            return true;
        }
        else{
            return false;
        }
    }

    public  class CameraSizeComparator implements Comparator<Camera.Size> {
        //按升序排列
        public int compare(Camera.Size lhs, Camera.Size rhs) {
            // TODO Auto-generated method stub
            if(lhs.width == rhs.width){
                return 0;
            }
            else if(lhs.width > rhs.width){
                return -1;
            }
            else{
                return 1;
            }
        }

    }

}


本文出自 “不会笑” 博客,请务必保留此出处http://8779947.blog.51cto.com/8769947/1733467

东拼西凑写的android 相机例子,包含一些遇到的坑

标签:android   自定义相机   照片模糊   照片质量差   

原文地址:http://8779947.blog.51cto.com/8769947/1733467

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!