android自定义相机(带边框和按钮)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nzzl54/article/details/79126220

前两个月项目要求不能调用系统的相机,那就只能用自定义的了,查了一些资料,自己再研究了一下,自定义的相机还是有点复杂的,布局和代码中都要用到一个重要的SurfaceView

一、建立布局,布局的背景框可以让美工给出,这里姑且就是一个蓝色的边框,然后下面有三个按钮,我里布局文件activity_custom_camera.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="1">
        <FrameLayout
            android:id="@+id/layout_camera"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="visible">

            <SurfaceView
                android:id="@+id/surfaceView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="visible"/>
            
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <TextView
                    android:id="@+id/view_text_top"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:gravity="center"
                    android:text="请将证件对准方框"
                    android:textSize="18dp"
                    android:visibility="visible"
                    android:textColor="@color/blue"
                    android:background="#000000"
                    android:alpha="0.5"/>

                <LinearLayout
                    android:id="@+id/view_main_content"
                    android:layout_marginTop="50dp"
                    android:layout_above="@+id/layout_button_btn"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="horizontal">

                    <View
                        android:id="@+id/view_left"
                        android:layout_width="30dp"
                        android:layout_height="match_parent"
                        android:background="#000000"
                        android:alpha="0.5"
                        android:visibility="visible"/>
                    
                    <!--中心布局,取景处-->
                    <View
                        android:id="@+id/bg_center_view"
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:layout_gravity="center"
                        android:adjustViewBounds="true"
                        android:scaleType="fitXY"
                        android:background="@drawable/qr_code_bg_take_photo"/>

                    <TextView
                        android:id="@+id/view_right"
                        android:textColor="@color/blue"
                        android:gravity="center"
                        android:layout_width="30dp"
                        android:layout_height="match_parent"
                        android:background="#000000"
                        android:alpha="0.5"
                        android:visibility="visible"
                        />
                </LinearLayout>

                <RelativeLayout
                    android:id="@+id/layout_button_btn"
                    android:layout_width="match_parent"
                    android:layout_height="80dp"
                    android:layout_alignParentBottom="true"
                    android:background="#000000"
                    android:alpha="0.5">
                    <Button
                        android:id="@+id/btn_cancel"
                        android:layout_marginLeft="30dp"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:background="@drawable/ic_cancel_take"
                        android:layout_alignParentLeft="true"
                        android:layout_centerVertical="true"/>
                    <Button
                        android:id="@+id/btn_take_photo"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_centerInParent="true"
                        android:background="@drawable/ic_take_photo_large" />
                    <Button
                        android:id="@+id/btn_finish"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginRight="30dp"
                        android:background="@drawable/ic_finish_take"
                        android:layout_alignParentRight="true"
                        android:layout_centerVertical="true"/>
                </RelativeLayout>

            </RelativeLayout>
        </FrameLayout>
    </RelativeLayout>

</LinearLayout>

效果图:左边是取消,右边的按钮当点击拍照按钮后才会出来

二、在Activity中实现SurfaceView和拍照,这里给出相机的主要代码,初始化View就省略了。

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setBaseContentLayoutWithoutTitle(R.layout.activity_custom_camera);
        //初始化相机
        initCamera();
        //初始化照片存储路径
        getFileSavePath();
    }

方法initCamera():

SurfaceHolder holder;
    private void initCamera(){
        surfaceview.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if(!status && camera != null){
                    camera.autoFocus(autoFocusCallback);
                }
                return false;
            }
        });
        MySurfaceCallback mySurfaceCallback = new MySurfaceCallback();
        holder = surfaceview.getHolder();
        holder.setKeepScreenOn(true);// 屏幕常亮
        holder.addCallback(mySurfaceCallback);
        holder.lockCanvas();
    }
    
     class MySurfaceCallback implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder surfaceHolder) {
            initCameraParamsAndOpen();
        }

        @Override
        public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
            //当surface的格式或大小发生改变,这个方法就被调用,或者View被隐藏
            status = false;
            btnTakePhoto.setVisibility(View.VISIBLE);  //拍照按钮显示
            btnFinish.setVisibility(View.GONE); //拍照完成按钮隐藏
            camera.release();
            camera = null;
            initCameraParamsAndOpen();  //重新初始化相机
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
            if (camera != null) {
                camera.stopPreview();
                camera.release();
                camera = null;
            }
        }
    }

    private void initCameraParamsAndOpen(){
        try {
            // surfaceview创建之后,就去打开相机
            camera = getCameraInstance();
            camera.setPreviewDisplay(holder);
            camera.setDisplayOrientation(90);
            updateCameraParameters();
            camera.startPreview();
        } catch (Exception e) {
            if(camera != null){
                camera.release();
            }
            e.printStackTrace();
        }
    }

    private void updateCameraParameters() {
        if (camera != null) {
            Camera.Parameters mParameters = camera.getParameters();
            Camera.Size picSize = mParameters.getPreviewSize();
            Camera.Size previewSize = getOptimalPreviewSize(mParameters.getSupportedPreviewSizes(), (double) picSize.width / picSize.height);
            if (previewSize != null) {
                mParameters.setPreviewSize(previewSize.width, previewSize.height);
            }
            picSize = mParameters.getPictureSize();
            Camera.Size pictureSize = getOptimalPictureSize(mParameters.getSupportedPictureSizes(), (double) picSize.width / picSize.height);

            if (pictureSize != null) {
                mParameters.setPictureSize(pictureSize.width, pictureSize.height);
            }
            mParameters.setRotation(90);//防止保存的图片旋转
            camera.setParameters(mParameters);

        }
    }
     
public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(0); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable }
    /**      * 设定的屏幕的比例不是图片的比例      *@date 创建时间 2017/4/15      *@author      *@company      *@name:zhongshuiping      *@Description 匹配分辨率      */     private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, double targetRatio) {         if (sizes == null)             return null;         Camera.Size optimalSize = null;         Collections.sort(sizes, new Comparator<Camera.Size>() {             @Override             public int compare(Camera.Size lhs, Camera.Size rhs) {                 return new Double(lhs.width).compareTo(new Double(rhs.width));             }         });         for (int i=sizes.size()-1;i>=0;i--) {             Camera.Size size = sizes.get(i);             if ((( Constants.EIGHT_HUNDRED < size.width && size.width < Constants.TWO_THOUSAND)                     || (Constants.EIGHT_HUNDRED< size.height && size.height < Constants.TWO_THOUSAND))                     && ((size.width * 9) == (size.height * 16) )) {                 optimalSize = size;                 break;             }         }         return optimalSize;     }
    /**      * 设置的是拍照的图片的比例      *@date 创建时间 2017/4/15      *@author      *@company      *@name:zhongshuiping      *@Description 匹配分辨率      */     private Camera.Size getOptimalPictureSize(List<Camera.Size> sizes, double targetRatio) {         if (sizes == null)             return null;         Camera.Size optimalSize = null;         Collections.sort(sizes, new Comparator<Camera.Size>() {             @Override             public int compare(Camera.Size lhs, Camera.Size rhs) {                 return new Double(lhs.width).compareTo(new Double(rhs.width));             }         });         for (int i=sizes.size()-1;i>=0;i--) {             Camera.Size size = sizes.get(i);             if (((Constants.NUMBER_ONE_THOUSAN < size.width && size.width < Constants.NUMBER_TWO_THOUSAND)                     || (Constants.NUMBER_ONE_THOUSAN < size.height && size.height < Constants.NUMBER_TWO_THOUSAND))                     && ((size.width * 10) ==(size.height * 16) )) {                 optimalSize = size;                 break;             }         }         /**如果没找到16/9的就选择最接近的*/         if(optimalSize == null)         {             double dMin = 100.0;             Camera.Size RightSize = null;             for (Camera.Size size : sizes) {                 double fRate = size.width/(float)size.height;                 double fDistance = Math.abs(fRate - 16.0/10);                 //找最接近16比9的size;                 if(fDistance < dMin)                 {                     dMin = fDistance;                     RightSize = size;                 }             }             //最接近的值赋给变量optimalSize             optimalSize = RightSize;         }         return optimalSize;     }
    Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {         @Override         public void onAutoFocus(boolean b, Camera camera) {
        }     } ;

存储路径getFileSavePath(), File  tempFile为拍照预留的存储位置文件,最后拍照完毕将会把照片流写入该文件中。

 private File mediaStorageDir;
    private void getFileSavePath(){
        try
        {
            mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        } catch (Exception e) {
            e.printStackTrace();
            showShortToastCenter("创建test失败");
            LogUtils.LogError("lenita", "Error in Creating mediaStorageDir: " + mediaStorageDir);
        }
        if (!mediaStorageDir.exists())
        {
            if (!mediaStorageDir.mkdirs())
            {
                // 在SD卡上创建文件夹需要权限:
                // <uses-permission
                // android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
                showShortToastCenter("创建test失败");
                LogUtils.LogError("lenita", "failed to create directory, check if you have the WRITE_EXTERNAL_STORAGE permission");
            }
        }
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                .format(new Date());
        tempFile = new File(mediaStorageDir.getPath() + File.separator
                + "IMG_" + timeStamp + ".jpg");
}    

相机调用,点击btnTakePhoto按钮(R.id.btn_take_photo),此处对拍照后的图片进行压缩,免得太大导致上传服务器出错:

//点击拍照
@Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_take_photo:
                if(camera != null){
                    camera.takePicture(null, null, myPictureCallback);
                    status = true;
                    btnTakePhoto.setVisibility(View.GONE);
                    btnFinish.setVisibility(View.VISIBLE);
                }else {
                    showShortToastCenter("请重新拍摄");
                    status = false;
                    btnTakePhoto.setVisibility(View.VISIBLE);
                    btnFinish.setVisibility(View.GONE);
                    camera.release();
                    camera = null;
                    initCameraParamsAndOpen();
                }
                break;
            case R.id.btn_finish:  //点击完成,进行图片的上传,上传到服务器代码省略
                if(tempBitmap == null || tempFile == null){
                    showShortToastCenter("拍照失败,请重新拍摄");
                    status = false;
                    btnFinish.setVisibility(View.GONE);
                    btnTakePhoto.setVisibility(View.VISIBLE);
                    camera.release();
                    camera = null;
                    initCameraParamsAndOpen();
                    return;
                }
                LogUtils.LogError("lenita","tempFile path = "+tempFile.getPath());
                formatFile();  //TODO 压缩查看
                //上传到服务器的代码省略。。。File tempFile为图片转换成的文件流
                break;
        }
    }
   
    private File mImageFile;
    private void formatFile(){
        try {
            //当大于2MB时用到
            String lRealFilePath = tempFile.getPath();
            String stringAll[] = lRealFilePath.split("\\.");
            int length = stringAll.length;
            String imageSuffix = stringAll[length - 1];
            String string[] = lRealFilePath.split("\\."+imageSuffix);
            String newFileSavePath = string[0] + "_compress.jpg";
            long fileLong = FileSizeUtil.getFileSize(tempFile);
            String fileString = FileSizeUtil.formatFileSizeUnder2MB(fileLong);
            if (TextUtils.isEmpty(fileString)) {   //TODO 证明是大于2MB的,要进行压缩
                Toast.Long(this,"正在压缩图片,请稍等片刻...");
                compressImageUtilbelow2MB(lRealFilePath,newFileSavePath);
                //压缩后替换要上传的路径
                lRealFilePath = newFileSavePath;
                mImageFile = new File(lRealFilePath);
                tempFile = mImageFile;
            }
        }catch (Exception e) {
            Log.e("lenita","e e = "+e.toString());
        }
    }

    private void compressImageUtilbelow2MB(String filePath,String newFilePath){
        //TODO 大于2MB的进行压缩
        ImageFactory imageFactory = new ImageFactory();
        try {
            imageFactory.compressAndGenImage(filePath,newFilePath,1024,false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    Camera.PictureCallback myPictureCallback = new Camera.PictureCallback(){
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            LogUtils.LogError("lenita"," onPictureTaken");
            //卡住预览页面,让用户看  
            camera.stopPreview();
            //保存图片
            tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            saveBitmap(tempBitmap);
        }
    };

    public void saveBitmap(Bitmap bitmap) {
        if(tempFile != null){
            try {
                FileOutputStream out = new FileOutputStream(tempFile);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
                out.flush();
                out.close();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                LogUtils.LogError("lenita","saveBitmap e = "+e);
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                LogUtils.LogError("lenita","saveBitmap e = "+e);
                e.printStackTrace();
            }
        }else {
            LogUtils.LogError("lenita","tempFile == null");
        }

    }
以上就是拍照的相关的代码,这里要注意的是,拍照之后最好调用camera.stopPreview();这个方法,否则有些手机会一直在preview,导致无法定在当前的拍照中,当然,如果你是想拍照完直接存下,而不要定下来查看的话,就不需要camera.stopPreview();来定住画面了,效果图如下所示:

 

保存的文件就会在Picture/test文件夹下,图片如下图:

最后给个参考文章,还是值得借鉴一下的:

http://blog.csdn.net/ming_csdn_/article/details/70154381

 
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/nzzl54/article/details/79126220