Android-给自定义相机增加贴纸

来源:互联网 发布:c语言程序的注释 编辑:程序博客网 时间:2024/04/28 17:23

转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/51706520
本文出自:【顾林海的博客】

前言

给自己的APP增加相机是一个不错的功能,在我们打开相机时,如果能动态给我们的脸上贴上标签,或者是贴上一个卡通的眼睛,最后点击拍照,一张合成的图片就产生了,这是不是一个好主意,下面的效果图是我在一边拍照一边摆弄我的卡通贴纸,最后进行拍照,并把图片保存到图库中,最后在下个页面显示我们的合成图。

这里写图片描述

这里写图片描述

碰到的坑

我们知道自定义相机可以通过Camera+SurfaceView来实现,那如何才能截取到SurfaceView和贴纸合成的图呢?

起初我是获取SurfaceView和贴纸的容器(SurfaceView与贴纸是重叠的),通过以下方法来获取合成图:

group.setDrawingCacheEnabled(true);group.buildDrawingCache();Bitmap bitmap = group.getDrawingCache();

最后发现获取到的bitmap是一张背景漆黑的只有贴纸的图,也就是SurfaceView中图获取不到。

最终我用了一个巧办法:

1.在SurfaceView上放置一个隐藏的ImageView.

2.通过android.hardware.Camera.takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)方法中的第三参数PictureCallback中的onPictureTaken方法回调中获取到拍照完的字节数组,也就是我们照片,将图片保存在本地,这时获取图片的bitmap将它设置在SurfaceView上的ImageView上。

3.将ImageView显示,再通过getDrawingCache获取容器视图并保存。

既然SurfaceView上的试图无法通过getDrawingCache获取,那我们就把图片设置在ImageView上,相当于截取Image View与贴纸重叠的视图。

部分代码展示

自定义相机的layout文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    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" >    <RelativeLayout        android:id="@+id/group"        android:layout_width="fill_parent"        android:layout_height="400dp" >        <SurfaceView            android:id="@+id/surfaceView1"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            android:layout_alignParentBottom="true"            android:layout_alignParentLeft="true"            android:layout_alignParentRight="true" >        </SurfaceView>        <ImageView            android:id="@+id/iv_show"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            android:scaleType="fitXY" />        <com.yxiaolv.camerasample.StickerView            android:id="@+id/sticker"            android:layout_width="fill_parent"            android:layout_height="fill_parent" />    </RelativeLayout>    <Button        android:id="@+id/btn_capture"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:text="Capture" />    <Button        android:id="@+id/btn_to"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_alignParentRight="true"        android:text="展示" /></RelativeLayout>

StickerView是一个可以移动放大缩小旋转的自定义控件。

package com.yxiaolv.camerasample;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.util.List;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.Bitmap.CompressFormat;import android.graphics.BitmapFactory;import android.hardware.Camera;import android.hardware.Camera.CameraInfo;import android.hardware.Camera.PictureCallback;import android.hardware.Camera.ShutterCallback;import android.media.MediaScannerConnection;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.provider.MediaStore;import android.view.Surface;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ImageView;import android.widget.RelativeLayout;import com.yxiaolv.camerasample.StickerView.OnStickerDeleteListener;import com.yxiaolv.camerasample.util.BitmapUtil;public class CameraActivity extends Activity {    private StickerView stickerView;    private ImageView iv_show;    private RelativeLayout group;    private Camera mCamera;    private SurfaceView surfaceView;    private SurfaceHolder surfaceHolder;    private Button btnCapture;    private Button btnTo;    private boolean previewing;    private String imagUrl = "";    int mCurrentCamIndex = 0;    SurfaceViewCallback surfaceViewCallback;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initViews();        initEvent();    }    private void initEvent() {        btnCapture.setOnClickListener(new Button.OnClickListener() {            public void onClick(View arg0) {                if (previewing) {                    mCamera.takePicture(shutterCallback, rawPictureCallback,                            jpegPictureCallback);                }            }        });        btnTo.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                // TODO Auto-generated method stub                to();            }        });    }    private void to() {        Intent intent = new Intent(CameraActivity.this, ShowImageActivity.class);        intent.putExtra("imagpath", imagUrl);        startActivity(intent);    }    /**     * 初始化View     */    private void initViews() {        btnCapture = (Button) findViewById(R.id.btn_capture);        btnTo = (Button) findViewById(R.id.btn_to);        group = (RelativeLayout) findViewById(R.id.group);        iv_show = (ImageView) findViewById(R.id.iv_show);        stickerView = (StickerView) findViewById(R.id.sticker);        iv_show.setVisibility(View.INVISIBLE);        group.setDrawingCacheEnabled(true);        group.buildDrawingCache();        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),                R.drawable.katong);        stickerView.setWaterMark(bitmap);        stickerView.setOnStickerDeleteListener(new OnStickerDeleteListener() {            @Override            public void onDelete() {            }        });        surfaceViewCallback = new SurfaceViewCallback();        surfaceView = (SurfaceView) findViewById(R.id.surfaceView1);        surfaceHolder = surfaceView.getHolder();        surfaceHolder.addCallback(surfaceViewCallback);        // surfaceHolder.addCallback(this);        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);    }    ShutterCallback shutterCallback = new ShutterCallback() {        @Override        public void onShutter() {        }    };    PictureCallback rawPictureCallback = new PictureCallback() {        @Override        public void onPictureTaken(byte[] arg0, Camera arg1) {        }    };    PictureCallback jpegPictureCallback = new PictureCallback() {        @Override        public void onPictureTaken(byte[] arg0, Camera arg1) {            save(arg0);        }        /**         * 保存图片         *          * @param data         */        private void save(byte[] data) {            String fileName = Environment.getExternalStoragePublicDirectory(                    Environment.DIRECTORY_DCIM).toString()                    + File.separator + System.currentTimeMillis() + ".jpg";            File file = new File(fileName);            if (!file.getParentFile().exists()) {                file.getParentFile().mkdir();            }            try {                BufferedOutputStream bos = new BufferedOutputStream(                        new FileOutputStream(file));                bos.write(data);                mCamera.stopPreview();                previewing = false;                bos.flush();                bos.close();                scanFileToPhotoAlbum(file.getAbsolutePath());                configPhoto(file);                saveNewPhoto();            } catch (Exception e) {            }            mCamera.startPreview();            previewing = true;        }        /**         * 保持合成后的图片         */        private void saveNewPhoto() {            Bitmap bitmap = group.getDrawingCache();            iv_show.setImageBitmap(bitmap);            saveImageToGallery(CameraActivity.this, bitmap);            iv_show.setVisibility(View.INVISIBLE);        }        /**         * 配置照片         *          * @param file         */        private void configPhoto(File file) {            Bitmap imagbitmap = null;            setCameraDisplayOrientation(CameraActivity.this, mCurrentCamIndex,                    mCamera);            if (cameraPosition == 1) {                imagbitmap = BitmapUtil.convert(BitmapUtil.rotaingImageView(                        270, BitmapFactory.decodeFile(file.getAbsolutePath())),                        0);            } else {                imagbitmap = BitmapUtil.decodeSampledBitmapFromResource(                        file.getAbsolutePath(), 200, 300);                imagbitmap = BitmapUtil.rotaingImageView(90, imagbitmap);            }            iv_show.setImageBitmap(imagbitmap);            iv_show.setVisibility(View.VISIBLE);        };    };    public void scanFileToPhotoAlbum(String path) {        MediaScannerConnection.scanFile(CameraActivity.this,                new String[] { path }, null,                new MediaScannerConnection.OnScanCompletedListener() {                    public void onScanCompleted(String path, Uri uri) {                    }                });    }    private final class SurfaceViewCallback implements            android.view.SurfaceHolder.Callback {        public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,                int arg3) {            if (previewing) {                mCamera.stopPreview();                previewing = false;            }            try {                mCamera.setPreviewDisplay(arg0);                mCamera.startPreview();                previewing = true;                setCameraDisplayOrientation(CameraActivity.this,                        mCurrentCamIndex, mCamera);            } catch (Exception e) {            }        }        public void surfaceCreated(SurfaceHolder holder) {            mCamera = openFrontFacingCameraGingerbread();        }        public void surfaceDestroyed(SurfaceHolder holder) {            mCamera.stopPreview();            mCamera.release();            mCamera = null;            previewing = false;        }    }    // 0表示后置,1表示前置    private int cameraPosition = 1;    private Camera openFrontFacingCameraGingerbread() {        int cameraCount = 0;        Camera cam = null;        CameraInfo cameraInfo = new CameraInfo();        cameraCount = Camera.getNumberOfCameras();// 得到摄像头的个数        if (cameraCount > 1) {            cameraPosition = 1;        } else {            cameraPosition = 0;        }        for (int i = 0; i < cameraCount; i++) {            Camera.getCameraInfo(i, cameraInfo);// 得到每一个摄像头的信息            if (cameraPosition == 1) {                // 现在是后置,变更为前置                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {                    cam = Camera.open(i);                    break;                }            } else {                // 现在是前置, 变更为后置                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {                    cam = Camera.open(i);                    break;                }            }        }        return cam;    }    // 根据横竖屏自动调节preview方向    private static void setCameraDisplayOrientation(Activity activity,            int cameraId, Camera camera) {        Camera.CameraInfo info = new Camera.CameraInfo();        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;        }        int result;        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {            result = (info.orientation + degrees) % 360;            result = (360 - result) % 360;        } else {            result = (info.orientation - degrees + 360) % 360;        }        camera.setDisplayOrientation(result);    }    /**     * 保存合成后的图片     *      * @param context     * @param bmp     */    public void saveImageToGallery(Context context, Bitmap bmp) {        // 首先保存图片        String fileName = Environment.getExternalStoragePublicDirectory(                Environment.DIRECTORY_DCIM).toString()                + File.separator + System.currentTimeMillis() + ".jpg";        File file = new File(fileName);        try {            FileOutputStream fos = new FileOutputStream(file);            bmp.compress(CompressFormat.JPEG, 100, fos);            fos.flush();            fos.close();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        // 其次把文件插入到系统图库        try {            MediaStore.Images.Media.insertImage(context.getContentResolver(),                    file.getAbsolutePath(), fileName, null);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        // 最后通知图库更新        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,                Uri.parse(file.getAbsolutePath())));        imagUrl = file.getAbsolutePath();    }}

如果手机支持前置摄像头的话,会优先使用前置摄像头。

BitmapUtil工具类:

package com.yxiaolv.camerasample.util;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Rect;public class BitmapUtil {    private BitmapUtil() {    }    /**     * 旋转     *      * @param angle     * @param bitmap     * @return     */    public static Bitmap rotaingImageView(int angle, Bitmap bitmap) {        // 旋转图片 动作        Matrix matrix = new Matrix();        matrix.postRotate(angle);        // 创建新的图片        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,                bitmap.getWidth(), bitmap.getHeight(), matrix, true);        return resizedBitmap;    }    /**     * 翻转     *      * @param a     * @return     */    public static Bitmap convert(Bitmap a,int index) {        int w = a.getWidth();        int h = a.getHeight();        Bitmap newb = Bitmap.createBitmap(w, h, Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图        Canvas cv = new Canvas(newb);        Matrix m = new Matrix();        //        if(index==0){        m.postScale(-1, 1); // 镜像水平翻转        }else{             m.postScale(1, -1); // 镜像垂直翻转        }        // m.postRotate(-90); // 旋转-90度        Bitmap new2 = Bitmap.createBitmap(a, 0, 0, w, h, m, true);        cv.drawBitmap(new2, new Rect(0, 0, new2.getWidth(), new2.getHeight()),                new Rect(0, 0, w, h), null);        return newb;    }    public static Bitmap decodeSampledBitmapFromResource(            String path, int reqWidth, int reqHeight) {        // First decode with inJustDecodeBounds=true to check dimensions        final BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeFile(path, options);        // Calculate inSampleSize        options.inSampleSize = calculateInSampleSize(options, reqWidth,                reqHeight);        // Decode bitmap with inSampleSize set        options.inJustDecodeBounds = false;        return BitmapFactory.decodeFile(path, options);    }    public static int calculateInSampleSize(BitmapFactory.Options options,            int reqWidth, int reqHeight) {        // Raw height and width of image        final int height = options.outHeight;        final int width = options.outWidth;        int inSampleSize = 1;        if (height > reqHeight || width > reqWidth) {            final int halfHeight = height / 2;            final int halfWidth = width / 2;            // Calculate the largest inSampleSize value that is a power of 2 and            // keeps both            // height and width larger than the requested height and width.            while ((halfHeight / inSampleSize) > reqHeight                    && (halfWidth / inSampleSize) > reqWidth) {                inSampleSize *= 2;            }        }        return inSampleSize;    }}

代码比较多,关于自定义的贴纸控件请下载完整项目查看。

项目下载

以下是完整的github项目地址
github项目源码地址:点击【项目源码】
如果喜欢本文,请支持我。

3 0
原创粉丝点击