反正一番周折之后选择使用第三方的InAppWebView。看源码可以看出本质上是用了Platform调回原生平台的webview,但是性能就是比自己写的要好。
常见配置1. InAppWebView.initialOptions
最基本的配置。InAppWebViewGroupOptions里面有三种,我这边用到crossPlatform和android
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
applicationNameForUserAgent: AppConfigureManager().androidUserAgent,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
),
useShouldOverrideUrlLoading就是启用shouldOverrideUrlLoading的回调,具体作用百度applicationNameForUserAgent在已有的UserAgent后面直接拼信息,这样就不需要在后续时机取已有UA再做拼接了
useHybridComposition等于使用Flutter自带WebView时的
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
反正就带上就是了,忘了不带会出啥问题了
2. InAppWebView.gestureRecognizers
手势识别,根据注释(Flutter的WebView也有的字段),如果不添加会只能响应点击事件。实践下来的话的确如此。因为我们的WebView放在了一个可以左右滑的视图里,如果不捕捉垂直滑动手势会导致WebView自身很难响应到上下滑手势。
gestureRecognizers: [
Factory(() => VerticalDragGestureRecognizer()),
].toSet(),
3. InAppWebView.onWebViewCreated
onWebViewCreated: (InAppWebViewController controller) async{
_webViewControllerAndroid = controller;
_webViewControllerAndroid..addJavaScriptHandler(handlerName: 'flutter_bridge', callback: (args) async{
List inArgs = [];
for(var tmp in args.first) {
if(tmp is String) {
inArgs.add(tmp);
}
else {
inArgs.add('');
}
}
NativePageJumpChannel tmp= await NativePageJumpChannel().webViewBridge(inArgs);
if((tmp.callbackStr??'').length > 0) {
_webViewControllerAndroid.loadUrl(
urlRequest: URLRequest(url: Uri.parse(
tmp.callbackStr ?? '')));
}
});
要点在于InAppWebViewController提供的addJavaScriptHandler方法(Flutter的WebView不提供的),给JS提供了一个回调InAppWebView的方法。在JS中调用这个JS接口的方法:
window.flutter_inappwebview.callHandler('flutter_bridge',[参数])
flutter_inappwebview是固定的object,应该是提前注入的
flutter_bridge是自己起的handler名称
最后传参数被封装进数组args中
Flutter端在callback中来实现业务逻辑,这里是把桥接分发给原生安卓端处理
注:addJavaScriptHandler方法注明要在onWebViewCreated和onLoadStart中使用,应该是注入时机的考量
4.InAppWebView.onLoadStop
没啥好说的,可以在这时跑一个evaluateJavascript方法,就是简单的注入脚本,在网页加载完成之后,需要注意时机问题。
注:evaluateJavascript被标注为不要在onWebViewCreated和onLoadStart中使用,但实际上onLoadStart中使用也可能注入成功,但结果并不可控。
相册问题处理表象:使用InAppWebView打开了一个H5页面,点击按钮H5调用了系统自带的相册,这时候走的是InAppWebView自己的响应逻辑,打开了其自定义的图片选择器,但是选择图片/放弃选择图片之后并没有返回结果(返回图片),再次点击也无法打开相同的照片选择界面了,变成无响应。
通过报错找到了InAppWebView响应H5打开相册事件的地方
路径:flutter_inappwebview-5.3.2/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java
能找到onShowFileChooser、openFileChooser的实现,结果回调在onActivityResult
为了用上我们自己的打开相册逻辑,直接让openFileChooser/onShowFileChooser反射调用原生项目里的相册,回调也不走onActivityResult而是使用代理实现了原生里的回调
调用调整⬇️
protected void openFileChooser(ValueCallback filePathCallback, String acceptType) {
// startPhotoPickerIntent(filePathCallback, acceptType);
showFileChooser(filePathCallback, null, acceptType);
}
protected void openFileChooser(ValueCallback filePathCallback) {
// startPhotoPickerIntent(filePathCallback, "");
showFileChooser(filePathCallback, null, null);
}
protected void openFileChooser(ValueCallback filePathCallback, String acceptType, String capture) {
// startPhotoPickerIntent(filePathCallback, acceptType);
showFileChooser(filePathCallback, null, acceptType);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) {
// String[] acceptTypes = fileChooserParams.getAcceptTypes();
// boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
// Intent intent = fileChooserParams.createIntent();
// return startPhotoPickerIntent(filePathCallback, intent, acceptTypes, allowMultiple);
showFileChooser(null, filePathCallback, null);
return true;
}
随便看看⬇️
//改动 文件选择器逻辑
public class FileChooserHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
String methodName = method.getName();
if(methodName.compareTo("onSelectFinish") == 0) {
if(args.length == 2) {
final int errorCode = (int)args[0];
final List picPath = ((List)args[1])!= null ? (List)args[1] : Arrays.asList();
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : plugin.activity;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
boolean hasResult = false;
String filePath = picPath.size() > 0 ? picPath.get(0): null;
if(errorCode == 0) {//ISelectPIcObsv.SELECT_SUCC
hasResult = true;
File file = new File(filePath);
if(InAppWebViewFlutterPlugin.filePathCallbackLegacy != null) {
InAppWebViewFlutterPlugin.filePathCallbackLegacy.onReceiveValue(Uri.fromFile(file));
InAppWebViewFlutterPlugin.filePathCallbackLegacy = null;
}
if(InAppWebViewFlutterPlugin.filePathCallback != null) {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(new Uri[]{Uri.fromFile(file)});
InAppWebViewFlutterPlugin.filePathCallback = null;
}
}
else {
hasResult = false;
filePath = null;
if(InAppWebViewFlutterPlugin.filePathCallbackLegacy != null) {
InAppWebViewFlutterPlugin.filePathCallbackLegacy.onReceiveValue(null);
InAppWebViewFlutterPlugin.filePathCallbackLegacy = null;
}
if(InAppWebViewFlutterPlugin.filePathCallback != null) {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(null);
InAppWebViewFlutterPlugin.filePathCallback = null;
}
}
}
});
}
}
return result;
}
}
private void showFileChooser(ValueCallback filePathCallback, ValueCallback filePathCallback21, String acceptType) {
InAppWebViewFlutterPlugin.filePathCallbackLegacy = filePathCallback;
InAppWebViewFlutterPlugin.filePathCallback = filePathCallback21;
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : plugin.activity;
try {
///TODO
Class clz = Class.forName("com.ll.llgame.engine.image.local.PicChooseEngine");
Method getInstance = clz.getMethod("getInstance");
Object engine = getInstance.invoke(null);//PicChooseEngine
Class iSelectPicObsv = Class.forName("com.ll.llgame.engine.image.local.ISelectPIcObsv");
FileChooserHandler handler = new FileChooserHandler();
Object fileChooserHandlerObj = Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{iSelectPicObsv}, handler);
Method selectPic = clz.getMethod("selectPic",new Class[]{Context.class, iSelectPicObsv});
selectPic.invoke(engine, new Object[]{activity, fileChooserHandlerObj});
} catch (Exception e) {
e.printStackTrace();
}
}
public interface ISelectPIcObsv {
int SELECT_SUCC = 0;
int SELECT_CANCEL = 1;
int SELECT_FAIL = 2;
int SELECT_FAIL_CAMERA_GALLRY_RESULT_NOT_OK = 3;
int SELECT_FAIL_CAMERA_NULL_NOT_EXIST = 4;
int SELECT_FAIL_CALL_CAMERA_FAIL = 5;
int SELECT_FAIL_CLIP_PIC_FAIL = 6;
int SELECT_FAIL_CLIP_PIC_SAVE_FAIL = 7;
void onSelectFinish(int errorCode, List picPath);
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)