1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 自定义Camera实现头像框效果 并裁剪指定区域合成

自定义Camera实现头像框效果 并裁剪指定区域合成

时间:2021-09-22 23:35:31

相关推荐

自定义Camera实现头像框效果 并裁剪指定区域合成

需要一个带框的相机,并且拍好后能合成框和人脸,不过需要人自己凑过去哈哈哈

这两天看了很多博客,然后自己根据自己的要求改了改,基本可以用,调节参数可以获得想要的效果

参考链接在最后面,要是看不懂我的,可以去看看他们的,都挺有参考价值的

效果图:

前一张为拍照界面,绿色的是拍照按钮。。。第二章为保存的照片,叠加了背景,中间是裁剪出来的,最上面一层是图片,一共三层。

实现思路:

自定义相机

在surface界面上加一个自定义的view,画上(或者直接覆盖)这张图片,

拍好后,在照片上裁剪出小恐龙图片大小的区域

选好背景,调好裁剪后的照片和小恐龙的位置,叠加照片

注意点:

裁剪时,比例是按照预览窗口中小恐龙控件的比例去裁剪

由于是自定义的相机,所以预览窗口(Preview)大小和照片(Picture)的大小都需要自己设置过。我这里是给了他两个相同的固定值,使其表现的一样。

需要适配的话,可以修改代码中的changePreviewSize方法,在其中设置预览窗口和照片大小,注意,一定要设置成宽高比例差不多的,不然保存的照片会出现拉伸的问题。

相机的自动聚焦,对于不同的机型可能会出现问题,问题的解决参考链接在最后

对于6.0以后,权限的申请我用的是第三方,EasyPermissions,使用方法见参考链接

不BB了,代码

1.在gradle里添加依赖(EasyPermissions)

implementation 'pub.devrel:easypermissions:1.3.0'

2.加权限

<uses-permission android:name="android.permission.CAMERA"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

3.activity_main.xml

<RelativeLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><FrameLayoutandroid:id="@+id/myFramelayout"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/combine_pic3" /><SurfaceViewandroid:id="@+id/surfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /><com.example.zhang.diycamerademo01.MyViewandroid:id="@+id/myView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center" /></FrameLayout><LinearLayoutandroid:id="@+id/linear"android:layout_width="55dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_marginRight="8dp"android:gravity="center_vertical"><Buttonandroid:id="@+id/takepicture_button"android:layout_width="50dp"android:layout_height="50dp"android:background="@drawable/ic_launcher_background"android:onClick="takepicture" /></LinearLayout></RelativeLayout>

4.MyView.java

public class MyView extends View {//获取屏幕的宽和高。根据屏幕的宽和高来算框的位置private int screenWidth, screenHeight;public MyView(Context context, AttributeSet attrs) {super(context, attrs);}public void setMyParams(int screenWidth, int screenHeight) {this.screenWidth = screenWidth;this.screenHeight = screenHeight;}@Overridepublic void onDraw(Canvas canvas) {Bitmap bg1 = ((BitmapDrawable) getResources().getDrawable(bine_touxiang3)).getBitmap();//Bitmap toghter = ((BitmapDrawable) getResources().getDrawable(bine_toghter1small)).getBitmap();//画一个头像框,正中间canvas.drawBitmap(bg1,(screenWidth-bg1.getWidth())/2,(screenHeight-bg1.getHeight())/2,null);//canvas.drawBitmap(toghter,0,0,null);super.onDraw(canvas);}}

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {private static final String TAG = "TEST";private MyView myView;private Camera camera;private SurfaceView surfaceview;private int screenWidth, screenHeight;private ToneGenerator tone;private Camera.Parameters parameters;//剪切后的图片宽高private int retX, retY;private float radioX,radioY;String[] permissions = new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE};// 声明一个集合,在后面的代码中用来存储用户拒绝授权的权List<String> mPermissionList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//把屏幕设置成横屏setContentView(R.layout.activity_main);//全屏getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);initRequest();WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);screenWidth = wm.getDefaultDisplay().getWidth();screenHeight = wm.getDefaultDisplay().getHeight();initScale();myView = findViewById(R.id.myView);myView.setMyParams(screenWidth, screenHeight);FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) myView.getLayoutParams();layoutParams.width = screenWidth;layoutParams.height = screenHeight;myView.setLayoutParams(layoutParams);initDIYCamera();}private Camera.AutoFocusCallback myAutoFocusCallback = new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {Log.w("print", "聚焦完成,,,,");//聚焦后发出提示音tone = new ToneGenerator(AudioManager.STREAM_MUSIC, ToneGenerator.MAX_VOLUME);tone.startTone(ToneGenerator.TONE_PROP_BEEP2);}};private void initScale(){Bitmap bg3 = ((BitmapDrawable) getResources().getDrawable(bine_touxiang3)).getBitmap();int test1=bg3.getHeight();int test2=bg3.getWidth();radioX=(float) test2/screenWidth;radioY=(float) test1/screenHeight;}private void initDIYCamera() {//当第一次进来时,首先执行这个方法,因为没有授权所以找不到相机//所以需要再授权后再次找到相机myView.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {camera.autoFocus(myAutoFocusCallback);return false;}});camera = Camera.open(0);surfaceview = (SurfaceView) findViewById(R.id.surfaceView);SurfaceHolder holder = surfaceview.getHolder();holder.addCallback(new MySurfaceCallback());holder.setKeepScreenOn(true);// 屏幕常亮 holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);holder.lockCanvas();}private void initRequest() {//使用easyPermissions//先检查是否已经授权if (EasyPermissions.hasPermissions(this, permissions)) {// 已经申请过权限,做想做的事} else {// 没有申请过权限,现在去申请,第二个参数为拒绝后再次申请时弹出的文字EasyPermissions.requestPermissions(this, "快来申请我哦",1, permissions);}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);// 把执行结果的操作给EasyPermissionsEasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);}//申请成功时调用@Overridepublic void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {//请求成功执行相应的操作//比如,举个例子switch (requestCode) {case 1://第一次进来,初始化时camera为空camera.open(0);Toast.makeText(this, "成功获取3个权限", Toast.LENGTH_SHORT).show();break;}}@Overridepublic void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {//比如,举个例子switch (requestCode) {case 1:Toast.makeText(this, "3个权限未被全部同意", Toast.LENGTH_SHORT).show();break;}}private final class MySurfaceCallback implements SurfaceHolder.Callback {@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//当surface的格式或大小发生改变,这个方法就被调用}@Overridepublic void surfaceCreated(SurfaceHolder holder) {try {if (camera==null){camera = Camera.open(0);}camera.stopPreview();camera.release();camera = null;camera = Camera.open(0);//设置相机预览时大小及图片的大小和camera的参数设置changePreviewSize(camera, surfaceview.getWidth(), surfaceview.getHeight());camera.setPreviewDisplay(surfaceview.getHolder());// 开启预览camera.startPreview();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {if (camera != null) {camera.release();camera = null;}}}public void takepicture(View v) {camera.takePicture(mShutterCallback, null, mPictureCallback);}Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {public void onShutter() {}};private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {Bitmap mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);Bitmap mBitmapCut = ImageCrop(mBitmap);Bitmap bg1 = ((BitmapDrawable) getResources().getDrawable(bine_pic1)).getBitmap();Bitmap bg3 = ((BitmapDrawable) getResources().getDrawable(bine_touxiang3)).getBitmap();Bitmap mBitmapFinal = CombineImage(bg1, mBitmapCut, bg3);String rootPath = Environment.getExternalStorageDirectory().toString() + File.separator;File file = new File(rootPath + System.currentTimeMillis() + ".jpg");try {file.createNewFile();BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));press(pressFormat.JPEG, 100, os);//100 是压缩率,100表示不压缩 os.flush();os.close();Toast.makeText(getApplicationContext(), "保存成功", Toast.LENGTH_SHORT).show();} catch (IOException e) {e.printStackTrace();}}};/*** 修改相机的预览尺寸和保存的相片的尺寸以及camera的属性** @param camera相机实例* @param viewWidth 预览的surfaceView的宽* @param viewHeight 预览的surfaceView的高*/protected void changePreviewSize(Camera camera, int viewWidth, int viewHeight) {if (camera == null) {return;}parameters = camera.getParameters();List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();Camera.Size closelySize = null;//储存最合适的尺寸for (Camera.Size size : sizeList) { //先查找preview中是否存在与surfaceview相同宽高的尺寸Log.i(TAG, "preview size.width=" + size.width + " size.height=" + size.height);if ((size.width == viewWidth) && (size.height == viewHeight)) {closelySize = size;}}if (closelySize == null) {// 得到与传入的宽高比最接近的sizefloat reqRatio = ((float) viewWidth) / viewHeight;float curRatio, deltaRatio;float deltaRatioMin = Float.MAX_VALUE;for (Camera.Size size : sizeList) {if (size.width < 1024) continue;//1024表示可接受的最小尺寸,否则图像会很模糊,可以随意修改curRatio = ((float) size.width) / size.height;deltaRatio = Math.abs(reqRatio - curRatio);if (deltaRatio < deltaRatioMin) {deltaRatioMin = deltaRatio;closelySize = size;}}}//设置裁剪后全屏高清的代码// 获取摄像头支持的PictureSize列表List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();List<Camera.Size> middlePicSizeList = new ArrayList<>();//储存比例最接近且与预览窗口最接近大小的尺寸Camera.Size closelyPicSize = null;//储存最合适的照片尺寸for (Camera.Size size : pictureSizeList) { //先查找Pic中是否存在与surfaceview相同宽高的尺寸Log.i(TAG, "pictureSizeList size.width=" + size.width + " size.height=" + size.height);if ((size.width == viewWidth) && (size.height == viewHeight)) {closelyPicSize = size;}}if (closelyPicSize == null) {// 得到与传入的宽高比最接近的PicSizefloat reqRatio = ((float) viewWidth) / viewHeight;float curRatio, deltaRatio;float deltaRatioMin = Float.MAX_VALUE;for (Camera.Size size : pictureSizeList) {if (size.width < 1024) continue;//1024表示可接受的最小尺寸,否则图像会很模糊,可以随意修改curRatio = ((float) size.width) / size.height;deltaRatio = Math.abs(reqRatio - curRatio);if (deltaRatio < deltaRatioMin) {deltaRatioMin = deltaRatio;middlePicSizeList.add(size);}}for (Camera.Size size:middlePicSizeList){float middleSize;Log.i(TAG, "pictureMiddleList size.width=" + size.width + " size.height=" + size.height);float middleSizeMin = Float.MAX_VALUE;middleSize = Math.abs(size.width - closelySize.width);if (middleSize<middleSizeMin){middleSizeMin = middleSize;closelyPicSize=size;}}}if (closelySize != null) {Log.i("changePreviewSize", "预览尺寸修改为:" + closelySize.width + "*" + closelySize.height);//设置大小//parameters.setPreviewSize(closelySize.width, closelySize.height);//由于裁剪是依据preview界面中MyView的比例来算的,所以preview界面和保存的照片的// 宽高比例要一致,才不会导致拉伸parameters.setPreviewSize(1920, 1080);//设置图片的大小,同样可以和preview一样修改,只是现在把他弄成了固定数值parameters.setPictureSize(1920, 1080);//设置自动对焦parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);camera.setParameters(parameters);}}/*** 按图片比例裁切图片*/public Bitmap ImageCrop(Bitmap bitmap) {// 得到图片的宽,高int w = bitmap.getWidth();int h = bitmap.getHeight();//剪切的初始xy位置,原点位置都是根据preview//界面的控件比例来算的int x = (int) ((w-(w * radioX ))/2);int y = (int) ((h-(h * radioY )) /2);//剪切后的宽和高retX = (int) (w * radioX);retY = (int) (h * radioY);Bitmap afterCrop = Bitmap.createBitmap(bitmap, x, y, retX, retY, null, false);return afterCrop;}/*叠加图片*/public Bitmap CombineImage(Bitmap b1, Bitmap b2, Bitmap b3) {//创建一张大小和背景图一致的位图int bgWidth = b1.getWidth();int bgHeight = b1.getHeight();int fgWidth = b2.getWidth(); //前景图宽度,用于后面计算前景图的绘制坐标int fgHeight = b2.getHeight(); //前景图高度,用于后面计算前景图的绘制坐标Bitmap newbmp = Bitmap.createBitmap(bgWidth, bgHeight, Bitmap.Config.RGB_565);//缩放照片int beforeX=b3.getWidth();int beforeY=b3.getHeight();float scaleWidth = ((float) retX) / beforeX;float scaleHeight = ((float) retY) / beforeY;// 取得想要缩放的matrix參數Matrix matrix = new Matrix();matrix.postScale(scaleWidth, scaleHeight);//两个参数未缩放的比例Bitmap afterCrop = Bitmap.createBitmap(b3, 0, 0, beforeX, beforeY, matrix, true);//新建画布Canvas cv = new Canvas(newbmp);//在0,0坐标开始画入bgcv.drawBitmap(b1, 0, 0, null);//开始画入fg,可以从任意位置画入,具体位置自己计算fcv.drawBitmap(b2, (bgWidth - fgWidth) / 2, (bgHeight - fgHeight) / 2, null);//开始画入第三层,可以从任意位置画入,具体位置自己计算fcv.drawBitmap(afterCrop, (bgWidth - fgWidth) / 2, (bgHeight - fgHeight) / 2, null);return newbmp;}}

总结:

这个demo还是很不完整的,很多的细节,比如位置等,都是需要自己看着调的,自己通过改一些东西也能实现大部分功能。

加油!!!哈哈哈

demo下载:/download/jiye111/10626903

参考链接:

自定义相机:/ming_csdn_/article/details/70154381

自动对焦:/yanzi1225627/article/details/8577682

图片叠加功能:/article/138971.htm

EasyPermissions使用方法:/u011138190/article/details/79776902

Fail to connect to camera service的几种原因和解决方法:/wang_shuai_ww/article/details/44560113

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。