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项目源码地址:点击【项目源码】
如果喜欢本文,请支持我。
- Android-给自定义相机增加贴纸
- Android开源项目:贴纸标签相机,快给你的图片贴上贴纸打上标签吧!~
- Android开源项目:贴纸标签相机,快给你的图片贴上贴纸打上标签吧!~
- Android Camera API/Camera2 API 相机预览及滤镜、贴纸等处理
- Android 自定义相机
- android自定义相机
- Android自定义相机
- android 自定义相机
- android 自定义相机
- android 自定义相机
- android自定义相机
- android自定义相机功能
- Android-自定义相机
- 自定义相机Camera-android
- android 自定义相机
- Android自定义相机
- Android自定义相机--2
- Android自定义相机
- JavaScript 计算笛卡尔积
- 丑数35
- Weex Android SDK源码分析之Module(stream)
- requestWindowFeature(Window.FEATURE_NO_TITLE)无效解决方法
- 【工具类】Unicode与中文互转
- Android-给自定义相机增加贴纸
- 矩阵求导常用公式
- linux磁盘IO信息查看方式
- 找出一个字符串中第一个只出现一次的字符
- DRP(五)——JSP与JavaBean
- 常见的作业调度和进程调度算法总结
- 基于TCP的Socket 编程
- c/c++字符串处理大集合
- Android L**MM music Service笔记