原文地址:
https://blog.csdn.net/qq_34902522/article/details/78654731
前言
本篇博文主要講解如何從相冊選取二維碼熄浓,進行掃碼解析蹋凝。如果對zxing掃碼不熟悉的建議閱讀本文之前看看之前的博文:
Android基于Zxing掃碼實現(xiàn)(一)
Android基于Zxing掃碼實現(xiàn)(二)
本文的代碼基于YZxing庫,如需查閱代碼可前往GitHub上面查看妖枚。項目地址如下:
YZxing
內(nèi)容
從相冊獲取二維碼,主要涉及到幾大步驟苍在。
第一绝页,進入相冊獲取照片。
第二忌穿,對照片進行壓縮抒寂。
第三,對照片上的二維碼進行decode掠剑。
第四,對掃碼結(jié)果進行處理郊愧。
我們一步一步的來看朴译,首先我們實現(xiàn)第一個環(huán)節(jié)的邏輯,代碼如下:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.scan_from_picture) {
//先申請權(quán)限
int checked = ContextCompat.checkSelfPermission(ScannerActivity.this
, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (checked == PackageManager.PERMISSION_GRANTED) {
goPicture();
} else {
ActivityCompat.requestPermissions(ScannerActivity.this
, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE_WRITE_EXTERNAL_STORAGE);
}
}
return true;
}
private void goPicture() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_CODE_GET_PIC_URI);
}
上面展示的部分代碼属铁。代碼邏輯是首先去check權(quán)限眠寿,然后通過goPicture這個方法去獲取相冊圖片。 照片信息通過onActivityResult方法返回焦蘑。對此進行處理盯拱,代碼如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_CODE_GET_PIC_URI:
Uri uri = data.getData();
String imagePath = UriUtils.getPicturePathFromUri(ScannerActivity.this, uri);
//對獲取到的二維碼照片進行壓縮
Bitmap bitmap = CommonUtils.compressPicture(imagePath);
Message message = mHandler.obtainMessage(MESSAGE_DECODE_FROM_BITMAP, bitmap);
mHandler.sendMessage(message);
Log.e(TAG, "onActivityResult: uri:" + uri.toString());
break;
}
}
}
這里涉及兩大邏輯,一是獲取照片的路徑例嘱,一是對照片進行壓縮處理狡逢。獲取照片路徑的代碼如下:
public static String getPicturePathFromUri(Context context, Uri uri) {
int sdkVersion = Build.VERSION.SDK_INT;
if (sdkVersion >= 19) {
return getPicturePathFromUriAboveApi19(context, uri);
} else {
return getPicturePathFromUriBelowAPI19(context, uri);
}
}
private static String getPicturePathFromUriBelowAPI19(Context context, Uri uri) {
return getDataColumn(context, uri, null, null);
}
private static String getPicturePathFromUriAboveApi19(Context context, Uri uri) {
String filePath = null;
if (DocumentsContract.isDocumentUri(context, uri)) {
// 如果是document類型的 uri, 則通過document id來進行處理
String documentId = DocumentsContract.getDocumentId(uri);
if (isMediaDocument(uri)) { // MediaProvider
// 使用':'分割
String id = documentId.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=?";
String[] selectionArgs = {id};
filePath = getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs);
} else if (isDownloadsDocument(uri)) { // DownloadsProvider
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId));
filePath = getDataColumn(context, contentUri, null, null);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// 如果是 content 類型的 Uri
filePath = getDataColumn(context, uri, null, null);
} else if ("file".equals(uri.getScheme())) {
// 如果是 file 類型的 Uri,直接獲取圖片對應(yīng)的路徑
filePath = uri.getPath();
}
return filePath;
}
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
String path = null;
String[] projection = new String[]{MediaStore.Images.Media.DATA};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndexOrThrow(projection[0]);
path = cursor.getString(columnIndex);
}
} catch (Exception e) {
if (cursor != null) {
cursor.close();
}
}
return path;
}
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
對照片進行壓縮的代碼如下:
public static Bitmap compressPicture(String imgPath) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgPath, options);
Log.e(TAG, "onActivityResult: 未壓縮之前圖片的寬:" + options.outWidth + "--未壓縮之前圖片的高:"
+ options.outHeight + "--未壓縮之前圖片大小:" + options.outWidth * options.outHeight * 4 / 1024 / 1024 + "M");
options.inSampleSize = calculateInSampleSize(options, 100, 100);
Log.e(TAG, "onActivityResult: inSampleSize:" + options.inSampleSize);
options.inJustDecodeBounds = false;
Bitmap afterCompressBm = BitmapFactory.decodeFile(imgPath, options);
// //默認的圖片格式是Bitmap.Config.ARGB_8888
Log.e(TAG, "onActivityResult: 圖片的寬:" + afterCompressBm.getWidth() + "--圖片的高:"
+ afterCompressBm.getHeight() + "--圖片大小:" + afterCompressBm.getWidth() * afterCompressBm.getHeight() * 4 / 1024 / 1024 + "M");
return afterCompressBm;
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
對照片壓縮這塊不熟悉的可以參考文章:
壓縮指定大小的BitMap
照片處理這塊,著重提一下拼卵,這邊對照片的處理是必須的奢浑,除了節(jié)省內(nèi)存不說,還可以提高掃碼成功率腋腮。這里做了不少測試雀彼,如果照片中二維碼大小和照片本身大小存在巨大差異的話,就是說照片中的二維碼特別小即寡,就會有掃不出來的情況徊哑,親測有效的一個解決方法是通過對照片進行裁剪,把除了二維碼外多余的部分裁減掉就可以成功解碼了 這里可以通過跳系統(tǒng)的裁剪界面聪富,也可以自己設(shè)計裁剪界面來實現(xiàn)裁剪莺丑。我這邊比較懶,就沒做裁剪過程善涨。如果想自定義裁剪界面的話窒盐,這邊推薦一個源碼寫的挺好草则,可以去看看。自定義裁剪界面 這個裁剪邏輯加在上面所說的第二步和第三步中間就行蟹漓,其他步驟都不變炕横。有需要的可以去看看。
接著看葡粒,照片壓縮之后就是對照片的解碼了份殿。代碼如下:
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
ScannerActivity activity = this.activity.get();
if (activity != null) {
if (msg.what == activity.MESSAGE_DECODE_FROM_BITMAP) {
Bitmap bm = (Bitmap) msg.obj;
DecodeUtils.DecodeAsyncTask decodeAsyncTask = new DecodeUtils.DecodeAsyncTask(activity);
decodeAsyncTask.execute(bm);
}
}
}
public static class DecodeAsyncTask extends AsyncTask<Bitmap, Integer, Result> {
private WeakReference<Context> mContext;
private Result result;
public DecodeAsyncTask(Context mContext) {
this.mContext = new WeakReference<Context>(mContext);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Result doInBackground(Bitmap... bitmaps) {
result = decodeFromPicture(bitmaps[0]);
return result;
}
@Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
if (result != null) {
String text = result.getText();
if (!TextUtils.isEmpty(text)) {
Intent intent = new Intent(mContext.get(), ShowResultActivity.class);
intent.putExtra(Constant.EXTRA_RESULT_TEXT_FROM_PIC, text);
mContext.get().startActivity(intent);
if (mContext.get() instanceof Activity) ((Activity) mContext.get()).finish();
}
} else {
Toast.makeText(mContext.get(), "解碼失敗", Toast.LENGTH_SHORT).show();
}
}
}
通過自定義的一個AsyncTask來處理解碼這一耗時操作。這邊掃碼成功后的邏輯是如果掃碼掃出來的是一個正確的網(wǎng)址就直接跳轉(zhuǎn)到網(wǎng)頁嗽交,如果不是的話卿嘲,就顯示到TextView上。
演示效果
掃碼圖片為:
[圖片上傳失敗...(image-6d8273-1541297223387)]
[圖片上傳失敗...(image-fbd2bb-1541297223387)]
YZxing
compile 'com.yangy:YZxing-lib:2.1'
YZxing目前已更新至2.1版本夫壁,修復部分已知bug拾枣,添加了從相冊獲取二維碼的功能,該功能默認情況下不啟用盒让,如需使用梅肤,需要在代碼中添加:
//設(shè)置是否啟用從相冊獲取二維碼。
// intent.putExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC,true);
上訴語句表示啟用此功能邑茄。關(guān)于YZxing的完整使用姨蝴,可以去YZxing項目瀏覽。
結(jié)語
上面所列的代碼肺缕,在YZxing項目上有完整代碼左医。如果有需要可以去YZxing項目 瀏覽使用。