X5内核的问题 - CSDN博客

mikel阅读(1673)

来源: X5内核的问题 – CSDN博客

常常被人问及微信中使用的X5内核的问题,其实我也不是很清楚,只知道它是基于Android 4.2的webkit,版本号是webkit 534。今天正好从X5团队拿到了一份问题汇总,梳理下发出来,给各位开发者以参考——不排除明天会删除这篇文章的可能。


1.Android WebView常见问题及解决方案汇总:

http://blog.csdn.net/t12x3456/article/details/13769731

2.请问各位碰到过X5浏览器内,局部滑动使用iscroll卡顿的问题么?

回答:是使用 iscroll.js 这个JS去滚动么?如果只是为了产生滚动,建议使用 overflow 属性来,目前有一种滚动优化在线上版本效率不是太好,通过JS去改变CSS的属性产生滚动

3.调用:-webkit-filter:
blur(10px);filter: blur(10px);
在Android下 背景图没有blur掉,只是被单纯的放大而已

回答:-webkit-filter目前还不支持,可以先用图片替换的方式 后续版本会支持这个属性

4.shadowBlur是阴影效果吧,我们是想实现图片毛玻璃

回答: http://blog.csdn.net/jia20003/article/details/9195915 这个有用么

5.LBS相关,定位频繁失败

第一步,首先确认定位失败是个别站点原因还是所有站点定位都失败。如果是所有站点定位都不成功,很有可能是内核问题,转内核相关同学跟进调查定位逻辑是否有缺陷;如果是某个站点才会出现的问题,继续第二步排查,从站点源码着手。

第二步,找到站点请求定位的js代码段,检查获取定位信息函数的options字段,很有可能是 options 中 timeout 字段设置的超时太短导致,建议前端开发将该字段时间设置长一些(建议10s以上)或者不设置该字段。如果js没有设置 timeout 字段的情况下仍然定位不成功,则转内核同学调查内核流程。

ps:H5获取LBS信息的js接口

回答:

navigator.geolocation.getCurrentPosition(showPosition,showError,{  
    enableHighAccuracy:false,
    timeout:10*1000,
    maximumAge:0
});

navigator.geolocation.watchPosition(watchPosition,showError,options);  
  • showPosition:定位成功时回调;
  • showError:定位出错时回调;
  • options:可选的地理定位请求特征
  • enableHightAccuracy:可选,是否开启高精度模式,参数默认值为 false
  • timeout:可选,单位为ms,浏览器需要在该时间段内完成定位,否则定位失败,默认值为 – – infinity,无穷大。如果该值设置较小,会有很高的定位失败率。
  • maximumAge:可选,单位ms,重新计算位置的时间间隔。默认为0,即每次时时计算位置信息。

6.打开视频播放,后退视频仍然在播放

回答:部分机型,浏览页面时打开视频播放,点击返回,页面上的视频仍然在播放。解决办法是捕获后退事件,主动调用 onHideCustomView() 方法,并且在该方法里将 onShowCustomView 里关联的view解除关联

7.请问一下微信浏览器的cookie清理机制是怎么样的?

回答:X5内核是不会清除的。

8.打开WWW页面,缩放显示的问题

回答:
使用webview打开www页面,如果页面被放大显示,确定websettings有没有设置,webSettings.setUseWideViewPort(true),默认为false,www页面不会被缩放显示的。手机QQ浏览器默认为true,显示www页面更美观

9. 关于滚动时候动画的问题

回答:

  1. 页面滑动过程中动画不会被触发
  2. 页面滑动过程中动画会被停止

这个是X5内核为了做滚动优化而做的限制

10.关于connection type定义的问题

回答:X5执行的标准比较老,NetworkInfo_API

enum ConnectionType {  
UNKNOWN = 0,  
ETHERNET = 1,  
WIFI = 2,  
CELL_2G = 3,  
CELL_3G = 4,  
CELL_4G = 5,  
NONE = 6,  
};

11.出现网络正常,但是页面打不开的情况

回答:关于设置里面的云加速试下是否可以打开。

12.js阻塞和css阻塞的不同

回答:css是阻塞渲染过程,js阻塞解析过程!对于用户来说,没什么区别,都是空白的 js的执行时,如果js中有读写css的属性的代码,并且下载队列中有待加载的css,js执行会被阻塞掉。

13.cookie的4k限制

回答:浏览器端cookie的数量可能会超过4k,有http请求时,内核只取前4k的cookie数据!

14.Js Defer与把js放到html底部的区别

回答:js defer:先加载,ondownload 后执行。和js放到html底部类似。不同的就是html预扫描到会先加载

15.首屏显示后,为什么又会重排版

回答:浏览器的排版宽度受上层ui设置的webview宽度影响。如果webview没设置或者是设置成0,浏览器内核会用默认的排版宽度320px进行排版。此时若webview的宽度值被正常设置,计算出来的排版宽度不是320px(一般是360px),这样浏览器就要用360px宽度,对页面进行重排。

16.canvas的数量是不是有限制

回答:

  • 小于1G的内存, canvas的内存不能超过100M
  • 1G到2G的内存, canvas的内存不能超过300M
  • 大于2G的内存 canvas的内存不能超过500M
  • 为了防止内存占用过多,硬件加速的CANVAS最多支持5个

小于等于1G内存手机,由于内存没办法精确统计,当达到75M以上,CANVAS数量最多支持20个

上面说的canvas内存,仅仅是说canvas 长宽计算出来的内存,不包括canavs使用的图片等资源内存

17.x5浏览器CSS3有些不支持,同样的样式,在chrome里能起到效果,在X5就没用。并且js性能也差的多,微信还内置X5,用起来太不爽

回答:是否方便具体说下是什么样的性能问题和css样式缺陷影响到您?我们这边可以跟进查看下原因。

X5内核也在不断改进,您的反馈可以帮助我们进一步优化。

chrome在标准的支持和性能优化方面确实目前走在业界前面,不过android系统的碎片化,android系统webview更是碎片化严重。

android系统上的web开发可能也需要考虑到不同rom的兼容情况。X5内核致力于为开发者提供统一的web体验,并通过不断的优化,来向业界标准对齐。

目前我们也在做基于chromium内核的研究工作,后续在标准的支持和性能上会有进一步提高。

18.请问下x5中js调用android怎么实现?

回答:跟系统内核下一样,都借助 addjavainterface 实现

19.请问一下,android手机微信上用的x5支持webgl吗?

回答:android手机中微信上的X5都是支持webgl的,不过部分机型上还有兼容性问题,由于兼容性问题,webgl之前我们是采用软绘的方式支持,目前切换到了硬绘,但整体来说还是会有兼容性和性能问题,后续这块还会持续更新

20.如果在某个设备第一次打开应用的时候 没有网络,x5内核可以启动吗?单网络恢复后还要再次认证,还是 不用在认证?

回答:第一次打开应用是不会拉起X5的 在第二次打开之后 不管有无网络都可以拉起

21.如果我不安装浏览器,安装微信,x5能调用起来吗?

回答:不能,只能安装QQ浏览器才能调用

22.web audio api是否已经支持?

回答:暂时还不支持

23.什么时候支持html5的onunload和onbeforeunload事件

回答:现在是支持html5的 onunload 和 onbeforeunload 事件的

24.要用X5的内核,必须要用QQ浏览器么?

回答:目前 SDK 版本是这样的,我们正在预研的版本,手机如果安装了微信或手Q ,其它 app 可以不依赖 QQ浏览器 而共享 x5 内核。

25.关于svg问题。

回答:关于SVG的问题:

关于svg模糊的问题,有2种情况:

  1. 直接访问一个.svg url的页面或者object,embed加入的svg, 出现模糊的问题
    此问题已解决, qq浏览器5.8上已经修复, tbs下个版本也会修复
  2. svg用作background-image, 模糊
    这个问题是我们目前渲染机制导致不能兼容非标准写法: 在用svg作为background-image的时候, 需要指定background-size, 不然会模糊

关于svg支持情况:
在5.3之前的qq浏览器不支持svg, 我们会在后台把svg转成一张jpeg图片, 供浏览器显示,5.4及以上版本支持svg,如果遇到被转成图片的问题,需要升级浏览器版本。

26.请问flexbox近期会支持吗?

回答:flexbox我们正在做开发支持

27.x5内核 目前是独立运行的 还是需要安装QQ浏览器?

回答:sdk是需要QQ浏览器的,微信手Q里的是不需要的

28.现在X5内核怎么调试?在微信或者手q或者qq浏览器中调试页面

回答:现在的微信手Q里面的X5还无法通过inspector调试 后期我们会把带有inspector调试的版本挂在开发者后台下载区 敬请期待

29.请问x5支持webgl的所有接口吗?能在所有版本的android机(4.0+)和ios机(5.0+)上运行webgl吗?

回答:webgl目前是支持的,不过部分机型上还有兼容性问题

30.X5上支持哪些扩展?支持多少个纹理单元?

回答:X5上只能支持:

WEBGL_lose_context  
EXT_texture_filter_anisotropic  
OES_texture_float  
OES_standard_derivatives  
OES_vertex_array_object  
WEBGL_debug_renderer_info  
WEBGL_debug_shaders  
WEBKIT_WEBGL_compressed_texture_s3tc  

而且这些是必须手机GPU有对应的扩展指令才行的。

关于纹理单元。我们这边没有限制

上面的扩展,也都是基本每一个对应opengl的一个扩展

支持多少纹理单元,也是从opengl查询得到的。

主要应该是看手机GPU支持到啥程度,我们是做个对接。

31.x5的文件分片功能解决了吗,blob
= file.webkitSlice(start, stop)
,这样分片出来的blob用不了啊。

回答:分片问题这边已经定位处理,浏览器会在5.8版本修复

32.手机qq浏览器是否有调试工具呢?

回答:有的,Inspector。

33.请问现在微信调用的手机QQ浏览器支持websocket 吗?

回答:支持websocket ,暂不支持wss,不支持webrtc

34.有计划支持webRTC吗?

回答:这个我们后续会评估的。

35.我是HTML5游戏开发者,制作中的H5游戏需要有音乐音效。但是我在android机器上使用QQ浏览器出现了如下问题:

  1. 循环播放BGM时,如果同时播放音效,BGM会被强行暂停
  2. 多个音效同时播放时,会出现明显的无法忍受的延迟和播放失败

这个问题在同一台机器的微信上同样存在,但是同一台机器的chrome没有这个问题。

我能想到的最合理的解释是:X5内核同一时间只能播放一个音频通道。

希望官方能解答我的疑惑和遇到的问题,谢谢。

PS:

  1. 使用的H5音频库 : SoundJS
  2. 使用的音频格式 : mp3
  3. 使用的Android机型:三星 Note3

回答:播放音效需要获取声音输出焦点,目前只支持同时播放一个音效 您提的需求我们会讨论评估后期是否能有方案现

36.播放音效时,为啥会把我后台的BGM播放给暂停掉?

回答:播放声音时当前音频需要获取 audiofocus ,系统在audiofocus丢失时会通知其它音频播放软件,这个暂停应该是播放软件自身的行为,我们本身并没有暂停后台音频,只是向系统申请了 audiofocus

37.300ms延迟是指什么?click 和 touch ?

回答:touch 点击之后,到 click 事件被触发,click 事件有延迟,touch不存在,用 touchstart 事件替代 click 事件就OK

38.现在微信内置的浏览器能支持flexbox么,现在有没有什么好办法能够替代呢?做好的网页一放到微信上,大量的flex的页面

回答:你好flexbox正在开发支持,flexbox在android4.1到4.3系统内核上,也有类似问题,页面是需要兼容的

39.有没有什么x5内核的论坛或者wiki之类的?可以参考下。

回答:http://bbs.browser.qq.com/ http://x5.tencent.com/ 这两个论坛可以关注下

另外有我们的公众账号 有问题可以随时交流

40.X5公众号叫什么?

回答:腾讯X5浏览服务

41.手q上面的内核应该也是x5的吧?这样直接在手q上面的效果和在微信里面应该是一样的?

回答:是的手Q微信内的webview都是X5。

42.x5不支持font-face吗?

回答:支持

43.x5 支持flex吗?有兼容性文档吗?

回答:不支持.http://1.h5support.sinaapp.com/incoming/cow.html

这个页面是参考caniuse的测试用例得到的测试结果,5.7是对应qq浏览器5.7版本,可以对比下和系统浏览器4.1~.4.4的支持度。有需要的同学可以先参考下,相关的文档建设我们也在逐步完……

44.iscroll+lazyload在x5浏览器里面卡顿很严重,有人碰到过类似问题么,小米手机 列表内元素200个左右。

回答:iscroll本身对内核要求比较高,较新的blink版本支持才比较好。可以对比测试下android 4.x 的系统浏览器看看。建议还是在前端做些优化,避免较长的元素,并减少动画效果。http://www.cnblogs.com/vbluebirdv/archive/2012/11/18/2776300.html 可以参考网上一些iscroll调优的文章

45.微信里面缓存问题,在安卓下和 ios下,刷新机制是不是不一样?

回答:ios因为有刷新功能,点击之后请求到的都是最新的文件,安卓下,不管怎样请求都不会更新文件

46. <meta
name="x5-orientation" content="portrait"/>
现在微信里面没有假如这个啊?为什么IOS QQ浏览器不识别这个?

回答:ios内核不是X5, ios 浏览器后续版本也会支持http://open.mb.qq.com/doc?id=1201
目前ios 浏览器仅支持 x5-page-mode

47.现在白鹭游戏引擎是不是内置在x5里面了?

回答:内置了白鹭引擎runtime

48.x5内核不支持 canvas 的 background 属性吗?

<!doctype html>  
<html xmlns="http://www.w3.org/1999/xhtml">  
<head>  
<meta charset="utf-8">  
<title>canvas test</title>  
    <!--<meta HTTP-EQUIV="pragma" CONTENT="no-cache">-->
    <!--<meta HTTP-EQUIV="Cache-Control" CONTENT="no-store, must-revalidate">-->
</head>

<body>  
<div>  
    <canvas id="testCanvas" width="500" height="300" style="background: #00FF00;"></canvas>

<!--<canvas id="testCanvas" width="1136" height="640" style="background:#000"></canvas> -->  
</div>  
</body>  
</html>  

背景颜色显示不出来

回答:这个是做了优化,当canvas下盖了背景,就没有去绘制背景图片,当初是因为一些手机的GPu在绘制这块,如果存在这种情况绘制非常慢

49.qq浏览器有没有准备支持asm.js的计划?

回答:我们也正在筹备相关工作,会对市面上各种游戏引擎进行全面评测并设计合理的架构方案整合。预计下半年可以推出,敬请期待

50.X5不支持canvas.toDataUrl()image/jpeg参数,还是转成了默认的png格式请问有什么替换方法吗?

回答:当前确实还不支持…我们在修复

51.问一下 ios版的微信里面是用的系统自带的UIWebView还是用的qq浏览器的内核?

回答:ios版微信里面用的是系统自带的

本文转自:http://www.qianduan.net/qqliu-lan-qi-x5nei-he-wen-ti-hui-zong/

android webview中上传控件点击无效的解决办法 - CSDN博客

mikel阅读(1105)

来源: android webview中上传控件点击无效的解决办法 – CSDN博客

一、介绍

当我们在使用webview控件打开一个web网页时,如果we页面中带有<input type=”file” …>的控件,在webview中能正常显示这个上传控件,但是你会发现无论你如何点击都无效果,这个是很让人恼火的,一时也不知道如何下手去改,这里阿汤哥会告诉你如何解决该问题,如果我的解决办法能帮到你,请给我点掌声,并给你自己点掌声。

二、解决办法

第一步:重写WebChromeClient

webview的坑比较多,在这个上传文件的坑中遇到一个问题:

Android 5.0+ 重写onShowFileChooser生效;

Android 4.4   重写openFileChooser没有生效;

Android 4.4- 重写openFileChooser生效;

  1. import android.net.Uri;
  2. import android.webkit.ValueCallback;
  3. import android.webkit.WebChromeClient;
  4. import android.webkit.WebView;
  5. /**
  6.  * Created by tangbin on 16/5/12.
  7.  */
  8. public class MyWebChromeClient extends WebChromeClient {
  9.     private WebCall webCall;
  10.     public void setWebCall(WebCall webCall) {
  11.         this.webCall = webCall;
  12.     }
  13.     // For Android 3.0+
  14.     public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
  15.         if (webCall != null)
  16.             webCall.fileChose(uploadMsg);
  17.     }
  18.     // For Android < 3.0
  19.     public void openFileChooser(ValueCallback<Uri> uploadMsg) {
  20.         openFileChooser(uploadMsg, “”);
  21.     }
  22.     // For Android > 4.1.1
  23.     public void openFileChooser(ValueCallback<Uri> uploadMsg,
  24.             String acceptType, String capture) {
  25.         openFileChooser(uploadMsg, acceptType);
  26.     }
  27.     // For Android > 5.0
  28.     @Override
  29.     public boolean onShowFileChooser(WebView webView,
  30.             ValueCallback<Uri[]> filePathCallback,
  31.             FileChooserParams fileChooserParams) {
  32.         if (webCall != null)
  33.             webCall.fileChose5(filePathCallback);
  34.         return super.onShowFileChooser(webView, filePathCallback,
  35.                 fileChooserParams);
  36.     }
  37.     public interface WebCall {
  38.         void fileChose(ValueCallback<Uri> uploadMsg);
  39.         void fileChose5(ValueCallback<Uri[]> uploadMsg);
  40.     }
  41. }

 

第二步:监听ValueCallback

  1. WebSettings webSettings = mWebView.getSettings();
  2.         // 设置WebView属性,能够执行JavaScript脚本
  3.         webSettings.setJavaScriptEnabled(true);
  4.         // 设置可以访问文件
  5.         webSettings.setAllowFileAccess(true);
  6.         mWebView.setWebViewClient(new webViewClient());
  1. public final static int FILECHOOSER_RESULTCODE = 1;
  2. public final static int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 2;
  3. public ValueCallback<Uri> mUploadMessage;
  4. public ValueCallback<Uri[]> mUploadMessageForAndroid5;

 

  1. @Override
  2. public void fileChose(ValueCallback<Uri> uploadMsg) {
  3.     openFileChooserImpl(uploadMsg);
  4. }
  5. @Override
  6. public void fileChose5(ValueCallback<Uri[]> uploadMsg) {
  7.     openFileChooserImplForAndroid5(uploadMsg);
  8. }
  9. private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
  10.     mUploadMessage = uploadMsg;
  11.     Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  12.     i.addCategory(Intent.CATEGORY_OPENABLE);
  13.     i.setType(“image/*”);
  14.     startActivityForResult(Intent.createChooser(i, “File Chooser”),
  15.             FILECHOOSER_RESULTCODE);
  16. }
  17. private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
  18.     mUploadMessageForAndroid5 = uploadMsg;
  19.     Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
  20.     contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
  21.     contentSelectionIntent.setType(“image/*”);
  22.     Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
  23.     chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
  24.     chooserIntent.putExtra(Intent.EXTRA_TITLE, “Image Chooser”);
  25.     startActivityForResult(chooserIntent,
  26.             FILECHOOSER_RESULTCODE_FOR_ANDROID_5);
  27. }

 

第三步:创建onActivityResult

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode,
  3.         Intent intent) {
  4.     if (requestCode == FILECHOOSER_RESULTCODE) {
  5.         if (null == mUploadMessage)
  6.             return;
  7.         Uri result = intent == null || resultCode != RESULT_OK ? null
  8.                 : intent.getData();
  9.         mUploadMessage.onReceiveValue(result);
  10.         mUploadMessage = null;
  11.     } else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
  12.         if (null == mUploadMessageForAndroid5)
  13.             return;
  14.         Uri result = (intent == null || resultCode != RESULT_OK) ? null
  15.                 : intent.getData();
  16.         if (result != null) {
  17.             mUploadMessageForAndroid5.onReceiveValue(new Uri[] { result });
  18.         } else {
  19.             mUploadMessageForAndroid5.onReceiveValue(new Uri[] {});
  20.         }
  21.         mUploadMessageForAndroid5 = null;
  22.     }
  23. }

搞定,最后就可以如愿以偿的完成文件上传了功能了

Android中多图片选择器PhotoPicker库的使用(仿微信) - CSDN博客

mikel阅读(1711)

来源: Android中多图片选择器PhotoPicker库的使用(仿微信) – CSDN博客

PhotoPicker

基于 donglua/PhotoPickerlovetuzitong/MultiImageSelector 修改的一个图片选择类库。

Gradle

compile 'com.foamtrace:photopicker:1.0'

效果图

   

使用方法

ImageConfig

可选属性, 用于过滤照片列表信息。

ImageConfig config = new ImageConfig();
config.minHeight = 400;
config.minWidth = 400;
config.mimeType = new String[]{"image/jpeg", "image/png"}; // 图片类型 image/gif ...
config.minSize = 1 * 1024 * 1024; // 1Mb 图片大小

单选

PhotoPickerIntent intent = new PhotoPickerIntent(MainActivity.this);
intent.setSelectModel(SelectModel.SINGLE);
intent.setShowCarema(true); // 是否显示拍照, 默认false
// intent.setImageConfig(config);
startActivityForResult(intent, REQUEST_CAMERA_CODE);

多选

PhotoPickerIntent intent = new PhotoPickerIntent(MainActivity.this);
intent.setSelectModel(SelectModel.MULTI);
intent.setShowCarema(true); // 是否显示拍照, 默认false
intent.setMaxTotal(9); // 最多选择照片数量,默认为9
intent.setSelectedPaths(imagePaths); // 已选中的照片地址, 用于回显选中状态
// intent.setImageConfig(config);
startActivityForResult(intent, REQUEST_CAMERA_CODE);

拍照

private ImageCaptureManager captureManager;

btnCarema.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        try {
            if(captureManager == null){
                captureManager = new ImageCaptureManager(MainActivity.this);
            }
            Intent intent = captureManager.dispatchTakePictureIntent();
            startActivityForResult(intent, ImageCaptureManager.REQUEST_TAKE_PHOTO);
        } catch (IOException e) {
            Toast.makeText(MainActivity.this, R.string.msg_no_camera, Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }
});

预览

PhotoPreviewIntent intent = new PhotoPreviewIntent(MainActivity.this);
intent.setCurrentItem(position); // 当前选中照片的下标
intent.setPhotoPaths(imagePaths); // 已选中的照片地址
startActivityForResult(intent, REQUEST_PREVIEW_CODE);

ActivityResult

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(resultCode == RESULT_OK) {
        switch (requestCode) {
            // 选择照片
            case REQUEST_CAMERA_CODE:
                refreshAdpater(data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT));
                break;
            // 拍照
            case ImageCaptureManager.REQUEST_TAKE_PHOTO:
                if(captureManager.getCurrentPhotoPath() != null) {
                    captureManager.galleryAddPic();
                    // 照片地址
                    String imagePaht = captureManager.getCurrentPhotoPath();
                    // ...
                }
                break;
            // 预览
            case REQUEST_PREVIEW_CODE:
                refreshAdpater(data.getStringArrayListExtra(PhotoPreviewActivity.EXTRA_RESULT));
                break;
        }
    }
}

private void refreshAdpater(ArrayList<String> paths){
    // 处理返回照片地址
}

配置信息

style.xml

<!-- 设置ActionBar菜单字体 -->
<style name="ActionMenuTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Menu">
    <item name="android:textSize">@dimen/action_text_size</item>
    <item name="android:textStyle">normal</item>
</style>

<style name="actionBarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
</style>

<style name="actionBarPopupTheme" parent="ThemeOverlay.AppCompat.Light">
</style>

<style name="PhotoPickerTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowBackground">@color/photopicker_background</item>
    <item name="actionMenuTextAppearance">@style/ActionMenuTextStyle</item>
    <!--<item name="actionBarTheme">@style/actionBarTheme</item>-->
    <!--<item name="actionBarPopupTheme">@style/actionBarPopupTheme</item>-->
</style>

AndroidManifest.xml

<activity
    android:name="com.foamtrace.photopicker.PhotoPickerActivity"
    android:theme="@style/PhotoPickerTheme"
    android:configChanges="orientation|screenSize"/>

<activity android:name="com.foamtrace.photopicker.PhotoPreviewActivity"
    android:theme="@style/PhotoPickerTheme"/>

转载地址:

https://github.com/wzfu/PhotoPicker

源码下载:

http://download.csdn.net/detail/zhaihaohao1/9545045

腾讯浏览服务X5内核集成 - 简书

mikel阅读(1448)

来源: 腾讯浏览服务X5内核集成 – 简书

一、什么是X5内核?

用官方文档的话说,就是解决系统webview兼容性差、加载速度慢、功能缺陷等问题,腾讯QQ浏览器大神们产出的一个WebView可以替换原生WebView。前面不重要,关键是开源的,还能解决我们开发中的一些问题和需求,举个例子:QQ浏览器独立小窗播放视频的功能,还有WebView播放原生H5Video难以很好解决的全屏播放问题~~~~….用X5都能解决。官方文档入口http://x5.tencent.com/doc?id=1004

二、需求场景

解决WebView播放H5 Video全屏问题

尝试过的其他方法

重写WebChromeClient的onShowCustomView开启全屏;onHideCustomView退出全屏。使用过这个方法的人,都应该知道Android4.4以后,不会在走这个回调

三、开始搬砖

准备工作:
集成准备工作就不多说了,步骤太简单不过了,就两个文件,一个tbs_sdk….的lib包,一个32位的liblbs.so库。直接可以从官方文档那里下。
需要提一下,如果是Eclipse用户,so库的导入,需要在工程libs目录创建armeabi文件夹,把so库放armeabi目录里(libs——>armeabi);如果是AndroidStudio目录,则直接在main目录下创建jniLib,然后在jniLibs目录下创建armeabi文件夹,再把so放armeabi目录(main——>jniLibs——>armeabi)

简单使用(直接上代码):

X5的WebView还原度非常高,使用起来和原生的WebView基本上是一模一样

MainActiviy

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set layout
    getWindow().setFormat(PixelFormat.TRANSLUCENT);
    setContentView(R.layout.activity_main);
    mWebView = (com.tencent.smtt.sdk.WebView) findViewById(R.id.forum_context);
    mWebView.getSettings().setJavaScriptEnabled(true);// 支持js
    mWebView.getSettings().setUseWideViewPort(true); //自适应屏幕
    mWebView.loadUrl("http://res.ky-express.com/h5/video/72.html");
}

activity_main

<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"
tools:context=".MainActivity">

<com.tencent.smtt.sdk.WebView
    android:id="@+id/forum_context"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"/>

</RelativeLayout>

这个很简单,几分钟就能看到X5的效果了,但是这样只能在部分手机上运行,不能适配大多数手机,原因在于我们只创建并armeabi的目录,也就是说只有arm cpu的手机上才能有效果。

四、优化

1、上述问题,适配所有的cpu的机型

解决方案也很简单,把其他cpu的目录也创建出来就ok了(mips,mips64,x86,x86_64,armeabi-v7a,armeabi,arm64-v8a);第二步就是把上面那个liblbs.so库依次放置到各个cpu目录下即可;最最关键的最后一步,在build.config文件中的defaultConfig里面加上这行配置代码

ndk {
        abiFilters"armeabi","armeabi-v7a","x86","mips"
    }

下面是我项目的build.config配置

defaultConfig {
    applicationId "spanlogistics.oxbix.com.webviewfullscreen"
    minSdkVersion 14
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"

    ndk {
        abiFilters"armeabi","armeabi-v7a","x86","mips"
    }
}

重新运行一下,应该就解决勒。

2、体验的问题

初次启动打开页面时,会有个白屏的过程,大概4秒钟左右(手机配置差的话可能会更长)
原因:使用X5内核需要有个初始化的过程,我们Demo中是直接用的,并未做预初始化的操作,换句话说,如果预初始化完成后,在去打开该就不会出现白屏体验了

解决方案:在我们应用的Application里面去对X5进行预初始化,我们创建一个服务去预加载它,然后在Application中去开启该服务

预初始化X5的服务代码,PreLoadX5Service
 @Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    initX5();
    preinitX5WebCore();
}

private void initX5() {
    QbSdk.initX5Environment(getApplicationContext(), QbSdk.WebviewInitType.FIRSTUSE_AND_PRELOAD, cb);
    Log.d("gggbbb","预加载中...");
}

QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {

    @Override
    public void onViewInitFinished(boolean arg0) {
        // TODO Auto-generated method stub
        Log.e("0912", " onViewInitFinished is " + arg0);
    }

    @Override
    public void onCoreInitFinished() {
        // TODO Auto-generated method stub

    }
};
 private void preinitX5WebCore() {

    if(!QbSdk.isTbsCoreInited()) {

        // preinit只需要调用一次,如果已经完成了初始化,那么就直接构造view

        QbSdk.preInit(MainActivity.this, null);// 设置X5初始化完成的回调接口

    }
}

Application代码

@Override
public void onCreate() {
    super.onCreate();
    initX5();
}
private void initX5() {
    Intent intent = new Intent(this, PreLoadX5Service.class);
    startService(intent);
}

3、我们会发现集成X5后,项目编译变慢了,如果项目太大的话,还不容易跑起来,可以在主module的build.gradle里面加上下面这段代码试试

 dexOptions {
    javaMaxHeapSize "4g"
    preDexLibraries = false
}

4、一般情况下根据上面操作,就已经完美集成勒,但是自己集成过程中很容易掉的一个坑,就是64位手机上不能使用X5,官方文档中也提到了相应了解决方案,就是把32位的so库直接放到64位的文件夹中当64位使用

补充

看到不少童鞋再问如何去除QQ浏览器推广,其实评论里已经有童鞋给了解决方案,可能有的小伙伴没能看到,在这里做个补充,先感谢下 @StoneHui 童鞋。下面是去除QQ浏览器推广的代码:

 getWindow().getDecorView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                ArrayList<View> outView = new ArrayList<View>();
                getWindow().getDecorView().findViewsWithText(outView,"QQ浏览器",View.FIND_VIEWS_WITH_TEXT);
                int size = outView.size();
                if(outView!=null&&outView.size()>0){
                    outView.get(0).setVisibility(View.GONE);
                }
            }
     });

怎么加呢?
很简单,直接在使用到X5 Webview的那个Activity里面加上这段代码就ok了

作者:_陈小平
链接:https://www.jianshu.com/p/8a7224ff371a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android 使用腾讯X5 Webview浏览器拍照或从相册上传图片 - CSDN博客

mikel阅读(1276)

来源: Android 使用腾讯X5 Webview浏览器拍照或从相册上传图片 – CSDN博客

最近在项目开发中,需要使用WebView上传文件。默认情况下情况下,使用Android的WebView是不能够支持上传文件的。

经过查找资料,得知需要重新WebChromeClient,根据选择到的文件Uri,传给页面去上传就可以了。

自定义WebChromeClient

先在WebViewActivity里面自定义MyWebChromeClient,代码如下:

public class MyWebChromeClient extends WebChromeClient {
    // For Android 3.0+
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        CLog.i("UPFILE", "in openFile Uri Callback");
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }
    // For Android 3.0+
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        CLog.i("UPFILE", "in openFile Uri Callback has accept Type" + acceptType);
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        String type = TextUtils.isEmpty(acceptType) ? "*/*" : acceptType;
        i.setType(type);
        startActivityForResult(Intent.createChooser(i, "File Chooser"),
                FILECHOOSER_RESULTCODE);
    }
    // For Android 4.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        CLog.i("UPFILE", "in openFile Uri Callback has accept Type" + acceptType + "has capture" + capture);
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        String type = TextUtils.isEmpty(acceptType) ? "*/*" : acceptType;
        i.setType(type);
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }
//Android 5.0+
    @Override
    @SuppressLint("NewApi")
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        CLog.i("UPFILE", "file chooser params:" + fileChooserParams.toString());
        mUploadMessage = filePathCallback;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        if (fileChooserParams != null && fileChooserParams.getAcceptTypes() != null
                && fileChooserParams.getAcceptTypes().length > 0) {
            i.setType(fileChooserParams.getAcceptTypes()[0]);
        } else {
            i.setType("*/*");
        }
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
        return true;
    }
}

上面openFileChooser是系统未暴露的接口,因此不需要加Override的注解,同时不同版本有不同的参数,其中的参数,第一个ValueCallback用于我们在选择完文件后,接收文件回调到网页内处理,acceptType为接受的文件mime type。在Android 5.0之后,系统提供了onShowFileChooser来让我们实现选择文件的方法,仍然有ValueCallback,在FileChooserParams参数中,同样包括acceptType。我们可以根据acceptType,来打开系统的或者我们自己创建文件选择器。当然如果需要打开相机拍照,也可以自己去使用打开相机拍照的Intent去打开即可。

处理选择的文件

因为我们前面是使用startActivityForResult来打开的选择页面,我们会在onActivityResult中接收到选择的结果。代码如下:

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

        Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
        if (result == null) {
            mUploadMessage.onReceiveValue(null);
            mUploadMessage = null;
            return;
        }

        String path =  FileUtils.getPath(this, result);
        if (TextUtils.isEmpty(path)) {
            mUploadMessage.onReceiveValue(null);
            mUploadMessage = null;
            return;
        }
        Uri uri = Uri.fromFile(new File(path));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mUploadMessage.onReceiveValue(new Uri[]{uri});
        } else {
            mUploadMessage.onReceiveValue(uri);
        }
        mUploadMessage = null;
    }
}

注意事项:

  1. 由于不同版本的差别,Android 5.0以下的版本,ValueCallback 的onReceiveValue接收的参数类型是Uri, 5.0及以上版本接收的是Uri数组,在传值的时候需要注意。
  2. 选择文件会使用系统提供的组件或者其他支持的app,返回的uri有的直接是文件的url,有的是contentprovider的uri,因此我们需要统一处理一下,转成文件的uri,可参考以下代码(获取文件的路径)。
  3. 即使获取的结果为null,也要传给webview,即直接调用mUploadMessage.onReceiveValue(null),否则网页会阻塞。
  4. 在打release包的时候,因为我们会混淆,要特别设置不要混淆WebChromeClient子类里面的openFileChooser方法,由于不是继承的方法,所以默认会被混淆,然后就无法选择文件了。

FileUtils工具类如下:

public class FileUtils {
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @author paulburke
     */
    @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }
}

看了上面的代码,你是不是感觉有点复杂呢?下面我们将介绍怎么通过使用腾讯X5 Webview浏览器实现拍照或从相册上传图片功能。

使用腾讯X5 Webview浏览器

TBS腾讯浏览器服务官网:http://x5.tencent.com
jar包下载:http://x5.tencent.com/doc?id=1004

集成教程:
http://www.jianshu.com/p/8a7224ff371a
http://blog.csdn.net/qq_17387361/article/details/52396338
http://www.jianshu.com/p/e4009688119b

环境调好后,我们就可以愉快的开始调试了。

public class MyWebChromeClient extends WebChromeClient {
           @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
                TLog.error("onShowFileChooser");
                return super.onShowFileChooser(webView, valueCallback, fileChooserParams);
            }

            // Android > 4.1.1 调用这个方法
            public void openFileChooser(ValueCallback<Uri> uploadMsg,
                                        String acceptType, String capture) {
                mUploadMessage = uploadMsg;
                choosePicture();

            }

            // 3.0 + 调用这个方法
            public void openFileChooser(ValueCallback<Uri> uploadMsg,
                                        String acceptType) {
                mUploadMessage = uploadMsg;
                choosePicture();

            }

            // Android < 3.0 调用这个方法
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                mUploadMessage = uploadMsg;
                choosePicture();
            }
}

这里选择图片使用了三方图片选择组件:PhotoPicker,项目地址:https://github.com/donglua/PhotoPicker

其中choosePicture方法如下,

private void choosePicture() {
        PhotoPicker.builder()
                .setPhotoCount(1)
                .setShowCamera(true)
                .setShowGif(true)
                .setPreviewEnabled(false)
                .start(TBSWebActivity.this, PhotoPicker.REQUEST_CODE);
    }

在onActivityResult中接收到选择的结果,处理如下:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (null == mUploadMessage) {
            return;
        }
        if (resultCode == RESULT_OK && requestCode == PhotoPicker.REQUEST_CODE) {
            ArrayList<String> photos = intent.getStringArrayListExtra(PhotoPicker.KEY_SELECTED_PHOTOS);

            Uri result = Uri.parse(photos.get(0));
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
        } else {
            mUploadMessage.onReceiveValue(null);
        }
    }

这里再强调下,即使获取的结果为null(比如按back键取消了),也要传给webview,即直接调用mUploadMessage.onReceiveValue(null),否则网页会阻塞。

H5前端

最后简单再说一下H5前端调用。

<div class="btn1">上传照片
    <input type="file"  accept="image/*" id="uploadImage" capture="camera" onchange="selectFileImage(this);"> 
</div>

上传相关的js代码如下:

var arr=new Array();

function selectFileImage(fileObj) {
    var file = fileObj.files['0'];
    //图片方向角 added by lzk
    var Orientation = null;

    if (file) {
        console.log("正在上传,请稍后...");
        var rFilter = /^(image\/jpeg|image\/png)$/i; // 检查图片格式
        if (!rFilter.test(file.type)) {
            //showMyTips("请选择jpeg、png格式的图片", false);
            return;
        }
        // var URL = URL || webkitURL;
        //获取照片方向角属性,用户旋转控制
        EXIF.getData(file, function() {
           // alert(EXIF.pretty(this));
            EXIF.getAllTags(this); 
            //alert(EXIF.getTag(this, 'Orientation')); 
            Orientation = EXIF.getTag(this, 'Orientation');
            //return;
        });

        var oReader = new FileReader();
        oReader.onload = function(e) {
            //var blob = URL.createObjectURL(file);
            //_compress(blob, file, basePath);
            var image = new Image();
            image.src = e.target.result;
            image.onload = function() {
                var expectWidth = this.naturalWidth;
                var expectHeight = this.naturalHeight;

                if (this.naturalWidth > this.naturalHeight && this.naturalWidth > 640) {
                    expectWidth = 640;
                    expectHeight = expectWidth * this.naturalHeight / this.naturalWidth;
                } else if (this.naturalHeight > this.naturalWidth && this.naturalHeight > 640) {
                    expectHeight = 640;
                    expectWidth = expectHeight * this.naturalWidth / this.naturalHeight;
                }
                //alert(expectWidth+','+expectHeight);
                var canvas = document.createElement("canvas");
                var ctx = canvas.getContext("2d");
                canvas.width = expectWidth;
                canvas.height = expectHeight;
                ctx.drawImage(this, 0, 0, expectWidth, expectHeight);
                //alert(canvas.width+','+canvas.height);

                var base64 = null;
                var mpImg = new MegaPixImage(image);
                    mpImg.render(canvas, {
                        maxWidth: 640,
                        maxHeight: 640,
                        quality: 0.8,
                        orientation: Orientation
                    });

                base64 = canvas.toDataURL("image/jpeg", 0.8);
                //alert(base64);
                //var img = 
                //修复ios
                if (navigator.userAgent.match(/iphone/i)) {
                    console.log('iphone');
                    //alert(expectWidth + ',' + expectHeight);
                    //如果方向角不为1,都需要进行旋转 added by lzk
                    /*if(Orientation != "" && Orientation != 1){
                        alert('旋转处理');
                        switch(Orientation){
                            case 6://需要顺时针(向左)90度旋转
                                alert('需要顺时针(向左)90度旋转');
                                rotateImg(this,'left',canvas);
                                break;
                            case 8://需要逆时针(向右)90度旋转
                                alert('需要顺时针(向右)90度旋转');
                                rotateImg(this,'right',canvas);
                                break;
                            case 3://需要180度旋转
                                alert('需要180度旋转');
                                rotateImg(this,'right',canvas);//转两次
                                rotateImg(this,'right',canvas);
                                break;
                        }       
                    }*/

                    /*var mpImg = new MegaPixImage(image);
                    mpImg.render(canvas, {
                        maxWidth: 800,
                        maxHeight: 1200,
                        quality: 0.8,
                        orientation: Orientation
                    });
                    base64 = canvas.toDataURL("image/jpeg", 0.8);*/
                }else if (navigator.userAgent.match(/Android/i)) {// 修复android
                    /*var encoder = new JPEGEncoder();
                    base64 = encoder.encode(ctx.getImageData(0, 0, expectWidth, expectHeight), 80);*/
                }else{
                    //alert(Orientation);
                    /*if(Orientation != "" && Orientation != 1){
                        //alert('旋转处理');
                        switch(Orientation){
                            case 6://需要顺时针(向左)90度旋转
                                alert('需要顺时针(向左)90度旋转');
                                rotateImg(this,'left',canvas);
                                break;
                            case 8://需要逆时针(向右)90度旋转
                                alert('需要顺时针(向右)90度旋转');
                                rotateImg(this,'right',canvas);
                                break;
                            case 3://需要180度旋转
                                alert('需要180度旋转');
                                rotateImg(this,'right',canvas);//转两次
                                rotateImg(this,'right',canvas);
                                break;
                        }       
                    }*/
                    /*var mpImg = new MegaPixImage(image);
                    mpImg.render(canvas, {
                        maxWidth: 800,
                        maxHeight: 1200,
                        quality: 0.8,
                        orientation: Orientation
                    });

                    base64 = canvas.toDataURL("image/jpeg", 0.8);*/
                }
                var imgName = uploadImage(base64);
                //alert("img===="+imgName);

                $(".overlayer").css("display","none");
                if($(fileObj).attr("flag")=="myImage"){
                    arr[0]=base64;
                    $("#myImage").attr("src", base64);
                    $("#myImage").css("opacity","1");
                    ajax_pram.img0 = imgName;
                }else{
                    arr[1]=base64;
                    $("#myImage1").attr("src", base64);
                    $("#myImage1").css("opacity","1");
                    ajax_pram.img1 = imgName;
                    }
                    input_validate();

            };
        };
        oReader.readAsDataURL(file);
    }
}


//对图片旋转处理 added by lzk
function rotateImg(img, direction,canvas) {
        //alert(img);
        //最小与最大旋转方向,图片旋转4次后回到原方向  
        var min_step = 0;  
        var max_step = 3;  
        //var img = document.getElementById(pid);  
        if (img == null)return;  
        //img的高度和宽度不能在img元素隐藏后获取,否则会出错  
        /*var height = img.height;  
        var width = img.width;  */
        var height = canvas.height;  
        var width = canvas.width;  
       // alert(width+','+height);
        //var step = img.getAttribute('step');  
        var step = 2;  
        if (step == null) {  
            step = min_step;  
        }  
        if (direction == 'right') {  
            step++;  
            //旋转到原位置,即超过最大值  
            step > max_step && (step = min_step);  
        } else {  
            step--;  
            step < min_step && (step = max_step);  
        }  
        //img.setAttribute('step', step);  
        /*var canvas = document.getElementById('pic_' + pid);  
        if (canvas == null) {  
            img.style.display = 'none';  
            canvas = document.createElement('canvas');  
            canvas.setAttribute('id', 'pic_' + pid);  
            img.parentNode.appendChild(canvas);  
        }  */
        //旋转角度以弧度值为参数  
        var degree = step * 90 * Math.PI / 180;  
        var ctx = canvas.getContext('2d');  
        switch (step) {  
            case 0:  
                canvas.width = width;  
                canvas.height = height;  
                ctx.drawImage(img, 0, 0);  
                break;  
            case 1:  
                canvas.width = height;  
                canvas.height = width;  
                ctx.rotate(degree);  
                ctx.drawImage(img, 0, -height);  
                break;  
            case 2:  
                canvas.width = width;  
                canvas.height = height;  
                ctx.rotate(degree);  
                ctx.drawImage(img, -width, -height);  
                break;  
            case 3:  
                canvas.width = height;  
                canvas.height = width;  
                ctx.rotate(degree);  
                ctx.drawImage(img, -width, 0);  
                break;  
        }  
    }  

/** 记录上传数据 */
function uploadImage(imageData) {
    if (imageData == undefined) {
        alert('没有要上传的图片');
        return false;
    }
    var result;
    $.ajax({
        type: "post",
        url: 'http://upload.domain.cn/index.php?r=v1/certificates/saveimg',
        data: {'baseStr':imageData},
        async:false,
        success: function(data) {
            result = data
        }
    });
    return result;
}

如果使用过程中有问题,请留言反馈。csdn代码排版始终不舒服,请大家包涵!

Layui中的table通过函数控制数据加载刷新

mikel阅读(1145)

            function usable(id, usable) {
                $.post('/Supplier/changeusable', { id: id, usable: usable }, function (result) {
                    if (result.status) {                        
                        layer.alert("更新成功", { icon: 6 });
                         layui.use(['table', 'element'], function () {
                            layui.table.reload('datatable');
                         });   

                    } else {
                        layer.alert(result.message);
                    }
                },'json');
            }

[Delphi-码农]Delphi EXCEL导入 - CSDN博客

mikel阅读(1408)

来源: [Delphi-码农]Delphi EXCEL导入 – CSDN博客

 

整理过的

  1. procedure TParameterALterFines.btnImportExcelClick(Sender: TObject);
  2. const
  3.   BeginRow = 2; BeginCol = 1;
  4. var
  5.   Excel: OleVariant;
  6.   iRow,iCol : integer;
  7.   xlsFilename: string;
  8.   ExcelRowCount  : integer;
  9. begin
  10.   OpenDialog1.Title := ‘请选择正确的excel文件’;
  11.   OpenDialog1.Filter := ‘Excel(*.xls)|*.xls’;
  12.   if OpenDialog1.Execute then
  13.     edit1.Text := OpenDialog1.FileName;
  14.   if (trim(edit1.Text) = then
  15.   begin
  16.     GetActiveWindow();
  17.     showmessage( ‘请选择正确的excel路径’);
  18.     exit;
  19.   end;
  20.   xlsFilename := trim(edit1.Text);
  21.   try
  22.     Excel := CreateOLEObject(‘Excel.Application’);
  23.   except
  24.     showmessage(‘excel没有安装’);
  25.     Exit;
  26.   end;
  27.   Excel.Visible := false;
  28.   Excel.WorkBooks.Open(xlsFilename);
  29.   ExcelRowCount := Excel.WorkSheets[1].UsedRange.Rows.Count;
  30.   //showmessage(inttoStr(ExcelRowCount));
  31.   try
  32.     iRow := BeginRow;
  33.     iCol := BeginCol;
  34.     while trim(Excel.WorkSheets[1].Cells[iRow,iCol].value) <>  do
  35.     //WHILE iRow<ExcelRowCount Do
  36.     begin
  37.       with ADOQuery1 do
  38.       begin
  39.         //Append;
  40.         //Fields[0].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol].value);
  41.         //Fields[1].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol+1].value);
  42.         //Fields[2].Asstring := trim(Excel.WorkSheets[1].Cells[iRow,iCol+2].value);
  43.           close;
  44.           SQL.clear;
  45.           SQL.Text:=‘insert into Temp (ID,Content) ‘;
  46.           sql.Text:=sql.Text+‘select   ‘+trim(Excel.WorkSheets[1].Cells[iRow,iCol].value);
  47.           sql.Text:=sql.Text+‘ , ‘+  trim(Excel.WorkSheets[1].Cells[iRow,iCol+1].value);
  48.           execsql;
  49.           iRow := iRow + 1;
  50.       end;
  51.     end;
  52.     Excel.Quit;
  53.     ADOQuery1.UpdateStatus ;
  54.   except
  55.     showmessage(‘导入数据出错’);
  56.     Excel.Quit;
  57.     //Exit;
  58.   end;
  59.   //MessageBox(GetActiveWindow(), ‘数据导入成功’, ‘提示信息’, MB_OK +  MB_ICONWARNING);
  60. end;

 

http://zhidao.baidu.com/link?url=hNhGnrIAKOWJEIICoZwdKtcof7zyj_LMp-PlV2xmjgRIdAKSWDRuPT3xxzqGdhSlZc6NXsKSckbPSOdk7cBJ2a

  1. 元接口部分引用 comobj 单元(uses )
  2. procedure TForm1.Button1Click(Sender: TObject);
  3. var excelx,excely : string;
  4. begin
  5.     try
  6.       ExcelApp := CreateOleObject(‘Excel.Application’);
  7.       WorkBook := ExcelApp.WorkBooks.Open(opendialog.FileName);//使用opendialog对话框指定
  8. //excel档路径
  9. ExcelApp.Visible := false;
  10.       ExcelRowCount := WorkBook.WorkSheets[1].UsedRange.Rows.Count;
  11.       for i := 1 to excelrowcount + 1 do
  12.       begin
  13.         excelx := excelapp.Cells[i,1].Value;
  14.         excely := excelapp.Cells[i,2].Value;
  15.         if ((excelapp.Cells[i,1].Value  = and (ExcelApp.Cells[i,2].Value = )) then
  16. //指定excel档的第 i 行 ,第 1,2(看情况而定)行如果为空就退出,这样的设定,最好是你的档案力这两行//对应数据库中不能为空的数据
  17.           exit
  18.         else
  19.         with query1 do
  20.         begin
  21.           close;
  22.           sql.clear;
  23. sql.add(insert into test(name,address) values(:name,:address));
  24. parambyname(‘name’).asstring := excelx;//excel档的第一列插入到test表的 name栏位;
  25. parambyname(‘address’).asstring := excely;//excel档的第二列插入到test表的 address 栏位;
  26. execsql;
  27.         end;
  28.       end;
  29.       finally
  30.         WorkBook.Close;
  31.         ExcelApp.Quit;
  32.         ExcelApp := Unassigned;
  33.         WorkBook := Unassigned;
  34.     end;
  35.   end;

 

Delphi Excel导入 的通用程序 .

  1. Delphi Excel导入 的通用程序 .
  2. 分类: delphi 20120924 18:19 127人阅读 评论(0) 收藏 举报
  3. exceldelphiintegerprocedure TForm1.btnClick(Sender: TObject);
  4. begin
  5.   OpenDialog1.Title := ‘请选择正确的excel文件’;
  6.   OpenDialog1.Filter := ‘Excel(*.xls)|*.xls’;
  7.   if OpenDialog1.Execute then
  8.   edit1.Text := OpenDialog1.FileName;
  9. end;
  10. procedure TForm1.btninClick(Sender: TObject);
  11. const
  12.   BeginRow = 2; BeginCol = 1;
  13. var
  14.   Excel: OleVariant;
  15.   iRow,iCol : integer;
  16.   xlsFilename: string;
  17. begin
  18. if (trim(edit1.Text) = then
  19.   begin
  20.   MessageBox(GetActiveWindow(), 请选择正确的excel路径’, MB_OK +
  21.   MB_ICONWARNING);
  22.   exit;
  23.   end;
  24.   xlsFilename := trim(edit1.Text);
  25.   try
  26.   Excel := CreateOLEObject(‘Excel.Application’);
  27.   except
  28.   Application.MessageBox(‘excel没有安装’‘提示信息’, MB_OK+MB_ICONASTERISK+MB_DEFBUTTON1+MB_APPLMODAL);
  29.   Exit;
  30.   end;
  31.   Excel.Visible := false;
  32.   Excel.WorkBooks.Open(xlsFilename);
  33.   try
  34.   iRow := BeginRow;
  35.   iCol := BeginCol;
  36.   while trim(Excel.WorkSheets[1].Cells[iRow,iCol].value) <>  do begin
  37.   with ADOQuery1 do begin
  38.   Append;
  39.   Fields[0].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol].value);
  40.   Fields[1].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol+1].value);
  41.   Fields[2].Asstring := trim(Excel.WorkSheets[1].Cells[iRow,iCol+2].value);
  42.   iRow := iRow + 1;
  43.   end;
  44.   end;
  45.   Excel.Quit;
  46.   ADOQuery1.UpdateStatus ;
  47.   except
  48.   Application.MessageBox(‘导入数据出错’‘提示信息’, MB_OK+MB_ICONASTERISK+MB_DEFBUTTON1+MB_APPLMODAL);
  49.   Excel.Quit;
  50.   end;
  51.   MessageBox(GetActiveWindow(), ‘数据导入成功’‘提示信息’, MB_OK +
  52.   MB_ICONWARNING);
  53. end;

Oracle 用户(user)和模式(schema)的区别 - 一路NET - 博客园

mikel阅读(935)

来源: Oracle 用户(user)和模式(schema)的区别 – 一路NET – 博客园

原文:Oracle 用户(user)和模式(schema)的区别

概述:

(一)什么Oracle叫用户(user):

A user is a name defined in the database that can connect to and access objects.

大意:Oracle用户是用连接数据库和访问数据库对象的。(用户是用来连接数据库访问数据库)。

(二)什么叫模式(schema):

A schema is a collection of database objects (used by a user.). Schema objects are the logical structures that directly refer to the database’s data.
大意:模式是数据库对象的集合。模式对象是数据库数据的逻辑结构。(把数据库对象用模式分开成不同的逻辑结构)。

(三)用户(user)与模式(schema)的区别:
Schemas and users help database administrators manage database security.
大意:用户是用来连接数据库对象。而模式用是用创建管理对象的。(模式跟用户在oracle 是一对一的关系。)

 


 

详解:

从官方的定义中,我们可以看出schema为数据库对象的集合。为了区分各个集合,我们需要给这个集合起个名字,这些名字就是我们在企业管理器的方案下看到 的许多类似用户名的节点,这些类似用户名的节点其实就是一个schema。

schema里面包含了各种对象如tables, views, sequences, stored procedures, synonyms, indexes, clusters, and database links。

 

一个用户一般对应一个schema,该用户的schema名等于用户名,并作为该用户缺省schema。这也就是我们在企业管理器的方案下看 到schema名都为数据库用户名的原因。而Oracle数据库中不能新创建一个schema,要想创建一个schema,只能通过创建一个用户的方法解决 (Oracle中虽然有create schema语句,但是它并不是用来创建一个schema的),在创建一个用户的同时为这个用户创建一个与用户名同名的schem并作为该用户的缺省 shcema。

即schema的个数同user的个数相同,而且schema名字同user名字一一 对应并且相同,所有我们可以称schema为user的别名,虽然这样说并不准确,但是更容易理解一些。

一个用户有一个缺省的schema,其schema名就等于用户名,当然一个用户还可以使用其他的schema。如果我们访问一个表时,没有指明该 表属于哪一个schema中的,系统就会自动给我们在表上加上缺省的sheman名。比如我们在访问数据库时,访问scott用户下的emp表,通过 select * from emp; 其实,这SQL语句的完整写法为select * from scott.emp。

在数据库中一个对象的完整名称为schema.object,而不属user.object。类似如果我们在创建对象时不指定该对象 的schema,在该对象的schema为用户的缺省schema。这就像一个用户有一个缺省的表空间,但是该用户还可以使用其他的表空间,如果我们在创 建对象时不指定表空间,则对象存储在缺省表空间中,要想让对象存储在其他表空间中,我们需要在创建对象时指定该对象的表空间。

在Android浏览器中通过WebView调用相机拍照/选择文件 上传到服务器 - CSDN博客

mikel阅读(1569)

来源: 在Android浏览器中通过WebView调用相机拍照/选择文件 上传到服务器 – CSDN博客

最近做的一个项目中,有这样一个要求,在浏览器中调用系统的拍照功能或者选择文件,然后将文件上传到服务器,类似修改头像。        简单而言,就是在一个html页面中有这样一段代码 <input class=”filePrew” type=”file” capture=”camera” accept=”image/*” name=”image”>

刚开始的时候,没有感觉很难的,因为在UC浏览器、系统自带的浏览器中都可以进行拍照/文件管理器选择,可是在自己所写的Activity中却不行。后来实在是没有思路了,就在网上找了一下,发现要        实现这种功能,都是在webview的WebChromeClient中覆盖掉openFileChooser方法,注意openFileChooser方法在WebChromeClient中有@hide标记。这里只管重写即可,下面将主要代码贴出来,做个记录

private ValueCallback<Uri> mUploadFile;  
    /**拍照/选择文件请求码*/  
    private static final int REQUEST_UPLOAD_FILE_CODE = 12343;  
    private void setWebChromeClient()  
    {  
        if (null != mMainWebView)  
        {  
            mMainWebView.setWebChromeClient(new WebChromeClient()  
            {  
                // Andorid 4.1+  
                public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture)  
                {  
                    openFileChooser(uploadFile);  
                }  
  
                // Andorid 3.0 +  
                public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType)  
                {  
                    openFileChooser(uploadFile);  
                }  
  
                // Android 3.0  
                public void openFileChooser(ValueCallback<Uri> uploadFile)  
                {  
                    // Toast.makeText(WebviewActivity.this, "上传文件/图片",Toast.LENGTH_SHORT).show();  
                    mUploadFile = uploadFile;  
                    startActivityForResult(Intent.createChooser(createCameraIntent(), "Image Browser"), REQUEST_UPLOAD_FILE_CODE);  
                }  
            });  
        }  
    }  
  
    private Intent createCameraIntent()  
    {  
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//拍照  
        //=======================================================  
        Intent imageIntent = new Intent(Intent.ACTION_GET_CONTENT);//选择图片文件  
        imageIntent.setType("image/*");  
        //=======================================================  
        return cameraIntent;  
    }  
    //最后在OnActivityResult中接受返回的结果  
    protected void onActivityResult(int requestCode, int resultCode, Intent data)  
    {  
        if (requestCode == REQUEST_UPLOAD_FILE_CODE && resultCode == RESULT_OK)  
        {  
            if (null == mUploadFile)  
            {  
                return;  
            }  
            Uri result = (null == data) ? null : data.getData();  
            if (null != result)  
            {  
                ContentResolver resolver = this.getContentResolver();  
                String[] columns = { MediaStore.Images.Media.DATA };  
                Cursor cursor = resolver.query(result, columns, null, null, null);  
                cursor.moveToFirst();  
                int columnIndex = cursor.getColumnIndex(columns[0]);  
                String imgPath = cursor.getString(columnIndex);  
                System.out.println("imgPath = " + imgPath);  
                if (null == imgPath)  
                {  
                    return;  
                }  
                File file = new File(imgPath);  
                   //将图片处理成大小符合要求的文件  
                result = Uri.fromFile(handleFile(file));  
                mUploadFile.onReceiveValue(result);  
                mUploadFile = null;       
            }  
        }  
        super.onActivityResult(requestCode, resultCode, data);  
    }  
    /**处理拍照/选择的文件*/  
    private File handleFile(File file)  
    {  
        DisplayMetrics dMetrics = getResources().getDisplayMetrics();  
        BitmapFactory.Options options = new Options();  
        options.inJustDecodeBounds = true;  
         BitmapFactory.decodeFile(file.getAbsolutePath(), options);  
        int imageWidth = options.outWidth;  
        int imageHeight = options.outHeight;  
        System.out.println("  imageWidth = " + imageWidth + " imageHeight = " + imageHeight);  
        int widthSample = (int) (imageWidth / (dMetrics.density * 90));  
        int heightSample = (int) (imageHeight / (dMetrics.density * 90));  
        System.out.println("widthSample = " + widthSample + " heightSample = " + heightSample);  
        options.inSampleSize = widthSample < heightSample ? heightSample : widthSample;  
        options.inJustDecodeBounds = false;  
        Bitmap newBitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);  
        System.out.println("newBitmap.size = " + newBitmap.getRowBytes() * newBitmap.getHeight());  
        File handleFile = new File(file.getParentFile(), "upload.png");  
        try  
        {  
            if (newBitmap.compress(CompressFormat.PNG, 50, new FileOutputStream(handleFile)))  
            {  
                System.out.println("保存图片成功");  
            }  
        }  
        catch (FileNotFoundException e)  
        {  
            e.printStackTrace();  
        }  
  
        return handleFile;  
  
    }  

这样就可以在WebView中上传文件了。记得要添加相应的权限!
参考:http://developer.Android.com/about/versions/Android-3.0.html

http://blog.sina.com.cn/s/blog_5749ead90101clrn.html

Android WebView中打开相机拍照和选择相册 - CSDN博客

mikel阅读(2558)

来源: Android WebView中打开相机拍照和选择相册 – CSDN博客

一般在项目中与js交互,可能会遇到上传文件图片等操作,避免不了一些坑,下面简单说一下,Android 在不同版本中webView调用相机,选择相册的方法是不一样的,3.0以下的调用

  1. public void openFileChooser(ValueCallback<Uri> uploadMsg)

3.0以上:

  1. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType)

4.4以下:

  1. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)

5.0以上:

  1. public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams)

这里有个漏洞,4.4.x的由于系统内核发生了改变,没法调用以上方法,现在仍然找不到解决办法,唯一的方法就是4.4直接使用手机浏览器打开,这个是可以的。

那下面具体的贴下代码:

继承自WebChromeClient,重写

//3.0++
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
    openFileChooserImpl(uploadMsg);
}

//3.0--
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
    openFileChooserImpl(uploadMsg);
}

//4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {

    openFileChooserImpl(uploadMsg);
}


@Override
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
    openFileChooserImplForAndroid5(filePathCallback);

    return true;
}

外层定义接收返回值:

private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadCallbackAboveL;

不同的版本调不同的方法:

private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
    mUploadCallbackAboveL = uploadMsg;
    dispatchTakePictureIntent();
}

//5.0以下的掉用
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
    mUploadMessage = uploadMsg;
    dispatchTakePictureIntent();
}

//拍照
private void dispatchTakePictureIntent() {
    selectImgDialog();

}

 

我下面就把代码全部贴出来吧:

   private void takePhoto() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
            Uri imageUri = null;
            try {
                imageUri = Uri.fromFile(createImageFile());
            } catch (IOException e) {
                e.printStackTrace();
            }
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
            startActivityForResult(takePictureIntent, FILECHOOSER_RESULTCODE);
        }
    }

    /**
     * 209.
     * 本地相册选择图片
     * 210.
     */
    private void chosePic() {
        Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT);
        String IMAGE_UNSPECIFIED = "image/*";
        innerIntent.setType(IMAGE_UNSPECIFIED); // 查看类型
        Intent wrapperIntent = Intent.createChooser(innerIntent, null);
        startActivityForResult(wrapperIntent, REQ_CHOOSE);
    }


    String mCurrentPhotoPath = null;
    String FileName = "forum";

    //创建文件夹包装图片
    private File createImageFile() throws IOException {
        File storageDir = new File(Util.getAppPath(getActivity()) + FileName);

        if (!storageDir.exists()) {
            storageDir.mkdirs();
        }
        storageDir = new File(Util.getAppPath(getActivity()) + FileName + "/", System.currentTimeMillis() + ".jpg");
        //保存当前图片路径
        mCurrentPhotoPath = storageDir.getAbsolutePath();
        return storageDir;
    }

    //onActivityResult回调
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == FILECHOOSER_RESULTCODE || requestCode == REQ_CHOOSE) {
            if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            if (mUploadCallbackAboveL != null) {
                onActivityResultAboveL(requestCode, data);
            } else if (mUploadMessage != null) {
                mUploadMessage.onReceiveValue(result);
                mUploadMessage = null;
            }
        }

    }

    //5.0以上版本,由于api不一样,要单独处理
//    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void onActivityResultAboveL(int requestCode, Intent data) {

        if (mUploadCallbackAboveL == null) {
            return;
        }
        Uri result = null;
        if (requestCode == FILECHOOSER_RESULTCODE) {
            File file = new File(mCurrentPhotoPath);
            Uri localUri = Uri.fromFile(file);
            Intent localIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, localUri);
            getActivity().sendBroadcast(localIntent);
            result = Uri.fromFile(file);

        } else if (requestCode == REQ_CHOOSE) {
            result = data.getData();
        }
        mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});
        mUploadCallbackAboveL = null;
        return;
    }

 

后补的代码:

private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
    mUploadCallbackAboveL = uploadMsg;
    dispatchTakePictureIntent();
}

//5.0以下的掉用
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
    mUploadMessage = uploadMsg;
    dispatchTakePictureIntent();
}

 

基本代码就是以上这些,selectImgDialog()方法是弹窗提示选择,关于这个弹窗布局的用法,之前写的一篇文章说的很清楚了(BottomSheet 的详解及注意事项),有疑问的欢迎提问。