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 = 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中,在取景框中央的红色扫描线或许跟我们需要的循环移动扫描还有些出入,这就需要我们来自定义属于自己的取景框。实现自定义取景框,我们需要改写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.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、绘制取景框边角,也即四个角的蓝色拐角

 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、绘制循环移动的扫描线

 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(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

 

posted @ 2014-11-26 16:49  KipMeister  阅读(1887)  评论(0编辑  收藏  举报