日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

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

發(fā)布時間:2024/3/24 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 移动端(ios and android)长按识别二维码(含js与原生互调) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

這篇文章就整理下移動端長按識別二維碼的實現(xiàn)吧!實現(xiàn)方式可以分為三種

一、長按原生控件,直接獲取控件中的圖片數(shù)據(jù)(src或background)

二、長按原生控件,截圖識別

三、長按web中的圖片,app識別其中的二維碼(js互調(diào))


第一二種好像沒多少可以說的,但還是按照順序來吧!首先先說下使用的庫,ios使用原生二維碼識別庫(好像是ios7之后才有的),然后說是WKWebView比UIWebView優(yōu)化了很多 東西,也解決了內(nèi)存泄漏的問題那么js交互的部分我們就用WKWebView吧(說到這里必須吐槽下android的webView內(nèi)存泄漏問題,一個字坑)。android沒原生的,我了解的比較大眾的就zxing和zbar,經(jīng)過測試發(fā)現(xiàn)在二維碼占圖片的比例較小識別時zbar的識別比zxing好一些,而且zxing使用截圖的方案實現(xiàn)時當(dāng)二維碼放在屏幕的底部時識別不出來,所以這里就直接只貼zbar的代碼吧!

然后呢因為是寫的demo,代碼是沒優(yōu)化過的,怎么方便怎么來,實際使用還是得根據(jù)自己的需要優(yōu)化一下,個人覺得重要的是實現(xiàn)方案和思路。

一、長按原生控件,直接獲取控件中的圖片數(shù)據(jù)(src或background)

這里基本上等于在介紹,二維碼識別的使用了。

(1)android

獲取圖片android就比較簡單了。長按事件就不說了,圖片通常會用ImageView,直接獲取src就行了,特殊點的放background,那么就獲取background就好了。直接貼代碼吧!

//src Bitmap mBitmap=((BitmapDrawable) imageView.getDrawable()).getBitmap();//background mBitmap=((BitmapDrawable) imageView.getBackground()).getBitmap();
下面就是關(guān)鍵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);//設(shè)置掃描的圖片Image barcode = new Image(bitmap.getWidth(), bitmap.getHeight(), "Y800");//設(shè)置掃描的圖片的區(qū)域,因為我們不知道二維碼在哪,所以直接設(shè)置整張圖片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是二維碼的個數(shù)(這點比zxing好,zxing只獲取從左上開始找到的第一個,不過也有可能是我調(diào)用的api不對也不一定)int result = scanner.scanImage(barcode);if (result != 0){SymbolSet syms = scanner.getResults();for (Symbol sym : syms){text=sym.getData().trim();//我們只獲取第一個非空二維碼,習(xí)慣性判空,沒測過幾個空字符串可不可以生成二維碼if(!text.isEmpty()){break;}}}return text;}--------------------------------------------------

拿到解碼后的數(shù)據(jù),就可以根據(jù)需求取實現(xiàn)功能了。

(2)ios

ios獲取UIImageView的圖片更容易直接就是imageView.image就可以了,原生識別二維碼的操作也簡單,個人覺得設(shè)置長按事件比這兩個加起來都麻煩點。所以這里主要就是設(shè)置長按事件的代碼了。貼碼。

//創(chuàng)建長按,imageLongClick即為長按響應(yīng)的函數(shù) UILongPressGestureRecognizer *longClick=[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(imageLongClick:)];//觸摸點數(shù),即多少手指點擊longClick.numberOfTouchesRequired=1;//開啟觸發(fā)事件處理imageView.userInteractionEnabled=YES;//imageView添加長按事件[imageView addGestureRecognizer:longClick];

ios原生識別二維碼,比zxing和zbar都簡單多了,當(dāng)然這沒做優(yōu)化策略的,android zbar那那個代碼也一樣

---------------------------------------------- -(void)imageLongClick:(UILongPressGestureRecognizer *)sender{//按下時if ([sender state]==UIGestureRecognizerStateBegan) {NSLog(@"image long click....");//創(chuàng)建識別器CIDetector *detector=[CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:nil];//image轉(zhuǎn)為CGImage進(jìn)行識別,結(jié)果為所有二維碼結(jié)果的對象數(shù)組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(@"找不到二維碼");}} } --------------------------------------------


在此直接獲取控件的圖片直接識別的就這樣結(jié)束了,如果要獲取相冊的也一樣,只需將從相冊獲取到的圖片轉(zhuǎn)為對應(yīng)的Bitmap(ios UIImage),其他的都不變就可。

這里需注意的是背景色如果是透明色是無法識別出來的,所以如果二維碼的來源是自己app的這種方式就很好了,如果是用戶上傳的建議用第二種方式,不可保證不會有哪個坑上傳個透明背景的圖片或上傳個長圖。


二、長按原生控件,截圖識別

長按事件和識別二維碼的代碼是一樣的,就不重復(fù),即獲取到截屏的圖片后調(diào)用識別的方法就可以了,所以這里就只剩截屏功能的代碼了,一樣直接上代碼

(1)android

----------------------------------------------------- //其實這里直接傳個View進(jìn)來也是可以的,比如第三種的長按網(wǎng)頁的就可以將WebView傳就來就可以了 public Bitmap snapshotView(Window window) {if (window != null) {//找到當(dāng)前頁面的根布局View view = window.getDecorView().getRootView();//獲取當(dāng)前屏幕的大小int width = view.getWidth();int height = view.getHeight();//設(shè)置緩存view.setDrawingCacheEnabled(true);view.buildDrawingCache();/*1、從緩存中獲取當(dāng)前屏幕的圖片,創(chuàng)建一個DrawingCache的拷貝,因為DrawingCache得到的位圖在禁用后會被回收*2、這里的88是去掉無用的部分即你確定是不會有二維碼的部分(當(dāng)然不做任何操作也是可以的),這里直接寫死是狀態(tài)欄的高度,*實際真正使用不會這么寫,而是是去獲取狀態(tài)欄的高度(這里懶就不寫了),我記得如果直接是控件調(diào)用buildDrawingCache*是該控件當(dāng)前顯示在屏幕上的部分就不用減去狀態(tài)欄的高度了*/Bitmap temBitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 88, width, height - 88);//禁用DrawingCahce否則會影響性能 ,而且不禁止會導(dǎo)致每次截圖到保存的是緩存的位圖view.destroyDrawingCache();view.setDrawingCacheEnabled(false);return temBitmap;}return null;} ---------------------------------------------------

(2)ios

--------------------------------------------- //跟android一樣這里的UIWindow也可以改成UIView,函數(shù)接受UIView的參數(shù),外部就可以直接調(diào)用截取指定控件顯示在屏幕的部分截圖了 - (UIImage *)snapshotView {UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];//這里只獲取大小,所以bounds還是frame都是一樣的CGRect rect = [keyWindow bounds];if(UIGraphicsBeginImageContextWithOptions != NULL){//iphone4之后采用Retina屏幕調(diào)用這個(不知道有沒有記反,也有其他的截圖方式,只是我就記得這種)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識別其中的二維碼

這里也是截圖實現(xiàn),為什么不是拿原始圖片識別,1是上面說的有可能是長圖或背景透明,2是截圖免下載,在速度體驗上好點。我記得微信也是這樣實現(xiàn)的,在哪提過我忘了。

既然涉及js,那我們就必須先來段js呀,js長按圖片功能代碼(本來是想用jquery的,但想想網(wǎng)頁不一定是自己的,有可能是別人的靜態(tài)網(wǎng)頁,根本沒導(dǎo)入jquery庫,而直接 注入<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>的形式是會有跨域的問題的,所以不用jquery,直接寫好了,代碼量其實也差不多):

----------------------------------------------------- //閉包,這里個人當(dāng)成跟java的匿名對象差不多記憶 (function () {//獲取所有圖片標(biāo)簽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);//設(shè)置定時器,js沒長按事件,就是使用定時器實現(xiàn)的,800毫秒后觸發(fā),img.src為圖片地址,這里可以拿到后做保存圖片發(fā)大圖等功能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);//過濾掉移動事件,不這樣做,當(dāng)你手指放在這個圖片往上或往下劃的時候,800毫秒后也會觸發(fā)長按事件,精確度可以自己調(diào)if (scx > 10 || scy > 10) { //取消定時器clearTimeout(timeout);} else {//相當(dāng)android的攔截分發(fā)event.preventDefault();}});//手指放開時取消定時器img.addEventListener('touchend', function(event) {clearTimeout(timeout);});}})();//立即執(zhí)行 -------------------------------------------------------

app要做的就是兩件事,1、將截圖識別二維碼對象注入js中。2、網(wǎng)頁加載結(jié)束后,加載執(zhí)行上面那個js函數(shù)代碼即可。

(1)android

android相對簡單點,創(chuàng)建注入對象

--------------------------------------------------- public class DemoJSBridge{@JavascriptInterfacepublic void longClickImage(String imgSrc){//調(diào)用截圖識別二維碼代碼} }//注入 wb.addJavascriptInterface(new DemoJSBridge(), "demoJSBridge");---------------------------------------------------
這樣就可以注入代碼了,其他功能直接在類中加方法即可,而前端js調(diào)用則是

//對象是注入到window對象中的,所以是window.對象.方法 window.demoJSBridge.longClickImage(img.src);
所以上面的js函數(shù)中的'longClick('+img.src+');'改掉,然后頁面加載完成后注入執(zhí)行即可。即

------------------------------------------------------wb.setWebViewClient(new WebViewClient() {@Overridepublic void onPageFinished(WebView view, String url) {super.onPageFinished(view, url);//加載js,而我們那個js函數(shù)是立即執(zhí)行的,所以一加載就會自動執(zhí)行,getQRJs()即為上面js函數(shù)的String格式wb.loadUrl("javascript:" + getQRJs());}}); ------------------------------------------------------

(2)ios個人覺得麻煩點,但安全點

注入js對象

------------------------------------------------------------------------- //構(gòu)建script對象,配置頁面加載完成后加載上面js函數(shù)的并執(zhí)行,getJSString即為上面js函數(shù)的字符串, //WKUserScriptInjectionTimeAtDocumentEnd頁面加載結(jié)束后注入 WKUserScript *script=[[WKUserScript alloc] initWithSource:[self getJSString] injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];//WKWebView配置對象WKWebViewConfiguration *config=[[WKWebViewConfiguration alloc] init];config.preferences=[WKPreferences new];//允許執(zhí)行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響應(yīng)處理者self[config.userContentController addScriptMessageHandler:self name:@"demoJSBridge"];WKWebView *webView=[[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; ---------------------------------------------------------------------------------

然后注意

1、script即我們上面代碼中加載js函數(shù)的形式也可以用第二種方式,跟android的差不多,即

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

2、實現(xiàn)協(xié)議WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler,第一個頁面加載進(jìn)度等事件的回調(diào)。第二個js對話框的回調(diào),WKWebView把js的對話框就是alert()這些給屏蔽了,我們需實現(xiàn)WKUIDelegate協(xié)議自己去彈窗。第三個js調(diào)用原生的方法。


3、WKWebView注入的對象跟android和UIWebView都不一樣了,他放在了window.webkit.messageHandlers里,所以前端js調(diào)用時為

window.webkit.messageHandlers.對象名.postMessage(參數(shù));所以所以上面的js函數(shù)中的'longClick('+img.src+');'改掉,參數(shù)直接傳js對象,如{functionName:"longClickImage",data:img.src},實現(xiàn),如果為減少與android的差異性,android也可以改為只有postMessage(String msg)方法,然后根據(jù)functionName去執(zhí)行對應(yīng)的功能。js對象傳到app后,ios會自動轉(zhuǎn)為字典,android為json字符串,自己轉(zhuǎn)成json就可以了。

個人理解是WKWebView取消了直接注入對象了,即沒有將self注入到j(luò)s中,而是于注入一個假對象,里面只有postMessage函數(shù),當(dāng)js調(diào)用這個函數(shù)時他對應(yīng)的再去調(diào)用原生的回調(diào)。


4、響應(yīng)js調(diào)用事件,即實現(xiàn)WKScriptMessageHandler協(xié)議

------------------------------------------------------------ - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{//message中的name即為注入的對象名,body為傳過來的數(shù)據(jù)if ([message.name isEqualToString:@"demoJSBridge"]) {//js對象傳過來后會自動轉(zhuǎn)為字典NSDictionary *d=message.body;if ([@"longClickImage" isEqualToString:[d objectForKey:@"functionName"]]) {//調(diào)用截屏,識別二維碼方法}} } -------------------------------------------------------------------

理論上js與原生的互調(diào)就這樣可以了,但為了保險一點的話,原生接到j(luò)s的調(diào)用以后再回調(diào)一下js比較好一點,因為有可能有些功能js需要app回傳數(shù)據(jù)做下一步操作。相當(dāng)于待人接物而言你叫我做一件事,做好了還是做不了,我得告知你一下,做個有交代有責(zé)任的人。


這篇好像有點長,寫得不好的地方還多請見諒,也可在評論指導(dǎo)一下。








總結(jié)

以上是生活随笔為你收集整理的移动端(ios and android)长按识别二维码(含js与原生互调)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。