安卓自定义照相机(横竖屏感应)
发布日期:2021-06-28 19:37:25
浏览次数:2
分类:技术文章
本文共 24175 字,大约阅读时间需要 80 分钟。
实现功能:
通过安卓自带的SensorManager(传感器)来判断,当用户横向拍照时照片出来是竖向显示的
实现代码:
final Intent intent = new Intent(TakePic_Activity.this, PhotoActivity.class);String url=Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "PICvkl" + File.separator + szCarNum + szCarNumType + StrCode + ".jpg";intent.putExtra("uri", url);startActivityForResult(intent,55);
PhotoActivity
public class PhotoActivity extends Activity { private SurfaceView surfaceView; //照片显示surfaceview private Button takephotoBtn; //拍照按钮 private TextView nameTv; //项目名 private SurfaceHolder surfaceHolder; //surfaceview控制器 private Camera camera; private ImageView lightImg; //闪光灯 private boolean isLightOpen = false; //闪光灯是否开启 ProgressDialog dialog; private String uri = ""; /** * 新加缩放控件 */ private SeekBar mZoomSeekBar; private ScreenSwitchUtils instance;//传感器工具类对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_photo); instance = ScreenSwitchUtils.init(getApplicationContext()); initView(); initListener(); } @Override protected void onStart() { super.onStart(); instance.start(this); } @Override protected void onStop() { super.onStop(); instance.stop(); } private void initView() { if (getIntent() != null) { uri = getIntent().getStringExtra("uri"); Log.e("lcb", "照片的保留地方:" + uri); } nameTv = (TextView) findViewById(R.id.name_tv); surfaceView = (SurfaceView) findViewById(R.id.surface_view); takephotoBtn = (Button) findViewById(R.id.takephoto_btn); lightImg = (ImageView) findViewById(R.id.light_img); mZoomSeekBar = (SeekBar) findViewById(R.id.zoomSeekBar); //设置标题文字 (请横拍)项目名: nameTv.setText("支持横竖拍,竖拍左右倾斜不要超过45度\n横拍请向左,保证\"拍照\"按钮在右边"); } private void initListener() { surfaceHolder = surfaceView.getHolder(); //设置Holder surfaceHolder.addCallback(surfaceCallback); //加入回调 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //设置缓冲类型 // 图片大小在200K-600K之间 (检测线的规格是2080X1560) surfaceHolder.setFixedSize(3264, 2448); //闪光灯开关控制器 lightImg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isLightOpen) { if (camera != null) { Camera.Parameters parameters = camera.getParameters(); parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); camera.setParameters(parameters); camera.startPreview(); } isLightOpen = true; lightImg.setImageResource(R.drawable.light_down); } else { if (camera != null) { Camera.Parameters parameters = camera.getParameters(); parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); camera.setParameters(parameters); camera.startPreview(); } isLightOpen = false; lightImg.setImageResource(R.drawable.light_up); } } }); //点击拍照按钮进行拍照。 takephotoBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { /* * shutter:快门被按下 * raw:相机所捕获的原始数据 * jpeg:相机处理的数据 */ takePhoto(); } }); } Camera.PictureCallback pictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { //保存图片 new SavePictureTask().execute(data, null, null); //开始预览 camera.startPreview(); } }; /** * 3、异步保存图片。 */ class SavePictureTask extends AsyncTask{ @Override protected String doInBackground(byte[]... params) { File file = new File(uri); try {// BitmapFactory.Options options=new BitmapFactory.Options();// options.inSampleSize=8; Bitmap bitmap = BitmapFactory.decodeByteArray(params[0], 0, params[0].length); //水印格式:流水号:XXX 车辆识别代号:XXX 时间:XXX (如果有车牌号的,则把车牌号也输入上去)查验员代号:XXX 红色 左上方 Log.v("lcb","当前角度:"+instance.getOrientation()+" 是否竖屏:"+instance.isPortrait()); if (instance.isPortrait()) { /*如果是竖屏压缩bitmap时候设置方向*/ Matrix matrix = new Matrix(); matrix.reset(); matrix.postRotate(90); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } FileOutputStream fos = new FileOutputStream(file.getPath()); //图片压缩到70% //如果第一张,清楚 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); //关流 bitmap.recycle(); //回收bitmap资源 dialog.dismiss(); setResult(Activity.RESULT_OK, null); finish(); } catch (Exception e) { Log.e("lcb", "photo156:" + e.toString()); } return null; } } /** * SurfaceHolder回调函数。重写SurfaceHolder.Callback接口的surfaceCreated、surfaceChanged、surfaceDestroyed方法。 */ SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() { private static final String TAG = "CameraPreview"; public void surfaceCreated(SurfaceHolder holder) { camera = Camera.open(); //打开摄像头 try { camera.setDisplayOrientation(90); //设置camera预览的角度,因为默认图片是倾斜90度的 camera.setPreviewDisplay(holder); //设置holder主要是用于surfaceView的图片的实时预览,以及获取图片等功能 } catch (IOException e) { camera.release(); //释放 camera = null; } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {// Camera.Parameters parameters = camera.getParameters();// parameters.setPictureSize(3264, 2448);// camera.setParameters(parameters);// camera.startPreview(); //开始预览// camera.autoFocus(autoFocusCallback); //当SurfaceView尺寸变化时(包括设备横屏竖屏改变时时),需要重新设定相关参数 if (holder.getSurface() == null) { //检查SurfaceView是否存在 return; } //改变设置前先关闭相机 try { camera.stopPreview(); } catch (Exception e) { e.printStackTrace(); } //使用最佳比例配置重启相机 try { camera.setPreviewDisplay(holder); Camera.Parameters parameters = camera.getParameters(); Camera.Size size = getBestPreviewSize(width, height); parameters.setPreviewSize(size.width, size.height); parameters.setPictureSize(3264, 2448); camera.setParameters(parameters); camera.startPreview(); } catch (Exception e) { Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { camera.stopPreview(); // stop preview camera.release(); // Release camera resources camera = null; } }; /** * 拍照操作 */ private void takePhoto() { try { camera.takePicture(null, null, pictureCallback); } catch (Exception e) { e.printStackTrace(); Log.e("lcb", "拍照操作报错:" + e.toString()); } if (dialog == null) { dialog = new ProgressDialog(PhotoActivity.this); dialog.setMessage("图片处理中……"); } dialog.show(); // 2017/4/26 dialog十秒没关,自动关掉dialog new Thread() { @Override public void run() { super.run(); try { sleep(10000); if (!PhotoActivity.this.isFinishing()) { dialog.dismiss(); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } private Camera.Size getBestPreviewSize(int width, int height) { Camera.Size result = null; final Camera.Parameters p = camera.getParameters(); //特别注意此处需要规定rate的比是大的比小的,不然有可能出现rate = height/width,但是后面遍历的时候,current_rate = width/height,所以我们限定都为大的比小的。 float rate = (float) Math.max(width, height) / (float) Math.min(width, height); float tmp_diff; float min_diff = -1f; for (Camera.Size size : p.getSupportedPreviewSizes()) { float current_rate = (float) Math.max(size.width, size.height) / (float) Math.min(size.width, size.height); tmp_diff = Math.abs(current_rate - rate); if (min_diff < 0) { min_diff = tmp_diff; result = size; } if (tmp_diff < min_diff) { min_diff = tmp_diff; result = size; } } return result; } /** * 记录是拖拉照片模式还是放大缩小照片模式 */ private static final int MODE_INIT = 0; /** * 放大缩小照片模式 */ private static final int MODE_ZOOM = 1; private int mode = MODE_INIT;// 初始状态 private float startDis; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override public boolean onTouchEvent(MotionEvent event) { /** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */ switch (event.getAction() & MotionEvent.ACTION_MASK) { // 手指压下屏幕 case MotionEvent.ACTION_DOWN: mode = MODE_INIT; break; case MotionEvent.ACTION_POINTER_DOWN: //如果mZoomSeekBar为null 表示该设备不支持缩放 直接跳过设置mode Move指令也无法执行 if (mZoomSeekBar == null) return true; //移除token对象为mZoomSeekBar的延时任务 mHandler.removeCallbacksAndMessages(mZoomSeekBar);// mZoomSeekBar.setVisibility(View.VISIBLE); mZoomSeekBar.setVisibility(View.GONE); mode = MODE_ZOOM; /** 计算两个手指间的距离 */ startDis = spacing(event); break; case MotionEvent.ACTION_MOVE: if (mode == MODE_ZOOM) { //只有同时触屏两个点的时候才执行 if (event.getPointerCount() < 2) return true; float endDis = spacing(event);// 结束距离 //每变化10f zoom变1 int scale = (int) ((endDis - startDis) / 10f); if (scale != 0) { int zoom = getZoom() + scale; //zoom不能超出范围 if (zoom > getMaxZoom()) zoom = getMaxZoom(); if (zoom < 0) zoom = 0; setZoom(zoom); mZoomSeekBar.setProgress(zoom); //将最后一次的距离设为当前距离 startDis = endDis; } } break; // 手指离开屏幕 case MotionEvent.ACTION_UP: if (mode != MODE_ZOOM) { //设置聚焦 Point point = new Point((int) event.getX(), (int) event.getY()); onCameraFocus(point); } else { //ZOOM模式下 在结束两秒后隐藏seekbar 设置token为mZoomSeekBar用以在连续点击时移除前一个定时任务 mHandler.postAtTime(new Runnable() { @Override public void run() { // TODO Auto-generated method stub mZoomSeekBar.setVisibility(View.GONE); } }, mZoomSeekBar, SystemClock.uptimeMillis() + 2000); } break; } return true; } /** * 两点的距离 */ private float spacing(MotionEvent event) { if (event == null) { return 0; } 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 onCameraFocus(final Point point) { onCameraFocus(point, false); } /** * @param needDelay 是否需要延时 * @author Longchengbin * @description 相机对焦 * @since 2020-11-2 10:02 **/ public void onCameraFocus(final Point point, boolean needDelay) { if (camera == null) { return; }// getMaxNumFocusAreas:获取支持的对焦区域的个数// setFocusAreas:设置对焦区域列表// getFocusAreas:获取对焦区域列表// getMaxNumMeteringAreas: 获取支持的测光区域的个数// setMeteringAreas:设置测光区域列表// getMeteringAreas:获取测光区域列表 Camera.Parameters parameters = camera.getParameters(); //不支持设置自定义聚焦,则使用自动聚焦,返回 if (parameters.getMaxNumFocusAreas() <= 0) { camera.autoFocus(autoFocusCallback); return; } List areas = new ArrayList (); int left = point.x - 30; int top = point.y - 30; int right = point.x + 30; int bottom = point.y + 30; left = left < -1000 ? -1000 : left; top = top < -1000 ? -1000 : top; right = right > 1000 ? 1000 : right; bottom = bottom > 1000 ? 1000 : bottom; areas.add(new Camera.Area(new Rect(left, top, right, bottom), 100)); parameters.setFocusAreas(areas); camera.cancelAutoFocus();//结束上一次的对焦操作,不管是有没有完成对焦 try { camera.setParameters(parameters); } catch (Exception e) { Log.e("lcb", "相机对焦报错:" + e.toString()); } camera.autoFocus(autoFocusCallback);//执行一次对焦操作,通过Camera.AutoFocusCallback返回对焦结果 } /** * 当前缩放 */ private int mZoom; public int getMaxZoom() { if (surfaceView == null) return -1; Camera.Parameters parameters = camera.getParameters(); if (!parameters.isZoomSupported()) return -1; return parameters.getMaxZoom() > 40 ? 40 : parameters.getMaxZoom(); } public void setZoom(int zoom) { if (camera == null) return; Camera.Parameters parameters; //注意此处为录像模式下的setZoom方式。在Camera.unlock之后,调用getParameters方法会引起android框架底层的异常 //stackoverflow上看到的解释是由于多线程同时访问Camera导致的冲突,所以在此使用录像前保存的mParameters。 parameters = camera.getParameters(); if (!parameters.isZoomSupported()) return; parameters.setZoom(zoom); camera.setParameters(parameters); mZoom = zoom; } public int getZoom() { return mZoom; } /** * @author Longchengbin * @description 自动对焦回调 * @since 2020-11-2 10:06 **/ Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean b, final Camera camera) { if (b) { camera.cancelAutoFocus(); new Thread() { @Override public void run() { super.run(); try { sleep(1000); } catch (Exception e) { e.printStackTrace(); } try { if (!PhotoActivity.this.isFinishing()) { // TODO: 2017/7/18 三星手机长时间自动对焦失败,取消自动对焦 camera.autoFocus(autoFocusCallback); } } catch (Exception e) { e.printStackTrace(); Log.e("lcb", "自动对焦回调报错:" + e.toString()); } } }.start(); } } };}
R.layout.activity_photo
ScreenSwitchUtils
public class ScreenSwitchUtils { private int mOrientation; private static final String TAG = "test"; private volatile static ScreenSwitchUtils mInstance; private Activity mActivity; // 是否是竖屏 private boolean isPortrait = true; private SensorManager sm; private OrientationSensorListener listener; private Sensor sensor; private SensorManager sm1; private Sensor sensor1; private OrientationSensorListener1 listener1; /** * 返回ScreenSwitchUtils单例 **/ public static ScreenSwitchUtils init(Context context) { if (mInstance == null) { synchronized (ScreenSwitchUtils.class) { if (mInstance == null) { mInstance = new ScreenSwitchUtils(context); } } } return mInstance; } private ScreenSwitchUtils(Context context) { Log.d(TAG, "初始化监听"); // 注册重力感应器,监听屏幕旋转 sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); if (sm != null) { sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); } //当前倾斜角度赋值 @SuppressLint("HandlerLeak") Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case 888: int orientation = msg.arg1; mOrientation = orientation;//当前倾斜角度赋值 if (orientation > 45 && orientation < 135) { if (isPortrait) { isPortrait = false; Log.w("test", "切换成横屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait()); } } else if (orientation > 135 && orientation < 225) { if (!isPortrait) { isPortrait = true; Log.e("test", "切换成竖屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait()); } } else if (orientation > 225 && orientation < 315) { if (isPortrait) { isPortrait = false; Log.w("test", "切换成横屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait()); } } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) { if (!isPortrait) { isPortrait = true; Log.e("test", "切换成竖屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait()); } } break; default: break; } } }; listener = new OrientationSensorListener(mHandler); // 根据 旋转之后/点击全屏之后 两者方向一致,激活sm. sm1 = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); if (sm1 != null) { sensor1 = sm1.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); } listener1 = new OrientationSensorListener1(); } public void start(Activity activity) { Log.d(TAG, "开始监听"); mActivity = activity; sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI); } public void stop() { Log.d(TAG, "停止监听"); sm.unregisterListener(listener); sm1.unregisterListener(listener1); } /** * 手动横竖屏切换方向 */ public void toggleScreen() { sm.unregisterListener(listener); sm1.registerListener(listener1, sensor1, SensorManager.SENSOR_DELAY_UI); if (isPortrait) { isPortrait = false; // 切换成横屏 mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { isPortrait = true; // 切换成竖屏 mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } } /*当前是否竖屏*/ public boolean isPortrait() { return this.isPortrait; } /** * 重力感应监听者 */ public class OrientationSensorListener implements SensorEventListener { private static final int _DATA_X = 0; private static final int _DATA_Y = 1; private static final int _DATA_Z = 2; public static final int ORIENTATION_UNKNOWN = -1; private Handler rotateHandler; public OrientationSensorListener(Handler handler) { rotateHandler = handler; } public void onAccuracyChanged(Sensor arg0, int arg1) { } public void onSensorChanged(SensorEvent event) { float[] values = event.values; int orientation = ORIENTATION_UNKNOWN; float X = -values[_DATA_X]; float Y = -values[_DATA_Y]; float Z = -values[_DATA_Z]; float magnitude = X * X + Y * Y; // Don't trust the angle if the magnitude is small compared to the y // value if (magnitude * 4 >= Z * Z) { // 屏幕旋转时 float OneEightyOverPi = 57.29577957855f; float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi; orientation = 90 - Math.round(angle); // normalize to 0 - 359 range while (orientation >= 360) { orientation -= 360; } while (orientation < 0) { orientation += 360; } } if (rotateHandler != null) { rotateHandler.obtainMessage(888, orientation, 0).sendToTarget(); } } } public class OrientationSensorListener1 implements SensorEventListener { private static final int _DATA_X = 0; private static final int _DATA_Y = 1; private static final int _DATA_Z = 2; public static final int ORIENTATION_UNKNOWN = -1; public OrientationSensorListener1() { } public void onAccuracyChanged(Sensor arg0, int arg1) { } public void onSensorChanged(SensorEvent event) { float[] values = event.values; int orientation = ORIENTATION_UNKNOWN; float X = -values[_DATA_X]; float Y = -values[_DATA_Y]; float Z = -values[_DATA_Z]; float magnitude = X * X + Y * Y; //如果幅度比y值小,则不要相信角度 if (magnitude * 4 >= Z * Z) { // 屏幕旋转时 float OneEightyOverPi = 57.29577957855f; float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi; orientation = 90 - Math.round(angle); // normalize to 0 - 359 range while (orientation >= 360) { orientation -= 360; } while (orientation < 0) { orientation += 360; } } if (orientation > 225 && orientation < 315) {// 检测到当前实际是横屏 if (!isPortrait) { sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI); sm1.unregisterListener(listener1); } } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {// 检测到当前实际是竖屏 if (isPortrait) { sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI); sm1.unregisterListener(listener1); } } } } /*返回当前角度*/ public int getOrientation() { return mOrientation; }}
转载地址:https://blog.csdn.net/xxdw1992/article/details/109449461 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
表示我来过!
[***.240.166.169]2024年04月21日 12时35分38秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
java中的二进制基础(慕课笔记)
2019-04-29
利用java socket和sampled实现点对点即时语音通信
2019-04-29
XML四种解析(慕课笔记)
2019-04-29
14javaSocket应用(慕课笔记)
2019-04-29
MyBatis输入映射、输出映射、动态SQL、关联关系、Spring集成加强笔记
2019-04-29
EXT的combobox的store动态加载固定DATA
2019-04-29
各种工作笔记
2019-04-29
回复 sql 相关
2019-04-29
Vue框架移动端开发组件集合
2019-04-29
Vue2.0图片上传及图片压缩自定义H5图片上传组件
2019-04-29
http请求头、请求状态码、http响应头详解
2019-04-29
解决Unknown host 'jcenter.bintray.com'问题
2019-04-29
HBuilder快速搭建H5+应用
2019-04-29
PMP读书笔记(第2章)
2019-04-29
PMP读书笔记(第4章)
2019-04-29
RedisTemplate value序列化导致的问题
2019-04-29
支付宝回调接口验签失败
2019-04-29
关于码云开源项目SpringBootAdmin多数据源配置
2019-04-29
jeesite使用心得(一)
2019-04-29