热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android开发技巧之五分钟实现二维码识别

这是一篇入门级的二维码功能简单实现,推荐很多像我一样的菜鸟快速理解并开发出二维码识别功能,该文章转载自:http:blog.csdn.netqq_17475155article

这是一篇入门级的二维码功能简单实现,推荐很多像我一样的菜鸟快速理解并开发出二维码识别功能,该文章转载自:http://blog.csdn.net/qq_17475155/article/details/51607141

二维码扫描解析现在已经成为一个综合型APP所不可或缺的一个功能了,有很多入门的开发者或许会苦恼二维码识别肯定是得会图像分析技术呀,难道还得学OpenCV吗?NO,Google早已想到这一点,因此也开源了二维码扫描、生成的代码——zxing,ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。  

官方github:https://github.com/zxing/zxing/tree/zxing-3.0.0

而官方提供的工程足足有100+M,那作为Android开发者,我们不可能依赖所有的代码,因此在这篇博文中,我仅仅介绍二维码的扫描以及识别,由于二维码只是其中的一部分功能,因此我在慕课上寻了一个module,大家只需要在github上download下来依赖即可:https://github.com/weizainiunai/libzxing,至于怎么依赖我会在接下来的博文中解释。

那么做一个二维码扫描APP,肯定得先了解二维码是什么东西?二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。更具体的大家可以去百度百科上去了解。


首先新建一个工程



打开选择之前所下载好的lib project进行依赖:



依赖好之后便是编写我们的界面:acitivity_main.xml:
xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_match_parent"
android:layout_match_parent">



android:id="@+id/btn_scan"
android:layout_match_parent"
android:layout_wrap_content"
android:text="scan"
/>

android:id="@+id/tv_result"
android:layout_wrap_content"
android:layout_wrap_content"
android:text="result"
/>


那界面写好之后,我们需要调用哪些方法才能实现二维码的识别功能呢? 之前我们所依赖的工程中有一个CaptureActivity.java文件,而这个activity便是二维码识别功能的执行以及回调:
/**
* This activity opens the camera and does the actual scanning on a background
* thread. It draws a viewfinder to help the user place the barcode correctly,
* shows feedback as the image processing is happening, and then overlays the
* results when a scan is successful.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
public final class CaptureActivity extends Activity implements SurfaceHolder.Callback {

private static final String TAG = CaptureActivity.class.getSimpleName();

private CameraManager cameraManager;
private CaptureActivityHandler handler;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;

private SurfaceView scanPreview = null;
private RelativeLayout scanContainer;
private RelativeLayout scanCropView;
private ImageView scanLine;

private Rect mCropRect = null;
private boolean isHasSurface = false;

public Handler getHandler() {
return handler;
}

public CameraManager getCameraManager() {
return cameraManager;
}

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);

Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_capture);

scanPreview = (SurfaceView) findViewById(R.id.capture_preview);
scanContainer = (RelativeLayout) findViewById(R.id.capture_container);
scanCropView = (RelativeLayout) findViewById(R.id.capture_crop_view);
scanLine = (ImageView) findViewById(R.id.capture_scan_line);

inactivityTimer = new InactivityTimer(this);
beepManager = new BeepManager(this);

TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation
.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT,
0.9f);
animation.setDuration(4500);
animation.setRepeatCount(-1);
animation.setRepeatMode(Animation.RESTART);
scanLine.startAnimation(animation);
}

@Override
protected void onResume() {
super.onResume();

// CameraManager must be initialized here, not in onCreate(). This is
// necessary because we don't
// want to open the camera driver and measure the screen size if we're
// going to show the help on
// first launch. That led to bugs where the scanning rectangle was the
// wrong size and partially
// off screen.
cameraManager = new CameraManager(getApplication());

handler = null;

if (isHasSurface) {
// The activity was paused but not stopped, so the surface still
// exists. Therefore
// surfaceCreated() won't be called, so init the camera here.
initCamera(scanPreview.getHolder());
} else {
// Install the callback and wait for surfaceCreated() to init the
// camera.
scanPreview.getHolder().addCallback(this);
}

inactivityTimer.onResume();
}

@Override
protected void onPause() {
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
inactivityTimer.onPause();
beepManager.close();
cameraManager.closeDriver();
if (!isHasSurface) {
scanPreview.getHolder().removeCallback(this);
}
super.onPause();
}

@Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!isHasSurface) {
isHasSurface = true;
initCamera(holder);
}
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isHasSurface = false;
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

/**
* A valid barcode has been found, so give an indication of success and show
* the results.
*
* @param rawResult The contents of the barcode.
* @param bundle The extras
*/
public void handleDecode(Result rawResult, Bundle bundle) {
inactivityTimer.onActivity();
beepManager.playBeepSoundAndVibrate();

Intent resultIntent = new Intent();
bundle.putInt("width", mCropRect.width());
bundle.putInt("height", mCropRect.height());
bundle.putString("result", rawResult.getText());
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);
CaptureActivity.this.finish();
}

private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a
// RuntimeException.
if (handler == null) {
handler = new CaptureActivityHandler(this, cameraManager, DecodeThread.ALL_MODE);
}

initCrop();
} catch (IOException ioe) {
Log.w(TAG, ioe);
displayFrameworkBugMessageAndExit();
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
displayFrameworkBugMessageAndExit();
}
}

private void displayFrameworkBugMessageAndExit() {
// camera error
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.app_name));
builder.setMessage("Camera error");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}

});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {

@Override
public void onCancel(DialogInterface dialog) {
finish();
}
});
builder.show();
}

public void restartPreviewAfterDelay(long delayMS) {
if (handler != null) {
handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
}
}

public Rect getCropRect() {
return mCropRect;
}

/**
* 初始化截取的矩形区域
*/
private void initCrop() {
int cameraWidth = cameraManager.getCameraResolution().y;
int cameraHeight = cameraManager.getCameraResolution().x;

/** 获取布局中扫描框的位置信息 */
int[] location = new int[2];
scanCropView.getLocationInWindow(location);

int cropLeft = location[0];
int cropTop = location[1] - getStatusBarHeight();

int cropWidth = scanCropView.getWidth();
int cropHeight = scanCropView.getHeight();

/** 获取布局容器的宽高 */
int cOntainerWidth= scanContainer.getWidth();
int cOntainerHeight= scanContainer.getHeight();

/** 计算最终截取的矩形的左上角顶点x坐标 */
int x = cropLeft * cameraWidth / containerWidth;
/** 计算最终截取的矩形的左上角顶点y坐标 */
int y = cropTop * cameraHeight / containerHeight;

/** 计算最终截取的矩形的宽度 */
int width = cropWidth * cameraWidth / containerWidth;
/** 计算最终截取的矩形的高度 */
int height = cropHeight * cameraHeight / containerHeight;

/** 生成最终的截取的矩形 */
mCropRect = new Rect(x, y, width + x, height + y);
}

private int getStatusBarHeight() {
try {
Class c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("status_bar_height");
int x = Integer.parseInt(field.get(obj).toString());
return getResources().getDimensionPixelSize(x);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}

如果你需要学习二维码识别更底层的代码,这里面可是有不少的干货哦,那对我们使用者来说,我们只需要了解这个class的回调方法就行,返回给我们什么参数?key是多少?
/**
* A valid barcode has been found, so give an indication of success and show
* the results.
*
* @param rawResult The contents of the barcode.
* @param bundle The extras
*/
public void handleDecode(Result rawResult, Bundle bundle) {
inactivityTimer.onActivity();
beepManager.playBeepSoundAndVibrate();

Intent resultIntent = new Intent();
bundle.putInt("width", mCropRect.width());
bundle.putInt("height", mCropRect.height());
bundle.putString("result", rawResult.getText());
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);
CaptureActivity.this.finish();
}

在这其中我们可以看见,它为我们回调了三个参数,分别是宽,高以及字符串(识别出来的内容),而我们,只需要result这个参数,那么知道这些,接下来便是主要代码的编写:EntrenceActivity.java
public class EntrenceActivity extends AppCompatActivity {
private Button mBtnScanner;
private TextView mTvResult;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_entrence);

initview();

}

private void initview() {
mBtnScanner = (Button) findViewById(R.id.btn_scan);
mTvResult = (TextView) findViewById(R.id.tv_result);

mBtnScanner.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivityForResult(new Intent(EntrenceActivity.this , CaptureActivity.class) , 0);

}
});

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_OK){
Bundle bundle = data.getExtras();
String result = bundle.getString("result");
mTvResult.setText(result);
}

}
}

了解完这些,大家大概也知道了如何用zxing实现一个简单的二维码识别app了,当然,我们并不需要在自己的主工程中添加相关的相机等权限,因为在谷歌提供给我们的module中已经将其添加进去了,所以大家可以放心的使用,看看是不是真的5分钟实现二维码扫描了呢~~,如果觉得还行,请给博主点赞哦!!


推荐阅读
  • 本文探讨了资源访问的学习路径与方法,旨在帮助学习者更高效地获取和利用各类资源。通过分析不同资源的特点和应用场景,提出了多种实用的学习策略和技术手段,为学习者提供了系统的指导和建议。 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • Spring Data JdbcTemplate 入门指南
    本文将介绍如何使用 Spring JdbcTemplate 进行数据库操作,包括查询和插入数据。我们将通过一个学生表的示例来演示具体步骤。 ... [详细]
  • Spring框架中的面向切面编程(AOP)技术详解
    面向切面编程(AOP)是Spring框架中的关键技术之一,它通过将横切关注点从业务逻辑中分离出来,实现了代码的模块化和重用。AOP的核心思想是将程序运行过程中需要多次处理的功能(如日志记录、事务管理等)封装成独立的模块,即切面,并在特定的连接点(如方法调用)动态地应用这些切面。这种方式不仅提高了代码的可维护性和可读性,还简化了业务逻辑的实现。Spring AOP利用代理机制,在不修改原有代码的基础上,实现了对目标对象的增强。 ... [详细]
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
  • 一个建表一个执行crud操作建表代码importandroid.content.Context;importandroid.database.sqlite.SQLiteDat ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 最详尽的4K技术科普
    什么是4K?4K是一个分辨率的范畴,即40962160的像素分辨率,一般用于专业设备居多,目前家庭用的设备,如 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 本文介绍了如何利用HTTP隧道技术在受限网络环境中绕过IDS和防火墙等安全设备,实现RDP端口的暴力破解攻击。文章详细描述了部署过程、攻击实施及流量分析,旨在提升网络安全意识。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 在处理 XML 数据时,如果需要解析 `` 标签的内容,可以采用 Pull 解析方法。Pull 解析是一种高效的 XML 解析方式,适用于流式数据处理。具体实现中,可以通过 Java 的 `XmlPullParser` 或其他类似的库来逐步读取和解析 XML 文档中的 `` 元素。这样不仅能够提高解析效率,还能减少内存占用。本文将详细介绍如何使用 Pull 解析方法来提取 `` 标签的内容,并提供一个示例代码,帮助开发者快速解决问题。 ... [详细]
  • 在处理遗留数据库的映射时,反向工程是一个重要的初始步骤。由于实体模式已经在数据库系统中存在,Hibernate 提供了自动化工具来简化这一过程,帮助开发人员快速生成持久化类和映射文件。通过反向工程,可以显著提高开发效率并减少手动配置的错误。此外,该工具还支持对现有数据库结构进行分析,自动生成符合 Hibernate 规范的配置文件,从而加速项目的启动和开发周期。 ... [详细]
  • 大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式
    大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式 ... [详细]
author-avatar
lee
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有