安卓实现二维码生成和扫描功能,扫描支持直接拍照扫码和相册图片扫码,还加了照明功能
Posted oooohuhu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓实现二维码生成和扫描功能,扫描支持直接拍照扫码和相册图片扫码,还加了照明功能相关的知识,希望对你有一定的参考价值。
最近在做二维码的生成和扫描,生成二维码相对而言较为简单,扫描相对复杂,遇到的问题较多,但是在实现二维码的生成和扫描之前最重要的一步
就是讲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
以上是关于安卓实现二维码生成和扫描功能,扫描支持直接拍照扫码和相册图片扫码,还加了照明功能的主要内容,如果未能解决你的问题,请参考以下文章