Android二维码扫描.(生成,扫描,相册,闪光灯)
项目用到了二维码的相关功能,作为菜鸟我,自己写,不可能存在的,无奈,只好去百度,找找有没有好用的第三方的,拿来用一用,实现实现功能呗。
地址一:https://github.com/bingoogolapple/BGAQRCode-Android
这个大家一百度就能百度的到,我也用项目了,但是测试的过程中,发现bug了,长时间扫描不出来,会出来一串随机数字,github上众多用户都在说这个问题,作者好像并没有解决这个问题。除了扫描结果有点不尽人意之外,其他都挺好用的。如果真的想用,那就判断下结果吧,如果是纯数字,那就重新扫描,但是如果你的二维码是纯数字生成的,那就完蛋咯,只能换个库了。
地址二:https://github.com/mylhyl/Android-Zxing
这个我写了demo,试了试,扫描和相册是可以的.
地址三:https://github.com/journeyapps/zxing-android-embedded
我用的这个,下载下来,界面不是很美观,还有点晕乎乎的,但是仔细看,仔细找,你会发现,扫描速度堪比微信啊,贼快贼快的,不过只有扫描和闪光灯功能,相册识别和生成的功能,自己加上吧。
地址四:https://www.imooc.com/article/20971
嘿嘿,根据这链接来做的。这个链接是根据"地址三"来自定义布局的做出来的.(我也是在这个链接里看到“地址二”的,[皱眉][皱眉])
大家先看看地址三和四,然后直接复制下面的代码,可以实现功能[吼吼][吼吼]
-------------------------------------上图上代码-------------------------------------
图:
扫描.gif相册.gif
代码:
build.gradle:
implementation('com.journeyapps:zxing-android-embedded:3.6.0') { transitive = false }
implementation 'com.google.zxing:core:3.3.0'
上面是github提供的,但是我没把 zxing:core 下载下来,我老大下载的jar包,放进项目里的,只要能编译过去,咋的都行,管他哪儿来的嘛.还有,关于相册识别二维码,这个是个耗时操作,需要在子线程,我们的项目使用rxjava来转换线程的,需要依赖的哦implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
和implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
,用异步任务也可以,但是注意下,内存泄露.
HomeFragment
ps:这里是入口,对相机是否可以使用进行了判断,百度来的,[羞羞].因为我们的targetSdkVersion=22.直接在清单文件设置权限就好了,但是要是手动关闭了相机权限,还是能进去二维码界面的,只是相机不能使用,一片黢黑黢黑的.
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.home_scan:
if (!cameraIsCanUse()) {
toastShort("请开启相机权限");
} else {
startActivity(new Intent(getActivity(), QRCodeActivity.class));
}
break;
}
public boolean cameraIsCanUse() {//百度判断相机的
boolean isCanUse = true;
Camera mCamera = null;
try {
mCamera = Camera.open();
Camera.Parameters mParameters = mCamera.getParameters(); //针对魅族手机
mCamera.setParameters(mParameters);
} catch (Exception e) {
isCanUse = false;
}
if (mCamera != null) {
try {
mCamera.release();
} catch (Exception e) {
e.printStackTrace();
return isCanUse;
}
}
return isCanUse;
}
QRCodeActivity.Java
ps:二维码界面,这里是二维码的主界面.照葫芦,画瓢,写写呗.
public class QRCodeActivity extends BaseToolbarActivity {
private DecoratedBarcodeView barcodeView;
private boolean isLight = false;
@NonNull
@Override
protected int getLayoutId() {
return R.layout.activity_qrcode;
}
@Override
protected void initIntentData(Intent intent) {
}
@Override
protected void initViews() {
setTitleWithBack("扫一扫");
barcodeView = findViewById(R.id.barcode_scanner);
Collection<BarcodeFormat> formats = Arrays.asList(BarcodeFormat.QR_CODE, BarcodeFormat.CODE_39);
barcodeView.getBarcodeView().setDecoderFactory(new DefaultDecoderFactory(formats));
barcodeView.decodeContinuous(callback);
addOnClickListeners(R.id.tv_zxing_gallery, R.id.tv_zxing_flashlight);
}
@Override
public void onClick(View v) {
super.onClick(v);
switch (v.getId()) {
case R.id.tv_zxing_gallery:
EasyPhotos.createAlbum(this, true, GlideEngine.getInstance())
.setFileProviderAuthority(Constants.fileProvider)
.setCount(1)
.start(Constants.PIC);
break;
case R.id.tv_zxing_flashlight:
if (!isLight) {
barcodeView.setTorchOn();
isLight = true;
} else {
isLight = false;
barcodeView.setTorchOff();
break;
}
}
}
private BarcodeCallback callback = new BarcodeCallback() {
@Override
public void barcodeResult(BarcodeResult result) {
if (result.getText() == null) {
// Prevent duplicate scans
return;
}
barcodeView.setStatusText(result.getText());
parseQRCode(result.getText(), new Bundle());
}
@Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
}
};
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case Constants.PIC:
if (data != null) {
showProgressDialog();
List<String> pathList = data.getStringArrayListExtra(EasyPhotos.RESULT_PATHS);
final String picturePath = pathList.get(0).toString();
barcodeView.pause();
Observable.create(new ObservableOnSubscribe<Result>() {
@Override
public void subscribe(ObservableEmitter<Result> e) throws Exception {
Result result = PicUtils.scanningImage(picturePath);
e.onNext(result);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.<Result>bindToLifecycle())
.subscribe(new Observer<Result>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Result result) {
onComplete();
parseQRCode(result.getText(), new Bundle());
}
@Override
public void onError(Throwable e) {
barcodeView.resume();
onComplete();
ToastUtils.showToast("照片中未识别到二维码");
}
@Override
public void onComplete() {
hideProgressDialog();
}
});
} else {
ToastUtils.showToast("获取图片失败");
}
break;
}
}
}
//解析二维码中的字符串
private void parseQRCode(String result, Bundle bundle) {
LogUtils.e("result is =========" + result);
hideProgressDialog();
if (TextUtils.isEmpty(result)) {
ToastUtils.showToast("照片中未识别到二维码");
return;
}
if (NetWorkUtil.isNetWorkUrl(result)) {
startActivity(new Intent(this, WebViewActivity.class).putExtra("url", result));
finish();
return;
} else {
bundle.putString("result", result);
startActivity(new Intent(this, ResultActivity.class).putExtras(bundle));
}
}
@Override
protected void getNetWorkData() {
}
@Override
public Object newPresenter() {
return null;
}
@Override
protected void onResume() {
super.onResume();
barcodeView.resume();
}
@Override
protected void onPause() {
super.onPause();
barcodeView.pause();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return barcodeView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
activity_qrcode.xml
ps:主界面的布局,直接复制就好
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
>
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/barcode_scanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_scanner_layout="@layout/scaner">
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:paddingBottom="@dimen/dpValue35">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:id="@+id/tv_zxing_gallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/dpValue12"
android:drawableTop="@drawable/zxing_gallery"
android:gravity="center"
android:paddingLeft="@dimen/dpValue10"
android:paddingRight="@dimen/dpValue10"
android:text="相册"
android:textColor="@color/white"
android:textSize="@dimen/textSizeValue12" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="2" />
<TextView
android:id="@+id/tv_zxing_flashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/dpValue12"
android:drawableTop="@drawable/zxing_flashlight"
android:gravity="center"
android:paddingLeft="@dimen/dpValue10"
android:paddingRight="@dimen/dpValue10"
android:text="手电筒"
android:textColor="@color/white"
android:textSize="@dimen/textSizeValue12" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</RelativeLayout>
scaner.xml
ps:不要改这里控件的id,重写的第三方控件,改了就找不到了,直接复制就好.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.journeyapps.barcodescanner.BarcodeView
android:id="@+id/zxing_barcode_surface"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_framing_rect_height="258dp"
app:zxing_framing_rect_width="258dp" />
<com.yangsu.chaodao.view.ZxingViewFinderView
android:id="@+id/zxing_viewfinder_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:scannerBoundColor="@color/white"
app:scannerBoundCornerHeight="24dp"
app:scannerBoundCornerWith="1.5dp"
app:scannerBoundWidth="0.5dp"
app:scannerLaserResId="@drawable/scan_icon_scanline"
app:scannerTipText="将二维码图片放到取景框内即可自动扫描"
app:scannerTipTextColor="@color/white"
app:scannerTipTextGravity="false"
app:scannerTipTextMargin="43dp"
app:scannerTipTextSize="14sp"
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask" />
</merge>
ZxingViewFinderView.Java
ps:这个是自定义的自定义的扫描框,复制粘贴即可,copy地址四的.
public class ZxingViewFinderView extends ViewfinderView {
private int scannerBoundColor;
private float scannerBoundWidth; // viewfinder width
private int scannerBoundCornerColor; // viewfinder corner color
private float scannerBoundCornerWidth; // viewfinder corner width
private float scannerBoundCornerHeight; // viewfinder corner height
private int scannerLaserResId; // laser resource
private String scannerTipText; // tip text
private float scannerTipTextSize; // tip text size
private int scannerTipTextColor; // tip text color
private float scannerTipTextMargin; // tip text margin between viewfinder
private boolean tipTextGravityBottom; // tip text gravity
private Bitmap scannerLaserBitmap; // laser bitmap
private int scannerLaserTop; // laser top position
private final static int LASER_MOVE_DISTANCE_PER_UNIT_TIME = 10;
private int LASER_MOVE_DIRECTION = 1;
public ZxingViewFinderView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
Resources resources = getResources(); // Get attributes on view
TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.ZxingViewFinderView);
scannerBoundColor = attributes.getColor(R.styleable.ZxingViewFinderView_scannerBoundColor, resources.getColor(R.color.white));
scannerBoundWidth = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerBoundWidth, 0.5f);
scannerBoundCornerColor = attributes.getColor(R.styleable.ZxingViewFinderView_scannerBoundCornerColor, resources.getColor(R.color.color_blue));
scannerBoundCornerWidth = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerBoundCornerWith, 1.5f);
scannerBoundCornerHeight = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerBoundCornerHeight, 24f);
scannerLaserResId = attributes.getResourceId(R.styleable.ZxingViewFinderView_scannerLaserResId, 0);
scannerTipText = attributes.getString(R.styleable.ZxingViewFinderView_scannerTipText);
scannerTipTextSize = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerTipTextSize, 14f);
scannerTipTextColor = attributes.getColor(R.styleable.ZxingViewFinderView_scannerTipTextColor, resources.getColor(R.color.white));
scannerTipTextMargin = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerTipTextMargin, 40f);
tipTextGravityBottom = attributes.getBoolean(R.styleable.ZxingViewFinderView_scannerTipTextGravity, true);
attributes.recycle();
}
@Override
public void onDraw(Canvas canvas) {
refreshSizes();
if (framingRect == null || previewFramingRect == null) {
return;
}
Rect frame = framingRect;
Rect previewFrame = previewFramingRect;
int width = canvas.getWidth();
int height = canvas.getHeight(); // Draw the exterior
drawExteriorDarkened(canvas, frame, width, height);
if (resultBitmap != null) { // Draw the opaque result bitmap over the scanning rectangle
paint.setAlpha(CURRENT_POINT_OPACITY);
canvas.drawBitmap(resultBitmap, null, frame, paint);
} else {
drawFrameBound(canvas, frame);
drawFrameCorner(canvas, frame);
drawLaserLine(canvas, frame);
drawTipText(canvas, frame, width);
drawResultPoint(canvas, frame, previewFrame); // Request another update at the animation interval,
// but only repaint the laser line, // not the entire viewfinder mask.
postInvalidateDelayed(ANIMATION_DELAY, frame.left - POINT_SIZE, frame.top - POINT_SIZE, frame.right + POINT_SIZE, frame.bottom + POINT_SIZE);
}
}
/**
* Draw a "laser scanner" line to show decoding is active
*/
private void drawLaserLine(Canvas canvas, Rect frame) {
if (scannerLaserResId == 0) {
paint.setColor(laserColor);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
int middle = frame.height() / 2 + frame.top;
canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
} else {
if (scannerLaserBitmap == null) {
scannerLaserBitmap = BitmapFactory.decodeResource(getResources(), scannerLaserResId);
}
if (scannerLaserBitmap != null) {
int LaserHeight = scannerLaserBitmap.getHeight();
if (scannerLaserTop < frame.top) {
scannerLaserTop = frame.top;
LASER_MOVE_DIRECTION = 1;
}
if (scannerLaserTop > frame.bottom - LaserHeight) {
scannerLaserTop = frame.bottom - LaserHeight;
LASER_MOVE_DIRECTION = -1;
}
Rect laserBitmapRect = new Rect(frame.left, scannerLaserTop, frame.right, scannerLaserTop + LaserHeight);
canvas.drawBitmap(scannerLaserBitmap, null, laserBitmapRect, paint);
scannerLaserTop = scannerLaserTop + LASER_MOVE_DISTANCE_PER_UNIT_TIME * LASER_MOVE_DIRECTION;
}
}
}
/**
* Draw result points
*/
private void drawResultPoint(Canvas canvas, Rect frame, Rect previewFrame) {
float scaleX = frame.width() / (float) previewFrame.width();
float scaleY = frame.height() / (float) previewFrame.height();
List<ResultPoint> currentPossible = possibleResultPoints;
List<ResultPoint> currentLast = lastPossibleResultPoints;
int frameLeft = frame.left;
int frameTop = frame.top;
if (currentPossible.isEmpty()) {
lastPossibleResultPoints = null;
} else {
possibleResultPoints = new ArrayList<>(5);
lastPossibleResultPoints = currentPossible;
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
for (ResultPoint point : currentPossible) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), POINT_SIZE, paint);
}
}
if (currentLast != null) {
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
paint.setColor(resultPointColor);
float radius = POINT_SIZE / 2.0f;
for (ResultPoint point : currentLast) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), radius, paint);
}
}
}
/**
* Draw tip text
*/
private void drawTipText(Canvas canvas, Rect frame, int width) {
if (TextUtils.isEmpty(scannerTipText)) {
scannerTipText = "提示";
}
paint.setColor(scannerTipTextColor);
paint.setTextSize(scannerTipTextSize);
float textWidth = paint.measureText(scannerTipText);
float x = (width - textWidth) / 2; //根据 drawTextGravityBottom 文字在扫描框上方还是下文,默认下方
float y = tipTextGravityBottom ? frame.bottom + scannerTipTextMargin : frame.top - scannerTipTextMargin;
canvas.drawText(scannerTipText, x, y, paint);
}
/**
* Draw scanner frame bound * Note: draw inside frame
*/
private void drawFrameBound(Canvas canvas, Rect frame) {
if (scannerBoundWidth <= 0) {
return;
}
paint.setColor(scannerBoundColor); // top
canvas.drawRect(frame.left, frame.top, frame.right, frame.top + scannerBoundWidth, paint); // left
canvas.drawRect(frame.left, frame.top, frame.left + scannerBoundWidth, frame.bottom, paint); // right
canvas.drawRect(frame.right - scannerBoundWidth, frame.top, frame.right, frame.bottom, paint); // bottom
canvas.drawRect(frame.left, frame.bottom - scannerBoundWidth, frame.right, frame.bottom, paint);
}
/**
* Draw scanner frame corner
*/
private void drawFrameCorner(Canvas canvas, Rect frame) {
if (scannerBoundCornerWidth <= 0 || scannerBoundCornerHeight <= 0) {
return;
}
paint.setColor(scannerBoundCornerColor); // left top
canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.top - scannerBoundCornerWidth, frame.left + scannerBoundCornerHeight, frame.top, paint);
canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.top - scannerBoundCornerWidth, frame.left, frame.top + scannerBoundCornerHeight, paint); // left bottom
canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.bottom + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.left, frame.bottom + scannerBoundCornerWidth, paint);
canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.bottom, frame.left - scannerBoundCornerWidth + scannerBoundCornerHeight, frame.bottom + scannerBoundCornerWidth, paint); // right top
canvas.drawRect(frame.right + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.top - scannerBoundCornerWidth, frame.right + scannerBoundCornerWidth, frame.top, paint);
canvas.drawRect(frame.right, frame.top - scannerBoundCornerWidth, frame.right + scannerBoundCornerWidth, frame.top - scannerBoundCornerWidth + scannerBoundCornerHeight, paint); // right bottom
canvas.drawRect(frame.right + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.bottom, frame.right + scannerBoundCornerWidth, frame.bottom + scannerBoundCornerWidth, paint);
canvas.drawRect(frame.right, frame.bottom + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.right + scannerBoundCornerWidth, frame.bottom + scannerBoundCornerWidth, paint);
}
/**
* Draw the exterior (i.e. outside the framing rect) darkened
*/
private void drawExteriorDarkened(Canvas canvas, Rect frame, int width, int height) {
paint.setColor(resultBitmap != null ? resultColor : maskColor); //top
canvas.drawRect(0, 0, width, frame.top, paint); //left
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); //right
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); //bottom
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
}
}
attrs.xml
ps:这个不用多说了
<attr name="scannerBoundColor" format="color" />
<attr name="scannerBoundCornerColor" format="color" />
<attr name="scannerTipTextColor" format="color" />
<attr name="scannerBoundWidth" format="dimension" />
<attr name="scannerBoundCornerWith" format="dimension" />
<attr name="scannerBoundCornerHeight" format="dimension" />
<attr name="scannerTipTextSize" format="dimension" />
<attr name="scannerTipTextMargin" format="dimension" />
<attr name="pstsTabBackground" format="reference" />
<attr name="scannerLaserResId" format="integer" />
<attr name="scannerTipText" format="string" />
<attr name="scannerTipTextGravity" format="boolean" />
</declare-styleable>
PicUtils.Java
处理图片,解析二维码图片使用
public class PicUtils {
static Uri uritempFile;
public static Uri getUritempFile() {
return uritempFile;
}
public static Uri pathToUri(Context context, String path) {
if (path != null) {
path = Uri.decode(path);
ContentResolver cr = context.getContentResolver();
StringBuffer buff = new StringBuffer();
buff.append("(")
.append(MediaStore.Images.ImageColumns.DATA)
.append("=")
.append("'" + path + "'")
.append(")");
Cursor cur = cr.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.ImageColumns._ID},
buff.toString(), null, null);
int index = 0;
for (cur.moveToFirst(); !cur.isAfterLast(); cur
.moveToNext()) {
index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID);
// set _id value
index = cur.getInt(index);
}
if (index == 0) {
//do nothing
} else {
Uri uri_temp = Uri
.parse("content://media/external/images/media/"
+ index);
return uri_temp;
}
}
return null;
}
/**
* 裁剪图片
*/
public static void startPhotoZoom2(Activity context, Uri uri, int width, int height) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// crop为true是设置在开启的intent中设置显示的view可以剪裁
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX,outputY 是剪裁图片的宽高
if (width != 0 && height != 0) {
intent.putExtra("outputX", 500);
intent.putExtra("outputY", 500 * height / width);
} else {
intent.putExtra("outputX", 300);
intent.putExtra("outputY", 300);
}
//裁剪后的图片Uri路径,uritempFile为Uri类变量
uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + String.valueOf(System.currentTimeMillis()) + ".jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
context.startActivityForResult(intent, Constants.ZOOM_IMAGE);
}
/**
* 扫描图片
*
* @param path
* @return
*/
public static Result scanningImage(String path) {
if (TextUtils.isEmpty(path)) {
return null;
}
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(path, opt);
// 获取到这个图片的原始宽度和高度
int picWidth = opt.outWidth;
int picHeight = opt.outHeight;
// 获取画布中间方框的宽度和高度
int screenWidth = 1200;
int screenHeight = 675;
// isSampleSize是表示对图片的缩放程度,比如值为2图片的宽度和高度都变为以前的1/2
opt.inSampleSize = 1;
// 根据屏的大小和图片大小计算出缩放比例
if (picWidth > picHeight) {
if (picWidth > screenWidth)
opt.inSampleSize = picWidth / screenWidth;
} else {
if (picHeight > screenHeight)
opt.inSampleSize = picHeight / screenHeight;
}
// 生成有像素经过缩放了的bitmap
opt.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(path, opt);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
if (bitmap == null) {
return null;
}
return QrUtils.decodeImage(QrUtils.getYUV420sp(width, height, bitmap), width, height);
}
}
QrUtils.Java
public class QrUtils {
private static byte[] yuvs;
/**
* YUV420sp
*
* @param inputWidth
* @param inputHeight
* @param scaled
* @return
*/
public static byte[] getYUV420sp(int inputWidth, int inputHeight, Bitmap scaled) {
int[] argb = new int[inputWidth * inputHeight];
scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
/**
* 需要转换成偶数的像素点,否则编码YUV420的时候有可能导致分配的空间大小不够而溢出。
*/
int requiredWidth = inputWidth % 2 == 0 ? inputWidth : inputWidth + 1;
int requiredHeight = inputHeight % 2 == 0 ? inputHeight : inputHeight + 1;
int byteLength = requiredWidth * requiredHeight * 3 / 2;
if (yuvs == null || yuvs.length < byteLength) {
yuvs = new byte[byteLength];
} else {
Arrays.fill(yuvs, (byte) 0);
}
encodeYUV420SP(yuvs, argb, inputWidth, inputHeight);
scaled.recycle();
return yuvs;
}
/*public static Bitmap decodeSampledBitmapFromFile(String imgPath, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgPath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imgPath, options);
}*/
/**
* Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, reuse the same reader
* objects from one decode to the next.
*/
public static Result decodeImage(byte[] data, int width, int height) {
// 处理
Result result = null;
try {
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
PlanarYUVLuminanceSource source =
new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);
/**
* HybridBinarizer算法使用了更高级的算法,但使用GlobalHistogramBinarizer识别效率确实比HybridBinarizer要高一些。
*
* GlobalHistogram算法:(http://kuangjianwei.blog.163.com/blog/static/190088953201361015055110/)
*
* 二值化的关键就是定义出黑白的界限,我们的图像已经转化为了灰度图像,每个点都是由一个灰度值来表示,就需要定义出一个灰度值,大于这个值就为白(0),低于这个值就为黑(1)。
* 在GlobalHistogramBinarizer中,是从图像中均匀取5行(覆盖整个图像高度),每行取中间五分之四作为样本;以灰度值为X轴,每个灰度值的像素个数为Y轴建立一个直方图,
* 从直方图中取点数最多的一个灰度值,然后再去给其他的灰度值进行分数计算,按照点数乘以与最多点数灰度值的距离的平方来进行打分,选分数最高的一个灰度值。接下来在这两个灰度值中间选取一个区分界限,
* 取的原则是尽量靠近中间并且要点数越少越好。界限有了以后就容易了,与整幅图像的每个点进行比较,如果灰度值比界限小的就是黑,在新的矩阵中将该点置1,其余的就是白,为0。
*/
// BinaryBitmap bitmap1 = new BinaryBitmap(new GlobalHistogramBinarizer(source));
BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader2 = new QRCodeReader();
result = reader2.decode(bitmap1, hints);
} catch (ReaderException e) {
}
return result;
}
/**
* 将bitmap里得到的argb数据转成yuv420sp格式
* 这个yuv420sp数据就可以直接传给MediaCodec, 通过AvcEncoder间接进行编码
*
* @param yuv420sp 用来存放yuv429sp数据
* @param argb 传入argb数据
* @param width bmpWidth
* @param height bmpHeight
*/
private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
// 帧图片的像素大小
final int frameSize = width * height;
// Y的index从0开始
int yIndex = 0;
// UV的index从frameSize开始
int uvIndex = frameSize;
// YUV数据, ARGB数据
int Y, U, V, a, R, G, B;
;
int argbIndex = 0;
// ---循环所有像素点,RGB转YUV---
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
// a is not used obviously
a = (argb[argbIndex] & 0xff000000) >> 24;
R = (argb[argbIndex] & 0xff0000) >> 16;
G = (argb[argbIndex] & 0xff00) >> 8;
B = (argb[argbIndex] & 0xff);
argbIndex++;
// well known RGB to YUV algorithm
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
Y = Math.max(0, Math.min(Y, 255));
U = Math.max(0, Math.min(U, 255));
V = Math.max(0, Math.min(V, 255));
// NV21 has a plane of Y and interleaved planes of VU each
// sampled by a factor of 2
// meaning for every 4 Y pixels there are 1 V and 1 U. Note the
// sampling is every other
// pixel AND every other scanline.
// ---Y---
yuv420sp[yIndex++] = (byte) Y;
// ---UV---
if ((j % 2 == 0) && (i % 2 == 0)) {
yuv420sp[uvIndex++] = (byte) V;
yuv420sp[uvIndex++] = (byte) U;
}
}
}
}
二维码示例.png
ZXingCodeUtils(生成二维码)
ps:这是二维码生成的工具类,四种情况,带logo,不带logo,带文字带logo,带文字无logo.(文字就是指二维码的邀请码部分)嗯.....公司需求,这里是将二维码和文字画在一个背景图上面的,要是不需要,改改就是了,上图,说我们公司需要的的样式.
public class ZXingCodeUtils {
private volatile static ZXingCodeUtils instance;
public static ZXingCodeUtils getInstance() {
if (instance == null) {
synchronized (ZXingCodeUtils.class) {
instance = new ZXingCodeUtils();
}
}
return instance;
}
//不带logo
public Bitmap createQRCode(String msg, int width, int height) {
return createQRCode(msg, width, height, null);
}
//带logo
public Bitmap createQRCode(String msg, int width, int height, Bitmap logo) {
if (!TextUtils.isEmpty(msg)) {
LogUtils.e("url is ------"+msg);
}
try {
int[] tempSize = new int[]{width, height};//要求生成的二维码的宽高
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix matrix = new QRCodeWriter().encode(msg, BarcodeFormat.QR_CODE, width, height, hints);
//调用去除白边方法
matrix = deleteWhite(matrix);
width = matrix.getWidth();
height = matrix.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (matrix.get(x, y)) {
pixels[y * width + x] = Color.BLACK;
} else {
pixels[y * width + x] = Color.WHITE;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
bitmap = addLogoToQRCode(bitmap, logo);
bitmap = zoomImg(bitmap, tempSize[0], tempSize[1]);//处理去掉白边后,二维码的尺寸变化问题。
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
//带文字无logo
public Bitmap createQRCodeWithText(ShareBitmapBean dataBean) {
return createQRCodeWithText(dataBean, null);
}
//带文字有logo
public Bitmap createQRCodeWithText(ShareBitmapBean dataBean, Bitmap logo) {
if (dataBean == null) {
throw new IllegalArgumentException("shareBitmap data can't be null");
}
Bitmap bg = dataBean.getBg();
if (bg == null) {
throw new IllegalArgumentException("background bitmap can't be null");
}
bg = bg.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(bg);
Paint paint = new Paint();
//绘制二维码
if (dataBean.getQrCodeList() != null) {
for (ShareBitmapBean.QRCodeBean qrCodeBean : dataBean.getQrCodeList()) {
Bitmap qrCode = createQRCode(qrCodeBean.getCodeMsg(), qrCodeBean.getCodeWidth(), qrCodeBean.getCodeHeight(), logo);
canvas.drawBitmap(qrCode, qrCodeBean.getCodeX(), qrCodeBean.getCodeY(), paint);
}
}
//绘制文字
if (dataBean.getTextList() != null) {
for (ShareBitmapBean.TextBean textBean : dataBean.getTextList()) {
int textTop = textBean.getSizePX() * 7 / 8 + textBean.getTextY();
paint.setColor(textBean.getTextColor());
paint.setTextSize(textBean.getSizePX());
if (textBean.isCenter()) {
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(textBean.getText(), 360, textTop, paint);
} else {
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(textBean.getText(), textBean.getTextX(), textTop, paint);
}
}
}
//绘制bitmap图像
if (dataBean.getBitmapList() != null) {
for (ShareBitmapBean.BitmapBean bitmapBean : dataBean.getBitmapList()) {
canvas.drawBitmap(bitmapBean.getBitmap(), bitmapBean.getBitmapX(), bitmapBean.getBitmapY(), paint);
}
}
return bg;
}
//添加logo到二维码图片上
private Bitmap addLogoToQRCode(Bitmap src, Bitmap logo) {
if (src == null || logo == null) {
return src;
}
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
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;
}
return bitmap;
}
private static BitMatrix deleteWhite(BitMatrix matrix) {
int[] rec = matrix.getEnclosingRectangle();
int resWidth = rec[2] + 1;
int resHeight = rec[3] + 1;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
resMatrix.clear();
for (int i = 0; i < resWidth; i++) {
for (int j = 0; j < resHeight; j++) {
if (matrix.get(i + rec[0], j + rec[1]))
resMatrix.set(i, j);
}
}
return resMatrix;
}
/**
* 按照指定的尺寸缩放Bitmap
*
* @param bm
* @param newWidth
* @param newHeight
* @return
*/
public Bitmap zoomImg(Bitmap bm, float newWidth, float newHeight) {
if (bm == null) {
return null;
}
// 获得图片的宽高
int width = bm.getWidth();
int height = bm.getHeight();
// 计算缩放比例
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 取得想要缩放的matrix参数
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
// 得到新的图片 www.2cto.com
Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
return newbm;
}
}
ShareBitmapBean.Java
这个是二维码使用Bean,使用文字,背景图片用的,需要就用,不需要就不用是了,具体都有备注的.
public class ShareBitmapBean implements Serializable{
private Bitmap bg;
private List<QRCodeBean> qrCodeList;
private List<TextBean> textList;
private List<BitmapBean> bitmapList;
public Bitmap getBg() {
return bg;
}
public void setBg(Bitmap bg) {
this.bg = bg;
}
public List<QRCodeBean> getQrCodeList() {
return qrCodeList;
}
public void setQrCodeList(List<QRCodeBean> qrCodeList) {
this.qrCodeList = qrCodeList;
}
public List<TextBean> getTextList() {
return textList;
}
public void setTextList(List<TextBean> textList) {
this.textList = textList;
}
public List<BitmapBean> getBitmapList() {
return bitmapList;
}
public void setBitmapList(List<BitmapBean> bitmapList) {
this.bitmapList = bitmapList;
}
public static class QRCodeBean implements Serializable{
//二维码内的信息
private String codeMsg;
//二维码的宽度
private int codeWidth;
//二维码的高度
private int codeHeight;
//二维码的X坐标
private int codeX;
//二维码的Y坐标
private int codeY;
public QRCodeBean(String codeMsg, int codeWidth, int codeHeight, int codeX, int codeY) {
this.codeMsg = codeMsg;
this.codeWidth = codeWidth;
this.codeHeight = codeHeight;
this.codeX = codeX;
this.codeY = codeY;
}
public String getCodeMsg() {
return codeMsg;
}
public void setCodeMsg(String codeMsg) {
this.codeMsg = codeMsg;
}
public int getCodeWidth() {
return codeWidth;
}
public void setCodeWidth(int codeWidth) {
this.codeWidth = codeWidth;
}
public int getCodeHeight() {
return codeHeight;
}
public void setCodeHeight(int codeHeight) {
this.codeHeight = codeHeight;
}
public int getCodeX() {
return codeX;
}
public void setCodeX(int codeX) {
this.codeX = codeX;
}
public int getCodeY() {
return codeY;
}
public void setCodeY(int codeY) {
this.codeY = codeY;
}
public List<QRCodeBean> toList() {
List<QRCodeBean> list = new ArrayList<>();
list.add(this);
return list;
}
}
public static class TextBean implements Serializable{
//文字信息
private String text;
//字号大小,单位像素
private int sizePX;
//文字的X坐标
private int textX;
//文字的Y坐标
private int textY;
//文字颜色
private int textColor;
private boolean isCenter=false;
public boolean isCenter() {
return isCenter;
}
public void setCenter(boolean center) {
isCenter = center;
}
public TextBean(String text, int sizePX, int textX, int textY, int textColor) {
this.text = text;
this.sizePX = sizePX;
this.textX = textX;
this.textY = textY;
this.textColor = textColor;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getSizePX() {
return sizePX;
}
public void setSizePX(int sizePX) {
this.sizePX = sizePX;
}
public int getTextX() {
return textX;
}
public void setTextX(int textX) {
this.textX = textX;
}
public int getTextY() {
return textY;
}
public void setTextY(int textY) {
this.textY = textY;
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
}
public List<TextBean> toList() {
List<TextBean> list = new ArrayList<>();
list.add(this);
return list;
}
}
public static class BitmapBean implements Serializable{
//bitmap
private Bitmap bitmap;
private int bitmapWidth;
private int bitmapHeight;
private int bitmapX;
private int bitmapY;
public BitmapBean(Bitmap bitmap, int bitmapWidth, int bitmapHeight, int bitmapX, int bitmapY) {
this.bitmap = bitmap;
this.bitmapWidth = bitmapWidth;
this.bitmapHeight = bitmapHeight;
this.bitmapX = bitmapX;
this.bitmapY = bitmapY;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public int getBitmapWidth() {
return bitmapWidth;
}
public void setBitmapWidth(int bitmapWidth) {
this.bitmapWidth = bitmapWidth;
}
public int getBitmapHeight() {
return bitmapHeight;
}
public void setBitmapHeight(int bitmapHeight) {
this.bitmapHeight = bitmapHeight;
}
public int getBitmapX() {
return bitmapX;
}
public void setBitmapX(int bitmapX) {
this.bitmapX = bitmapX;
}
public int getBitmapY() {
return bitmapY;
}
public void setBitmapY(int bitmapY) {
this.bitmapY = bitmapY;
}
public List<BitmapBean> toList() {
List<BitmapBean> list = new ArrayList<>();
list.add(this);
return list;
}
}
}
----------------------好了,到此为止了,大功告成---------------------
以上资源均来源于网络,如有侵权,联系我立刻删除!!!!
提供四张二维码供扫描:
1.png 2.png 3.png 4.png
此二维码liabrary:
链接:https://pan.baidu.com/s/1UbmscfIQyEiEzw2jrjPkwg
提取码:onqr
github地址:https://github.com/Mchunyan/QRTest