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

转载请标明出处:

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项目源码地址:点击【项目源码】

如果喜欢本文,请支持我。

时间: 2024-08-08 06:17:23

Android-给自定义相机增加贴纸的相关文章

android开发——自定义相机开发总结

最近这段时间我一直在开发自定义相机,谷歌了些网上的demo,发现有很多各种各样的问题.最终还是从API的camera类开始学习,进行改进.下面对之前的实现进行一些总结. 官方camera API: http://developer.android.com/guide/topics/media/camera.html 中文翻译: http://www.cnblogs.com/over140/archive/2011/11/16/2251344.html 自定义相机大致实现流程: 预览Camera这

Android—实现自定义相机倒计时拍照

这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: 两个TextView是用来显示提示信息和倒计时的秒数的 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" androi

【Android】自定义相机的实现(支持连续拍照、前后摄像头切换、连续对焦)

~转载请注明http://blog.csdn.net/u013015161/article/details/46921257 介绍 这几天,写了一个自定义照相机的demo,支持连续拍照和摄像头切换.由于自己以前没接触过相关的编程,也算是一个学习的过程,在这里做一下记录,同时也分享出来,并附上源码和工程. 效果如图: 左上角switch切换摄像头,右边snap按钮进行拍照. 一般流程 Android进行拍照,需要调用摄像头类android.hardware.Camera.而要进行预览,则需要用an

Android 手把手带你玩转自定义相机

概述 相机几乎是每个APP都要用到的功能,万一老板让你定制相机方不方?反正我是有点方.关于相机的两天奋斗总结免费送给你. 启动相机的两种方式 1.直接启动系统相机 Intent intent = new Intent(); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); startActivity(intent); 或者指定返回图片的名称mCurrentPhotoFile Intent intent = new Intent(MediaSto

Android Multimedia框架总结(十四)Camera框架初识及自定义相机案例

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52738492 前言:国庆节告一段落,又是新一月,上月主要是围绕MediaPlayer相关展开,从今天开始,开始分析多媒体框架中的Camera模块,看下今天的Agenda: Camera拍照 Camera录像 新API android.hardware.camera2 新旧API特点对比 Camera自定义相机 新API andro

Android调用系统相机、自定义相机、处理大图片

Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人SurfaceView出现黑屏的原因. Android应用拍照的两种方式,下面为两种形式的Demo展示出来的效果.    知识点: 一.调用系统自带的相机应用 二.自定义我们自己的拍照界面 三.关于计算机解析图片原理(如何正确加载图片到Android应用中) 所需

Android自定义相机超详细讲解

Android自定义相机超详细讲解 转载请标明出处: http://blog.csdn.net/vinicolor/article/details/49642861: 由于网上关于Android自定义相机的文章写得不是太详细,Google官方的文档又说得不太容易理解,所以今天我来详细讲解一下Android自定义相机. 这篇文章主要写给一些刚刚接触Android的那些看官方API困难以及不太了解Android机制的同学们,所以熟练开发者可以绕道了. 最近在使用Camera类的时候发现居然被弃用了,

android 自定义相机画面倒立解决方案

有部分手机的影像是倒立的,如何解决这个问题呢? 请看下面 public static void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); android.hardware.Camera

Android 自定义相机Demo源码

Github源码:https://github.com/LinJZong/AndroidProject.git 模仿360相机,图片资源来源于360相机,仅供学习使用.截图如下: 目前完成了拍照.保存.图片压缩.触摸聚焦.拍照成功附带动画效果.闪光灯切换.手势缩放等功能,功能持续更新中. 最近更新的较频繁,就不放csdn了,等功能全做完了再传csdn.介绍下目前主要几个功能类凑够200字. public class CameraView extends SurfaceView implement