您的位置:首页 > 其它

安卓实现二维码生成和扫描功能,扫描支持直接拍照扫码和相册图片扫码,还加了照明功能

2016-12-30 16:30 886 查看
最近在做二维码的生成和扫描,生成二维码相对而言较为简单,扫描相对复杂,遇到的问题较多,但是在实现二维码的生成和扫描之前最重要的一步

就是讲Zxing包导入,后面的内容大部分是使用包中的内容,

那我就从二维码的生成讲起吧!

二维码生成:

直接贴代码了

1  //要转换的地址或字符串,可以是中文,输入内容生成二维码
2     public Bitmap createQRImage(String string) {
3         try {
4             Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
5             hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
6             //图像数据转换,使用了矩阵转换
7             BitMatrix bitMatrix = new QRCodeWriter().encode(string, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints);
8             int[] pixels = new int[QR_WIDTH * QR_HEIGHT];
9             //下面这里按照二维码的算法,逐个生成二维码的图片,
10             //两个for循环是图片横列扫描的结果
11             for (int y = 0; y < QR_HEIGHT; y++) {
12                 for (int x = 0; x < QR_WIDTH; x++) {
13                     if (bitMatrix.get(x, y)) {
14                         pixels[y * QR_WIDTH + x] = 0xff000000;
15                     } else {
16                         pixels[y * QR_WIDTH + x] = 0xffffffff;
17                     }
18                 }
19             }
20             //生成二维码图片的格式,使用ARGB_8888
21             Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);
22             bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);
23             return bitmap;
24         } catch (WriterException e) {
25             e.printStackTrace();
26         }
27
28
29         return null;
30     }


使用这个方法即可生成二维码了

接下来是扫描二维码了

扫描二维码:

我百度了一下,哟不少需要我们自己写的,太多了,我就不一一一写了,就贴出我认为比较重要的两个,一个是CameraManager,一个是ViewfinderView

这两个都可以我们根据自己的爱好相对于的修改样式或扫描框的大小,上代码了

ViewfinderView:

1     private static final String TAG = "log";
2     /**
3      * 刷新界面的时间
4      */
5     private static final long ANIMATION_DELAY = 30L;
6     private static final int OPAQUE = 0xFF;
7
8     /**
9      * 四个绿色边角对应的长度
10      */
11     private int ScreenRate;
12
13     /**
14      * 四个绿色边角对应的宽度
15      */
16     private static final int CORNER_WIDTH = 10;
17     /**
18      * 扫描框中的中间线的宽度
19      */
20     private static final int MIDDLE_LINE_WIDTH = 6;
21
22     /**
23      * 扫描框中的中间线的与扫描框左右的间隙
24      */
25     private static final int MIDDLE_LINE_PADDING = 5;
26
27     /**
28      * 中间那条线每次刷新移动的距离
29      */
30     private static final int SPEEN_DISTANCE = 5;
31
32     /**
33      * 手机的屏幕密度
34      */
35     private static float density;
36     /**
37      * 字体大小
38      */
39     private static final int TEXT_SIZE = 16;
40     /**
41      * 字体距离扫描框下面的距离
42      */
43     private static final int TEXT_PADDING_TOP = 30;
44
45     /**
46      * 画笔对象的引用
47      */
48     private Paint paint;
49
50     /**
51      * 中间滑动线的最顶端位置
52      */
53     private int slideTop;
54
55     /**
56      * 中间滑动线的最底端位置
57      */
58     private int slideBottom;
59
60     private Bitmap resultBitmap;
61     private final int maskColor;
62     private final int resultColor;
63     private final int frameColor;
64     private final int resultPointColor;
65     private Collection<ResultPoint> possibleResultPoints;
66     private Collection<ResultPoint> lastPossibleResultPoints;
67
68     boolean isFirst;
69
70     public ViewfinderView(Context context, AttributeSet attrs) {
71         super(context, attrs);
72
73         density = context.getResources().getDisplayMetrics().density;
74         //将像素转换成dp
75         ScreenRate = (int) (20 * density);
76
77         paint = new Paint();
78         Resources resources = getResources();
79         maskColor = resources.getColor(R.color.viewfinder_mask);
80         resultColor = resources.getColor(R.color.result_view);
81         frameColor = resources.getColor(R.color.viewfinder_frame);
82         resultPointColor = resources.getColor(R.color.possible_result_points);
83         possibleResultPoints = new HashSet<ResultPoint>(5);
84     }
85
86     @Override
87     public void onDraw(Canvas canvas) {
88         //中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改
89         Rect frame = CameraManager.get().getFramingRect();
90         if (frame == null) {
91             return;
92         }
93
94         //初始化中间线滑动的最上边和最下边
95         if (!isFirst) {
96             isFirst = true;
97             slideTop = frame.top;
98             slideBottom = frame.bottom;
99         }
100
101         //获取屏幕的宽和高
102         int width = canvas.getWidth();
103         int height = canvas.getHeight();
104
105         paint.setColor(resultBitmap != null ? resultColor : maskColor);
106
107         //画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面
108         //扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边
109         canvas.drawRect(0, 0, width, frame.top, paint);
110         canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
111         canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
112                 paint);
113         canvas.drawRect(0, frame.bottom + 1, width, height, paint);
114
115
116         if (resultBitmap != null) {
117             // Draw the opaque result bitmap over the scanning rectangle
118             paint.setAlpha(OPAQUE);
119             canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
120         } else {
121 //这是我自己加的扫描框内部的白线,让扫描框看起来更好看
122             paint.setColor(frameColor);
123             canvas.drawRect(frame.left + CORNER_WIDTH, frame.top + CORNER_WIDTH, frame.right + 1 - CORNER_WIDTH, frame.top + 2 + CORNER_WIDTH, paint);
124             canvas.drawRect(frame.left + CORNER_WIDTH, frame.top + 2 + CORNER_WIDTH, frame.left + 2 + CORNER_WIDTH, frame.bottom - 1 - CORNER_WIDTH, paint);
125             canvas.drawRect(frame.right - 1 - CORNER_WIDTH, frame.top + CORNER_WIDTH, frame.right + 1 - CORNER_WIDTH, frame.bottom - 1 - CORNER_WIDTH, paint);
126             canvas.drawRect(frame.left + CORNER_WIDTH, frame.bottom - 1 - CORNER_WIDTH, frame.right + 1 - CORNER_WIDTH, frame.bottom + 1 - CORNER_WIDTH, paint);
127
128             //画扫描框边上的角,总共8个部分
129             paint.setColor(Color.parseColor("#FF038CF7"));
130             //左上
131             canvas.drawRect(frame.left, frame.top - CORNER_WIDTH, frame.left + ScreenRate,
132                     frame.top + CORNER_WIDTH - CORNER_WIDTH, paint);
133             canvas.drawRect(frame.left - CORNER_WIDTH, frame.top - CORNER_WIDTH, frame.left + CORNER_WIDTH - CORNER_WIDTH, frame.top
134                     + ScreenRate - CORNER_WIDTH + CORNER_WIDTH, paint);
135             //右上
136             canvas.drawRect(frame.right - ScreenRate, frame.top - CORNER_WIDTH, frame.right + CORNER_WIDTH,
137                     frame.top + CORNER_WIDTH - CORNER_WIDTH, paint);
138             canvas.drawRect(frame.right - CORNER_WIDTH + CORNER_WIDTH, frame.top, frame.right + CORNER_WIDTH, frame.top
139                     + ScreenRate, paint);
140             //左下
141             canvas.drawRect(frame.left - CORNER_WIDTH, frame.bottom - ScreenRate, frame.left + CORNER_WIDTH - CORNER_WIDTH
142                     , frame.bottom, paint);
143             canvas.drawRect(frame.left - CORNER_WIDTH, frame.bottom,
144                     frame.left + ScreenRate, frame.bottom + CORNER_WIDTH, paint);
145             //右下
146             canvas.drawRect(frame.right, frame.bottom - ScreenRate,
147                     frame.right + CORNER_WIDTH, frame.bottom, paint);
148             canvas.drawRect(frame.right - ScreenRate, frame.bottom,
149                     frame.right + CORNER_WIDTH, frame.bottom + CORNER_WIDTH, paint);
150
151
152             //绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
153             slideTop += SPEEN_DISTANCE;
154             if (slideTop >= frame.bottom) {
155                 slideTop = frame.top;
156             }
157             canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH / 2, frame.right - MIDDLE_LINE_PADDING, slideTop + MIDDLE_LINE_WIDTH / 2, paint);
158
159
160             //画扫描框下面的字
161             paint.setColor(Color.WHITE);
162             paint.setTextSize(TEXT_SIZE * density);
163             paint.setAlpha(0x40);
164             paint.setTypeface(Typeface.create("System", Typeface.BOLD));
165             canvas.drawText(getResources().getString(R.string.scan_text), frame.left, (float) (frame.bottom + (float) TEXT_PADDING_TOP * density), paint);
166
167
168             Collection<ResultPoint> currentPossible = possibleResultPoints;
169             Collection<ResultPoint> currentLast = lastPossibleResultPoints;
170             if (currentPossible.isEmpty()) {
171                 lastPossibleResultPoints = null;
172             } else {
173                 possibleResultPoints = new HashSet<ResultPoint>(5);
174                 lastPossibleResultPoints = currentPossible;
175                 paint.setAlpha(OPAQUE);
176                 paint.setColor(resultPointColor);
177                 for (ResultPoint point : currentPossible) {
178                     canvas.drawCircle(frame.left + point.getX(), frame.top
179                             + point.getY(), 6.0f, paint);
180                 }
181             }
182             if (currentLast != null) {
183                 paint.setAlpha(OPAQUE / 2);
184                 paint.setColor(resultPointColor);
185                 for (ResultPoint point : currentLast) {
186                     canvas.drawCircle(frame.left + point.getX(), frame.top
187                             + point.getY(), 3.0f, paint);
188                 }
189             }
190
191
192             //只刷新扫描框的内容,其他地方不刷新
193             postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
194                     frame.right, frame.bottom);
195
196         }
197     }
198
199     public void drawViewfinder() {
200         resultBitmap = null;
201         invalidate();
202     }
203
204     /**
205      * Draw a bitmap with the result points highlighted instead of the live
206      * scanning display.
207      *
208      * @param barcode An image of the decoded barcode.
209      */
210     public void drawResultBitmap(Bitmap barcode) {
211         resultBitmap = barcode;
212         invalidate();
213     }
214
215     public void addPossibleResultPoint(ResultPoint point) {
216         possibleResultPoints.add(point);
217     }


CameraManager:

1 private static final String TAG = CameraManager.class.getSimpleName();
2
3   private static final int MIN_FRAME_WIDTH = 240;
4   private static final int MIN_FRAME_HEIGHT = 240;
5   private static final int MAX_FRAME_WIDTH = 720;
6   private static final int MAX_FRAME_HEIGHT = 480;
7
8   private static CameraManager cameraManager;
9
10   static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT
11   static {
12     int sdkInt;
13     try {
14       sdkInt = Integer.parseInt(Build.VERSION.SDK);
15     } catch (NumberFormatException nfe) {
16       // Just to be safe
17       sdkInt = 10000;
18     }
19     SDK_INT = sdkInt;
20   }
21
22   private final Context context;
23   private final CameraConfigurationManager configManager;
24   private Camera camera;
25   private Rect framingRect;
26   private Rect framingRectInPreview;
27   private boolean initialized;
28   private boolean previewing;
29   private final boolean useOneShotPreviewCallback;
30   /**
31    * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
32    * clear the handler so it will only receive one message.
33    */
34   private final PreviewCallback previewCallback;
35   /** Autofocus callbacks arrive here, and are dispatched to the Handler which requested them. */
36   private final AutoFocusCallback autoFocusCallback;
37
38   /**
39    * Initializes this static object with the Context of the calling Activity.
40    *
41    * @param context The Activity which wants to use the camera.
42    */
43   public static void init(Context context) {
44     if (cameraManager == null) {
45       cameraManager = new CameraManager(context);
46     }
47   }
48
49   /**
50    * Gets the CameraManager singleton instance.
51    *
52    * @return A reference to the CameraManager singleton.
53    */
54   public static CameraManager get() {
55     return cameraManager;
56   }
57
58   private CameraManager(Context context) {
59
60     this.context = context;
61     this.configManager = new CameraConfigurationManager(context);
62
63     // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
64     // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
65     // the more efficient one shot callback, as the older one can swamp the system and cause it
66     // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
67     //useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
68     useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3; // 3 = Cupcake
69
70     previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
71     autoFocusCallback = new AutoFocusCallback();
72   }
73   //控制开启闪光灯
74   public void flashHandler() {
75     //camera.startPreview();
76     Camera.Parameters parameters = camera.getParameters();
77     // 判断闪光灯当前状态來修改
78     if (Camera.Parameters.FLASH_MODE_OFF.equals(parameters.getFlashMode())) {
79       turnOn(parameters);
80     } else if (Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode())) {
81       turnOff(parameters);
82     }
83   }
84   //开
85   private void turnOn(Camera.Parameters parameters) {
86     parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
87     camera.setParameters(parameters);
88   }
89   //关
90   private void turnOff(Camera.Parameters parameters) {
91     parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
92     camera.setParameters(parameters);
93   }
94   /**
95    * Opens the camera driver and initializes the hardware parameters.
96    *
97    * @param holder The surface object which the camera will draw preview frames into.
98    * @throws IOException Indicates the camera driver failed to open.
99    */
100   public void openDriver(SurfaceHolder holder) throws IOException {
101
102     if (camera == null) {
103       System.out.println("wanggsx openDriver camera = null");
104       camera = Camera.open();
105       if (camera == null) {
106         throw new IOException();
107       }
108       camera.setPreviewDisplay(holder);
109
110       if (!initialized) {
111         initialized = true;
112         configManager.initFromCameraParameters(camera);
113       }
114       configManager.setDesiredCameraParameters(camera);
115       FlashlightManager.enableFlashlight();
116     } else {
117       System.out.println("wangying openDriver camera != null");
118       camera.setPreviewDisplay(holder);//此处为新增方法
119     }
120 /***
121  * 以上内容为更改内容,经实践结果我们发现,viewfinder会出现黑屏的情况,以上内容就是解决的方法,
122  * 我遇到的情况是我从相册中选取图片进行扫描的时候,返回扫描界面的时候就出现viewFinder黑屏,大家复制上面
123  * 的内容,放到这个方法就可以啦!
124  */
125
126 //    if (camera == null) {
127 //      camera = Camera.open();
128 //      if (camera == null) {
129 //        throw new IOException();
130 //      }
131 //      camera.setPreviewDisplay(holder);
132 //
133 //      if (!initialized) {
134 //        initialized = true;
135 //        configManager.initFromCameraParameters(camera);
136 //      }
137 //      configManager.setDesiredCameraParameters(camera);
138
139       //FIXME
140  //     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
141       //�Ƿ�ʹ��ǰ��
142 //      if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
143 //        FlashlightManager.enableFlashlight();
144 //      }
145 //      FlashlightManager.enableFlashlight();
146 //    }
147   }
148
149   /**
150    * Closes the camera driver if still in use.
151    */
152   public void closeDriver() {
153     if (camera != null) {
154       FlashlightManager.disableFlashlight();
155       camera.release();
156       camera = null;
157     }
158   }
159
160   /**
161    * Asks the camera hardware to begin drawing preview frames to the screen.
162    */
163   public void startPreview() {
164     if (camera != null && !previewing) {
165       camera.startPreview();
166       previewing = true;
167     }
168   }
169
170   /**
171    * Tells the camera to stop drawing preview frames.
172    */
173   public void stopPreview() {
174     if (camera != null && previewing) {
175       if (!useOneShotPreviewCallback) {
176         camera.setPreviewCallback(null);
177       }
178       camera.stopPreview();
179       previewCallback.setHandler(null, 0);
180       autoFocusCallback.setHandler(null, 0);
181       previewing = false;
182     }
183   }
184
185   /**
186    * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
187    * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
188    * respectively.
189    *
190    * @param handler The handler to send the message to.
191    * @param message The what field of the message to be sent.
192    */
193   public void requestPreviewFrame(Handler handler, int message) {
194     if (camera != null && previewing) {
195       previewCallback.setHandler(handler, message);
196       if (useOneShotPreviewCallback) {
197         camera.setOneShotPreviewCallback(previewCallback);
198       } else {
199         camera.setPreviewCallback(previewCallback);
200       }
201     }
202   }
203
204   /**
205    * Asks the camera hardware to perform an autofocus.
206    *
207    * @param handler The Handler to notify when the autofocus completes.
208    * @param message The message to deliver.
209    */
210   public void requestAutoFocus(Handler handler, int message) {
211     if (camera != null && previewing) {
212       autoFocusCallback.setHandler(handler, message);
213       //Log.d(TAG, "Requesting auto-focus callback");
214       camera.autoFocus(autoFocusCallback);
215     }
216   }
217
218   /**
219    * Calculates the framing rect which the UI should draw to show the user where to place the
220    * barcode. This target helps with alignment as well as forces the user to hold the device
221    * far enough away to ensure the image will be in focus.
222    *
223    * @return The rectangle to draw on screen in window coordinates.
224    */
225   public Rect getFramingRect() {
226     Point screenResolution = configManager.getScreenResolution();
227     if (framingRect == null) {
228       if (camera == null) {
229         return null;
230       }
231       int width = screenResolution.x * 3 / 4;
232       if (width < MIN_FRAME_WIDTH) {
233         width = MIN_FRAME_WIDTH;
234       } else if (width > MAX_FRAME_WIDTH) {
235         width = MAX_FRAME_WIDTH;
236       }
237       int height = screenResolution.y * 3 / 6;
238       if (height < MIN_FRAME_HEIGHT) {
239         height = MIN_FRAME_HEIGHT;
240       } else if (height > MAX_FRAME_HEIGHT) {
241         height = MAX_FRAME_HEIGHT;
242       }
243       int leftOffset = (screenResolution.x - width) / 2;
244       int topOffset = (screenResolution.y - height) / 2;
245       framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
246       Log.d(TAG, "Calculated framing rect: " + framingRect);
247     }
248     return framingRect;
249   }
250
251   /**
252    * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
253    * not UI / screen.
254    */
255   public Rect getFramingRectInPreview() {
256     if (framingRectInPreview == null) {
257       Rect rect = new Rect(getFramingRect());
258       Point cameraResolution = configManager.getCameraResolution();
259       Point screenResolution = configManager.getScreenResolution();
260       //modify here
261 //      rect.left = rect.left * cameraResolution.x / screenResolution.x;
262 //      rect.right = rect.right * cameraResolution.x / screenResolution.x;
263 //      rect.top = rect.top * cameraResolution.y / screenResolution.y;
264 //      rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
265       rect.left = rect.left * cameraResolution.y / screenResolution.x;
266       rect.right = rect.right * cameraResolution.y / screenResolution.x;
267       rect.top = rect.top * cameraResolution.x / screenResolution.y;
268       rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
269       framingRectInPreview = rect;
270     }
271     return framingRectInPreview;
272   }
273
274   /**
275    * Converts the result points from still resolution coordinates to screen coordinates.
276    *
277    * @param points The points returned by the Reader subclass through Result.getResultPoints().
278    * @return An array of Points scaled to the size of the framing rect and offset appropriately
279    *         so they can be drawn in screen coordinates.
280    */
281   /*
282   public Point[] convertResultPoints(ResultPoint[] points) {
283     Rect frame = getFramingRectInPreview();
284     int count = points.length;
285     Point[] output = new Point[count];
286     for (int x = 0; x < count; x++) {
287       output[x] = new Point();
288       output[x].x = frame.left + (int) (points[x].getX() + 0.5f);
289       output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
290     }
291     return output;
292   }
293    */
294
295   /**
296    * A factory method to build the appropriate LuminanceSource object based on the format
297    * of the preview buffers, as described by Camera.Parameters.
298    *
299    * @param data A preview frame.
300    * @param width The width of the image.
301    * @param height The height of the image.
302    * @return A PlanarYUVLuminanceSource instance.
303    */
304   public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
305     Rect rect = getFramingRectInPreview();
306     int previewFormat = configManager.getPreviewFormat();
307     String previewFormatString = configManager.getPreviewFormatString();
308     switch (previewFormat) {
309       // This is the standard Android format which all devices are REQUIRED to support.
310       // In theory, it's the only one we should ever care about.
311       case PixelFormat.YCbCr_420_SP:
312       // This format has never been seen in the wild, but is compatible as we only care
313       // about the Y channel, so allow it.
314       case PixelFormat.YCbCr_422_SP:
315         return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
316             rect.width(), rect.height());
317       default:
318         // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
319         // Fortunately, it too has all the Y data up front, so we can read it.
320         if ("yuv420p".equals(previewFormatString)) {
321           return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
322             rect.width(), rect.height());
323         }
324     }
325     throw new IllegalArgumentException("Unsupported picture format: " +
326         previewFormat + '/' + previewFormatString);
327   }
328
329     public Context getContext() {
330         return context;
331     }


接下来我们就看看扫描界面的操作了

首先看一下扫描界面,看了之后要做的东西,一目了然了

camera.xml:


1 <?xml version="1.0" encoding="utf-8"?>
2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3     android:layout_width="fill_parent"
4     android:layout_height="fill_parent" >
5
6     <SurfaceView
7         android:id="@+id/preview_view"
8         android:layout_width="wrap_content"
9         android:layout_height="wrap_content"
10         android:layout_gravity="center" />
11
12     <com.wangy.mycode.Zxing.view.ViewfinderView
13         android:id="@+id/viewfinder_view"
14         android:layout_width="wrap_content"
15         android:layout_height="wrap_content" />
16     <RelativeLayout
17         android:layout_width="fill_parent"
18         android:layout_height="fill_parent"
19         android:layout_gravity="center"
20         android:orientation="vertical" >
21
22         <TextView
23             android:layout_width="fill_parent"
24             android:layout_height="wrap_content"
25             android:layout_alignParentTop="true"
26             android:layout_centerInParent="true"
27             android:background="#038cf7"
28             android:gravity="center"
29             android:paddingBottom="10dp"
30             android:paddingTop="10dp"
31             android:text="二维码扫描"
32             android:textColor="@android:color/white"
33             android:textSize="18sp"
34             android:textStyle="bold" />
35         <ImageView
36             android:id="@+id/result_back"
37             android:layout_width="wrap_content"
38             android:layout_height="wrap_content"
39             android:src="@drawable/icon_back"
40             android:layout_margin="10dp"
41             android:layout_alignParentLeft="true"
42             />
43         <TextView
44             android:layout_width="match_parent"
45             android:layout_height="wrap_content"
46             android:minHeight="60dp"
47             android:text="将取景框对准二维码,\n即可自动扫描。"
48             android:textColor="#fff"
49             android:gravity="center"
50             android:textSize="16sp"
51             android:layout_above="@+id/ll_type"
52             />
53         <LinearLayout
54             android:layout_width="wrap_content"
55             android:layout_height="200dp"
56             android:layout_alignParentRight="true"
57             android:gravity="center"
58             android:orientation="vertical">
59             <TextView
60                 android:id="@+id/btn_shine"
61                 android:layout_width="wrap_content"
62                 android:layout_height="wrap_content"
63                 android:layout_margin="10dp"
64                 android:padding="5dp"
65                 android:background="@drawable/white"
66                 android:text="照明"
67                 android:textColor="#fff"
68                 android:textSize="15sp"
69                 />
70         </LinearLayout>
71
72         <LinearLayout
73             android:id="@+id/ll_type"
74             android:layout_width="match_parent"
75             android:layout_marginTop="10dp"
76             android:layout_alignParentBottom="true"
77             android:gravity="center"
78             android:layout_height="wrap_content">
79         <Button
80             android:id="@+id/btn_scan"
81             android:layout_width="70dp"
82             android:layout_height="70dp"
83             android:layout_margin="20dp"
84             android:background="@drawable/blueroll"
85             android:layout_alignParentBottom="true"
86             android:layout_marginBottom="75dp"
87             android:text="扫码"
88             android:textColor="#fff"
89             android:textSize="15sp"
90              />
91         <Button
92             android:id="@+id/btn_phone"
93             android:layout_width="70dp"
94             android:layout_height="70dp"
95             android:layout_margin="20dp"
96             android:background="@drawable/tranroll"
97             android:layout_alignParentBottom="true"
98             android:layout_marginBottom="75dp"
99             android:text="相册"
100             android:textColor="#fff"
101             android:textSize="15sp"
102             />
103         </LinearLayout>
104     </RelativeLayout>
105
106 </FrameLayout>


看看扫描界面吧!

1 package com.wangy.mycode.Zxing;
2
3 import android.content.Intent;
4 import android.content.res.AssetFileDescriptor;
5 import android.database.Cursor;
6 import android.graphics.Bitmap;
7 import android.graphics.BitmapFactory;
8 import android.graphics.Color;
9 import android.media.AudioManager;
10 import android.media.MediaPlayer;
11 import android.os.Bundle;
12 import android.os.Handler;
13 import android.os.Vibrator;
14 import android.provider.MediaStore;
15 import android.view.KeyEvent;
16 import android.view.SurfaceHolder;
17 import android.view.SurfaceView;
18 import android.view.View;
19 import android.widget.Button;
20 import android.widget.ImageView;
21 import android.widget.TextView;
22 import android.widget.Toast;
23
24 import com.google.zxing.BarcodeFormat;
25 import com.google.zxing.BinaryBitmap;
26 import com.google.zxing.DecodeHintType;
27 import com.google.zxing.Result;
28 import com.google.zxing.common.HybridBinarizer;
29 import com.google.zxing.qrcode.QRCodeReader;
30 import com.wangy.mycode.Base.BaseActivity;
31 import com.wangy.mycode.R;
32 import com.wangy.mycode.RGBLuminanceSource;
33 import com.wangy.mycode.ScanResultActivity;
34 import com.wangy.mycode.Zxing.camera.CameraManager;
35 import com.wangy.mycode.Zxing.decoding.CaptureActivityHandler;
36 import com.wangy.mycode.Zxing.decoding.InactivityTimer;
37 import com.wangy.mycode.Zxing.view.ViewfinderView;
38 import com.wangy.mycode.utils.IntentUtils;
39
40 import java.io.IOException;
41 import java.util.Hashtable;
42 import java.util.Vector;
43
44 /**
45  * Created by xhb on 2016/12/28.
46  */
47 public class CaptureActivity extends BaseActivity implements SurfaceHolder.Callback, View.OnClickListener {
48     private CaptureActivityHandler handler;
49     private ViewfinderView viewfinderView;
50     private boolean hasSurface;
51     private Vector<BarcodeFormat> decodeFormats;
52     private String characterSet;
53     private InactivityTimer inactivityTimer;
54     private MediaPlayer mediaPlayer;
55     private boolean playBeep;
56     private static final float BEEP_VOLUME = 0.10f;
57     private boolean vibrate;
58     private ImageView result_back;
59     private Button btn_scan;
60     private Button btn_phone;
61     private static final int CHOOSE_PIC = 0;
62     private TextView btn_shine;
63     private boolean isopen;
64
65     /**
66      * Called when the activity is first created.
67      */
68     @Override
69     public void onCreate(Bundle savedInstanceState) {
70         super.onCreate(savedInstanceState);
71         setContentView(R.layout.camera);
72         //ViewUtil.addTopView(getApplicationContext(), this, R.string.scan_card);
73         CameraManager.init(getApplication());
74         initview();
75         viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
76         hasSurface = false;
77         inactivityTimer = new InactivityTimer(this);
78     }
79
80     private void initview() {
81         result_back = (ImageView) findViewById(R.id.result_back);
82         btn_scan = (Button) findViewById(R.id.btn_scan);
83         btn_phone = (Button) findViewById(R.id.btn_phone);
84         btn_shine = (TextView)findViewById(R.id.btn_shine);
85         result_back.setOnClickListener(this);
86         btn_scan.setOnClickListener(this);
87         btn_phone.setOnClickListener(this);
88         btn_shine.setOnClickListener(this);
89     }
90
91     @Override
92     protected void onResume() {
93         super.onResume();
94         SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
95         SurfaceHolder surfaceHolder = surfaceView.getHolder();
96         if (hasSurface) {
97             initCamera(surfaceHolder);
98         } else {
99             surfaceHolder.addCallback(this);
100             surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
101         }
102         decodeFormats = null;
103         characterSet = null;
104
105         playBeep = true;
106         AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
107         if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
108             playBeep = false;
109         }
110         initBeepSound();
111         vibrate = true;
112
113     }
114
115     @Override
116     protected void onPause() {
117         super.onPause();
118         if (handler != null) {
119             handler.quitSynchronously();
120             handler = null;
121         }
122         CameraManager.get().closeDriver();
123     }
124
125     @Override
126     protected void onDestroy() {
127         inactivityTimer.shutdown();
128         super.onDestroy();
129     }
130
131     /**
132      * Handler scan result
133      * @param result
134      * @param barcode
135      */
136     public void handleDecode(Result result, Bitmap barcode) {
137         inactivityTimer.onActivity();
138         playBeepSoundAndVibrate();
139         String resultString = result.getText();
140         //FIXME
141         if (resultString.equals("")) {
142             Toast.makeText(CaptureActivity.this, "扫描失败!", Toast.LENGTH_SHORT).show();
143         } else {
144             Intent intent = new Intent(CaptureActivity.this, ScanResultActivity.class);
145             intent.putExtra("result", resultString);
146             IntentUtils.enterIntent(CaptureActivity.this, intent);
147         }
148 //        CaptureActivity.this.finish();
149     }
150
151     private void initCamera(SurfaceHolder surfaceHolder) {
152         try {
153             CameraManager.get().openDriver(surfaceHolder);
154         } catch (IOException ioe) {
155             return;
156         } catch (RuntimeException e) {
157             return;
158         }
159         if (handler == null) {
160             handler = new CaptureActivityHandler(this, decodeFormats,
161                     characterSet);
162         }
163     }
164
165     @Override
166     public void surfaceChanged(SurfaceHolder holder, int format, int width,
167                                int height) {
168
169     }
170
171     @Override
172     public void surfaceCreated(SurfaceHolder holder) {
173         if (!hasSurface) {
174             hasSurface = true;
175             initCamera(holder);
176         }
177     }
178
179     @Override
180     public void surfaceDestroyed(SurfaceHolder holder) {
181         hasSurface = false;
182     }
183
184     public ViewfinderView getViewfinderView() {
185         return viewfinderView;
186     }
187
188     public Handler getHandler() {
189         return handler;
190     }
191
192     public void drawViewfinder() {
193         viewfinderView.drawViewfinder();
194     }
195
196     private void initBeepSound() {
197         if (playBeep && mediaPlayer == null) {
198             // The volume on STREAM_SYSTEM is not adjustable, and users found it
199             // too loud,
200             // so we now play on the music stream.
201             setVolumeControlStream(AudioManager.STREAM_MUSIC);
202             mediaPlayer = new MediaPlayer();
203             mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
204             mediaPlayer.setOnCompletionListener(beepListener);
205
206             AssetFileDescriptor file = getResources().openRawResourceFd(
207                     R.raw.beep);
208             try {
209                 mediaPlayer.setDataSource(file.getFileDescriptor(),
210                         file.getStartOffset(), file.getLength());
211                 file.close();
212                 mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
213                 mediaPlayer.prepare();
214             } catch (IOException e) {
215                 mediaPlayer = null;
216             }
217         }
218     }
219
220     private static final long VIBRATE_DURATION = 200L;
221
222     private void playBeepSoundAndVibrate() {
223         if (playBeep && mediaPlayer != null) {
224             mediaPlayer.start();
225         }
226         if (vibrate) {
227             Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
228             vibrator.vibrate(VIBRATE_DURATION);
229         }
230     }
231
232     /**
233      * When the beep has finished playing, rewind to queue up another one.
234      */
235     private final MediaPlayer.OnCompletionListener beepListener = new MediaPlayer.OnCompletionListener() {
236         public void onCompletion(MediaPlayer mediaPlayer) {
237             mediaPlayer.seekTo(0);
238         }
239     };
240
241     @Override
242     public void onClick(View v) {
243         switch (v.getId()) {
244             case R.id.btn_scan: {
245                 btn_scan.setBackgroundResource(R.drawable.blueroll);
246                 btn_phone.setBackgroundResource(R.drawable.tranroll);
247                 break;
248             }
249             case R.id.btn_phone: {
250                 btn_phone.setBackgroundResource(R.drawable.blueroll);
251                 btn_scan.setBackgroundResource(R.drawable.tranroll);
252                 //跳转到图片选择界面去选择一张二维码图片
253                 Intent intent1 = new Intent();
254                 intent1.setAction(Intent.ACTION_PICK);
255
256                 intent1.setType("image/*");
257
258                 Intent intent2 = Intent.createChooser(intent1, "选择二维码图片");
259                 startActivityForResult(intent2, CHOOSE_PIC);
260                 break;
261             }
262             case R.id.result_back: {
263                 overridePendingTransition(R.anim.in_from_left, R.anim.out_to_right);
264                 finish();
265                 break;
266             }
267             case R.id.btn_shine:{
268                 if (isopen){
269                     btn_shine.setTextColor(Color.WHITE);
270                     btn_shine.setBackgroundResource(R.drawable.white);
271                 }else {
272                     btn_shine.setTextColor(Color.parseColor("#038cf7"));
273                     btn_shine.setBackgroundResource(R.drawable.blue);
274                 }
275                 isopen=!isopen;
276                 //控制开启和关闭闪光灯
277                 CameraManager.get().flashHandler();
278             }
279             default: {
280                 break;
281             }
282         }
283     }
284
285     @Override
286     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
287         super.onActivityResult(requestCode, resultCode, data);
288         String imgPath = null;
289         if (resultCode == RESULT_OK) {
290             switch (requestCode) {
291                 case CHOOSE_PIC:
292                     try {
293                         String[] proj = new String[]{MediaStore.Images.Media.DATA};
294                         Cursor cursor = CaptureActivity.this.getContentResolver().query(data.getData(), proj, null, null, null);
295
296                         if (cursor.moveToFirst()) {
297                             int columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
298                             System.out.println(columnIndex);
299                             //获取到用户选择的二维码图片的绝对路径
300                             imgPath = cursor.getString(columnIndex);
301                         }
302                         cursor.close();
303                         //获取解析结果
304                         final String finalImgPath = imgPath;
305                         new Thread(new Runnable() {
306                             @Override
307                             public void run() {
308                                 Result ret = parseQRcodeBitmap(finalImgPath);
309                                 Intent intent = new Intent(CaptureActivity.this, ScanResultActivity.class);
310                                 intent.putExtra("result", ret.toString());
311                                 IntentUtils.enterIntent(CaptureActivity.this, intent);
312                             }
313                         }).start();
314
315                     } catch (Exception e) {
316                         Toast.makeText(CaptureActivity.this,  "扫描失败", Toast.LENGTH_LONG).show();
317                     }
318
319                     break;
320             }
321         }
322     }
323     //解析二维码图片,返回结果封装在Result对象中
324     private com.google.zxing.Result  parseQRcodeBitmap(String bitmapPath){
325         //解析转换类型UTF-8
326         Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
327         hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
328         //获取到待解析的图片
329         BitmapFactory.Options options = new BitmapFactory.Options();
330         //如果我们把inJustDecodeBounds设为true,那么BitmapFactory.decodeFile(String path, Options opt)
331         //并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你
332         options.inJustDecodeBounds = true;
333         //此时的bitmap是null,这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了
334         Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath,options);
335         //我们现在想取出来的图片的边长(二维码图片是正方形的)设置为400像素
336         /**
337          options.outHeight = 400;
338          options.outWidth = 400;
339          options.inJustDecodeBounds = false;
340          bitmap = BitmapFactory.decodeFile(bitmapPath, options);
341          */
342         //以上这种做法,虽然把bitmap限定到了我们要的大小,但是并没有节约内存,如果要节约内存,我们还需要使用inSimpleSize这个属性
343         options.inSampleSize = options.outHeight / 400;
344         if(options.inSampleSize <= 0){
345             options.inSampleSize = 1; //防止其值小于或等于0
346         }
347         /**
348          * 辅助节约内存设置
349          *
350          * options.inPreferredConfig = Bitmap.Config.ARGB_4444;    // 默认是Bitmap.Config.ARGB_8888
351          * options.inPurgeable = true;
352          * options.inInputShareable = true;
353          */
354         options.inJustDecodeBounds = false;
355         bitmap = BitmapFactory.decodeFile(bitmapPath, options);
356         //新建一个RGBLuminanceSource对象,将bitmap图片传给此对象
357         RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(bitmap);
358         //将图片转换成二进制图片
359         BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(rgbLuminanceSource));
360         //初始化解析对象
361         QRCodeReader reader = new QRCodeReader();
362         //开始解析
363         Result result = null;
364         try {
365             result = reader.decode(binaryBitmap, hints);
366         } catch (Exception e) {
367             // TODO: handle exception
368         }
369
370         return result;
371     }
372
373     @Override
374     public boolean onKeyDown(int keyCode, KeyEvent event) {
375         if (keyCode == KeyEvent.KEYCODE_BACK) {
376             overridePendingTransition(R.anim.in_from_left, R.anim.out_to_right);
377             finish();
378             return false;
379         } else {
380             return super.onKeyDown(keyCode, event);
381         }
382     }
383 }


好了,扫描之后,肯定也要对扫描结果进行处理,那就看看ScanResultActivity吧

结果的处理,我是参照qq做的,也没啥难的地方,大家可以看看

1 package com.wangy.mycode;
2
3 import android.content.Intent;
4 import android.os.Bundle;
5 import android.view.Gravity;
6 import android.view.KeyEvent;
7 import android.view.View;
8 import android.webkit.WebChromeClient;
9 import android.webkit.WebSettings;
10 import android.webkit.WebView;
11 import android.webkit.WebViewClient;
12 import android.widget.ImageView;
13 import android.widget.LinearLayout;
14 import android.widget.TextView;
15 import android.widget.Toast;
16
17 import com.wangy.mycode.Base.BaseActivity;
18 import com.wangy.mycode.Zxing.CaptureActivity;
19
20 /**
21  * Created by xhb on 2016/12/28.
22  */
23 public class ScanResultActivity extends BaseActivity {
24
25     private ImageView result_back;
26     private TextView scanresult_word;
27     private WebView scanresult_webview;
28     private String result;
29     private LinearLayout ll_result_word;
30
31     @Override
32     protected void onCreate(Bundle savedInstanceState) {
33         super.onCreate(savedInstanceState);
34         setContentView(R.layout.scanresultlayout);
35         Intent intent = getIntent();
36         if (intent != null) {
37             result = intent.getStringExtra("result");
38         }
39         initView();
40     }
41
42     private void initView() {
43         result_back = (ImageView) findViewById(R.id.result_back);
44         result_back.setOnClickListener(new View.OnClickListener() {
45             @Override
46             public void onClick(View v) {
47 //                startActivity(new Intent(ScanResultActivity.this,CaptureActivity.class));
48 //                overridePendingTransition(R.anim.in_from_left, R.anim.out_to_right);
49                 finish();
50             }
51         });
52         ll_result_word = (LinearLayout)findViewById(R.id.ll_result_word);
53         scanresult_word = (TextView) findViewById(R.id.scanresult_word);
54         scanresult_webview = (WebView) findViewById(R.id.scanresult_webview);
55         if ((result.contains("http://") || result.contains("https://"))) {
56             scanresult_webview.setVisibility(View.VISIBLE);
57             ll_result_word.setVisibility(View.GONE);
58             setScanresult_webview();
59         }else {
60             scanresult_webview.setVisibility(View.GONE);
61             ll_result_word.setVisibility(View.VISIBLE);
62             scanresult_word.setText(result);
63         }
64
65     }
66 private void setScanresult_webview(){
67     if ((result.contains("http://") || result.contains("https://"))) {
68         //加载需要显示的网页
69         scanresult_webview.loadUrl(result);
70     }
71
72     //覆盖WebView默认使用第三方或系统默认浏览器打开网页的行为,使网页用WebView打开
73     scanresult_webview.setWebViewClient(new WebViewClient() {
74         @Override
75         public boolean shouldOverrideUrlLoading(WebView view, String url) {
76             // TODO Auto-generated method stub
77             //返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
78             view.loadUrl(url);
79             return true;
80         }
81     });
82     WebSettings webSettings = scanresult_webview.getSettings();
83     //设置可以访问文件
84     webSettings.setAllowFileAccess(true);
85     webSettings.setJavaScriptEnabled(true);//支持js
86     webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
87     webSettings.setDomStorageEnabled(true);
88     //设置支持缩放
89     webSettings.setBuiltInZoomControls(true);
90    // 优先使用缓存
91     scanresult_webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
92     scanresult_webview.setWebChromeClient(new WebChromeClient() {
93         @Override
94         public void onProgressChanged(WebView view, int newProgress) {
95             // TODO Auto-generated method stub
96             if (newProgress == 100) {
97                 // 网页加载完成
98                 showCustomToast("网页加载完成");
99             } else {
100                 // 加载中
101 //                showCustomToast("网页加载中...");
102             }
103
104         }
105     });
106 //    scanresult_webview.setDownloadListener(new DownloadListener() {
107 //        @Override
108 //        public void onDownloadStart(String s, String s1, String s2, String s3, long l) {
109 //            String html = "<a href='" + s + "'>" + s + "</a>";
110 //            scanresult_webview.loadData(html, "text/html", "UTF-8");
111 //        }
112 //    });
113
114
115 }
116     //弹出警告框
117     public void showCustomToast(String text) {
118         Toast toast = Toast.makeText(ScanResultActivity.this,
119                 text, Toast.LENGTH_SHORT);
120         toast.setGravity(Gravity.CENTER, 20, 20);
121         LinearLayout toastView = (LinearLayout) toast.getView();
122         ImageView imageCodeProject = new ImageView(ScanResultActivity.this);
123         imageCodeProject.setImageResource(R.drawable.ico_warning);
124         toastView.addView(imageCodeProject, 0);
125         toastView.setPadding(100, 85, 100, 85);
126         toast.show();
127     }
128
129 //    返回上一次浏览的页面
130
131     //改写物理按键——返回的逻辑
132     @Override
133     public boolean onKeyDown(int keyCode, KeyEvent event) {
134         // TODO Auto-generated method stub
135         if(keyCode==KeyEvent.KEYCODE_BACK)
136         {
137             if(scanresult_webview.canGoBack())
138             {
139                 scanresult_webview.goBack();//返回上一页面
140                 return true;
141             }
142             else
143             {
144 //                startActivity(new Intent(ScanResultActivity.this,CaptureActivity.class));
145 //                overridePendingTransition(R.anim.in_from_left, R.anim.out_to_right);
146                 finish();
147                 return false;
148             }
149         }
150         return super.onKeyDown(keyCode, event);
151     }
152 }


界面也给大家看看

scanresultlayout


1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3     android:layout_width="match_parent"
4     android:layout_height="match_parent"
5     android:orientation="vertical">
6     <RelativeLayout
7         android:layout_width="match_parent"
8         android:layout_height="wrap_content"
9         android:minHeight="50dp"
10         android:background="#038cf7"
11         >
12         <TextView
13             android:layout_width="wrap_content"
14             android:layout_height="wrap_content"
15             android:layout_centerInParent="true"
16             android:text="二维码扫描结果"
17             android:textSize="18sp"
18             android:textColor="#fff"
19             />
20         <ImageView
21             android:id="@+id/result_back"
22             android:layout_width="wrap_content"
23             android:layout_height="wrap_content"
24             android:src="@drawable/icon_back"
25             android:layout_margin="10dp"
26             android:layout_centerVertical="true"
27             android:layout_alignParentLeft="true"
28             />
29     </RelativeLayout>
30     <LinearLayout
31         android:id="@+id/ll_result_word"
32         android:layout_width="match_parent"
33         android:orientation="vertical"
34         android:visibility="gone"
35         android:layout_height="wrap_content">
36    <TextView
37        android:layout_width="match_parent"
38        android:layout_height="wrap_content"
39       android:layout_marginTop="30dp"
40        android:textColor="#e78727"
41        android:text="以下内容非本app提供,请谨慎使用"
42        android:gravity="center"
43        />
44     <TextView
45         android:layout_width="match_parent"
46         android:layout_height="wrap_content"
47         android:layout_marginTop="5dp"
48         android:text="如需使用请复制"
49         android:textColor="#e78727"
50         android:gravity="center"
51         />
52
53     <TextView
54         android:id="@+id/scanresult_word"
55         android:layout_width="wrap_content"
56         android:layout_height="wrap_content"
57         android:minWidth="300dp"
58         android:minHeight="100dp"
59         android:background="@drawable/yellow"
60         android:layout_marginTop="50dp"
61         android:layout_gravity="center_horizontal"
62         android:text="如需使用请复制"
63         android:textColor="#f5262121"
64         android:textStyle="normal"
65         android:gravity="center"
66         />
67     </LinearLayout>
68
69     <WebView
70         android:id="@+id/scanresult_webview"
71         android:layout_width="match_parent"
72         android:visibility="gone"
73         android:layout_height="match_parent">
74
75     </WebView>
76 </LinearLayout>


就简单介绍到这里吧!我做的app源码地址,有需要的可以看看,参照写一下

下载地址: http://download.csdn.net/detail/sinat_32804317/9725715
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐