目前版本:2.2.5
GitHub:https://github.com/yuzhiqiang1993/zxing
效果图
配置
- 在 project/build.gradle 中添加 allprojects 如下:
dependencies {
classpath "com.android.tools.build:gradle:4.1.2"
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.0'
}
---------------------------------------------------------------------
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
- 在 app/build.gradle 中添加如下:
//Butter Knife
implementation 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
implementation 'com.tbruyelle.rxpermissions2:rxpermissions:+'
implementation 'com.github.yuzhiqiang1993:zxing:2.2.5'
- 申请权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
使用
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_scan"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="34dp"
android:text="@string/btn_scan"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="17dp"
android:text="@string/btn_create"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_scan" />
<ImageView
android:id="@+id/iv_zxing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="279dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
- MainActivity.java
private static final int REQUEST_CODE_SCAN = 1001;
@BindView(R.id.btn_scan)
Button mScan;
@BindView(R.id.btn_create)
Button mCreate;
@BindView(R.id.iv_zxing)
ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
new RxPermissions(this)
.request(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Boolean aBoolean) {
Log.d("amy", "onNext: "+aBoolean);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
@OnClick({R.id.btn_scan,R.id.btn_create})
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_scan:
Intent intent = new Intent(MainActivity.this, CaptureActivity.class);
/*ZxingConfig是配置类 可以设置是否显示底部布局,闪光灯,相册,是否播放提示音 震动等动能
* 也可以不传这个参数
* 不传的话 默认都为默认不震动 其他都为true
* */
ZxingConfig config = new ZxingConfig();
config.setPlayBeep(true);//是否播放扫描声音 默认为true
config.setShake(true);//是否震动 默认为true
config.setDecodeBarCode(true);//是否扫描条形码 默认为true
config.setReactColor(R.color.purple_700);//设置扫描框四个角的颜色 默认为白色
config.setFrameLineColor(R.color.white);//设置扫描框边框颜色 默认无色
config.setScanLineColor(R.color.teal_200);//设置扫描线的颜色 默认白色
config.setFullScreenScan(false);//是否全屏扫描 默认为true 设为false则只会在扫描框中扫描
intent.putExtra(Constant.INTENT_ZXING_CONFIG, config);
startActivityForResult(intent, REQUEST_CODE_SCAN);
break;
case R.id.btn_create:
try {
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
Bitmap bitmap = CodeCreator.createQRCode("Android开发", 400, 400, null);
mImageView.setImageBitmap(bitmap);
}catch (Exception e) {
e.printStackTrace();
}
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 扫描二维码/条码回传
if (requestCode == REQUEST_CODE_SCAN && resultCode == RESULT_OK) {
if (data != null) {
String content = data.getStringExtra(Constant.CODED_CONTENT);
// result.setText("扫描结果为:" + content);
Toast.makeText(this,content,Toast.LENGTH_SHORT).show();
}
}
}
生成带 logo 的二维码
- 添加 project/build.gradle 依赖:
buildscript {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.0"
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
- 添加 app/build.gradle 依赖:
implementation 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
implementation 'com.tbruyelle.rxpermissions2:rxpermissions:+'
implementation'com.journeyapps:zxing-android-embedded:3.0.2@aar'
implementation'com.google.zxing:core:3.2.0'
- 在清单文件中申请权限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 工具类 CreatQRCodeImg.java
/**
* Created on 2021/3/30 16:12
*
* @author Gong Youqiang
*/
public class CreatQRCodeImg {
/**
* 生成二维码Bitmap
*
* @param content 内容
* @param widthPix 图片宽度
* @param heightPix 图片高度
* @param logoBm 二维码中心的Logo图标(可以为null)
* @return 生成二维码及保存文件是否成功
*/
public static boolean createQRImage(String content, int widthPix, int heightPix, Bitmap logoBm,String filePath) {
try {
if (content == null || "".equals(content)) {
return false;
}
//配置参数
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//设置空白边距的宽度
// hints.put(EncodeHintType.MARGIN, 2); //default is 4
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
int[] pixels = new int[widthPix * heightPix];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < heightPix; y++) {
for (int x = 0; x < widthPix; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * widthPix + x] = 0xff000000;
} else {
pixels[y * widthPix + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
if (logoBm != null) {
bitmap = addLogo(bitmap, logoBm);
}
//必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大!
return bitmap != null && bitmap.compress(Bitmap.CompressFormat.JPEG, 70, new FileOutputStream(filePath));
} catch (WriterException | IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 在二维码中间添加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.restore();
} catch (Exception e) {
bitmap = null;
e.getStackTrace();
}
return bitmap;
}
}
- MainActivity.java
public class MainActivity extends AppCompatActivity {
@BindView(R.id.iv_qr_code)
ImageView mImageView;
private String filePath = Environment.getExternalStorageDirectory().getPath() + "/Pic";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
new RxPermissions(this)
.request(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Boolean aBoolean) {
Log.d("amy", "onNext: "+aBoolean);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
boolean qrImage = CreatQRCodeImg.createQRImage("https://blog.csdn.net/duoduo_11011",
500,500, BitmapFactory.decodeResource(getResources(), R.mipmap.qr_logo),filePath);
if (qrImage){
mImageView.setImageBitmap(getQrImage());
}
}
private Bitmap getQrImage() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
options.inSampleSize = calculateInSampleSize(options, 500, 500);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, options);
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? widthRatio : heightRatio;
}
return inSampleSize;
}
}