看了下“自行”这个二维码扫码组件的demo,UI代码挺多的。

两个handler,一个处理摄像机,一个处于酱油线程中处理解码,。。。
干得好,我选择用一个 handler,不过那个酱油线程还是省不了,不然解码过程发生在主线程就不好了。
简要流程是:首先开启摄像机,调用startPreview
,开始预览。
解码是通过在第一个handler中不断给摄像机设置setOneShotPreviewCallback(cb)
,在这个回调中拿到一帧图像的字节数组,传给解码Handler最后传给zxing-core。
解码的时候会将数据旋转一下,似乎没有必要,传送横着的数据,只要矩形范围设对了,zxing-core照样能识别出来。(不过也有可能zxing-core内部进行了旋转。)
原来摄像机的自动对焦是需要开发者手动调用并延时、连续调用autoFocus(cb)
的。
于是,demo里面每次都新开一个 AsyncTask ,在 AsyncTask 里面调用 Thread.sleep(2000) 完成延时的效果。
干得好,我选择 postDelayed ……
方向传感器触发autoFocus:
https://www.cnblogs.com/wjw334/p/6744741.html?utm_source=itdadao&utm_medium=referral
于onResume中进行初始化,于onPause中销毁,这波难道都成了demo的标配了么(参考vlc官方demo)
干得好,我选择 onCreate 和 onDestory ……
与视频组件一样,camera除了可以用surfaceView预览,也可以选用textureView。后者暂停和恢复不会出现问题。
暂停久了,onResume的时候,摄像机会抛出 startPreview failed
、 setPreviewTexture failed
等异常,此时,需要在 onResume 中重新初始化摄像机。
zxing-demo中还有许多没有用到的代码,比如闪光灯、曝光值,可以利用一下。
and:
处理画面拉伸。
扫码机,你得像个相机。不过这样一来,矩形范围要重新计算,倒是变得复杂了。
处理拉伸很简单,根据摄像机宽高分辨率、屏幕分辨率,计算textview.layoutparams的width、height。
和处理视频播放器的逻辑差不多,不多有一点是反着的,播放器一般是要把全部画面纳入(fit_center),摄像机最好还是屏幕上不留空白(center_crop)。

分屏模式,当宽度大于高度时,也算作 Configuration.ORIENTATION_LANDSCAPE的一种情况。所以横竖屏不能以 Configuration 中的数值为依据,而是要用到Display.getRotation
:
public int getScreenOrientation() {
int PORT=1,LAND=2;
WindowManager windowService = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
int rotation = windowService.getDefaultDisplay().getRotation();
if (Surface.ROTATION_0 == rotation) {
rotation = PORT;
} else if(Surface.ROTATION_180 == rotation) {
rotation = PORT;
} else if(Surface.ROTATION_90 == rotation) {
rotation = LAND;
} else if(Surface.ROTATION_270 == rotation) {
rotation = LAND;
}
return rotation;
}
来自 https://stackoverflow.com/questions/28041137/android-get-current-device-orientation

then:
可以让激光飞一会儿,飞出画面上的方框,此时,调大postInvalidateDelayed的间隔:

然后调低平时的间隔,使激光更流畅。
精简:

不过这也差不多了,区区一个二维码的界面效果,还不至于耗费太多时间。
ISBN码是有方向的。结果 zxing-lite 横着竖着都能扫,为啥?检测了多次而已。一次旋转数据,一次不旋转。
try
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {// continue
} finally {
multiFormatReader.reset();
}
旋转数据,这就不可避免了,较慢。如果handler放在主线程,就会发现摄像机明显掉帧了。
可以通过只旋转、并传送扫码框内部的数据,达到减少计算量、节省内存的目的。
before:(旋转并传送整张图片)
https://github.com/yangxch/ScanZxing/blob/master/app/src/main/java/com/example/xch/scanzxing/zxing/decode/DecodeHandler.java搜索/***************竖屏更改3**********************/
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width; // Here we are swapping, that's the difference to #11
width = height;
height = tmp;
data = rotatedData;
after: (只处理框内内容)
if(rotate) {
byte[] rotatedData = acquireTmpData(heightheight*widthwidth);
for (int y = 0; y < heightheight; y++) {
for (int x = 0; x < widthwidth; x++)
rotatedData[x + y * widthwidth] = data[(y+left) + (x+top) * sWidth];
}
data = rotatedData;
return new PlanarYUVLuminanceSource(data, heightheight, widthwidth, 0, 0, heightheight, widthwidth, false);
}
数学关系还没搞明白,但灵感一闪就这么来了。上下文如下:
PlanarYUVLuminanceSource(源图像数据,原宽, 原高,竖屏模式,翻转一下图像再传给zxing-core)
public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int sWidth, int sHeight, boolean rotated, boolean rotate) {
Rect rect = getFramingRect();
if (rect == null) {
return null;
}
// Go ahead and assume it's YUV rather than die.
//if(true) return new PlanarYUVLuminanceSource(data, sWidth, sHeight, 0, 0, sWidth, sHeight, false);
//CMN.Log("buildLuminanceSource", width, height, rect.left, rect.top, rect.width(), rect.height());
//CMN.Log("scale="+a.scale, "trans="+a.vTranslate);
float scale = a.scale; // 源图像显示后的缩放比,比如0.5、0.9
int left=(int)((rect.left-a.vTranslate.x)/scale);
int top=(int)((rect.top-a.vTranslate.y)/scale);
if(rotated) { //竖屏模式下,图像的数据其实还是横屏的样子。所以交换一下宽高。
int tmp=sWidth;
sWidth = sHeight;
sHeight = tmp;
tmp=left;
left=top;
top=tmp;
}
if(left<0) left=0;
int widthwidth=(int)(rect.height()/scale);
if(widthwidth+left>sWidth) {
widthwidth=sWidth-left;
}
if(top<0) top=0;
int heightheight=(int)(rect.height()/scale);
if(heightheight+top>sHeight) {
heightheight=sHeight-top;
}
if(rotate) {//必须旋转数据时,较为耗费CPU时间,故只处理必要的部分。
byte[] rotatedData = acquireTmpData(heightheight*widthwidth);
for (int y = 0; y < heightheight; y++) {
for (int x = 0; x < widthwidth; x++)
rotatedData[x + y * widthwidth] = data[(y+left) + (x+top) * sWidth];
}
data = rotatedData;
return new PlanarYUVLuminanceSource(data, heightheight, widthwidth, 0, 0, heightheight, widthwidth, false);
}
return new PlanarYUVLuminanceSource(data, sWidth, sHeight, left, top, widthwidth, heightheight, false);
}
这里的旋转应该只是翻转,不知道有无翻转敏感的条码格式……
网友评论