相机的使用 --- 拍照,相册导入
来源:互联网 发布:手机数据老是自动打开 编辑:程序博客网 时间:2024/05/21 11:05
一、相机的基本使用
1,获取SurfaceView控件的初始化
surfaceView = (SurfaceView) findViewById(R.id.surface_sv); holder = surfaceView.getHolder(); surfaceView.setBackgroundColor(0); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.addCallback(this);
2,添加CallBack事件监听 @Override public void surfaceCreated(SurfaceHolder holder) { startPreview(camera, holder); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { camera.stopPreview(); startPreview(camera, holder); } @Override public void surfaceDestroyed(SurfaceHolder holder) { releaseCamera(); }
3,预览相机并设置参数 /** * 预览相机 */ private void startPreview(Camera camera, SurfaceHolder holder) { try { setupCamera(camera); camera.setPreviewDisplay(holder); //亲测的一个方法 基本覆盖所有手机 将预览矫正 CameraUtil.getInstance().setCameraDisplayOrientation(this, 0, camera); camera.startPreview(); if (camera.getParameters() != null) { Camera.Size size = camera.getParameters().getPreviewSize(); // 获取预览大小 if (Integer.parseInt(Build.VERSION.SDK) >= 8) { initSurfaceView(size.height, size.width); } else { initSurfaceView(size.width, size.height); } } else { Toast.makeText(MainActivity.this, "无法启动相机", Toast.LENGTH_SHORT).show(); finish(); } } catch (Exception e) { Toast.makeText(MainActivity.this, "无法启动相机", Toast.LENGTH_SHORT).show(); e.printStackTrace(); finish(); } }
/** * 设置 */ private void setupCamera(Camera camera) { try { Camera.Parameters parameters = camera.getParameters(); if (parameters.getSupportedFocusModes().contains( Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); } //这里第三个参数为最小尺寸 getPropPreviewSize方法会对从最小尺寸开始升序排列 取出所有支持尺寸的最小尺寸 Camera.Size previewSize = CameraUtil.getInstance().getPropSizeForHeight(parameters.getSupportedPreviewSizes(), 800); parameters.setPreviewSize(previewSize.width, previewSize.height); Camera.Size pictrueSize = CameraUtil.getInstance().getPropSizeForHeight(parameters.getSupportedPictureSizes(), 800); parameters.setPictureSize(pictrueSize.width, pictrueSize.height); camera.setParameters(parameters); /** * 设置surfaceView的尺寸 因为camera默认是横屏,所以取得支持尺寸也都是横屏的尺寸 * 我们在startPreview方法里面把它矫正了过来,但是这里我们设置设置surfaceView的尺寸的时候要注意 previewSize.height<previewSize.width * previewSize.width才是surfaceView的高度 * 一般相机都是屏幕的宽度 这里设置为屏幕宽度 高度自适应 你也可以设置自己想要的大小 * */ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(screenWidth, screenHeight); surfaceView.setLayoutParams(params); } catch (Exception e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "无法启动相机", Toast.LENGTH_SHORT).show(); finish(); } }
4,纠正和矫正Surfaceview /** * 初始化SurfaceView * * @param camera_w * @param camera_h */ private void initSurfaceView(int camera_w, int camera_h) { int height = MainApplication.getInstance().getHeight() - ScreenUtil.getStatusBarHeight(); int width = MainApplication.getInstance().getWidth(); int w = (int) ((height * camera_w) / camera_h); int h = (int) ((camera_h * width) / camera_w); int h_outer = 0; int w_outer = 0; if (w <= width) { Log.e("", "高度为屏幕高度"); h_outer = height; w_outer = w; } else if (h <= height) { Log.e("", "宽度为屏幕宽度");// .. h_outer = h; w_outer = width; } else { Log.e("", "都不合适========================"); } if (w_outer > 0) { if (w_outer < width || h_outer < height) { //无法铺满全屏 float scale_w = (float) width / (float) w_outer; float scale_h = (float) height / (float) h_outer; float scale = ((scale_h - scale_w) > 0) ? scale_h : scale_w; w_outer = (int) (w_outer * scale); h_outer = (int) (h_outer * scale); } preViewSize[0] = w_outer; preViewSize[1] = h_outer; FrameLayout.LayoutParams lpsurface = new FrameLayout.LayoutParams(w_outer, h_outer);// lpsurface.setMargins((width-w_outer)/2,(height-h_outer)/2,0,0); surfaceView.setLayoutParams(lpsurface); Log.e("111", "相机预览:" + camera_w + "x" + camera_h);//960x1280 Log.e("111", "surfaceview :" + w_outer + "x" + h_outer);//1305x1740 1080x1440 } }
5,合适的地方释放相机资源 /** * 释放相机资源 */ private void releaseCamera() { if (camera != null) { camera.setPreviewCallback(null); camera.stopPreview(); camera.release(); camera = null; } }
此时,相机应该可以正常使用。
【在4中,适配不同的相机预览,便于获取图片不变形】
6,生命周期中的维护
@Override protected void onResume() { super.onResume(); if (camera == null) { camera = getCamera(); if (holder != null) { startPreview(camera, holder); } } } @Override protected void onDestroy() { super.onDestroy(); releaseCamera(); }
/** * 获取Camera实例 * * @return */ private Camera getCamera() { Camera camera = null; try { camera = Camera.open(0); } catch (Exception e) { } return camera; }
7,拍照
private void captrue() { camera.takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { //将data 转换为位图 或者你也可以直接保存为文件使用 FileOutputStream //这里我相信大部分都有其他用处把 比如加个水印 后续再讲解 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); Bitmap saveBitmap = CameraUtil.getInstance().setTakePicktrueOrientation(0, bitmap); bitmap.recycle(); bitmap = Bitmap.createScaledBitmap(saveBitmap, preViewSize[0], preViewSize[1], true); saveBitmap.recycle(); rotateMIV.setmSrc(bitmap); changeToShowPic(); } }); }
二、图片处理 --- 旋转,缩放,截图
1,图片处理的主要实现类,由以下自定义控件实现
@SuppressLint("AppCompatCustomView")public class MatrixImageView extends ImageView { private static final int MODE_NONE = 0x00123;// 默认的触摸模式 private static final int MODE_DRAG = 0x00321;// 拖拽模式 private static final int MODE_ZOOM = 0x00132;// 缩放or旋转模式 private int mode;// 当前的触摸模式 private float preMove = 1F;// 上一次手指移动的距离 private float saveRotate = 0F;// 保存了的角度值 private float rotate = 0F;// 旋转的角度 private float[] preEventCoor;// 上一次各触摸点的坐标集合 private PointF startPointF, midPointF;// 起点、中点对象 private Matrix currentMatrix, savedMatrix;// 当前和保存了的Matrix对象 /** * 新增截图保存功能 * 旋转展示的图片和页面上截图的矩形运算,形成新的图片,将图片保存成为新图片即可! * 1,触发事件 * 2,截图矩形获取 * 3,图片保存 -- 保存路径 */ /** * 保存文件的路径 */ private String fileURL; /** * 单字Code码 -- 文件保存名称 */ private String fontCode; /** * 截图矩形 */ private Rect cutRect; //原始图片 private Bitmap mSrc; //控件的宽度 private int mWidth; // 控件的高度 private int mHeight; private PaintFlagsDrawFilter mDrawFilter; int i = 0; private Context context; public MatrixImageView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; // 初始化 init(); } /** * 初始化 */ private void init() { // 实例化对象 currentMatrix = new Matrix(); savedMatrix = new Matrix(); startPointF = new PointF(); midPointF = new PointF(); mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); // 模式初始化 mode = MODE_NONE; Drawable drawable = getDrawable(); try { mSrc = drawableToBitamp(drawable); } catch (Exception e) { mSrc = null; } if (mSrc == null) { //如果为空,赋默认值 mSrc = BitmapFactory.decodeResource(getResources(), R.mipmap.desc6, null); } } /** * 计算控件的高度和宽度 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 设置宽度 int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); //match_parent或者设置的精确值获取 //MeasureSpec.EXACTLY if (specMode == MeasureSpec.EXACTLY) { mWidth = specSize; } else { // 由图片决定的宽 //getPaddingLeft(),getPaddingRight()这两个值是控件属性的向内偏移的距离值,所以的一起计算 //区别于layout_marginLeft,两个控件的左间距值设置 int desireByImg = getPaddingLeft() + getPaddingRight() + mSrc.getWidth(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { //所以最小的值,宽度的话是左右内偏移距离之和 mWidth = Math.min(desireByImg, specSize); } else mWidth = desireByImg; } // 设置高度,部分解释同上 specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); //match_parent或者设置的精确值获取 //MeasureSpec.EXACTLY if (specMode == MeasureSpec.EXACTLY) { mHeight = specSize; } else { int desire = getPaddingTop() + getPaddingBottom() + mSrc.getHeight(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { mHeight = Math.min(desire, specSize); } else mHeight = desire; } //计算好的宽度以及高度是值,设置进去 setMeasuredDimension(mWidth, mHeight); } //drawable转bitmap private Bitmap drawableToBitamp(Drawable drawable) { //从控件的src获取背景,也是drawable文件获取 if (drawable instanceof BitmapDrawable) { BitmapDrawable bd = (BitmapDrawable) drawable; return bd.getBitmap(); } //如果没有绘图一个,只不过是空白的图片// int w = drawable.getIntrinsicWidth();// int h = drawable.getIntrinsicHeight(); int w = MainApplication.getInstance().getWidth(); int h = MainApplication.getInstance().getHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); drawable.draw(canvas); return bitmap; } final Paint paint = new Paint(); @Override protected void onDraw(Canvas canvas) { //消除锯齿, 图片旋转后的锯齿消除不成功,实在不行图片边缘加一些白色像素点 canvas.setDrawFilter(mDrawFilter); //画经过Matrix变化后的图 canvas.drawBitmap(mSrc, currentMatrix, null); /** * 测试切图区域和显示区域一致 */ if (cutRect != null) { paint.setColor(Color.RED); canvas.drawRect(cutRect, paint); } } /** * 裁剪图片并保存到本地 */ public boolean cutBitmapAndSave() { try { Bitmap out = Bitmap.createBitmap(cutRect.width(), cutRect.height(), Bitmap.Config.ARGB_8888); //画布在右上角 Canvas outC = new Canvas(out); //平移画布到和截图区域一致【注意反向】 outC.translate(-cutRect.left, -cutRect.top); outC.drawBitmap(mSrc, currentMatrix, null); String fileParent = Environment.getExternalStorageDirectory().toString() + "/camera/demo/"; File fileDirectory = new File(fileParent); if (!fileDirectory.exists()) { boolean mkdirs = fileDirectory.mkdirs(); Log.i("parent", mkdirs + ""); if (mkdirs) { File file = new File(fileParent + System.currentTimeMillis() + ".png"); if (!file.exists()) { boolean newFile = file.createNewFile(); if (newFile) { BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20); return true; } else { Toast.makeText(context, "创建文件失败", Toast.LENGTH_SHORT).show(); return false; } } else { BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20); return true; } } else { Toast.makeText(context, "创建文件夹失败", Toast.LENGTH_SHORT).show(); return false; } } else { File file = new File(fileParent + System.currentTimeMillis() + ".png"); if (!file.exists()) { boolean newFile = file.createNewFile(); if (newFile) { BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20); return true; } else { Toast.makeText(context, "创建文件失败", Toast.LENGTH_SHORT).show(); return false; } } else { BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20); return true; } }// return true; } catch (Exception e) { e.printStackTrace(); return false; } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN:// 单点接触屏幕时 savedMatrix.set(currentMatrix); startPointF.set(event.getX(), event.getY()); //单点触摸是移动模式 mode = MODE_DRAG; preEventCoor = null; break; case MotionEvent.ACTION_POINTER_DOWN:// 第二个点接触屏幕时 preMove = calSpacing(event); if (preMove > 10F) { savedMatrix.set(currentMatrix); // 计算两个触摸点的中点坐标 calMidPoint(midPointF, event); //两点是旋转或者缩放模式 mode = MODE_ZOOM; } preEventCoor = new float[4]; preEventCoor[0] = event.getX(0); preEventCoor[1] = event.getX(1); preEventCoor[2] = event.getY(0); preEventCoor[3] = event.getY(1); saveRotate = calRotation(event); break; case MotionEvent.ACTION_UP:// 单点离开屏幕时 case MotionEvent.ACTION_POINTER_UP:// 第二个点离开屏幕时 mode = MODE_NONE; preEventCoor = null; break; case MotionEvent.ACTION_MOVE:// 触摸点移动时 /* * 单点触控拖拽平移 */ if (mode == MODE_DRAG) { currentMatrix.set(savedMatrix); float dx = event.getX() - startPointF.x; float dy = event.getY() - startPointF.y; currentMatrix.postTranslate(dx, dy); } /* * 两点触控拖放旋转 */ else if (mode == MODE_ZOOM && event.getPointerCount() == 2) { float currentMove = calSpacing(event); currentMatrix.set(savedMatrix); /* * 指尖移动距离大于10F缩放 */ if (currentMove > 10F) { float scale = currentMove / preMove; currentMatrix.postScale(scale, scale, midPointF.x, midPointF.y); } /* * 保持两点时旋转 */ if (preEventCoor != null) { rotate = calRotation(event); r = rotate - saveRotate; currentMatrix.postRotate(r, getMeasuredWidth() / 2, getMeasuredHeight() / 2); } } break; } setImageMatrix(currentMatrix); return true; } float r; /** * 计算两个触摸点间的距离 */ private float calSpacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } /** * 计算两个触摸点的中点坐标 */ private void calMidPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } /** * 计算旋转角度 * * @param * @return 角度值 */ private float calRotation(MotionEvent event) { double deltaX = (event.getX(0) - event.getX(1)); double deltaY = (event.getY(0) - event.getY(1)); double radius = Math.atan2(deltaY, deltaX); return (float) Math.toDegrees(radius); } /** * 设置原始图片 * * @param mSrc */ public void setmSrc(Bitmap mSrc) { this.mSrc = mSrc; currentMatrix = new Matrix(); requestLayout(); postInvalidate(); } public void setFileURL(String fileURL) { this.fileURL = fileURL; } public void setFontCode(String fontCode) { this.fontCode = fontCode; } public void setCutRect(Rect cutRect) { this.cutRect = cutRect; } /** * 更新当前Matrix */ public void setCurrentMatrix() { this.currentMatrix = new Matrix(); }}
2,控件的正常使用,需要设置原始图片的默认值
if (mSrc == null) { //如果为空,赋默认值 mSrc = BitmapFactory.decodeResource(getResources(), R.mipmap.desc6, null); } /** * 设置原始图片 * * @param mSrc */ public void setmSrc(Bitmap mSrc) { this.mSrc = mSrc; currentMatrix = new Matrix(); requestLayout(); postInvalidate(); }
在切换到显示图片时,对控件设置原始图片,并刷新显示,绘制。
3,在Ondraw()中绘制裁剪区域
/** * 测试切图区域和显示区域一致 */ if (cutRect != null) { paint.setColor(Color.RED); canvas.drawRect(cutRect, paint); }
在Activity中获取裁剪区域大小:
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); cutRectIV.post(new Runnable() { @Override public void run() { Rect viewRect = new Rect(); int[] location = new int[2]; int[] location1 = new int[2];// cutRectIV.getLocalVisibleRect(viewRect); cutRectIV.getLocationOnScreen(location); contentTakePicRL.getLocationOnScreen(location1); /** * getLeft() /getTop() /getBottom() /getRight() * 都是相对父控件的位置 */ //设置截图区域 viewRect.left = cutRectIV.getLeft(); viewRect.top = contentTakePicRL.getTop(); viewRect.bottom = cutRectIV.getBottom() + contentTakePicRL.getTop(); viewRect.right = cutRectIV.getRight(); rotateMIV.setCutRect(viewRect); } }); }
特别注意:gettop(),getLeft()都是相对父布局,在计算时,父布局与实际所在的位置一定要清晰。
三、相册导入
1,相册导入一般使用系统相册导入
注意点:
(1)自己构建相册需要管理相册内存;
(2)实际倒入效果并不会有很好的提升。
导入时注意对图片进行压缩:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //获取图片路径 if (requestCode == IMAGE && resultCode == Activity.RESULT_OK && data != null) { Uri selectedImage = data.getData(); String[] filePathColumns = {MediaStore.Images.Media.DATA}; Cursor c = getContentResolver().query(selectedImage, filePathColumns, null, null, null); c.moveToFirst(); int columnIndex = c.getColumnIndex(filePathColumns[0]); String imagePath = c.getString(columnIndex); int[] size = getImageWidthHeight(imagePath); int width = screenWidth; //导入进来,让宽适配屏幕宽度 int height = (int) (((float) (screenWidth * size[1])) / ((float) size[0])); Bitmap bitmap = BitmapUtil.getBitmapBySize(imagePath, width, height); rotateMIV.setmSrc(bitmap); changeToShowPic(); c.close(); } }
现获取图片大小,再依据最后显示大小,压缩图片后再显示。
public static Bitmap getBitmapBySize(String path, int width, int height) { BitmapFactory.Options option = new BitmapFactory.Options(); option.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, option); option.inSampleSize = computeSampleSize(option, -1, width * height); option.inJustDecodeBounds = false; Bitmap bitmap = null; try { bitmap = BitmapFactory.decodeFile(path, option); if (bitmap.getWidth() != width || bitmap.getHeight() != height) { Matrix mx = new Matrix(); mx.setScale((float) width / (float) bitmap.getWidth(), (float) height / (float) bitmap.getHeight()); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mx, true); } } catch (Exception e) { e.printStackTrace(); } return bitmap; }
2,保存图片
在图片保存中,存在已经设置权限了,还是创建目录不成功的情形,需要动态设置权限。
onCreate()中添加权限动态访问:
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android .Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
在权限获取结果中处理文件夹的创建管理:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case 1: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { //创建文件夹 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File file = new File(Environment.getExternalStorageDirectory().toString() + "/camera/demo/"); if (!file.exists()) { Log.d("fileCreate", "file create:" + file.mkdirs()); } } break; } } }
3,有些手机有虚拟导航,为保持页面的一致,需要管控页面。
@SuppressLint("NewApi") private void hideVirtualGuideBar() { //隐藏不显示 Window window = getWindow(); WindowManager.LayoutParams params = window.getAttributes(); params.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE; window.setAttributes(params); }
因为需求的关系,获取到的图片,还需要做二值化。
且听下回分解。
这里是简化版本呐
传送门 -- 源码哦
偶尔会依旧很迷茫,偶尔会找不到自身的定位。总是在跌跌撞撞中找寻。。。
阅读全文
0 0
- 相机的使用 --- 拍照,相册导入
- 相机拍照,相册照片
- 微信小程序从本地相册选择图片或使用相机拍照之我的理解
- 使用系统相册,相机拍照裁剪图片--记录
- IOS 永久保存相册或相机拍照的图片
- Android相机、相册获取图片,解决相机拍照图片被压缩模糊的情况
- overridePendingTransition的使用与调用相机拍照
- iOS10 相机相册等权限的使用
- 兼容7.0的相机相册使用
- 调用系统相机拍照和选择相册
- 调用系统相册和系统相机拍照
- 从本地选取相册和相机拍照
- 实现相机拍照和相册裁剪
- 启动相机功能拍照之后刷新相册
- Android调用系统相册和相机拍照
- 79使用相机拍照
- 使用相机实现拍照
- 相机、相册使用
- UML-基础
- 58. Length of Last Word
- 电阻换算表
- 判断手指上下左右的滑动距离
- 链接
- 相机的使用 --- 拍照,相册导入
- 集合
- 【读书笔记】《What is Mathematics》第一章:自然数
- 流程图
- webstorm git配置
- 卷积神经网络的网络结构——以LeNet-5为例
- 云星数据---Scala实战系列(精品版)】:Scala入门教程027-Scala实战源码-Scala 的特质 (接口)03
- 文档
- 设计模式-模版方法模式