Android自定义相机填坑随笔
笔者一个小小的Android开发,于某厂辛勤的拧螺丝。日常面对如洪水猛兽般的需求大潮,坚守自己一方微不足道的人权小岛!@#¥%……
牢骚差不多了,开始进入正式吐槽……尊敬的观众姥爷没听错,奏是继续吐槽。
随着笔者公司的战略重心迁移,我们开展了新的业务线。因业务需求中,我需要集成自定义相机,于是开始了旷日持久的自定义相机趟坑之旅。作为模块集成在第三方app中,沿用了第三方app配置,由于第三方适配机型版本较低,兼容性问题也是相当的多。回归到自定义相机的开发,简单叙述下开发相机过程踩过的大大小小的坑。
1.照片转置问题
在项目伊始,需求尚不明确,我们调用自定义相机获取照片。在某些机型上照片出现了旋转,由于测试设备匮乏(直至笔者今日都没解决),几经周折,找到了问题机型最多三星测试机,根据图片MetaData的旋转角将图片做相应的还原。
**
* 读取图片的旋转的角度
*
* @param path 图片绝对路径
* @return 图片的旋转角度
*/
public static int getBitmapDegree(String path) {
int degree = 0;
try {
// 从指定路径下读取图片,并获取其EXIF信息
ExifInterface exifInterface = new ExifInterface(path);
// 获取图片的旋转信息
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
MetaData仅仅在相机返回图片字节流生成的文件信息中能获取,一经压缩转换成bitmap之后就会丢失。笔者取到的旋转角始终为0,让笔者的胃很是翻江倒海。最后经过排查找到图片确实经过了转换。于是乎只要照片重置生成旋转角0的照片不就问题即将迎刃而解了?青年你的九九八十一难怎可如此简简单单就给你来个痛快,只有更多细腻的阻绊使你的开发之路多姿多彩。
2.相机重力问题
看到在三星测试机上转置处理完成,笔者喜形于色忍不住笑出猪叫来。可笔者拿起测试机拍摄了一张横拍照片,纳尼扩类,这就给转成一张竖屏照片了哈?!歪斜的图片笔者不自然的随着皂片歪起了脖子,久病之下的颈椎又刺激了中枢神经。所以说是笔者too young too navi,没有看出bug小妖背后隐藏的诸路仙君。由于是竖屏有拍摄问题,而横屏拍摄本身就是将照片做了旋转处理,如果仅仅按照皂片metadata做重置还原,只会将横屏照片还原为竖屏照片。
根据重力判断一下拍摄时是否是横屏,再做还原应该就可以了。于是乎笔者根据按下快门时重力做了相应处理。可为了适配横竖屏的情况,笔者在加入了横屏布局。也不得不因此委屈求全通过宽高判断屏幕朝向。
DisplayMetrics dm = new DisplayMetrics();
CameraActivity.this.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
if (height > width){
// 竖屏
isVertiPhoto = true;
}else{
// 横屏
isVertiPhoto = false;
}
终于旷日持久的相机大战中,笔者获得阶段性胜利。胜利的成果是喜悦的,胜利的路程是曲折的,曲折绝不是一时的。没错,在机型适配上,自定义相机继续绊了笔者一脚。
3.API判断错误
@SuppressWarnings("WrongConstant")
public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (isInEditMode()) {
mCallbacks = null;
mDisplayOrientationDetector = null;
return;
}
// Internal setup
final PreviewImpl preview = createPreviewImpl(context);
mCallbacks = new CallbackBridge();
if (Build.VERSION.SDK_INT < 21) {
mImpl = new Camera1(mCallbacks, preview);
} else if (Build.VERSION.SDK_INT < 23) {
mImpl = new Camera2(mCallbacks, preview, context);
} else {
mImpl = new Camera2Api23(mCallbacks, preview, context);
}
// Attributes
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CameraView, defStyleAttr,
R.style.Widget_CameraView);
mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, false);
setFacing(a.getInt(R.styleable.CameraView_facing, FACING_BACK));
String aspectRatio = a.getString(R.styleable.CameraView_aspectRatio);
if (aspectRatio != null) {
setAspectRatio(AspectRatio.parse(aspectRatio));
} else {
setAspectRatio(Constants.DEFAULT_ASPECT_RATIO);
}
setAutoFocus(a.getBoolean(R.styleable.CameraView_autoFocus, true));
setFlash(a.getInt(R.styleable.CameraView_flash, Constants.FLASH_AUTO));
a.recycle();
// Display orientation detector
mDisplayOrientationDetector = new DisplayOrientationDetector(context) {
@Override
public void onDisplayOrientationChanged(int displayOrientation) {
mImpl.setDisplayOrientation(displayOrientation);
}
};
}
不难看出google提供的Api小于21,会实例化Camera1作为相机视图。但是令人惊奇的是,收到的崩溃日志Android 7.0的机器于Camera1处报错,大为惊诧之后,想想bug不修推锅给谷歌不现实,于是乎开始了调试之旅。
几经调查之后Camera1中会因为调用后stop()方法后,调用cancelAutoFocus()会报出无法在release()后使用api的错误。
笔者只能加上防护代码抵消报错。不过更神奇的是亲测小米8 Android 9.0 的测试机居然也初始化了Camera1的实例,奈何如何走到Camera1实例中又成为这个世纪最大的未解之谜。面对手上楚楚可怜几个测试机,只得双手合十,在2020年的心愿单上加上公司尽早补上主流机型测试机资源,早日助笔者脱离兼容性苦海。
随笔至此已入尾声,望着20后都要出生的新新时代,笔者再次感到万分压力。今年的职级是否能顺利提升,薪资是否能追上cpi的脚步,七大姑八大姨是否能在过年忘记笔者,都成为笔者来年的稀疏头顶的最大敌人。
望来年大事可成,未来可期。
网友评论