不得不说这个过程看上去简单,实际上写到我差不多一个星期的时间,由于超出预定时间,此功能被放弃,出于对技术的追求,于是继续写,终于被我弄出来。
其实两种方法的本质都是通过byte[]data获取数据,通过camera获取长宽。扫码过程中主要由两种途径:
1.camera.takePicture(自己写回调,灵活性相对高,推荐)
2.camera.setPreviewCallback(最终通过handler传递到DecodeHandler里面的decode())
说说思路-方法1:拍照
1.使用camera拍照
2.把拍照的data格式转换进行保存
听上去很简单吧?那我们试一下。
我的手机是mi5x,看看效果:
预览效果:
拍摄图片:
takePicture回调:
public void takePicture(final TakePictureCallback callback){
if (camera != null && previewing) {
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
byte[] rotatedData = new byte[data.length];
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = new Matrix();
matrix.setRotate(90);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
bitmap = createBitmap(bitmap, 0, 0, width, height, matrix, true);
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "mCamera", "b.jpg");
saveBitmap(file.getAbsolutePath(), bitmap);
if(callback!=null)
callback.callback();
}
});
}
}
这个效果看的我n脸懵逼,摄像头怎么了?怎么拍摄的照片不完整?随后换了其他手机,如华为,vivo又不会有这样的问题,拍摄图片显示正常。几经周折,终于在不断尝试下找到解决办法:
摄像头初始化setPreviewSize前,要使用setPictureSize:
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
int position =0;
if(supportedPreviewSizes.size()>2){
position=supportedPreviewSizes.size()/2+1;//supportedPreviewSizes.get();
}else {
position=supportedPreviewSizes.size()/2;
}
int width = supportedPreviewSizes.get(position).width;
int height = supportedPreviewSizes.get(position).height;
Log.d(TAG, "Setting preview size: " + cameraResolution);
parameters.setPictureSize(width,height);
camera.setDisplayOrientation(90);
cameraResolution.x=width;
cameraResolution.y=height;
parameters.setPreviewSize(width,height);
setFlash(parameters);
setZoom(parameters);
//setSharpness(parameters);
camera.setParameters(parameters);
估计setPictureSize就是实际拍摄的大小了,没有设置就会按照默认的来,小米这里可能又挖坑了。
另:实测发现vivo手机等加入setPictureSize后可能会在解码旋转图片过程中出现ArrayIndexOutOfBoundsException问题,如果碰到可能需要识别手机型号,小米才进行setPictureSize,其他手机采用默认【这方面可能需要查更多的资料,现在找到的信息有限】。
java.lang.ArrayIndexOutOfBoundsException: length=4147200; index=4913280
说说思路-方法2:扫码解码后直接保存
zxing的DecoderHandler里面有一个decode()方法用于解码,里面可以拿到data和长宽,可以直接在此处保存。
步骤1:使用传过来的原始长宽以及data
步骤2:进行解码保存在文件中
使用renderCroppedGreyscaleBitmap,图变灰:
public Bitmap renderCroppedGreyscaleBitmap() {
int width = getWidth();
int height = getHeight();
int[] pixels = new int[width * height];
byte[] yuv = yuvData;
int inputOffset = top * dataWidth + left;
for (int y = 0; y < height; y++) {
int outputOffset = y * width;
for (int x = 0; x < width; x++) {
int grey = yuv[inputOffset + x] & 0xff;
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
}
inputOffset += dataWidth;
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
成功变成黑白照。
另外说说为什么用原始长宽和data。在zxing解码这个部分,我们通常会看到这样一段代码:
Result rawResult = null;
//modify here
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;
这是解码前做的准备,简单的说就是把图片进行了一下翻转,但如果是截图,请不要直接使用这个rotateData以及转换之后的长宽,否则效果如下:
【这张图本身也很奇怪,用手机看是全的,用电脑看下面变灰色】
由上面两个结果得出的结论是:
1.保存时候图片先不要旋转
2.不要使用renderCroppedGreyscaleBitmap()进行图片保存
于是用了原始参数,是这样传的:
source.renderCroppedGreyscaleBitmap2(data,height,width,file.getAbsolutePath());
内部如下:
public void renderCroppedGreyscaleBitmap2(final byte[] data, int width, int height, String path) {
FileOutputStream outStream = null;
try {
YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21,width,height,null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0,0,width,height), 100, baos);
outStream = new FileOutputStream(path);
outStream.write(baos.toByteArray());
outStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
得到图片:
可以看到是彩色的,选择的方式上面有贴出,建议新开线程来处理,这里再贴一下:
路径获取bitmap:
FileInputStream fis = new FileInputStream(path);
Bitmap bitmap = BitmapFactory.decodeStream(fis);
旋转:
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = new Matrix();
matrix.setRotate(90);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
bitmap = createBitmap(bitmap, 0, 0, width, height, matrix, true);
保存:
public static String saveBitmap(String dir, Bitmap b) {
try {
File f = new File(dir);
if (f.exists()) {
f.delete();
}
f.createNewFile();
FileOutputStream fout = new FileOutputStream(f);
BufferedOutputStream bos = new BufferedOutputStream(fout);
b.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
return dir;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}