博客转载自:https://blog.csdn.net/skillcollege/article/details/38852183android
在Android平台作过二维码相关模块的确定都熟知ZXing开源项目,Z*是一个开源Java类库用于解析多种格式的1D/2D条形码。目标是可以对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。其GitHub地址是:传送门git
Z*项目里面代码不少,实现的功能也不少,咱们的应用只须要剥离其中的扫描模块便可,再多一点也就是生成二维码的功能;接下来咱们就一块儿来精简ZXing项目,最终造成一个小的Demo案例,固然江湖上已经有过N多种版本的ZXing精简项目,什么横屏改竖屏,绘制扫描界面,开启闪光灯等等,而且许多都是基于ZXing2.3.0来作精简的,后续有许多更新的版本,包括自动对焦,Camera管理,bug修复等等新功能;笔者使用的是ZXing3.1.0版本,这里须要说明的就是个人这版Demo绝对是江湖上面尚未出现的,也算是一点点小小的创新把,那就是去除ZXing项目中恼人的ViewFinderView的绘制,使用XML布局扫描界面,添加扫描动画,精确计算扫描区域github
1
|
git clone https:
//github.com/zxing/zxing.git
|
打开ZXing项目的文件夹,能够看到以下文件目录:算法
其中咱们主要关注2个文件夹里的内容: canvas
1. core : Z*项目的核心代码,能够新建一个Java工程,而后export成jar来调用。以下图所示:maven
免打包便可得到的zxing-3.1.0.jar 猛戳下载 工具
2. android : Android示例工程代码,成功运行以后就是一个专业的扫码应用了。以下图所示:布局
免引入免整理的zxing原始工程 ZXingRawProject 猛戳下载动画
可是这样就让你知足了,那怎么能够说是极致二维码扫描呢,有木有感受ZXing的扫描框的绘制很不爽啊?自定义的View绘制的很丑,多屏幕适配的时候还常常不兼容,原始项目仍是横屏模式的,目前你们都习惯竖屏扫描呢。怎么办?别怕,我来告诉你,我要将ViewFinderView砍掉,使用xml界面布局,添加扫描动画,最终同样准确无误的扫描到二维码数据,只须要对准,是的,毫厘不差的对准就能够了。ui
1. 去掉Z*中一些和扫描无关的代码,最终留下的代码结构以下图所示,最关键的是你看不到ViewFinderView 了
2. 布局扫描界面,xml代码以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
<?xml version=
"1.0"
encoding=
"utf-8"
?>
<RelativeLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"@android:color/transparent"
android:orientation=
"vertical"
>
<SurfaceView
android:id=
"@+id/capture_preview"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
<RelativeLayout
android:id=
"@+id/capture_container"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<ImageView
android:id=
"@+id/capture_mask_top"
android:layout_width=
"match_parent"
android:layout_height=
"120dp"
android:layout_alignParentTop=
"true"
android:background=
"@drawable/shadow"
/>
<RelativeLayout
android:id=
"@+id/capture_crop_view"
android:layout_width=
"200dp"
android:layout_height=
"200dp"
android:layout_below=
"@id/capture_mask_top"
android:layout_centerHorizontal=
"true"
android:background=
"@drawable/qr_code_bg"
>
<ImageView
android:id=
"@+id/capture_scan_line"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_alignParentTop=
"true"
android:layout_marginBottom=
"5dp"
android:layout_marginTop=
"5dp"
android:src=
"@drawable/scan_line"
/>
</RelativeLayout>
<ImageView
android:id=
"@+id/capture_mask_bottom"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_alignParentBottom=
"true"
android:layout_below=
"@id/capture_crop_view"
android:background=
"@drawable/shadow"
/>
<ImageView
android:id=
"@+id/capture_mask_left"
android:layout_width=
"wrap_content"
android:layout_height=
"match_parent"
android:layout_above=
"@id/capture_mask_bottom"
android:layout_alignParentLeft=
"true"
android:layout_below=
"@id/capture_mask_top"
android:layout_toLeftOf=
"@id/capture_crop_view"
android:background=
"@drawable/shadow"
/>
<ImageView
android:id=
"@+id/capture_mask_right"
android:layout_width=
"wrap_content"
android:layout_height=
"match_parent"
android:layout_above=
"@id/capture_mask_bottom"
android:layout_alignParentRight=
"true"
android:layout_below=
"@id/capture_mask_top"
android:layout_toRightOf=
"@id/capture_crop_view"
android:background=
"@drawable/shadow"
/>
</RelativeLayout>
</RelativeLayout>
|
3. 计算截取区域 贴心注解: 若是你没有看上一篇ZBar扫描中关于扫描区域计算的解释,那赶忙回去,咱不能急,看完再来接上,不然你会不理解的!传送门
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
private
void
initCrop() {
int
cameraWidth = cameraManager.getCameraResolution().y;
int
cameraHeight = cameraManager.getCameraResolution().x;
/** 获取布局中扫描框的位置信息 */
int
[] location =
new
int
[2];
scanCropView.getLocationInWindow(location);
int
cropLeft = location[0];
int
cropTop = location[1] - getStatusBarHeight();
int
cropWidth = scanCropView.getWidth();
int
cropHeight = scanCropView.getHeight();
/** 获取布局容器的宽高 */
int
containerWidth = scanContainer.getWidth();
int
containerHeight = scanContainer.getHeight();
/** 计算最终截取的矩形的左上角顶点x坐标 */
int
x = cropLeft * cameraWidth / containerWidth;
/** 计算最终截取的矩形的左上角顶点y坐标 */
int
y = cropTop * cameraHeight / containerHeight;
/** 计算最终截取的矩形的宽度 */
int
width = cropWidth * cameraWidth / containerWidth;
/** 计算最终截取的矩形的高度 */
int
height = cropHeight * cameraHeight / containerHeight;
/** 生成最终的截取的矩形 */
mCropRect =
new
Rect(x, y, width + x, height + y);
}
|
5. 完整项目代码: 猛戳下载
添加依赖
compile 'com.google.zxing:core:3.3.0'
建立工具类:ZXingUtils
public class ZXingUtils {
/**
* 生成二维码 要转换的地址或字符串,能够是中文
* 不须要logo最后一个参数传null
* @param url
* @param width
* @param height
* @return
*/
public static Bitmap createQRImage(String url, final int width, final int height, Bitmap logoBitmap) {
try {
// 判断URL合法性
if (url == null || "".equals(url) || url.length() < 1) {
return null;
}
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(url,
BarcodeFormat.QR_CODE, width, height, hints);
int[] pixels = new int[width * height];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * width + x] = 0xff000000;
} else {
pixels[y * width + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
if (logoBitmap != null) {
bitmap = addLogo(bitmap, logoBitmap);
}
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
/**
* 在二维码中间添加Logo图案
*/
private static Bitmap addLogo(Bitmap src, Bitmap logo) {
if (src == null) {
return null;
}
if (logo == null) {
return src;
}
//获取图片的宽高
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
if (srcWidth == 0 || srcHeight == 0) {
return null;
}
if (logoWidth == 0 || logoHeight == 0) {
return src;
}
//logo大小为二维码总体大小的1/5
float scaleFactor = srcWidth * 1.0f / 5 / logoWidth;
Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
try {
Canvas canvas = new Canvas(bitmap);
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();
}
return bitmap;
}
}
activity代码
Bitmap bitmap = ZXingUtils.createQRImage(url, 600, 600,BitmapFactory.decodeResource(getResources(), R.drawable.call));
imageView.setImageBitmap(bitmap);
下面的是网上找到的一个工具类
/**
*
* 生成条形码和二维码的工具
*/
public class ZXingUtils {
/**
* 生成二维码 要转换的地址或字符串,能够是中文
*
* @param url
* @param width
* @param height
* @return
*/
public static Bitmap createQRImage(String url, final int width, final int height) {
try {
// 判断URL合法性
if (url == null || "".equals(url) || url.length() < 1) {
return null;
}
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(url,
BarcodeFormat.QR_CODE, width, height, hints);
int[] pixels = new int[width * height];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * width + x] = 0xff000000;
} else {
pixels[y * width + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
/**
* 生成条形码
*
* @param context
* @param contents
* 须要生成的内容
* @param desiredWidth
* 生成条形码的宽带
* @param desiredHeight
* 生成条形码的高度
* @param displayCode
* 是否在条形码下方显示内容
* @return
*/
public static Bitmap creatBarcode(Context context, String contents,
int desiredWidth, int desiredHeight, boolean displayCode) {
Bitmap ruseltBitmap = null;
/**
* 图片两端所保留的空白的宽度
*/
int marginW = 20;
/**
* 条形码的编码类型
*/
BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128;
if (displayCode) {
Bitmap barcodeBitmap = encodeAsBitmap(contents, barcodeFormat,
desiredWidth, desiredHeight);
Bitmap codeBitmap = creatCodeBitmap(contents, desiredWidth + 2
* marginW, desiredHeight, context);
ruseltBitmap = mixtureBitmap(barcodeBitmap, codeBitmap, new PointF(
0, desiredHeight));
} else {
ruseltBitmap = encodeAsBitmap(contents, barcodeFormat,
desiredWidth, desiredHeight);
}
return ruseltBitmap;
}
/**
* 生成条形码的Bitmap
*
* @param contents
* 须要生成的内容
* @param format
* 编码格式
* @param desiredWidth
* @param desiredHeight
* @return
* @throws WriterException
*/
protected static Bitmap encodeAsBitmap(String contents,
BarcodeFormat format, int desiredWidth, int desiredHeight) {
final int WHITE = 0xFFFFFFFF;
final int BLACK = 0xFF000000;
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result = null;
try {
result = writer.encode(contents, format, desiredWidth,
desiredHeight, null);
} catch (WriterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
// All are 0, or black, by default
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
/**
* 生成显示编码的Bitmap
*
* @param contents
* @param width
* @param height
* @param context
* @return
*/
protected static Bitmap creatCodeBitmap(String contents, int width,
int height, Context context) {
TextView tv = new TextView(context);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
tv.setLayoutParams(layoutParams);
tv.setText(contents);
tv.setHeight(height);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setWidth(width);
tv.setDrawingCacheEnabled(true);
tv.setTextColor(Color.BLACK);
tv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
tv.buildDrawingCache();
Bitmap bitmapCode = tv.getDrawingCache();
return bitmapCode;
}
/**
* 将两个Bitmap合并成一个
*
* @param first
* @param second
* @param fromPoint
* 第二个Bitmap开始绘制的起始位置(相对于第一个Bitmap)
* @return
*/
protected static Bitmap mixtureBitmap(Bitmap first, Bitmap second,
PointF fromPoint) {
if (first == null || second == null || fromPoint == null) {
return null;
}
int marginW = 20;
Bitmap newBitmap = Bitmap.createBitmap(
first.getWidth() + second.getWidth() + marginW,
first.getHeight() + second.getHeight(), Config.ARGB_4444);
Canvas cv = new Canvas(newBitmap);
cv.drawBitmap(first, marginW, 0, null);
cv.drawBitmap(second, fromPoint.x, fromPoint.y, null);
cv.save(Canvas.ALL_SAVE_FLAG);
cv.restore();
return newBitmap;
}
}
http://repo1.maven.org/maven2/com/google/zxing