Android基于google Zxing实现各类二维码扫描效果
随着微信的到来,二维码越来越火爆,随处能看到二维码,比如商城里面,肯德基,餐厅等等,对于二维码扫描我们使用的是google的开源框架zxing,我们可以去下载源码和jar包,之前我项目中的二维码扫描功能只实现了扫描功能,其ui真的是其丑无比,一个好的应用软件,其ui界面也要被大众所接纳,不然人家就不会用你的软件啦,所以说应用软件功能和界面一样都很重要,例如微信,相信微信ui被很多应用软件所模仿,我也仿照微信扫描二维码效果进行模仿,虽然没有微信做的那么精致,但是效果还是可以的,所以将自己修改ui的代码和扫描二维码的代码分享给大家,一是自己以后项目遇到同样的功能直接拷贝来用,二是给还没有加入二维码功能的人一个参考,站在巨人的肩膀上,哈哈,我之前也是站在巨人的肩膀上加上此功能,接下来跟着我一步一步来实现此项功能,里面去除了很多不必要的文件
我们先看下项目的结构
- 如果你项目也想加入此功能,你直接将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)里面,我们只需要在里面写出相对应的处理代码即可,其他的地方都不用改得,我这里处理扫描结果和扫描拍的照片
/** * 处理扫描结果 * @param result * @param barcode */ public void handledecode(result result, bitmap barcode) { inactivitytimer.onactivity(); playbeepsoundandvibrate(); string resultstring = result.gettext(); if (resultstring.equals("")) { toast.maketext(mipcaactivitycapture.this, "scan failed!", toast.length_short).show(); }else { intent resultintent = new intent(); bundle bundle = new bundle(); bundle.putstring("result", resultstring); bundle.putparcelable("bitmap", barcode); resultintent.putextras(bundle); this.setresult(result_ok, resultintent); } mipcaactivitycapture.this.finish(); }
我对mipcaactivitycapture界面的布局做了自己的改动,先看下效果图,主要是用到framelayout,里面嵌套relativelayout。
布局代码如下
<?xml version="1.0" encoding="utf-8"?> <framelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <relativelayout android:layout_width="fill_parent" android:layout_height="fill_parent" > <surfaceview android:id="@+id/preview_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center" /> <com.mining.app.zxing.view.viewfinderview android:id="@+id/viewfinder_view" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <include android:id="@+id/include1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignparenttop="true" layout="@layout/activity_title" /> </relativelayout> </framelayout>
在里面我将界面上面部分写在另一个布局里面,然后include进来,因为这个activity_title在我项目里面还供其他的activity使用,我也是直接拷贝出来的
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/mmtitle_bg_alpha" > <button android:id="@+id/button_back" android:layout_width="75.0dip" android:text="返回" android:background="@drawable/mm_title_back_btn" android:textcolor="@android:color/white" android:layout_height="wrap_content" android:layout_centervertical="true" android:layout_marginleft="2dip" /> <textview android:id="@+id/textview_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignbaseline="@+id/button_back" android:layout_alignbottom="@+id/button_back" android:layout_centerhorizontal="true" android:gravity="center_vertical" android:text="二维码扫描" android:textcolor="@android:color/white" android:textsize="18sp" /> </relativelayout>
在我这个demo里面,有一个主界面mainactivity,里面一个button, 一个imageview和一个textview,点击button进入到二维码扫描界面,当扫描ok的时候,回到主界面,将扫描的结果显示到textview,将图片显示到imageview里面,然后你可以不处理图片,我这里随带的加上图片,主界面的布局很简单如下
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffe1e0de" > <button android:id="@+id/button1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignparenttop="true" android:text="扫描二维码" /> <textview android:id="@+id/result" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/button1" android:lines="2" android:gravity="center_horizontal" android:textcolor="@android:color/black" android:textsize="16sp" /> <imageview android:id="@+id/qrcode_bitmap" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_alignparentleft="true" android:layout_below="@+id/result"/> </relativelayout>
mainactivity里面的代码如下,里面的功能在上面已经说了
package com.example.qr_codescan; import android.app.activity; import android.content.intent; import android.graphics.bitmap; import android.os.bundle; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.widget.imageview; import android.widget.textview; public class mainactivity extends activity { private final static int scannin_grequest_code = 1; /** * 显示扫描结果 */ private textview mtextview ; /** * 显示扫描拍的图片 */ private imageview mimageview; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mtextview = (textview) findviewbyid(r.id.result); mimageview = (imageview) findviewbyid(r.id.qrcode_bitmap); //点击按钮跳转到二维码扫描界面,这里用的是startactivityforresult跳转 //扫描完了之后调到该界面 button mbutton = (button) findviewbyid(r.id.button1); mbutton.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { intent intent = new intent(); intent.setclass(mainactivity.this, mipcaactivitycapture.class); intent.setflags(intent.flag_activity_clear_top); startactivityforresult(intent, scannin_grequest_code); } }); } @override protected void onactivityresult(int requestcode, int resultcode, intent data) { super.onactivityresult(requestcode, resultcode, data); switch (requestcode) { case scannin_grequest_code: if(resultcode == result_ok){ bundle bundle = data.getextras(); //显示扫描到的内容 mtextview.settext(bundle.getstring("result")); //显示 mimageview.setimagebitmap((bitmap) data.getparcelableextra("bitmap")); } break; } } }
上面的代码还是比较简单,但是要想做出像微信那样只的扫描框,紧紧上面的代码是没有那种效果的,我们必须重写com.mining.app.zxing.view包下面的viewfinderview类,微信里面的都是用的图片,我是自己画出来的,代码注释的比较清楚,大家直接看代码吧,相信你能理解的,如果你要修改扫描框的大小,去cameramanager类里面修改
/* * copyright (c) 2008 zxing authors * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package com.mining.app.zxing.view; import java.util.collection; import java.util.hashset; import android.content.context; import android.content.res.resources; import android.graphics.bitmap; import android.graphics.canvas; import android.graphics.color; import android.graphics.paint; import android.graphics.rect; import android.graphics.typeface; import android.util.attributeset; import android.view.view; import com.example.qr_codescan.r; import com.google.zxing.resultpoint; import com.mining.app.zxing.camera.cameramanager; /** * this view is overlaid on top of the camera preview. it adds the viewfinder * rectangle and partial transparency outside it, as well as the laser scanner * animation and result points. * */ public final class viewfinderview extends view { private static final string tag = "log"; /** * 刷新界面的时间 */ private static final long animation_delay = 10l; private static final int opaque = 0xff; /** * 四个绿色边角对应的长度 */ private int screenrate; /** * 四个绿色边角对应的宽度 */ private static final int corner_width = 10; /** * 扫描框中的中间线的宽度 */ private static final int middle_line_width = 6; /** * 扫描框中的中间线的与扫描框左右的间隙 */ private static final int middle_line_padding = 5; /** * 中间那条线每次刷新移动的距离 */ private static final int speen_distance = 5; /** * 手机的屏幕密度 */ private static float density; /** * 字体大小 */ private static final int text_size = 16; /** * 字体距离扫描框下面的距离 */ private static final int text_padding_top = 30; /** * 画笔对象的引用 */ private paint paint; /** * 中间滑动线的最顶端位置 */ private int slidetop; /** * 中间滑动线的最底端位置 */ private int slidebottom; private bitmap resultbitmap; private final int maskcolor; private final int resultcolor; private final int resultpointcolor; private collection<resultpoint> possibleresultpoints; private collection<resultpoint> lastpossibleresultpoints; boolean isfirst; public viewfinderview(context context, attributeset attrs) { super(context, attrs); density = context.getresources().getdisplaymetrics().density; //将像素转换成dp screenrate = (int)(20 * density); paint = new paint(); resources resources = getresources(); maskcolor = resources.getcolor(r.color.viewfinder_mask); resultcolor = resources.getcolor(r.color.result_view); resultpointcolor = resources.getcolor(r.color.possible_result_points); possibleresultpoints = new hashset<resultpoint>(5); } @override public void ondraw(canvas canvas) { //中间的扫描框,你要修改扫描框的大小,去cameramanager里面修改 rect frame = cameramanager.get().getframingrect(); if (frame == null) { return; } //初始化中间线滑动的最上边和最下边 if(!isfirst){ isfirst = true; slidetop = frame.top; slidebottom = frame.bottom; } //获取屏幕的宽和高 int width = canvas.getwidth(); int height = canvas.getheight(); paint.setcolor(resultbitmap != null ? resultcolor : maskcolor); //画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面 //扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边 canvas.drawrect(0, 0, width, frame.top, paint); canvas.drawrect(0, frame.top, frame.left, frame.bottom + 1, paint); canvas.drawrect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); canvas.drawrect(0, frame.bottom + 1, width, height, paint); if (resultbitmap != null) { // draw the opaque result bitmap over the scanning rectangle paint.setalpha(opaque); canvas.drawbitmap(resultbitmap, frame.left, frame.top, paint); } else { //画扫描框边上的角,总共8个部分 paint.setcolor(color.green); canvas.drawrect(frame.left, frame.top, frame.left + screenrate, frame.top + corner_width, paint); canvas.drawrect(frame.left, frame.top, frame.left + corner_width, frame.top + screenrate, paint); canvas.drawrect(frame.right - screenrate, frame.top, frame.right, frame.top + corner_width, paint); canvas.drawrect(frame.right - corner_width, frame.top, frame.right, frame.top + screenrate, paint); canvas.drawrect(frame.left, frame.bottom - corner_width, frame.left + screenrate, frame.bottom, paint); canvas.drawrect(frame.left, frame.bottom - screenrate, frame.left + corner_width, frame.bottom, paint); canvas.drawrect(frame.right - screenrate, frame.bottom - corner_width, frame.right, frame.bottom, paint); canvas.drawrect(frame.right - corner_width, frame.bottom - screenrate, frame.right, frame.bottom, paint); //绘制中间的线,每次刷新界面,中间的线往下移动speen_distance slidetop += speen_distance; if(slidetop >= frame.bottom){ slidetop = frame.top; } canvas.drawrect(frame.left + middle_line_padding, slidetop - middle_line_width/2, frame.right - middle_line_padding,slidetop + middle_line_width/2, paint); //画扫描框下面的字 paint.setcolor(color.white); paint.settextsize(text_size * density); paint.setalpha(0x40); paint.settypeface(typeface.create("system", typeface.bold)); canvas.drawtext(getresources().getstring(r.string.scan_text), frame.left, (float) (frame.bottom + (float)text_padding_top *density), paint); collection<resultpoint> currentpossible = possibleresultpoints; collection<resultpoint> currentlast = lastpossibleresultpoints; if (currentpossible.isempty()) { lastpossibleresultpoints = null; } else { possibleresultpoints = new hashset<resultpoint>(5); lastpossibleresultpoints = currentpossible; paint.setalpha(opaque); paint.setcolor(resultpointcolor); for (resultpoint point : currentpossible) { canvas.drawcircle(frame.left + point.getx(), frame.top + point.gety(), 6.0f, paint); } } if (currentlast != null) { paint.setalpha(opaque / 2); paint.setcolor(resultpointcolor); for (resultpoint point : currentlast) { canvas.drawcircle(frame.left + point.getx(), frame.top + point.gety(), 3.0f, paint); } } //只刷新扫描框的内容,其他地方不刷新 postinvalidatedelayed(animation_delay, frame.left, frame.top, frame.right, frame.bottom); } } public void drawviewfinder() { resultbitmap = null; invalidate(); } /** * draw a bitmap with the result points highlighted instead of the live * scanning display. * * @param barcode * an image of the decoded barcode. */ public void drawresultbitmap(bitmap barcode) { resultbitmap = barcode; invalidate(); } public void addpossibleresultpoint(resultpoint point) { possibleresultpoints.add(point); } }
上面的代码中,中间那根线微信是用的图片,我这里是画的,如果你想更加仿真点就将下面的代码
canvas.drawrect(frame.left + middle_line_padding, slidetop - middle_line_width/2, frame.right - middle_line_padding,slidetop + middle_line_width/2, paint);
改成
rect linerect = new rect(); linerect.left = frame.left; linerect.right = frame.right; linerect.top = slidetop; linerect.bottom = slidetop + 18; canvas.drawbitmap(((bitmapdrawable)(getresources().getdrawable(r.drawable.qrcode_scan_line))).getbitmap(), null, linerect, paint);
那条扫描线自己去微信里面找一下,我贴出来的失真了,下载微信apk,将后缀名改成zip,然后解压就行了
画扫描框下面字体的代码需要修改下,这样子能根据字体自动排列在中间,如果字太长我没有处理,那个要自动换行,你可以自行处理
paint.setcolor(color.white); paint.settextsize(text_size * density); paint.setalpha(0x40); paint.settypeface(typeface.default_bold); string text = getresources().getstring(r.string.r.string.scan_text); float textwidth = paint.measuretext(text); canvas.drawtext(text, (width - textwidth)/2, (float) (frame.bottom + (float)text_padding_top *density), paint)
运行界面截图,其中中间的那根绿色的线会上下移动,跟微信的效果差不多,当然运行你还需要相对应的权限问题。
大家可以参考专题:java二维码进行学习
以上就是本文的全部内容,希望对大家学习android软件编程有所帮助。
上一篇: Android监听Home键实例详解
下一篇: 采集微信平台实时消息json有关问题
推荐阅读
-
Android基于google Zxing实现各类二维码扫描效果
-
Android基于google Zxing实现各类二维码扫描效果
-
Android基于google Zxing实现二维码的生成
-
Android-Zxing实现二维码的扫描与生成
-
Android中利用zxing实现自己的二维码扫描识别详解
-
Android-Zxing实现二维码的扫描与生成
-
Android中利用zxing实现自己的二维码扫描识别详解
-
Android中google Zxing实现二维码与条形码扫描
-
Android基于zxing的二维码(网格)扫描 仿支付宝网格扫描
-
Android中google Zxing实现二维码与条形码扫描