1、支持条形码下方显示Code。

2、优化扫条形码。
3、优化相机预览尺寸遍历策略、从而降低变形的可能性。
This commit is contained in:
jenly1314
2018-09-12 17:07:51 +08:00
parent cdb116200f
commit 06694d28a0
20 changed files with 463 additions and 49 deletions

View File

@@ -5,12 +5,10 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!-- unavailable in API 23 -->
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<application>
<activity

View File

@@ -167,12 +167,10 @@ public class CaptureActivity extends Activity implements SurfaceHolder.Callback
handler = null;
lastResult = null;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean(Preferences.KEY_DISABLE_AUTO_ORIENTATION, true)) {
setRequestedOrientation(getCurrentOrientation());
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
resetStatusView();

View File

@@ -28,17 +28,17 @@ import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
final class DecodeFormatManager {
public final class DecodeFormatManager {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
static final Set<BarcodeFormat> PRODUCT_FORMATS;
static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
private static final Set<BarcodeFormat> ONE_D_FORMATS;
static final Set<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
static final Set<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
static final Set<BarcodeFormat> AZTEC_FORMATS = EnumSet.of(BarcodeFormat.AZTEC);
static final Set<BarcodeFormat> PDF417_FORMATS = EnumSet.of(BarcodeFormat.PDF_417);
public static final Set<BarcodeFormat> PRODUCT_FORMATS;
public static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
public static final Set<BarcodeFormat> ONE_D_FORMATS;
public static final Set<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
public static final Set<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
public static final Set<BarcodeFormat> AZTEC_FORMATS = EnumSet.of(BarcodeFormat.AZTEC);
public static final Set<BarcodeFormat> PDF417_FORMATS = EnumSet.of(BarcodeFormat.PDF_417);
static {
PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,
BarcodeFormat.UPC_E,

View File

@@ -74,7 +74,15 @@ final class DecodeHandler extends Handler {
private void decode(byte[] data, int width, int height) {
long start = System.currentTimeMillis();
Result rawResult = null;
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
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;
width = height;
height = tmp;
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height);
if (source != null) {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {

View File

@@ -32,7 +32,7 @@ import com.google.zxing.DecodeHintType;
/**
* @author Lachezar Dobrev
*/
final class DecodeHintManager {
public final class DecodeHintManager {
private static final String TAG = DecodeHintManager.class.getSimpleName();

View File

@@ -85,6 +85,7 @@ public final class ViewfinderView extends View {
private final float labelTextSize;
public static int scannerStart = 0;
public static int scannerEnd = 0;
private boolean isShowResultPoint;
private List<ResultPoint> possibleResultPoints;
private List<ResultPoint> lastPossibleResultPoints;
@@ -320,7 +321,15 @@ public final class ViewfinderView extends View {
invalidate();
}
// /**
public boolean isShowResultPoint() {
return isShowResultPoint;
}
public void setShowResultPoint(boolean showResultPoint) {
isShowResultPoint = showResultPoint;
}
// /**
// * Draw a bitmap with the result points highlighted instead of the live scanning display.
// *
// * @param barcode An image of the decoded barcode.
@@ -331,15 +340,18 @@ public final class ViewfinderView extends View {
// }
public void addPossibleResultPoint(ResultPoint point) {
List<ResultPoint> points = possibleResultPoints;
synchronized (points) {
points.add(point);
int size = points.size();
if (size > MAX_RESULT_POINTS) {
// trim it
points.subList(0, size - MAX_RESULT_POINTS / 2).clear();
if(isShowResultPoint){
List<ResultPoint> points = possibleResultPoints;
synchronized (points) {
points.add(point);
int size = points.size();
if (size > MAX_RESULT_POINTS) {
// trim it
points.subList(0, size - MAX_RESULT_POINTS / 2).clear();
}
}
}
}
}

View File

@@ -123,6 +123,7 @@ final class CameraConfigurationManager {
display.getSize(theScreenResolution);
screenResolution = theScreenResolution;
Log.i(TAG, "Screen resolution in current orientation: " + screenResolution);
cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
Log.i(TAG, "Camera resolution: " + cameraResolution);
bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
@@ -157,6 +158,9 @@ final class CameraConfigurationManager {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
theCamera.setDisplayOrientation(90);
theCamera.setParameters(parameters);
initializeTorch(parameters, prefs, safeMode);
CameraConfigurationUtils.setFocus(

View File

@@ -25,6 +25,7 @@ import android.util.Log;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
@@ -44,7 +45,7 @@ public final class CameraConfigurationUtils {
private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
private static final double MAX_ASPECT_DISTORTION = 0.15;
private static final double MAX_ASPECT_DISTORTION = 0.10;
private static final int MIN_FPS = 10;
private static final int MAX_FPS = 20;
private static final int AREA_PER_1000 = 400;
@@ -270,7 +271,7 @@ public final class CameraConfigurationUtils {
}
}
public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
public static Point findBestPreviewSizeValue(Camera.Parameters parameters,final Point screenResolution) {
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
if (rawSupportedSizes == null) {
@@ -282,6 +283,7 @@ public final class CameraConfigurationUtils {
return new Point(defaultSize.width, defaultSize.height);
}
if (Log.isLoggable(TAG, Log.INFO)) {
StringBuilder previewSizesString = new StringBuilder();
for (Camera.Size size : rawSupportedSizes) {
@@ -291,7 +293,7 @@ public final class CameraConfigurationUtils {
}
double screenAspectRatio = screenResolution.x / (double) screenResolution.y;
Log.i(TAG, "screenAspectRatio: " + screenAspectRatio);
// Find a suitable size, with max resolution
int maxResolution = 0;
Camera.Size maxResPreviewSize = null;
@@ -304,10 +306,13 @@ public final class CameraConfigurationUtils {
}
boolean isCandidatePortrait = realWidth < realHeight;
int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
int maybeFlippedWidth = isCandidatePortrait ? realWidth: realHeight ;
int maybeFlippedHeight = isCandidatePortrait ? realHeight : realWidth;
Log.i(TAG, String.format("maybeFlipped:%d * %d",maybeFlippedWidth,maybeFlippedHeight));
double aspectRatio = maybeFlippedWidth / (double) maybeFlippedHeight;
Log.i(TAG, "aspectRatio: " + aspectRatio);
double distortion = Math.abs(aspectRatio - screenAspectRatio);
Log.i(TAG, "distortion: " + distortion);
if (distortion > MAX_ASPECT_DISTORTION) {
continue;
}
@@ -325,6 +330,21 @@ public final class CameraConfigurationUtils {
}
}
if (!rawSupportedSizes.isEmpty()) {
Collections.sort(rawSupportedSizes, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size o1, Camera.Size o2) {
int delta1 = Math.abs(o1.height-screenResolution.x);
int delta2 = Math.abs(o2.height-screenResolution.x);
return delta1 - delta2;
}
});
Camera.Size bestPreview = rawSupportedSizes.get(0);
Point bestSize = new Point(bestPreview.width, bestPreview.height);
Log.i(TAG, "Using largest suitable bestSize: " + bestSize);
return bestSize;
}
// If no exact match, use largest preview size. This was not a great idea on older devices because
// of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
// the CPU is much more powerful.

View File

@@ -271,6 +271,12 @@ public final class CameraManager {
// rect.right = rect.right * cameraResolution.x / screenResolution.x;
// rect.top = rect.top * cameraResolution.y / screenResolution.y;
// rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
framingRectInPreview = rect;
}
return framingRectInPreview;

View File

@@ -16,19 +16,36 @@
package com.king.zxing.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.text.TextPaint;
import android.text.TextUtils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.king.zxing.DecodeFormatManager;
import com.king.zxing.DecodeHintManager;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;
/**
@@ -144,17 +161,127 @@ public class CodeUtils {
canvas.drawBitmap(src, 0, 0, null);
canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
} catch (Exception e) {
bitmap = null;
e.getStackTrace();
e.printStackTrace();
}
return bitmap;
}
/**
* 解析二维码图片
* @param bitmapPath
* @return
*/
public static String parseQRCode(String bitmapPath) {
Map<DecodeHintType, Object> hints = new HashMap<>();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
return parseQRCode(bitmapPath,hints);
}
/**
* 解析二维码图片
* @param bitmapPath
* @param hints
* @return
*/
public static String parseQRCode(String bitmapPath, Map<DecodeHintType,?> hints){
try {
Result result = new QRCodeReader().decode(getBinaryBitmap(compressBitmap(bitmapPath)), hints);
return result.getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 解析一维码/二维码图片
* @param bitmapPath
* @return
*/
public static String parseCode(String bitmapPath){
Map<DecodeHintType,Object> hints = new HashMap<>();
//添加可以解析的编码类型
Vector<BarcodeFormat> decodeFormats = new Vector<>();
decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS);
decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS);
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
return parseCode(bitmapPath,hints);
}
/**
* 解析一维码/二维码图片
* @param bitmapPath
* @param hints 解析编码类型
* @return
*/
public static String parseCode(String bitmapPath, Map<DecodeHintType,Object> hints){
try {
MultiFormatReader reader = new MultiFormatReader();
reader.setHints(hints);
Result result = reader.decodeWithState(getBinaryBitmap(compressBitmap(bitmapPath)));
return result.getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 压缩图片
* @param path
* @return
*/
private static Bitmap compressBitmap(String path){
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 开始读入图片此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;//获取原始图片大小
BitmapFactory.decodeFile(path, newOpts);// 此时返回bm为空
int w = newOpts.outWidth;
int h = newOpts.outHeight;
float width = 800f;
float height = 480f;
// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;// be=1表示不缩放
if (w > h && w > width) {// 如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / width);
} else if (w < h && h > height) {// 如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / height);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;// 设置缩放比例
// 重新读入图片注意此时已经把options.inJustDecodeBounds 设回false了
newOpts.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, newOpts);
}
/**
* 获取二进制图片
* @param bitmap
* @return
*/
private static BinaryBitmap getBinaryBitmap(@NonNull Bitmap bitmap){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);
//得到二进制图片
return new BinaryBitmap(new HybridBinarizer(source));
}
/**
* 生成条形码
* @param content
@@ -167,6 +294,7 @@ public class CodeUtils {
return createBarCode(content,format,desiredWidth,desiredHeight,null);
}
/**
* 生成条形码
* @param content
@@ -176,7 +304,37 @@ public class CodeUtils {
* @param hints
* @return
*/
public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight,Map<EncodeHintType,?> hints) {
public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType,?> hints) {
return createBarCode(content,format,desiredWidth,desiredHeight,hints,false,40,Color.BLACK);
}
/**
* 生成条形码
* @param content
* @param format
* @param desiredWidth
* @param desiredHeight
* @param hints
* @param isShowText
* @return
*/
public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType,?> hints, boolean isShowText) {
return createBarCode(content,format,desiredWidth,desiredHeight,hints,isShowText,40,Color.BLACK);
}
/**
* 生成条形码
* @param content
* @param format
* @param desiredWidth
* @param desiredHeight
* @param hints
* @param isShowText
* @param textSize
* @param textColor
* @return
*/
public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight,Map<EncodeHintType,?> hints,boolean isShowText,int textSize,@ColorInt int textColor) {
if(TextUtils.isEmpty(content)){
return null;
}
@@ -201,6 +359,9 @@ public class CodeUtils {
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
if(isShowText){
return addCode(bitmap,content,textSize,textColor,textSize/2);
}
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
@@ -208,4 +369,48 @@ public class CodeUtils {
return null;
}
/**
* 条形码下面添加文本信息
* @param src
* @param code
* @param textSize
* @param textColor
* @return
*/
private static Bitmap addCode(Bitmap src,String code,int textSize,@ColorInt int textColor,int offset) {
if (src == null) {
return null;
}
if (TextUtils.isEmpty(code)) {
return src;
}
//获取图片的宽高
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
if (srcWidth <= 0 || srcHeight <= 0) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight + textSize + offset * 2, Bitmap.Config.ARGB_8888);
try {
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(src, 0, 0, null);
TextPaint paint = new TextPaint();
paint.setTextSize(textSize);
paint.setColor(textColor);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(code,srcWidth/2,srcHeight + textSize /2 + offset,paint);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
} catch (Exception e) {
bitmap = null;
e.printStackTrace();
}
return bitmap;
}
}