87497718 2012-02-19
扫描和识别条形码的android支持库 android支持扫描条形码。不过手里没有真机,无法理解该功能是一个应用,还是一个可API调用的功能。不过在网上找到一个免费开源的android处理条形码的库。见: http://code.google.com/p/zxing 可以作为应用安装到android上,见: http://code.google.com/p/zxing/wiki/GettingStarted 或者,在另外的程序中使用该功能: http://code.google.com/p/zxing/wiki/ScanningViaIntent 已经有java的第三方开源条形码识别库(zxing)了,在自己的应用中扫描条形码实际上只需调用该第三方库的实现即可。写扫描和识别条形码的android支持库的时候还没有g1手机,现在可以试试了。 为了能调用zxing,需要先安装google market中的barcode scanner: 为了能调用zxing,需要先安装google market中的barcode scanner: 程序很简陋,第一个界面: 第二个界面,跳到了zxing库提供的Activity去做条形码扫描,这里扫描的是生成QR二维条码中的本站url。 qr码瞬间就可以识别到,然后从zxing的Activity返回到程序中,显示条目内容: 编写代码很简单。 java代码: package com.easymorse; import android.app.Activity; importandroid.content.Intent; importandroid.os.Bundle; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; import android.widget.TextView;public class ShowBarcodeActivity extends Activity implements OnClickListener { private Button button; private TextView textView; /** Called when the activity is first created. */ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main);this.button = (Button) this.findViewById(R.id.Button01);this.button.setOnClickListener(this); this.textView = (TextView) this.findViewById(R.id.hello);} @Override publicvoidonClick(Viewview){ Intentintent=newIntent(“com.google.zxing.client.android.SCAN”); intent.putExtra(“SCAN_MODE”,“QR_CODE_MODE”); this.startActivityForResult(intent,0); }@Override protectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){ if(requestCode!=0){ return; }this.textView.setText(data.getStringExtra(“SCAN_RESULT”)); } }layout目录下面的main.xml: <?xml version=”1.0″ encoding=”utf-8″?> <LinearLayoutxmlns:android=”http://schemas.android.com/apk/res/android” android:orientation=”vertical” android:layout_width=”fill_parent” android:layout_height=”fill_parent” > <TextView android:id=”@+id/hello” android:layout_width=”fill_parent” android:layout_height=”wrap_content” android:text=”@string/hello” /><Button android:text=”@string/Button01″ android:id=”@+id/Button01″ android:layout_width=”wrap_content” android:layout_height=”wrap_content”></Button></LinearLayout> values目录下的strings.xml <?xml version=”1.0″ encoding=”utf-8″?> <resources> <stringname=”hello”>目前没有扫描结果。</string> <stringname=”app_name”>购物小参考</string> <stringname=”Button01″>点击开始扫描…</string> </resources>从这个示例中也可以看到android强大的地方,可以复用(reuse)其他应用的服务(Service)或者Activity。 刚才写的编写最简单的android扫描条形码功能,只能扫描qr二维码。如果要扫描普通的商品条形码,需要在该程序上座一点儿修改。 界面的改动: 扫描条形码时的效果: 显示扫描结果(识别速度明显低于二维码): 代码的改动: package com.easymorse; import android.app.Activity; importandroid.content.Intent; importandroid.os.Bundle; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; import android.widget.TextView;public class ShowBarcodeActivity extends Activity { private Button button; private Button button2; private TextView textView; /** Called when the activity is first created. */ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main);this.button = (Button) this.findViewById(R.id.Button01);this.button.setOnClickListener(new OnClickListener() { @Override publicvoidonClick(Viewv){ Intentintent=newIntent( “com.google.zxing.client.android.SCAN”); intent.putExtra(“SCAN_MODE”,“QR_CODE_MODE”); startActivityForResult(intent,0); } });this.button2 = (Button) this.findViewById(R.id.Button02);this.button2.setOnClickListener(new OnClickListener() { @Override publicvoidonClick(Viewv){ Intentintent=newIntent( “com.google.zxing.client.android.SCAN”); intent.putExtra(“SCAN_MODE”,“EAN_13″); startActivityForResult(intent,0); } });this.textView = (TextView) this.findViewById(R.id.hello);} @Override protectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){ if(requestCode!=0){ return; }this.textView.setText(data.getStringExtra(“SCAN_RESULT”)); } }写完这个示例,才发现其实现在不需要写: intent.putExtra(“SCAN_MODE”, “QR_CODE_MODE”); zxing现在的库可以自动的识别是哪种编码。如果写了,反而是指定类型,不能识别其他类型的编码。实际上,上面的: intent.putExtra(“SCAN_MODE”, “EAN_13″); 写的不对。具体常量参数,见: http://zxing.org/w/docs/javadoc/constant-values.html 上面的内容不修改了,正确的源代码共享在google code的svn中,见: http://easymorse.googlecode.com/svn/tags/barcode.proto.0.1.0/ 写了个技术原型,通过手机识别图书的ISBN条形码,然后通过豆瓣网的API,输出ISBN号对应的图书信息。 示例很简单,上来是个按钮,点击后,进入zxing的activity,用于扫描图书ISBN号。 使用zxing的扫描功能,这次还出现了点儿问题。我的nexus one摄像头比较脏了,造成对焦后无法识别,或者识别的很慢。吓了我一跳,因为以前没有问题的。 更换了几个版本zxing,问题依旧,擦拭了镜头,问题立刻得到了解决。 nexus one的像素和自动对焦能力,也可能包括cpu的处理因素,稍稍对中条码,立刻得到结果。之前的G1就要慢一些。 这也带来了捕获上面截图的困难,因为一瞬间扫描就结束了。怎么办呢?我用手在镜头上揉搓了几下,就可以了,哈哈。 提示找到图书后,通过豆瓣API,获取到图书的XML,这里要说,豆瓣的图书信息还是比较全的,我用国外图书也找到了信息。 因为使用联通WCDMA卡,整个过程很快。 下面说说代码中的一些要点。 如何扫描条形码现在做条形码扫描,java一般用zxing。zxing有多种复用方式,最简单的方式是,安装zxing软件,通过google market。在自己的应用中,通过Intent调用zxing的扫描条码的Activity,然后将扫描到的结果返回自己的Activity。 比较复杂的办法是,将zxing库包含在自己的程序中,这比较适合正式的应用。 本文使用的是前者。 示例代码示例源代码见: http://easymorse.googlecode.com/svn/tags/sou.book-0.2 还有一个更为简单的示例,可用于理解调用zxing和豆瓣API的基本过程,见: http://easymorse.googlecode.com/svn/tags/sou.book-0.1 实现activity跳转在0.2版本中,涉及到3个activity,首先是搜索图书的activity,见最上面的图。点击按钮后,进入zxing的activity,从视觉上看,zxing获取到ISBN号后,进入了第三个activity,显示图书的详细信息。 其实,从zxing得到ISBN号以后,还是返回到搜索图书的activity,但是该activity马上发起新的intent,进入新的activity。另外,这里也演示了如果通过intent共享activity之间的参数。 以下代码创建新的activity,并设置参数: Intent intent = new Intent(); intent.setClass(this,SearchBookActivity.class); intent.putExtra("ISBN",data.getExtras().getString("SCAN_RESULT")); this.startActivity(intent);以下是从intent中获取到参数: this.getIntent().getExtras().getString("ISBN");有关zxing的使用,可以参考我另外一个示例,android扫描商品条形码。webview使用本地页面的考虑在显示图书查询结果的activity中,使用webview,这样显示可以做的比较灵活和简单。 显示页面有多种做法,比如可以是远程服务器上的页面,这要求有服务器端的支持。看起来这样比较省心,尤其是比如有iphone,有android等等异构终端的情况下。手机开发人员可以将这部分都空出来,交给服务器端去解决。而且是统一的解决方案。 但是,实践中,有两个问题: 服务器端开发人员要为不同手机客户端实现不同的样式,而且服务器端开发者往往并不配备手机设备,这也确实是难以做到的,难道要为一个做适配的服务器端开发人员配备iphone、android和s60手机么?手机终端开发者无法灵活的控制webview中的细节,webview(这里指android环境,iphone环境类似的)支持通过javascript访问手机的服务或者对象,但这是平台相关的,android有自己的代码,iphone也有自己的代码,很难在服务器端统一解决。我在这个原型中试图解决,即,HTML页面在手机端本地,手机端和服务器交互,只是取得数据,而不包含显示样式(也就是HTML页面的内容),手机端通过javascript,将取得的数据注入到本地页面中显示。 这种方式的好处是: 手机端和服务器端交互是获取数据,这是各种手机系统都通用的;这种方式类似与AJAX,对数据流量的要求也会下降;手机端开发人员可以灵活的决定页面的样式和交互动作。在本例中,将html和css放置在项目的assets目录下,另外,该目录下,还有一张图片,用于未加载到图书图片时占位。想像一下,如果不使用webview,这个需求需要编程处理,是比较繁琐的,可参见android异步加载ListView中的图片中的处理。 实现webview的代码很简单,这是一个更简单的示例,见android编写简单的Webview。本例相关代码: this.resultWeb = (WebView) this.findViewById(R.id.resultWeb);this.resultWeb.getSettings().setSupportZoom(false); this.resultWeb.getSettings().setJavaScriptCanOpenWindowsAutomatically( true); this.resultWeb.getSettings().setJavaScriptEnabled(true); this.resultWeb.loadUrl(file:///android_asset/results.html); 这里要注意,setJavaScriptEnabled启用javascript,否则javascript无效。 android java与html页面中javascript互操作javascript和java之间的通信问题,本例只通过javascript调用java,还是比较简单的。如果是java调用javascript,应该需要启动单独的线程来实现。 需要为webview对象创建一个javascript调用的接口对象: this.resultWeb.addJavascriptInterface(new Object() { public String getBookName() { return bookInfo.getName(); } public String getBookSummary() { return bookInfo.getSummary(); } public String getBookImageUrl() { return bookInfo.getImageUrl(); } public String getBookAuthor() { return bookInfo.getAuthor(); } }, "searchResult"); 其中第一个参数是javascript调用的对象,第二个参数,是调用对象的名称。 看看javascript中怎样调用这个对象: document.getElementById("bookName").innerHTML=window.searchResult.getBookName();可以看到window对象多了个searchResult对象属性,这个searchResult就是上面方法中第二个参数。通过该名字,就调用到了上面第一个参数创建的内部类对象。 使用豆瓣API豆瓣API官方网址: http://www.douban.com/service/apidoc/ 这里仅用到根据ISBN得到图书信息xml的功能。在这里: http://www.douban.com/service/apidoc/reference/subject#获取书籍信息 如果匿名使用这个API,有一定的限制: API调用被限制为每分钟请求不超过10次。 这里的调用次数是每ip地址的。对于手机应用来说,大于6秒钟扫描一次图书,应该是够用了。 解析豆瓣XML查询结果对豆瓣的xml查询结果,使用了android自带的xmlpull api进行解析。 这里和android通过xmlpull解析xml稍有差别在于,后者是获取xml的属性值,这里是获取节点中的文本内容。 需要: if (i == XmlPullParser.START_TAG && parser.getName().equals("attribute") && parser.getAttributeValue(0).equals("title")) { bookInfo.setName(parser.nextText()); Log.v("soubook", "title>>" + bookInfo.getName()); continue; } 通过parser.nextText()方法获取。另外,要注意在获取完毕后,continue结束本次循环。 HTML与CSS本例中显示查询结果的webview使用了比较难看的灰色背景,主要是为了演示外部css可以很好的和html结合使用。 同理,也应该可以在html页面中引用外部的javascript,打算在下一个版本中加入jquery。 |