当前位置: 代码迷 >> Android >> Android实战技能之四十七:不用预览拍照与图片缩放剪裁
  详细解决方案

Android实战技能之四十七:不用预览拍照与图片缩放剪裁

热度:127   发布时间:2016-04-27 22:11:26.0
Android实战技巧之四十七:不用预览拍照与图片缩放剪裁

副标题:Take Picture without preview Android

Google出于对隐私的保护,制定了一条门槛,即在Android应用开发中编写拍照程序是必需要有图像预览的。这会对那些恶意程序比如Android中泛滥的Service在后台偷偷记录手机用户的行为与周边信息。这样的门槛还包括手机厂商自带的相机软件在拍照时必须是有声音,这样要避免一些偷拍的情况。

处于技术调研与一些特殊无害场景的使用,我们要用到不用预览的拍照。此文就是以此为背景,做的一些调研。只是用不多与五款手机测试,不保证所有的设备都无问题。

免责声明:本文纯属技术研究,请勿用于有害场景!

方案

既然Google在技术上做出了特定要求,网友们想出各种workaround的方法,比如用一个空的SurfaceView或将此preview设成透明。
但这两个办法我都失败了。而网友Sam给出的将preview设置成1x1大小的方案起了作用。下面的demo就是演示此方案:
界面甚是简单,一个按钮用以拍照,onClick事件方法名为onTakePhotoClicked。

public void onTakePhotoClicked(View view) {        final SurfaceView preview = new SurfaceView(this);        SurfaceHolder holder = preview.getHolder();        // deprecated setting, but required on Android versions prior to 3.0        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);        holder.addCallback(new SurfaceHolder.Callback() {            @Override            //The preview must happen at or after this point or takePicture fails            public void surfaceCreated(SurfaceHolder holder) {                Log.d(TAG, "Surface created");                Camera camera = null;                try {                    camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);                    Log.d(TAG, "Opened camera");                    try {                        camera.setPreviewDisplay(holder);                    } catch (IOException e) {                        throw new RuntimeException(e);                    }                    camera.startPreview();                    Log.d(TAG, "Started preview");                    camera.takePicture(null, null, pictureCallback);                } catch (Exception e) {                    if (camera != null)                        camera.release();                    throw new RuntimeException(e);                }            }            @Override            public void surfaceDestroyed(SurfaceHolder holder) {}            @Override            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}        });        WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);        WindowManager.LayoutParams params = new WindowManager.LayoutParams(                1, 1, //Must be at least 1x1                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,                0,                //Don't know if this is a safe default                PixelFormat.UNKNOWN);        //Don't set the preview visibility to GONE or INVISIBLE        wm.addView(preview, params);    }

图片缩放与裁剪

现在几乎就可以了,pictureCallback是拍照成功后的回调,我们将在此回调中做一些对图片数据的处理工作。
首先,图片数据以字节数组的形式返回的,原型如下:

        @Override        public void onPictureTaken(byte[] data, Camera camera) {}

我们要将数组转换成bitmap,用BitmapFactory.decodeByteArray方法即可。
但你会发现,图片是向左倒着显示的,不要急,我们要把它正过来。此时我们要用matrix.postRotate方法配上Bitmap.createBitmap创建新的bitmap。
谈谈缩放吧,参考Bitmap.createScaledBitmap方法即可。
那么裁剪呢?新建一个Rect,注意它要在整个bitmap中,比如程序中我将进行距离原图1/4处裁剪。
这还没完,做完这些工作,我会将这三个bitmap存成三张图片。注意在做bitmap的compress操作时,第二个参数quality很重要,图片的质量越高,占用的空间越大。
好了,下面是代码,请参考:

private Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {        @Override        public void onPictureTaken(byte[] data, Camera camera) {            Log.d(TAG, "onPictureTaken");            if(null == data){                return;            }            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);            camera.stopPreview();            Matrix matrix = new Matrix();            matrix.postRotate((float) 90.0);            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),                    bitmap.getHeight(), matrix, false);            Log.d(TAG, "original bitmap width: " + bitmap.getWidth() +                    " height: " + bitmap.getHeight());            Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap,                    bitmap.getWidth()/3, bitmap.getHeight()/3, true);            Log.d(TAG,"size bitmap width "+sizeBitmap.getWidth()+" height "+sizeBitmap.getHeight());            //裁剪bitmap            int leftOffset = (int)(sizeBitmap.getWidth() * 0.25);            int topOffset = (int)(sizeBitmap.getHeight() * 0.25);            Rect rect = new Rect(leftOffset, topOffset, sizeBitmap.getWidth() - leftOffset,                    sizeBitmap.getHeight() - topOffset);            Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap,                    rect.left, rect.top, rect.width(), rect.height());            try {                FileOutputStream outputStream = new FileOutputStream(Environment                        .getExternalStorageDirectory().toString()+"/photoResize.jpg");                sizeBitmap.compress(Bitmap.CompressFormat.JPEG, 30, outputStream);                outputStream.close();                FileOutputStream outputStreamOriginal = new FileOutputStream(Environment                        .getExternalStorageDirectory().toString()+"/photoOriginal.jpg");                bitmap.compress(Bitmap.CompressFormat.JPEG, 20, outputStreamOriginal);                outputStreamOriginal.close();                FileOutputStream outputStreamCut = new FileOutputStream(Environment                        .getExternalStorageDirectory().toString()+"/photoCut.jpg");                rectBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStreamCut);                outputStreamCut.close();                Log.d(TAG,"picture saved!");            } catch(Exception e) {                e.printStackTrace();            }        }    };

你需要的权限:

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

参考:
http://stackoverflow.com/questions/2386025/taking-picture-from-camera-without-preview

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案