首页 > 代码库 > Android开源之行之走进zxing,轻松实现二维码扫描(二)

Android开源之行之走进zxing,轻松实现二维码扫描(二)

    对于Zxing开源项目的简化上文已给出,源码经过测试且不断修改。众所周知,Zxing项目的扫描是横向的,这么引用的用户体验确实不好;然而盲目的修改会出现拉伸以及样本采集的偏离。所以这里说一下如何将横屏修改为竖屏扫描

    解决办法引用原文:http://blog.csdn.net/aaawqqq/article/details/24804939

 

一、Zxing扫描框架竖屏切换

     1、menifest.xml中,Activitiy必须要设为竖屏的,添加属性

  android:screenOrientation="portrait"

    2、camera扫描过程中,有两个视图:当前扫描view(取景框)和预览preview。为了修正预览的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()设置我们需要设置的Camera参数,设置preView大小,因此我们我们需要在这里添加                    setDisplayOrientation()来设置camera旋转90度旋转;调用位置: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)之前添加以下代码,防止转化的bitmap与取景框得到的扫描图不一致

1 byte[] rotatedData = http://www.mamicode.com/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 = http://www.mamicode.com/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*48017         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中,在取景框中央的红色扫描线或许跟我们需要的循环移动扫描还有些出入,这就需要我们来自定义属于自己的取景框。实现自定义取景框,我们需要改写ViewfinderView来绘制自  己的View。

      首先需要确定一点,在Zxing源代码中,有两个Rect:一个是frame,通过getFramingRect()取得,这是我们要的取景框的Rect;而另一个previewFrame则是预览视图。我们要做的是以frame为参照物进行我们自己的绘制。

   

    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.top15                 - statusPaddingTop, paint);16 17         int textWidth2 = (int) paint.measureText(statusText2);18         canvas.drawText(statusText2, (width - textWidth2) / 2, frame.top19                 - statusPaddingTop + 60, paint);20     }
View Code

     

    2、绘制取景框边角,也即四个角的蓝色拐角

 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.top17                 + corLength, paint);18         canvas.drawRect(frame.left - corWidth, frame.top - corWidth, frame.left19                 + 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.left29                 + corLength, frame.bottom + corWidth, paint);30         // 右下角31         canvas.drawRect(frame.right, frame.bottom - corLength, frame.right32                 + corWidth, frame.bottom, paint);33         canvas.drawRect(frame.right - corLength, frame.bottom, frame.right34                 + corWidth, frame.bottom + corWidth, paint);35     }
View Code

 

    3、绘制循环移动的扫描线

 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

 

     通过以上的三步绘制,我们就可以实现展示图的效果。整体的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. 这是一个位于相机顶部的预览view,它增加了一个外部部分透明的取景框,以及激光扫描动画和结果组件  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             // 如果有二维码结果的Bitmap,在扫取景框内绘制不透明的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(frameLeft122                                 + (int) (point.getX() * scaleX), frameTop123                                 + (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(frameLeft135                                 + (int) (point.getX() * scaleX), frameTop136                                 + (int) (point.getY() * scaleY), radius, paint);137                     }138                 }139             }140 141             // Request another update at the animation interval, but only142             // 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 canvas154      * @param frame155      */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.top172                 + corLength, paint);173         canvas.drawRect(frame.left - corWidth, frame.top - corWidth, frame.left174                 + 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.left184                 + corLength, frame.bottom + corWidth, paint);185         // 右下角186         canvas.drawRect(frame.right, frame.bottom - corLength, frame.right187                 + corWidth, frame.bottom, paint);188         canvas.drawRect(frame.right - corLength, frame.bottom, frame.right189                 + corWidth, frame.bottom + corWidth, paint);190     }191 192     /**193      * 绘制提示文字194      * 195      * @param canvas196      * @param frame197      * @param width198      */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.top213                 - statusPaddingTop, paint);214 215         int textWidth2 = (int) paint.measureText(statusText2);216         canvas.drawText(statusText2, (width - textWidth2) / 2, frame.top217                 - statusPaddingTop + 60, paint);218     }219 220     /**221      * 绘制移动扫描线222      * 223      * @param canvas224      * @param frame225      */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 live253      * scanning display.254      * 255      * @param barcode256      *            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 it270                 points.subList(0, size - MAX_RESULT_POINTS / 2).clear();271             }272         }273     }274 275 }
View Code

 

Android开源之行之走进zxing,轻松实现二维码扫描(二)