这篇文章就整理下移动端长按识别二维码的实现吧!实现方式可以分为三种

一、长按原生控件,直接获取控件中的图片数据(src或background)

二、长按原生控件,截图识别

三、长按web中的图片,app识别其中的二维码(js互调)

第一二种好像没多少可以说的,但还是按照顺序来吧!首先先说下使用的库,ios使用原生二维码识别库(好像是ios7之后才有的),然后说是WKWebView比UIWebView优化了很多 东西,也解决了内存泄漏的问题那么js交互的部分我们就用WKWebView吧(说到这里必须吐槽下android的webView内存泄漏问题,一个字坑)。android没原生的,我了解的比较大众的就zxing和zbar,经过测试发现在二维码占图片的比例较小识别时zbar的识别比zxing好一些,而且zxing使用截图的方案实现时当二维码放在屏幕的底部时识别不出来,所以这里就直接只贴zbar的代码吧!

然后呢因为是写的demo,代码是没优化过的,怎么方便怎么来,实际使用还是得根据自己的需要优化一下,个人觉得重要的是实现方案和思路。

一、长按原生控件,直接获取控件中的图片数据(src或background)

这里基本上等于在介绍,二维码识别的使用了。

(1)android

获取图片android就比较简单了。长按事件就不说了,图片通常会用ImageView,直接获取src就行了,特殊点的放background,那么就获取background就好了。直接贴代码吧!

//src
Bitmap mBitmap=((BitmapDrawable) imageView.getDrawable()).getBitmap();//background
mBitmap=((BitmapDrawable) imageView.getBackground()).getBitmap();

下面就是关键zbar 识别图片中二维码的代码了

-----------------------------------------------
public String parseRQ(Bitmap bitmap) {String text = null;ImageScanner scanner=new ImageScanner();scanner.setConfig(0, Config.X_DENSITY,3);scanner.setConfig(0, Config.Y_DENSITY, 3);//设置扫描的图片Image barcode = new Image(bitmap.getWidth(), bitmap.getHeight(), "Y800");//设置扫描的图片的区域,因为我们不知道二维码在哪,所以直接设置整张图片barcode.setCrop(0, 0, bitmap.getWidth(), bitmap.getHeight());int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];byte[] bitmapPixels =new byte[bitmap.getWidth() * bitmap.getHeight()];bitmap.getPixels(data, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());for (int i = 0; i < data.length; i++) {bitmapPixels[i] = (byte) data[i];}barcode.setData(bitmapPixels);//识别图片中的二维码,result是二维码的个数(这点比zxing好,zxing只获取从左上开始找到的第一个,不过也有可能是我调用的api不对也不一定)int result = scanner.scanImage(barcode);if (result != 0){SymbolSet syms = scanner.getResults();for (Symbol sym : syms){text=sym.getData().trim();//我们只获取第一个非空二维码,习惯性判空,没测过几个空字符串可不可以生成二维码if(!text.isEmpty()){break;}}}return text;}--------------------------------------------------

拿到解码后的数据,就可以根据需求取实现功能了。

(2)ios

ios获取UIImageView的图片更容易直接就是imageView.image就可以了,原生识别二维码的操作也简单,个人觉得设置长按事件比这两个加起来都麻烦点。所以这里主要就是设置长按事件的代码了。贴码。

//创建长按,imageLongClick即为长按响应的函数
UILongPressGestureRecognizer *longClick=[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(imageLongClick:)];//触摸点数,即多少手指点击longClick.numberOfTouchesRequired=1;//开启触发事件处理imageView.userInteractionEnabled=YES;//imageView添加长按事件[imageView addGestureRecognizer:longClick];

ios原生识别二维码,比zxing和zbar都简单多了,当然这没做优化策略的,android zbar那那个代码也一样

----------------------------------------------
-(void)imageLongClick:(UILongPressGestureRecognizer *)sender{//按下时if ([sender state]==UIGestureRecognizerStateBegan) {NSLog(@"image long click....");//创建识别器CIDetector *detector=[CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:nil];//image转为CGImage进行识别,结果为所有二维码结果的对象数组NSArray *results=[detector featuresInImage:[CIImage imageWithCGImage:[self snapshotView].CGImage]];if (results.count>0) {//这里只拿第一个CIQRCodeFeature *feature=[results firstObject];//feature.messageString即为解码后的字符串,这里直接打开浏览器[[UIApplication sharedApplication] openURL:[NSURL URLWithString:feature.messageString]];}else{NSLog(@"找不到二维码");}}
}
--------------------------------------------

在此直接获取控件的图片直接识别的就这样结束了,如果要获取相册的也一样,只需将从相册获取到的图片转为对应的Bitmap(ios UIImage),其他的都不变就可。

这里需注意的是背景色如果是透明色是无法识别出来的,所以如果二维码的来源是自己app的这种方式就很好了,如果是用户上传的建议用第二种方式,不可保证不会有哪个坑上传个透明背景的图片或上传个长图。

二、长按原生控件,截图识别

长按事件和识别二维码的代码是一样的,就不重复,即获取到截屏的图片后调用识别的方法就可以了,所以这里就只剩截屏功能的代码了,一样直接上代码

(1)android

-----------------------------------------------------
//其实这里直接传个View进来也是可以的,比如第三种的长按网页的就可以将WebView传就来就可以了
public Bitmap snapshotView(Window window) {if (window != null) {//找到当前页面的根布局View view = window.getDecorView().getRootView();//获取当前屏幕的大小int width = view.getWidth();int height = view.getHeight();//设置缓存view.setDrawingCacheEnabled(true);view.buildDrawingCache();/*1、从缓存中获取当前屏幕的图片,创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收*2、这里的88是去掉无用的部分即你确定是不会有二维码的部分(当然不做任何操作也是可以的),这里直接写死是状态栏的高度,*实际真正使用不会这么写,而是是去获取状态栏的高度(这里懒就不写了),我记得如果直接是控件调用buildDrawingCache*是该控件当前显示在屏幕上的部分就不用减去状态栏的高度了*/Bitmap temBitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 88, width, height - 88);//禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是缓存的位图view.destroyDrawingCache();view.setDrawingCacheEnabled(false);return temBitmap;}return null;}
---------------------------------------------------

(2)ios

---------------------------------------------
//跟android一样这里的UIWindow也可以改成UIView,函数接受UIView的参数,外部就可以直接调用截取指定控件显示在屏幕的部分截图了
- (UIImage *)snapshotView {UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];//这里只获取大小,所以bounds还是frame都是一样的CGRect rect = [keyWindow bounds];if(UIGraphicsBeginImageContextWithOptions != NULL){//iphone4之后采用Retina屏幕调用这个(不知道有没有记反,也有其他的截图方式,只是我就记得这种)UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);} else {UIGraphicsBeginImageContext(rect.size);}CGContextRef context = UIGraphicsGetCurrentContext();[keyWindow.layer renderInContext:context];UIImage *img = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();return img;
}
-----------------------------------------------------------------------------------------------

三、长按web中的图片,app识别其中的二维码

这里也是截图实现,为什么不是拿原始图片识别,1是上面说的有可能是长图或背景透明,2是截图免下载,在速度体验上好点。我记得微信也是这样实现的,在哪提过我忘了。

既然涉及js,那我们就必须先来段js呀,js长按图片功能代码(本来是想用jquery的,但想想网页不一定是自己的,有可能是别人的静态网页,根本没导入jquery库,而直接 注入<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>的形式是会有跨域的问题的,所以不用jquery,直接写好了,代码量其实也差不多):

-----------------------------------------------------
//闭包,这里个人当成跟java的匿名对象差不多记忆
(function () {//获取所有图片标签var allImage = document.getElementsByTagName("img");var img;for (var i = 0; i < allImage.length; i++) {img = allImage[i];//添加触摸事件img.addEventListener('touchstart', function(event) {touch = event.touches[0];startevent = event;//保存触摸点的x,y轴startX = Number(touch.pageX);startY = Number(touch.pageY);//设置定时器,js没长按事件,就是使用定时器实现的,800毫秒后触发,img.src为图片地址,这里可以拿到后做保存图片发大图等功能timeout = setTimeout('longClick('+img.src+');', 800);});//移动事件img.addEventListener('touchmove', function(event) {touch = event.touches[0];scx = Math.abs(Number(touch.pageX) - startX);scy = Math.abs(Number(touch.pageY) - startY);//过滤掉移动事件,不这样做,当你手指放在这个图片往上或往下划的时候,800毫秒后也会触发长按事件,精确度可以自己调if (scx > 10 || scy > 10) { //取消定时器clearTimeout(timeout);} else {//相当android的拦截分发event.preventDefault();}});//手指放开时取消定时器img.addEventListener('touchend', function(event) {clearTimeout(timeout);});}})();//立即执行
-------------------------------------------------------

app要做的就是两件事,1、将截图识别二维码对象注入js中。2、网页加载结束后,加载执行上面那个js函数代码即可。

(1)android

android相对简单点,创建注入对象

---------------------------------------------------
public class DemoJSBridge{@JavascriptInterfacepublic void longClickImage(String imgSrc){//调用截图识别二维码代码}
}//注入
wb.addJavascriptInterface(new DemoJSBridge(), "demoJSBridge");---------------------------------------------------

这样就可以注入代码了,其他功能直接在类中加方法即可,而前端js调用则是

//对象是注入到window对象中的,所以是window.对象.方法
window.demoJSBridge.longClickImage(img.src);

所以上面的js函数中的'longClick('+img.src+');'改掉,然后页面加载完成后注入执行即可。即

------------------------------------------------------wb.setWebViewClient(new WebViewClient() {@Overridepublic void onPageFinished(WebView view, String url) {super.onPageFinished(view, url);//加载js,而我们那个js函数是立即执行的,所以一加载就会自动执行,getQRJs()即为上面js函数的String格式wb.loadUrl("javascript:" + getQRJs());}});
------------------------------------------------------

(2)ios个人觉得麻烦点,但安全点

注入js对象

-------------------------------------------------------------------------
//构建script对象,配置页面加载完成后加载上面js函数的并执行,getJSString即为上面js函数的字符串,
//WKUserScriptInjectionTimeAtDocumentEnd页面加载结束后注入
WKUserScript *script=[[WKUserScript alloc] initWithSource:[self getJSString] injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];//WKWebView配置对象WKWebViewConfiguration *config=[[WKWebViewConfiguration alloc] init];config.preferences=[WKPreferences new];//允许执行javaScriptconfig.preferences.javaScriptEnabled=YES;//WKWebView自带长按事件,会拦截掉我们添加的事件,所以屏蔽掉NSMutableString *javascript = [NSMutableString string];//禁止webkitTouchCallout[javascript appendString:@"document.documentElement.style.webkitTouchCallout='none';"];[javascript appendString:@"document.documentElement.style.webkitUserSelect='none';"];//禁止选择WKUserScript *noneSelectScript = [[WKUserScript alloc] initWithSource:javascript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];[config.userContentController addUserScript:noneSelectScript];[config.userContentController addUserScript: script];//注入对象addScriptMessageHandler响应处理者self[config.userContentController addScriptMessageHandler:self name:@"demoJSBridge"];WKWebView *webView=[[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
---------------------------------------------------------------------------------

然后注意

1、script即我们上面代码中加载js函数的形式也可以用第二种方式,跟android的差不多,即

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{[self.webView evaluateJavaScript:[self getJSString] completionHandler:nil];
}

2、实现协议WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler,第一个页面加载进度等事件的回调。第二个js对话框的回调,WKWebView把js的对话框就是alert()这些给屏蔽了,我们需实现WKUIDelegate协议自己去弹窗。第三个js调用原生的方法。

3、WKWebView注入的对象跟android和UIWebView都不一样了,他放在了window.webkit.messageHandlers里,所以前端js调用时为

window.webkit.messageHandlers.对象名.postMessage(参数);

所以所以上面的js函数中的'longClick('+img.src+');'改掉,参数直接传js对象,如{functionName:"longClickImage",data:img.src},实现,如果为减少与android的差异性,android也可以改为只有postMessage(String msg)方法,然后根据functionName去执行对应的功能。js对象传到app后,ios会自动转为字典,android为json字符串,自己转成json就可以了。

个人理解是WKWebView取消了直接注入对象了,即没有将self注入到js中,而是于注入一个假对象,里面只有postMessage函数,当js调用这个函数时他对应的再去调用原生的回调。

4、响应js调用事件,即实现WKScriptMessageHandler协议

------------------------------------------------------------
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{//message中的name即为注入的对象名,body为传过来的数据if ([message.name isEqualToString:@"demoJSBridge"]) {//js对象传过来后会自动转为字典NSDictionary *d=message.body;if ([@"longClickImage" isEqualToString:[d objectForKey:@"functionName"]]) {//调用截屏,识别二维码方法}}
}
-------------------------------------------------------------------

理论上js与原生的互调就这样可以了,但为了保险一点的话,原生接到js的调用以后再回调一下js比较好一点,因为有可能有些功能js需要app回传数据做下一步操作。相当于待人接物而言你叫我做一件事,做好了还是做不了,我得告知你一下,做个有交代有责任的人。

这篇好像有点长,写得不好的地方还多请见谅,也可在评论指导一下。

移动端(ios and android)长按识别二维码(含js与原生互调)相关推荐

  1. 前端页面中iOS版微信长按识别二维码的bug与解决方案

    发现问题 页面做出来后测试,发现在安卓版微信能正常识别,但iOS 版微信(iPhone 或 iPad)皆无法正常识别.出来问题一开头固然是怀疑自己的代码有问题,上网搜索相关资料,尝试以下方案均无法解决 ...

  2. 前端页面中iOS版微信长按识别二维码的bug

    问题描述: H5首页banner图,在安卓微信内长按可识别二维码,但在ios部分机型上,长按图片识别不了二维码. 测试机型: iphone6s plus 系统12.1 微信版本6.7.4 iphone ...

  3. IOS手机全屏长按识别二维码HTML代码

    代码段作用讲解: 1. 二维码的全屏样式, opacity: 0; 透明样式, touch-callout: none; -webkit-touch-callout: none; -ms-touch- ...

  4. IOS长按识别二维码失败

    IOS长按不识别二维码,出现放大图片的问题解决. CSS加入样式: touch-callout: none; -webkit-touch-callout: none; -ms-touch-callou ...

  5. 微信长按识别二维码 -- 页面多个二维码如何识别?

    常规的在公众号html页面中要实现长按识别二维码,直接使用img显示图片就可以了,如下: <img name="qrCodeImg" src="/images/it ...

  6. (补充)微信长按识别二维码 -- 页面多个二维码如何识别?(二)

    基于上一篇微信长按识别二维码 -- 页面多个二维码如何识别? )在部分设备上,如果图片非常多,还是会出现识别错误的bug(主要是二维码显示一半或居于底部时),修改了识别流程,改为点击图片弹窗,然后长按 ...

  7. 二维码扫描+长按识别二维码demo

    二维码扫描+长按识别二维码demo,已封装好 源码下载

  8. 微信小程序web-view 实现长按识别二维码

    小程序长按识别二维码, 网页在小程序中长按识别二维码,web-view长按识别二维码 效果图: html 代码: <!DOCTYPE html> <html><head& ...

  9. jquery实现微信长按识别二维码

    jquery实现微信长按识别二维码 (完整代码如下) <!DOCTYPE html> <html> <head><meta charset="utf ...

最新文章

  1. Yolo模型部署的两种方法
  2. android本地存储SharedPreferences
  3. docker commit 命令
  4. Boost:bind绑定__cdecl(成员函数)测试程序
  5. Matplotlib 可视化之多图层叠加
  6. 黑发不知勤学早,白首方悔读书迟———颜真卿
  7. 计算机磁盘读取信息,VBA如何获取电脑磁盘信息,这个方法一定要知道
  8. maven,eclipse--build时出现No compiler is provided in this environment
  9. GitChat · 安全 | 揭秘我国的电子取证技术
  10. java rhino js类_Rhino -- 基于java的javascript实现
  11. rgb sw 线主板接口在哪_配置升级性能再突破,华硕TUF GAMING B460M-PRO 重炮手主板爆款来袭...
  12. Oracle 如何定义自动增量autocreament的主键ID?
  13. docker安装php拓展
  14. theos tweak导入自定义类
  15. VMware虚拟机共享主机无线网络联网的设置方法
  16. 信号卷积和图像卷积滤波
  17. Python库——Faker
  18. C语言二元一次方程求解(求出共轭实根)
  19. 乱舞之双刀--mhp2怪物猎人双刀攻略…
  20. python代码画樱花教程-如何用Python代码实现樱花树效果

热门文章

  1. 老友记台词学习笔记 SE01EP05(一)
  2. 【2020HBU天梯赛训练】7-11 打折
  3. 保护卡下机房维护(装缷软件,改变设置)
  4. Linux下的Backlight子系统(二)
  5. Burp Suite抓取安卓手机微信小程序数据包(HTTPS) 主机有线/无线连接两种方式
  6. Proteus基于51单片机通过PWM脉冲调制控制电机转速_按键与串口控制转速_电机转速可测
  7. 清洗完相机拍照测试年龄的软件,算年龄的照相软件 那个拍照测年龄的软件叫什么...
  8. 撸一串趣图,给晚上加班打个鸡血
  9. JAVA string数组转list去重
  10. 利用拉马努金公式和蒙特卡洛方法计算圆周率