二維碼的掃描和生成(zxing-android-embedded)的基礎使用
阿新 • • 發佈:2018-11-15
簡述:這個部落格主要記載zxing-android-embedded的簡單使用,如何替換相機的佈局,如何去掉生成二維碼的空白等等一系列問題.
zxing-android-embedded的使用
1.首先新增依賴
implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
2.生成二維碼
private void createQrCode(){ BarcodeEncoder barcodeEncoder = new BarcodeEncoder(); try { BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, 400, 400); bitMatrix =deleteWhite(bitMatrix); //刪除二維碼周邊的空白處 qrCodeBitmap=barcodeEncoder.createBitmap(bitMatrix); qrCodeImg.setImageBitmap(qrCodeBitmap); } catch (WriterException e) { e.printStackTrace(); } }
3.掃描二維碼
/** * 掃描二維碼 */ private void scanQrCode(){ IntentIntegrator intentIntegrator = new IntentIntegrator(this); intentIntegrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE); //設定掃描的型別 intentIntegrator.setOrientationLocked(false); //方向鎖定 intentIntegrator.setCaptureActivity(CustomCaptureActivity.class); intentIntegrator.setCameraId(0); //前置相機還是後置相機 intentIntegrator.setBeepEnabled(false); //是否發出成功的聲音 intentIntegrator.setBarcodeImageEnabled(true); intentIntegrator.initiateScan(); }
方向鎖定的話,你還需要設定CaptureActivity,假如你是按依賴新增的而不是匯入原始碼,那麼需要
public class CustomCaptureActivity extends CaptureActivity {
}
在AndroidManifest裡面設定
<activity android:name=".custom.CustomCaptureActivity" android:screenOrientation="fullSensor" tools:replace="screenOrientation"/>
在掃描完成之後,他會自動返回到onActivityResult()函式裡面
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); if(result != null) { if(result.getContents() == null) { Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show(); } } else { super.onActivityResult(requestCode, resultCode, data); } }
這裡的result.getContents()就是二維碼內含的值
刪除二維碼生成時候的周圍的空白
zing-android-embedde原始生成的二維碼周圍會有很多的空白,所以自己需要刪除空白
/** * 刪除二維碼的空白區域 * @param matrix * @return */ private static BitMatrix deleteWhite(BitMatrix matrix) { int[] rec = matrix.getEnclosingRectangle(); //數字7 代表了留的空白區域的大小 int resWidth = rec[2] + 10; int resHeight = rec[3] + 10; BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); resMatrix.clear(); for (int i = 10; i < resWidth; i++) { for (int j = 10; j < resHeight; j++) { if(matrix.get(i+rec[0],j+rec[1])) resMatrix.set(i,j); } } return resMatrix; }
自定義二維碼掃描時候掃描的大小
因為zxing-android-embedded生成的介面比較難看,所以一般需要修改原生的介面。一般是修改原始碼或者是繼承
本文因為是採用依賴導成jar進來的,所以採用的是繼承的方式。
public class CustomCaptureActivity extends CaptureActivity { private ImageView oldBackImg; private CustomDecoratedBarcodeView customDecoratedBarcodeView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); oldBackImg = findViewById(R.id.oldBackImg); oldBackImg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { finish(); } }); } @Override protected DecoratedBarcodeView initializeContent(){ setContentView(R.layout.custom_activity_zxing_capture); customDecoratedBarcodeView= findViewById(R.id.zxing_custom_barcode_scanner); return customDecoratedBarcodeView; }
}
最主要的是inittializeContent()方法,這個方法可以設定除了掃描以外的介面,比如加一個標題欄等等
DecoratedBarcodeView 就是掃描最重要的組成部分,他由兩個View組成,第一個BarcodeView就是掃描的視窗,
第二個Viewfinder有效果。 所以我們如果想繪製掃描的介面可以重寫Viewfineder的onDraw()方法
package backup.wingos.com.wingbackup.custom; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import com.journeyapps.barcodescanner.ViewfinderView; import backup.wingos.com.wingbackup.R; /** * Created by xiongbo on 2018/11/14. */ public class CustomViewfinderView extends ViewfinderView { private static final String TAG = "CustomViewfinderView"; /** * 畫相機四個角落的值 */ private Paint customPaint; /** * 獲取相機的區域 */ private Rect frame; /** * 修改這個值 可以修改角落繪製的顏色 */ private int color; /** * 四個角落的寬度 */ private int cornerWidth; /** * 四個角落的厚度 */ private int cornerHeight; /** * 你需要在相機下面顯示的文字 */ private String text; /** * 中間橫線豎直的位置 */ private int direction; /** * 代表了第一次繪製 */ private boolean isFirst; /** * 是否獲取二維碼的結果 */ private boolean isResult; /** * 是否退出執行緒 */ private boolean isExit; private Thread thread; public CustomViewfinderView(Context context, AttributeSet attrs) { super(context, attrs); customPaint = new Paint(); customPaint.setStyle(Paint.Style.FILL); color=getResources().getColor(R.color.colorPrimary); customPaint.setAntiAlias(true); cornerWidth=50; cornerHeight=8; text="Scan the QR code from new phone"; isFirst = true; isResult=false; isExit=false; thread = new Thread(new Runnable() { @Override public void run() { while(!isExit){ setDirection(direction+2); try { thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }); thread.start(); } @Override public void onDraw(Canvas canvas) { super.refreshSizes(); if (framingRect == null || previewFramingRect == null) { return; } frame = framingRect; if(isFirst){ isFirst=false; direction=frame.top+10; } customPaint.setColor(color); customPaint.setStrokeWidth((float) 1.5); /** * 繪製四條邊框 */ canvas.drawLine(frame.left,frame.top,frame.right,frame.top,customPaint); canvas.drawLine(frame.left,frame.top,frame.left,frame.bottom,customPaint); canvas.drawLine(frame.right,frame.top,frame.right,frame.bottom,customPaint); canvas.drawLine(frame.right,frame.bottom,frame.left,frame.bottom,customPaint); final int width = canvas.getWidth(); final int height = canvas.getHeight(); // Draw the exterior (i.e. outside the framing rect) darkened paint.setColor(resultBitmap != null ? resultColor : maskColor); canvas.drawRect(0, 0, width, frame.top, paint); canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); canvas.drawRect(0, frame.bottom + 1, width, height, paint); if (resultBitmap != null) { // Draw the opaque result bitmap over the scanning rectangle paint.setAlpha(CURRENT_POINT_OPACITY); canvas.drawBitmap(resultBitmap, null, frame, paint); isResult=true; } else { customPaint.setAlpha(100); Rect rect = new Rect(frame.left,frame.top,frame.right, direction); canvas.drawRect(rect,customPaint); customPaint.setAlpha(255); customPaint.setStrokeWidth(3); canvas.drawLine(frame.left,direction,frame.right,direction,customPaint); if(direction>=frame.bottom){ direction=frame.top; } } if(isResult){ isExit=true; }else{ isExit=false; } drawCorner(1,canvas); drawCorner(2,canvas); drawCorner(3,canvas); drawCorner(4,canvas); drawText(text,canvas); } public void setColor(int color){ this.color=color; invalidate(); } /** * 繪製四個角落 type: 1--左上 2--右上 3--左下 4--右下 */ private void drawCorner(int type,Canvas canvas){ Rect verticalRect=null; Rect horizontalRect =null; if(type==1){ horizontalRect = new Rect(frame.left,frame.top,frame.left+cornerWidth,frame.top+cornerHeight); verticalRect = new Rect(frame.left,frame.top,frame.left+cornerHeight,frame.top+cornerWidth); }else if(type==2){ horizontalRect = new Rect(frame.right-cornerWidth,frame.top,frame.right,frame.top+cornerHeight); verticalRect = new Rect(frame.right-cornerHeight,frame.top,frame.right,frame.top+cornerWidth); }else if(type==3){ horizontalRect = new Rect(frame.left,frame.bottom-cornerWidth,frame.left+cornerHeight,frame.bottom); verticalRect = new Rect(frame.left,frame.bottom-cornerHeight,frame.left+cornerWidth,frame.bottom); }else{ horizontalRect = new Rect(frame.right-cornerWidth,frame.bottom-cornerHeight,frame.right,frame.bottom); verticalRect = new Rect(frame.right-cornerHeight,frame.bottom-cornerWidth,frame.right,frame.bottom); } canvas.drawRect(horizontalRect,customPaint); canvas.drawRect(verticalRect,customPaint); } public void setText(String text){ this.text=text; } private void drawText(String text,Canvas canvas){ customPaint.setColor(getResources().getColor(R.color.colorWhite)); customPaint.setTextSize(50); canvas.drawText(text,frame.left-30,frame.bottom+100,customPaint); } private void setDirection(int direction){ this.direction=direction; invalidate(); } public void stopThread(){ isExit=true; if(thread!=null){ thread=null; } } }首先我們的framingRect 代表了視窗區域,我們繪製的時候不能繪製在視窗的區域裡面,要不然會攔住視窗,因為Viewfinder是繪製在BarcodeView的上方的.修改視窗的大小,其實是有屬性的,他們自定義View的style如下
<?xml version="1.0" encoding="UTF-8"?> <resources> <declare-styleable name="zxing_view"> <attr name="zxing_scanner_layout" format="reference"/> </declare-styleable> <declare-styleable name="zxing_camera_preview"> <attr name="zxing_framing_rect_width" format="dimension" /> //視窗大小 <attr name="zxing_framing_rect_height" format="dimension" /> <attr name="zxing_use_texture_view" format="boolean" /> <attr name="zxing_preview_scaling_strategy" format="enum"> <enum name="centerCrop" value="1" /> <enum name="fitCenter" value="2" /> <enum name="fitXY" value="3" /> </attr> </declare-styleable> <declare-styleable name="zxing_finder"> <attr name="zxing_possible_result_points" format="color"/> <attr name="zxing_result_view" format="color"/> <attr name="zxing_viewfinder_laser" format="color"/> <attr name="zxing_viewfinder_mask" format="color"/> //繪製背景的顏色 </declare-styleable> </resources>
我們自定義的話,可以把這個style複製到自己專案裡面。也可以直接用;只要在BarcodeView設定屬性就好了
<backup.wingos.com.wingbackup.custom.CustomBarcodeView android:id="@+id/zxing_custom_barcode_surface" android:layout_width="match_parent" android:layout_height="match_parent" app:zxing_framing_rect_height="240dp" app:zxing_framing_rect_width="240dp" /> <backup.wingos.com.wingbackup.custom.CustomViewfinderView android:id="@+id/zxing_custom_viewfinder_view" android:layout_width="match_parent" android:layout_height="match_parent" app:zxing_viewfinder_mask="#757575" />
這樣就完成效果了。而且原生的話,關閉會有一定的延時效果,所以我們可以把這個延時關閉掉,影響使用者體驗
/** * 造成了延時效果 * 本來為 2000000000 修改成了 10000000 */ @Override public void pauseAndWait() { CameraInstance instance = getCameraInstance(); pause(); long startTime = System.nanoTime(); while(instance != null && !instance.isCameraClosed()) { if(System.nanoTime() - startTime > 10000000) { // Don't wait for longer than 2 seconds break; } try { Thread.sleep(1); } catch (InterruptedException e) { break; } } }這需要在CustomBarcodeView裡面修改的,因為原生的BarcodeView在退出的時候會阻塞主執行緒2s鍾。