Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果

分类:Android 高手进阶 89096人阅读 评论(274) 收藏 举报 android二维码扫描 Zxing 仿微信二维码效果

转载请注明出处:http://blog.csdn.net/xiaanming/article/details/10163203

了解二维码这个东西还是从微信中,当时微信推出二维码扫描功能,自己感觉挺新颖的,从一张图片中扫一下竟然能直接加好友,不可思议啊,那时候还不了解二维码,呵呵,然后做项目的时候,老板说要加上二维码扫描功能,然后自己的屁颠屁颠的去百度,google啥的,发现很多朋友都有介绍二维码扫描的功能,然后我就跟着人家的介绍自己搞起了二维码扫描功能,跟着人家的帖子,很快我的项目就加入了扫描二维码的功能,然后自己还很开心。


随着微信的到来,二维码越来越火爆,随处能看到二维码,比如商城里面,肯德基,餐厅等等,对于二维码扫描我们使用的是google的开源框架Zxing,我们可以去http://code.google.com/p/zxing/下载源码和Jar包,之前我项目中的二维码扫描功能只实现了扫描功能,其UI真的是其丑无比,一个好的应用软件,其UI界面也要被大众所接纳,不然人家就不会用你的软件啦,所以说应用软件功能和界面一样都很重要,例如微信,相信微信UI被很多应用软件所模仿,我也仿照微信扫描二维码效果进行模仿,虽然没有微信做的那么精致,但是效果还是可以的,所以将自己修改UI的代码和扫描二维码的代码分享给大家,一是自己以后项目遇到同样的功能直接拷贝来用,二是给还没有加入二维码功能的人一个参考,站在巨人的肩膀上,哈哈,我之前也是站在巨人的肩膀上加上此功能,接下来跟着我一步一步来实现此项功能,里面去除了很多不必要的文件


我们先看下项目的结构

Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果_第1张图片

  • 如果你项目也想加入此功能,你直接将com.mining.app.zxing.camera,com.mining.app.zxing.decoding,com.mining.app.zxing.view这三个包拷贝到你的项目中,然后引入相对应的资源进去,我也是从我的项目中直接引用过来的,包名都没改呢,当然还需要引用Zxing.jar

  • com.example.qr_codescan包里面有一个MipcaActivityCapture,也是直接引入我之前项目的代码的,这个Activity主要处理扫描界面的类,比如,扫描成功有声音和振动等等,主要关注里面的handleDecode(Result result, Bitmap barcode)方法,扫描完成之后将扫描到的结果和二维码的bitmap当初参数传递到handleDecode(Result result, Bitmap barcode)里面,我们只需要在里面写出相对应的处理代码即可,其他的地方都不用改得,我这里处理扫描结果和扫描拍的照片

[java] view plain copy
  1. /**
  2. *处理扫描结果
  3. *@paramresult
  4. *@parambarcode
  5. */
  6. publicvoidhandleDecode(Resultresult,Bitmapbarcode){
  7. inactivityTimer.onActivity();
  8. playBeepSoundAndVibrate();
  9. StringresultString=result.getText();
  10. if(resultString.equals("")){
  11. Toast.makeText(MipcaActivityCapture.this,"Scanfailed!",Toast.LENGTH_SHORT).show();
  12. }else{
  13. IntentresultIntent=newIntent();
  14. Bundlebundle=newBundle();
  15. bundle.putString("result",resultString);
  16. bundle.putParcelable("bitmap",barcode);
  17. resultIntent.putExtras(bundle);
  18. this.setResult(RESULT_OK,resultIntent);
  19. }
  20. MipcaActivityCapture.this.finish();
  21. }

  • 我对MipcaActivityCapture界面的布局做了自己的改动,先看下效果图,主要是用到FrameLayout,里面嵌套RelativeLayout,里面的图片也是从微信里面拿出来的,平常我看到需要什么图片就去微信里面找,没有美工的公司的程序员就是苦逼

Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果_第2张图片

布局代码如下

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent">
  5. <RelativeLayout
  6. android:layout_width="fill_parent"
  7. android:layout_height="fill_parent">
  8. <SurfaceView
  9. android:id="@+id/preview_view"
  10. android:layout_width="fill_parent"
  11. android:layout_height="fill_parent"
  12. android:layout_gravity="center"/>
  13. <com.mining.app.zxing.view.ViewfinderView
  14. android:id="@+id/viewfinder_view"
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"/>
  17. <include
  18. android:id="@+id/include1"
  19. android:layout_width="fill_parent"
  20. android:layout_height="wrap_content"
  21. android:layout_alignParentTop="true"
  22. layout="@layout/activity_title"/>
  23. </RelativeLayout>
  24. </FrameLayout>
在里面我将界面上面部分写在另一个布局里面,然后include进来,因为这个activity_title在我项目里面还供其他的Activity使用,我也是直接拷贝出来的

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="wrap_content"
  5. android:background="@drawable/mmtitle_bg_alpha">
  6. <Button
  7. android:id="@+id/button_back"
  8. android:layout_width="75.0dip"
  9. android:text="返回"
  10. android:background="@drawable/mm_title_back_btn"
  11. android:textColor="@android:color/white"
  12. android:layout_height="wrap_content"
  13. android:layout_centerVertical="true"
  14. android:layout_marginLeft="2dip"/>
  15. <TextView
  16. android:id="@+id/textview_title"
  17. android:layout_width="wrap_content"
  18. android:layout_height="wrap_content"
  19. android:layout_alignBaseline="@+id/button_back"
  20. android:layout_alignBottom="@+id/button_back"
  21. android:layout_centerHorizontal="true"
  22. android:gravity="center_vertical"
  23. android:text="二维码扫描"
  24. android:textColor="@android:color/white"
  25. android:textSize="18sp"/>
  26. </RelativeLayout>
  • 在我这个demo里面,有一个主界面MainActivity,里面一个Button, 一个ImageView和一个TextView,点击Button进入到二维码扫描界面,当扫描OK的时候,回到主界面,将扫描的结果显示到TextView,将图片显示到ImageView里面,然后你可以不处理图片,我这里随带的加上图片,主界面的布局很简单如下

[html] view plain copy
  1. <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:background="#ffe1e0de">
  6. <Button
  7. android:id="@+id/button1"
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:layout_alignParentTop="true"
  11. android:text="扫描二维码"/>
  12. <TextView
  13. android:id="@+id/result"
  14. android:layout_width="fill_parent"
  15. android:layout_height="wrap_content"
  16. android:layout_below="@+id/button1"
  17. android:lines="2"
  18. android:gravity="center_horizontal"
  19. android:textColor="@android:color/black"
  20. android:textSize="16sp"/>
  21. <ImageView
  22. android:id="@+id/qrcode_bitmap"
  23. android:layout_width="fill_parent"
  24. android:layout_height="fill_parent"
  25. android:layout_alignParentLeft="true"
  26. android:layout_below="@+id/result"/>
  27. </RelativeLayout>
  • MainActivity里面的代码如下,里面的功能在上面已经说了

[java] view plain copy
  1. packagecom.example.qr_codescan;
  2. importandroid.app.Activity;
  3. importandroid.content.Intent;
  4. importandroid.graphics.Bitmap;
  5. importandroid.os.Bundle;
  6. importandroid.view.View;
  7. importandroid.view.View.OnClickListener;
  8. importandroid.widget.Button;
  9. importandroid.widget.ImageView;
  10. importandroid.widget.TextView;
  11. publicclassMainActivityextendsActivity{
  12. privatefinalstaticintSCANNIN_GREQUEST_CODE=1;
  13. /**
  14. *显示扫描结果
  15. */
  16. privateTextViewmTextView;
  17. /**
  18. *显示扫描拍的图片
  19. */
  20. privateImageViewmImageView;
  21. @Override
  22. protectedvoidonCreate(BundlesavedInstanceState){
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.activity_main);
  25. mTextView=(TextView)findViewById(R.id.result);
  26. mImageView=(ImageView)findViewById(R.id.qrcode_bitmap);
  27. //点击按钮跳转到二维码扫描界面,这里用的是startActivityForResult跳转
  28. //扫描完了之后调到该界面
  29. ButtonmButton=(Button)findViewById(R.id.button1);
  30. mButton.setOnClickListener(newOnClickListener(){
  31. @Override
  32. publicvoidonClick(Viewv){
  33. Intentintent=newIntent();
  34. intent.setClass(MainActivity.this,MipcaActivityCapture.class);
  35. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  36. startActivityForResult(intent,SCANNIN_GREQUEST_CODE);
  37. }
  38. });
  39. }
  40. @Override
  41. protectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){
  42. super.onActivityResult(requestCode,resultCode,data);
  43. switch(requestCode){
  44. caseSCANNIN_GREQUEST_CODE:
  45. if(resultCode==RESULT_OK){
  46. Bundlebundle=data.getExtras();
  47. //显示扫描到的内容
  48. mTextView.setText(bundle.getString("result"));
  49. //显示
  50. mImageView.setImageBitmap((Bitmap)data.getParcelableExtra("bitmap"));
  51. }
  52. break;
  53. }
  54. }
  55. }


  • 上面的代码还是比较简单,但是要想做出像微信那样只的扫描框,紧紧上面的代码是没有那种效果的,我们必须重写com.mining.app.zxing.view包下面的ViewfinderView类,微信里面的都是用的图片,我是自己画出来的,代码注释的比较清楚,大家直接看代码吧,相信你能理解的,如果你要修改扫描框的大小,去CameraManager类里面修改

[java] view plain copy
  1. /*
  2. *Copyright(C)2008ZXingauthors
  3. *
  4. *LicensedundertheApacheLicense,Version2.0(the"License");
  5. *youmaynotusethisfileexceptincompliancewiththeLicense.
  6. *YoumayobtainacopyoftheLicenseat
  7. *
  8. *http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. *Unlessrequiredbyapplicablelaworagreedtoinwriting,software
  11. *distributedundertheLicenseisdistributedonan"ASIS"BASIS,
  12. *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
  13. *SeetheLicenseforthespecificlanguagegoverningpermissionsand
  14. *limitationsundertheLicense.
  15. */
  16. packagecom.mining.app.zxing.view;
  17. importjava.util.Collection;
  18. importjava.util.HashSet;
  19. importandroid.content.Context;
  20. importandroid.content.res.Resources;
  21. importandroid.graphics.Bitmap;
  22. importandroid.graphics.Canvas;
  23. importandroid.graphics.Color;
  24. importandroid.graphics.Paint;
  25. importandroid.graphics.Rect;
  26. importandroid.graphics.Typeface;
  27. importandroid.util.AttributeSet;
  28. importandroid.view.View;
  29. importcom.example.qr_codescan.R;
  30. importcom.google.zxing.ResultPoint;
  31. importcom.mining.app.zxing.camera.CameraManager;
  32. /**
  33. *Thisviewisoverlaidontopofthecamerapreview.Itaddstheviewfinder
  34. *rectangleandpartialtransparencyoutsideit,aswellasthelaserscanner
  35. *animationandresultpoints.
  36. *
  37. */
  38. publicfinalclassViewfinderViewextendsView{
  39. privatestaticfinalStringTAG="log";
  40. /**
  41. *刷新界面的时间
  42. */
  43. privatestaticfinallongANIMATION_DELAY=10L;
  44. privatestaticfinalintOPAQUE=0xFF;
  45. /**
  46. *四个绿色边角对应的长度
  47. */
  48. privateintScreenRate;
  49. /**
  50. *四个绿色边角对应的宽度
  51. */
  52. privatestaticfinalintCORNER_WIDTH=10;
  53. /**
  54. *扫描框中的中间线的宽度
  55. */
  56. privatestaticfinalintMIDDLE_LINE_WIDTH=6;
  57. /**
  58. *扫描框中的中间线的与扫描框左右的间隙
  59. */
  60. privatestaticfinalintMIDDLE_LINE_PADDING=5;
  61. /**
  62. *中间那条线每次刷新移动的距离
  63. */
  64. privatestaticfinalintSPEEN_DISTANCE=5;
  65. /**
  66. *手机的屏幕密度
  67. */
  68. privatestaticfloatdensity;
  69. /**
  70. *字体大小
  71. */
  72. privatestaticfinalintTEXT_SIZE=16;
  73. /**
  74. *字体距离扫描框下面的距离
  75. */
  76. privatestaticfinalintTEXT_PADDING_TOP=30;
  77. /**
  78. *画笔对象的引用
  79. */
  80. privatePaintpaint;
  81. /**
  82. *中间滑动线的最顶端位置
  83. */
  84. privateintslideTop;
  85. /**
  86. *中间滑动线的最底端位置
  87. */
  88. privateintslideBottom;
  89. privateBitmapresultBitmap;
  90. privatefinalintmaskColor;
  91. privatefinalintresultColor;
  92. privatefinalintresultPointColor;
  93. privateCollection<ResultPoint>possibleResultPoints;
  94. privateCollection<ResultPoint>lastPossibleResultPoints;
  95. booleanisFirst;
  96. publicViewfinderView(Contextcontext,AttributeSetattrs){
  97. super(context,attrs);
  98. density=context.getResources().getDisplayMetrics().density;
  99. //将像素转换成dp
  100. ScreenRate=(int)(20*density);
  101. paint=newPaint();
  102. Resourcesresources=getResources();
  103. maskColor=resources.getColor(R.color.viewfinder_mask);
  104. resultColor=resources.getColor(R.color.result_view);
  105. resultPointColor=resources.getColor(R.color.possible_result_points);
  106. possibleResultPoints=newHashSet<ResultPoint>(5);
  107. }
  108. @Override
  109. publicvoidonDraw(Canvascanvas){
  110. //中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改
  111. Rectframe=CameraManager.get().getFramingRect();
  112. if(frame==null){
  113. return;
  114. }
  115. //初始化中间线滑动的最上边和最下边
  116. if(!isFirst){
  117. isFirst=true;
  118. slideTop=frame.top;
  119. slideBottom=frame.bottom;
  120. }
  121. //获取屏幕的宽和高
  122. intwidth=canvas.getWidth();
  123. intheight=canvas.getHeight();
  124. paint.setColor(resultBitmap!=null?resultColor:maskColor);
  125. //画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面
  126. //扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边
  127. canvas.drawRect(0,0,width,frame.top,paint);
  128. canvas.drawRect(0,frame.top,frame.left,frame.bottom+1,paint);
  129. canvas.drawRect(frame.right+1,frame.top,width,frame.bottom+1,
  130. paint);
  131. canvas.drawRect(0,frame.bottom+1,width,height,paint);
  132. if(resultBitmap!=null){
  133. //Drawtheopaqueresultbitmapoverthescanningrectangle
  134. paint.setAlpha(OPAQUE);
  135. canvas.drawBitmap(resultBitmap,frame.left,frame.top,paint);
  136. }else{
  137. //画扫描框边上的角,总共8个部分
  138. paint.setColor(Color.GREEN);
  139. canvas.drawRect(frame.left,frame.top,frame.left+ScreenRate,
  140. frame.top+CORNER_WIDTH,paint);
  141. canvas.drawRect(frame.left,frame.top,frame.left+CORNER_WIDTH,frame.top
  142. +ScreenRate,paint);
  143. canvas.drawRect(frame.right-ScreenRate,frame.top,frame.right,
  144. frame.top+CORNER_WIDTH,paint);
  145. canvas.drawRect(frame.right-CORNER_WIDTH,frame.top,frame.right,frame.top
  146. +ScreenRate,paint);
  147. canvas.drawRect(frame.left,frame.bottom-CORNER_WIDTH,frame.left
  148. +ScreenRate,frame.bottom,paint);
  149. canvas.drawRect(frame.left,frame.bottom-ScreenRate,
  150. frame.left+CORNER_WIDTH,frame.bottom,paint);
  151. canvas.drawRect(frame.right-ScreenRate,frame.bottom-CORNER_WIDTH,
  152. frame.right,frame.bottom,paint);
  153. canvas.drawRect(frame.right-CORNER_WIDTH,frame.bottom-ScreenRate,
  154. frame.right,frame.bottom,paint);
  155. //绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
  156. slideTop+=SPEEN_DISTANCE;
  157. if(slideTop>=frame.bottom){
  158. slideTop=frame.top;
  159. }
  160. canvas.drawRect(frame.left+MIDDLE_LINE_PADDING,slideTop-MIDDLE_LINE_WIDTH/2,frame.right-MIDDLE_LINE_PADDING,slideTop+MIDDLE_LINE_WIDTH/2,paint);
  161. //画扫描框下面的字
  162. paint.setColor(Color.WHITE);
  163. paint.setTextSize(TEXT_SIZE*density);
  164. paint.setAlpha(0x40);
  165. paint.setTypeface(Typeface.create("System",Typeface.BOLD));
  166. canvas.drawText(getResources().getString(R.string.scan_text),frame.left,(float)(frame.bottom+(float)TEXT_PADDING_TOP*density),paint);
  167. Collection<ResultPoint>currentPossible=possibleResultPoints;
  168. Collection<ResultPoint>currentLast=lastPossibleResultPoints;
  169. if(currentPossible.isEmpty()){
  170. lastPossibleResultPoints=null;
  171. }else{
  172. possibleResultPoints=newHashSet<ResultPoint>(5);
  173. lastPossibleResultPoints=currentPossible;
  174. paint.setAlpha(OPAQUE);
  175. paint.setColor(resultPointColor);
  176. for(ResultPointpoint:currentPossible){
  177. canvas.drawCircle(frame.left+point.getX(),frame.top
  178. +point.getY(),6.0f,paint);
  179. }
  180. }
  181. if(currentLast!=null){
  182. paint.setAlpha(OPAQUE/2);
  183. paint.setColor(resultPointColor);
  184. for(ResultPointpoint:currentLast){
  185. canvas.drawCircle(frame.left+point.getX(),frame.top
  186. +point.getY(),3.0f,paint);
  187. }
  188. }
  189. //只刷新扫描框的内容,其他地方不刷新
  190. postInvalidateDelayed(ANIMATION_DELAY,frame.left,frame.top,
  191. frame.right,frame.bottom);
  192. }
  193. }
  194. publicvoiddrawViewfinder(){
  195. resultBitmap=null;
  196. invalidate();
  197. }
  198. /**
  199. *Drawabitmapwiththeresultpointshighlightedinsteadofthelive
  200. *scanningdisplay.
  201. *
  202. *@parambarcode
  203. *Animageofthedecodedbarcode.
  204. */
  205. publicvoiddrawResultBitmap(Bitmapbarcode){
  206. resultBitmap=barcode;
  207. invalidate();
  208. }
  209. publicvoidaddPossibleResultPoint(ResultPointpoint){
  210. possibleResultPoints.add(point);
  211. }
  212. }

上面的代码中,中间那根线微信是用的图片,我这里是画的,如果你想更加仿真点就将下面的代码

[java] view plain copy
  1. canvas.drawRect(frame.left+MIDDLE_LINE_PADDING,slideTop-MIDDLE_LINE_WIDTH/2,frame.right-MIDDLE_LINE_PADDING,slideTop+MIDDLE_LINE_WIDTH/2,paint);

改成

[java] view plain copy
  1. RectlineRect=newRect();
  2. lineRect.left=frame.left;
  3. lineRect.right=frame.right;
  4. lineRect.top=slideTop;
  5. lineRect.bottom=slideTop+18;
  6. canvas.drawBitmap(((BitmapDrawable)(getResources().getDrawable(R.drawable.qrcode_scan_line))).getBitmap(),null,lineRect,paint);

那条扫描线自己去微信里面找一下,我贴出来的失真了,下载微信apk,将后缀名改成zip,然后解压就行了

画扫描框下面字体的代码需要修改下,这样子能根据字体自动排列在中间,如果字太长我没有处理,那个要自动换行,你可以自行处理

[java] view plain copy
  1. paint.setColor(Color.WHITE);
  2. paint.setTextSize(TEXT_SIZE*density);
  3. paint.setAlpha(0x40);
  4. paint.setTypeface(Typeface.DEFAULT_BOLD);
  5. Stringtext=getResources().getString(R.string.R.string.scan_text);
  6. floattextWidth=paint.measureText(text);
  7. canvas.drawText(text,(width-textWidth)/2,(float)(frame.bottom+(float)TEXT_PADDING_TOP*density),paint)

运行界面截图,其中中间的那根绿色的线会上下移动,跟微信的效果差不多,当然运行你还需要相对应的权限问题,有兴趣的朋友可以去下载demo

Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果_第3张图片Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果_第4张图片Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果_第5张图片


从8点多写这篇博客写到现在,看起来这么点字,但实际上还是比较耗时间的,如果你觉得这篇文章对你有帮助,你就顶一下,哈哈,洗澡睡觉去了,上面的项目中还有一些资源文件我没有贴出来,想要看效果可以下载源码


我在Android 基于google Zxing实现对手机中的二维码进行扫描这篇文章中实现了对手机中二维码照片的扫描,并且替换了中间的扫描线,和微信效果更加相似,建议大家去下那文章的项目源码


项目源码,点击下载


更多相关文章

  1. Android Animation动画 控制动画的执行效果,速度等
  2. Android TextView加上阴影效果
  3. android 动态壁纸 3 解决 动态壁纸列表界面icon问题
  4. Android 5.0 MaterialDesign Ripple效果水波纹效果
  5. 【摘录】从Android界面开发谈起
  6. Android用户界面 UI组件--TextView及其子类(一) TextView

随机推荐

  1. Android属性动画Property Animation系列
  2. Android模拟器无法保存数据
  3. Android 使用ActivityOptions实现Activit
  4. 【Android】android:padding属性设置对Im
  5. Android Studio生成javadoc出错的解决办
  6. 关于android中的EditView,TextView的图片
  7. Android Studio 配置 OpenCV for Android
  8. 【android】与pc机进行UDP通信
  9. android 背景图片设置
  10. Android 在从全屏切换到非全屏的时候闪动