Android开源之行之走进zxing,轻松实现二维码扫描(二)
? ? 對于Zxing開源項目的簡化上文已給出,源碼經(jīng)過測試且不斷修改。眾所周知,Zxing項目的掃描是橫向的,這么引用的用戶體驗確實不好;然而盲目的修改會出現(xiàn)拉伸以及樣本采集的偏離。所以這里說一下如何將橫屏修改為豎屏掃描
? ? 解決辦法引用原文:http://blog.csdn.net/aaawqqq/article/details/24804939
?
一、Zxing掃描框架豎屏切換
? ? ?1、menifest.xml中,Activitiy必須要設(shè)為豎屏的,添加屬性
android:screenOrientation="portrait"? ? 2、camera掃描過程中,有兩個視圖:當(dāng)前掃描view(取景框)和預(yù)覽preview。為了修正預(yù)覽的90度的偏離,我們需要修正PreView。因此我們要修改CameraManager中的 ? ? ? ? ? ? ? ? ? ? ? ?getFramingRectInPreview()的preView的邊框
1 rect.left = rect.left * cameraResolution.y / screenResolution.x; 2 rect.right = rect.right * cameraResolution.y / screenResolution.x; 3 rect.top = rect.top * cameraResolution.x / screenResolution.y; 4 rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y; View Code? ?3、在CameraConfigurationManager中setDesiredCameraParameters()設(shè)置我們需要設(shè)置的Camera參數(shù),設(shè)置preView大小,因此我們我們需要在這里添加 ? ? ? ? ? ? ? ? ? ?setDisplayOrientation()來設(shè)置camera旋轉(zhuǎn)90度旋轉(zhuǎn);調(diào)用位置:setDesiredCameraParameters()中camera.setParameters(parameters)之前
1 void setDisplayOrientation(Camera camera, int angle) { 2 3 Method method; 4 try { 5 method = camera.getClass().getMethod("setDisplayOrientation", 6 new Class[] { int.class }); 7 if (method != null) 8 method.invoke(camera, new Object[] { angle }); 9 } catch (Exception e1) { 10 e1.printStackTrace(); 11 } 12 } View Code? 4、在DecodeHandler中,decode(byte[] data, int width, int height)在PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height)之前添加以下代碼,防止轉(zhuǎn)化的bitmap與取景框得到的掃描圖不一致
1 byte[] rotatedData = new byte[data.length]; 2 for (int y = 0; y < height; y++) { 3 for (int x = 0; x < width; x++) 4 rotatedData[x * height + height - y - 1] = data[x + y * width]; 5 } 6 int tmp = width; // Here we are swapping, that's the difference to #11 7 width = height; 8 height = tmp; 9 data = rotatedData; View Code? 5、在CameraConfigurationManager中,initFromCameraParameters(Camera camera)替換為如下代碼? ?
1 void initFromCameraParameters(Camera camera) { 2 Camera.Parameters parameters = camera.getParameters(); 3 WindowManager manager = (WindowManager) context 4 .getSystemService(Context.WINDOW_SERVICE); 5 Display display = manager.getDefaultDisplay(); 6 Point theScreenResolution = new Point(display.getWidth(), 7 display.getHeight()); 8 screenResolution = theScreenResolution; 9 Log.i(TAG, "Screen resolution: " + screenResolution); 10 11 /************** 豎屏更改4 ******************/ 12 Point screenResolutionForCamera = new Point(); 13 screenResolutionForCamera.x = screenResolution.x; 14 screenResolutionForCamera.y = screenResolution.y; 15 16 // preview size is always something like 480*320, other 320*480 17 if (screenResolution.x < screenResolution.y) { 18 screenResolutionForCamera.x = screenResolution.y; 19 screenResolutionForCamera.y = screenResolution.x; 20 } 21 22 cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue( 23 parameters, screenResolutionForCamera); 24 Log.i(TAG, "Camera resolution: " + cameraResolution); 25 26 } View Code?
? 非常感謝NDK-baozi的解決方法!
?
二、解決自定義取景框的問題
? ? ? Zxing中,在取景框中央的紅色掃描線或許跟我們需要的循環(huán)移動掃描還有些出入,這就需要我們來自定義屬于自己的取景框。實現(xiàn)自定義取景框,我們需要改寫ViewfinderView來繪制自 ?己的View。
? ? ? 首先需要確定一點,在Zxing源代碼中,有兩個Rect:一個是frame,通過getFramingRect()取得,這是我們要的取景框的Rect;而另一個previewFrame則是預(yù)覽視圖。我們要做的是以frame為參照物進(jìn)行我們自己的繪制。
? ?
? ? 1、繪制描述的文字,也即取景框上方的提示消息? ? ??
1 private void drawStatusText(Canvas canvas, Rect frame, int width) { 2 3 String statusText1 = getResources().getString( 4 R.string.viewfinderview_status_text1); 5 String statusText2 = getResources().getString( 6 R.string.viewfinderview_status_text2); 7 int statusTextSize = 45; 8 int statusPaddingTop = 180; 9 10 paint.setColor(statusColor); 11 paint.setTextSize(statusTextSize); 12 13 int textWidth1 = (int) paint.measureText(statusText1); 14 canvas.drawText(statusText1, (width - textWidth1) / 2, frame.top 15 - statusPaddingTop, paint); 16 17 int textWidth2 = (int) paint.measureText(statusText2); 18 canvas.drawText(statusText2, (width - textWidth2) / 2, frame.top 19 - statusPaddingTop + 60, paint); 20 } View Code? ? ?
? ? 2、繪制取景框邊角,也即四個角的藍(lán)色拐角
1 private void drawFrameBounds(Canvas canvas, Rect frame) { 2 3 paint.setColor(Color.WHITE); 4 paint.setStrokeWidth(2); 5 paint.setStyle(Paint.Style.STROKE); 6 7 canvas.drawRect(frame, paint); 8 9 paint.setColor(Color.BLUE); 10 paint.setStyle(Paint.Style.FILL); 11 12 int corWidth = 15; 13 int corLength = 45; 14 15 // 左上角 16 canvas.drawRect(frame.left - corWidth, frame.top, frame.left, frame.top 17 + corLength, paint); 18 canvas.drawRect(frame.left - corWidth, frame.top - corWidth, frame.left 19 + corLength, frame.top, paint); 20 // 右上角 21 canvas.drawRect(frame.right, frame.top, frame.right + corWidth, 22 frame.top + corLength, paint); 23 canvas.drawRect(frame.right - corLength, frame.top - corWidth, 24 frame.right + corWidth, frame.top, paint); 25 // 左下角 26 canvas.drawRect(frame.left - corWidth, frame.bottom - corLength, 27 frame.left, frame.bottom, paint); 28 canvas.drawRect(frame.left - corWidth, frame.bottom, frame.left 29 + corLength, frame.bottom + corWidth, paint); 30 // 右下角 31 canvas.drawRect(frame.right, frame.bottom - corLength, frame.right 32 + corWidth, frame.bottom, paint); 33 canvas.drawRect(frame.right - corLength, frame.bottom, frame.right 34 + corWidth, frame.bottom + corWidth, paint); 35 } View Code?
? ? 3、繪制循環(huán)移動的掃描線
1 private void drawScanLight(Canvas canvas, Rect frame) { 2 3 if (scanLineTop == 0) { 4 scanLineTop = frame.top; 5 } 6 7 if (scanLineTop >= frame.bottom) { 8 scanLineTop = frame.top; 9 } else { 10 scanLineTop += SCAN_VELOCITY; 11 } 12 Rect scanRect = new Rect(frame.left, scanLineTop, frame.right, 13 scanLineTop + 30); 14 canvas.drawBitmap(scanLight, null, scanRect, paint); 15 } View Code?
? ? ?通過以上的三步繪制,我們就可以實現(xiàn)展示圖的效果。整體的ViewfinderView代碼:? ?
1 /** 2 * This view is overlaid on top of the camera preview. It adds the viewfinder 3 * rectangle and partial transparency outside it, as well as the laser scanner 4 * animation and result points. 這是一個位于相機頂部的預(yù)覽view,它增加了一個外部部分透明的取景框,以及激光掃描動畫和結(jié)果組件 5 * 6 * @author dswitkin@google.com (Daniel Switkin) 7 */ 8 public final class ViewfinderView extends View { 9 10 private static final int[] SCANNER_ALPHA = { 0, 64, 128, 192, 255, 192, 11 128, 64 }; 12 private static final long ANIMATION_DELAY = 80L; 13 private static final int CURRENT_POINT_OPACITY = 0xA0; 14 private static final int MAX_RESULT_POINTS = 20; 15 private static final int POINT_SIZE = 6; 16 17 private CameraManager cameraManager; 18 private final Paint paint; 19 private Bitmap resultBitmap; 20 private final int maskColor; // 取景框外的背景顏色 21 private final int resultColor;// result Bitmap的顏色 22 private final int laserColor; // 紅色掃描線的顏色 23 private final int resultPointColor; // 特征點的顏色 24 private final int statusColor; // 提示文字顏色 25 private int scannerAlpha; 26 private List<ResultPoint> possibleResultPoints; 27 private List<ResultPoint> lastPossibleResultPoints; 28 // 掃描線移動的y 29 private int scanLineTop; 30 // 掃描線移動速度 31 private final int SCAN_VELOCITY = 5; 32 // 掃描線 33 Bitmap scanLight; 34 35 public ViewfinderView(Context context, AttributeSet attrs) { 36 super(context, attrs); 37 38 // Initialize these once for performance rather than calling them every 39 // time in onDraw(). 40 paint = new Paint(Paint.ANTI_ALIAS_FLAG); 41 Resources resources = getResources(); 42 maskColor = resources.getColor(R.color.viewfinder_mask); 43 resultColor = resources.getColor(R.color.result_view); 44 laserColor = resources.getColor(R.color.viewfinder_laser); 45 resultPointColor = resources.getColor(R.color.possible_result_points); 46 statusColor = resources.getColor(R.color.status_text); 47 scannerAlpha = 0; 48 possibleResultPoints = new ArrayList<ResultPoint>(5); 49 lastPossibleResultPoints = null; 50 scanLight = BitmapFactory.decodeResource(resources, 51 R.drawable.scan_light); 52 } 53 54 public void setCameraManager(CameraManager cameraManager) { 55 this.cameraManager = cameraManager; 56 } 57 58 @SuppressLint("DrawAllocation") 59 @Override 60 public void onDraw(Canvas canvas) { 61 if (cameraManager == null) { 62 return; // not ready yet, early draw before done configuring 63 } 64 65 // frame為取景框 66 Rect frame = cameraManager.getFramingRect(); 67 Rect previewFrame = cameraManager.getFramingRectInPreview(); 68 if (frame == null || previewFrame == null) { 69 return; 70 } 71 int width = canvas.getWidth(); 72 int height = canvas.getHeight(); 73 74 // Draw the exterior (i.e. outside the framing rect) darkened 75 // 繪制取景框外的暗灰色的表面,分四個矩形繪制 76 paint.setColor(resultBitmap != null ? resultColor : maskColor); 77 canvas.drawRect(0, 0, width, frame.top, paint);// Rect_1 78 canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); // Rect_2 79 canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, 80 paint); // Rect_3 81 canvas.drawRect(0, frame.bottom + 1, width, height, paint); // Rect_4 82 83 if (resultBitmap != null) { 84 // Draw the opaque result bitmap over the scanning rectangle 85 // 如果有二維碼結(jié)果的Bitmap,在掃取景框內(nèi)繪制不透明的result Bitmap 86 paint.setAlpha(CURRENT_POINT_OPACITY); 87 canvas.drawBitmap(resultBitmap, null, frame, paint); 88 } else { 89 // Draw a red "laser scanner" line through the middle to show 90 // decoding is active 91 drawFrameBounds(canvas, frame); 92 drawStatusText(canvas, frame, width); 93 94 // 繪制掃描線 95 // paint.setColor(laserColor); 96 // paint.setAlpha(SCANNER_ALPHA[scannerAlpha]); 97 // scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; 98 // int middle = frame.height() / 2 + frame.top; 99 // canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, 100 // middle + 2, paint); 101 102 drawScanLight(canvas, frame); 103 104 float scaleX = frame.width() / (float) previewFrame.width(); 105 float scaleY = frame.height() / (float) previewFrame.height(); 106 107 // 繪制掃描線周圍的特征點 108 List<ResultPoint> currentPossible = possibleResultPoints; 109 List<ResultPoint> currentLast = lastPossibleResultPoints; 110 int frameLeft = frame.left; 111 int frameTop = frame.top; 112 if (currentPossible.isEmpty()) { 113 lastPossibleResultPoints = null; 114 } else { 115 possibleResultPoints = new ArrayList<ResultPoint>(5); 116 lastPossibleResultPoints = currentPossible; 117 paint.setAlpha(CURRENT_POINT_OPACITY); 118 paint.setColor(resultPointColor); 119 synchronized (currentPossible) { 120 for (ResultPoint point : currentPossible) { 121 canvas.drawCircle(frameLeft 122 + (int) (point.getX() * scaleX), frameTop 123 + (int) (point.getY() * scaleY), POINT_SIZE, 124 paint); 125 } 126 } 127 } 128 if (currentLast != null) { 129 paint.setAlpha(CURRENT_POINT_OPACITY / 2); 130 paint.setColor(resultPointColor); 131 synchronized (currentLast) { 132 float radius = POINT_SIZE / 2.0f; 133 for (ResultPoint point : currentLast) { 134 canvas.drawCircle(frameLeft 135 + (int) (point.getX() * scaleX), frameTop 136 + (int) (point.getY() * scaleY), radius, paint); 137 } 138 } 139 } 140 141 // Request another update at the animation interval, but only 142 // repaint the laser line, 143 // not the entire viewfinder mask. 144 postInvalidateDelayed(ANIMATION_DELAY, frame.left - POINT_SIZE, 145 frame.top - POINT_SIZE, frame.right + POINT_SIZE, 146 frame.bottom + POINT_SIZE); 147 } 148 } 149 150 /** 151 * 繪制取景框邊框 152 * 153 * @param canvas 154 * @param frame 155 */ 156 private void drawFrameBounds(Canvas canvas, Rect frame) { 157 158 paint.setColor(Color.WHITE); 159 paint.setStrokeWidth(2); 160 paint.setStyle(Paint.Style.STROKE); 161 162 canvas.drawRect(frame, paint); 163 164 paint.setColor(Color.BLUE); 165 paint.setStyle(Paint.Style.FILL); 166 167 int corWidth = 15; 168 int corLength = 45; 169 170 // 左上角 171 canvas.drawRect(frame.left - corWidth, frame.top, frame.left, frame.top 172 + corLength, paint); 173 canvas.drawRect(frame.left - corWidth, frame.top - corWidth, frame.left 174 + corLength, frame.top, paint); 175 // 右上角 176 canvas.drawRect(frame.right, frame.top, frame.right + corWidth, 177 frame.top + corLength, paint); 178 canvas.drawRect(frame.right - corLength, frame.top - corWidth, 179 frame.right + corWidth, frame.top, paint); 180 // 左下角 181 canvas.drawRect(frame.left - corWidth, frame.bottom - corLength, 182 frame.left, frame.bottom, paint); 183 canvas.drawRect(frame.left - corWidth, frame.bottom, frame.left 184 + corLength, frame.bottom + corWidth, paint); 185 // 右下角 186 canvas.drawRect(frame.right, frame.bottom - corLength, frame.right 187 + corWidth, frame.bottom, paint); 188 canvas.drawRect(frame.right - corLength, frame.bottom, frame.right 189 + corWidth, frame.bottom + corWidth, paint); 190 } 191 192 /** 193 * 繪制提示文字 194 * 195 * @param canvas 196 * @param frame 197 * @param width 198 */ 199 private void drawStatusText(Canvas canvas, Rect frame, int width) { 200 201 String statusText1 = getResources().getString( 202 R.string.viewfinderview_status_text1); 203 String statusText2 = getResources().getString( 204 R.string.viewfinderview_status_text2); 205 int statusTextSize = 45; 206 int statusPaddingTop = 180; 207 208 paint.setColor(statusColor); 209 paint.setTextSize(statusTextSize); 210 211 int textWidth1 = (int) paint.measureText(statusText1); 212 canvas.drawText(statusText1, (width - textWidth1) / 2, frame.top 213 - statusPaddingTop, paint); 214 215 int textWidth2 = (int) paint.measureText(statusText2); 216 canvas.drawText(statusText2, (width - textWidth2) / 2, frame.top 217 - statusPaddingTop + 60, paint); 218 } 219 220 /** 221 * 繪制移動掃描線 222 * 223 * @param canvas 224 * @param frame 225 */ 226 private void drawScanLight(Canvas canvas, Rect frame) { 227 228 if (scanLineTop == 0) { 229 scanLineTop = frame.top; 230 } 231 232 if (scanLineTop >= frame.bottom) { 233 scanLineTop = frame.top; 234 } else { 235 scanLineTop += SCAN_VELOCITY; 236 } 237 Rect scanRect = new Rect(frame.left, scanLineTop, frame.right, 238 scanLineTop + 30); 239 canvas.drawBitmap(scanLight, null, scanRect, paint); 240 } 241 242 public void drawViewfinder() { 243 Bitmap resultBitmap = this.resultBitmap; 244 this.resultBitmap = null; 245 if (resultBitmap != null) { 246 resultBitmap.recycle(); 247 } 248 invalidate(); 249 } 250 251 /** 252 * Draw a bitmap with the result points highlighted instead of the live 253 * scanning display. 254 * 255 * @param barcode 256 * An image of the decoded barcode. 257 */ 258 public void drawResultBitmap(Bitmap barcode) { 259 resultBitmap = barcode; 260 invalidate(); 261 } 262 263 public void addPossibleResultPoint(ResultPoint point) { 264 List<ResultPoint> points = possibleResultPoints; 265 synchronized (points) { 266 points.add(point); 267 int size = points.size(); 268 if (size > MAX_RESULT_POINTS) { 269 // trim it 270 points.subList(0, size - MAX_RESULT_POINTS / 2).clear(); 271 } 272 } 273 } 274 275 } View Code?
轉(zhuǎn)載于:https://www.cnblogs.com/kipMeister/p/4123676.html
總結(jié)
以上是生活随笔為你收集整理的Android开源之行之走进zxing,轻松实现二维码扫描(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自动计算尺寸列表功能案例ios源码
- 下一篇: 自定义实现moveable button