Flutter(三):实现Flutter代码调用Android原生代码(创建WebView Plugin)
本文实现flutter代码调用Android原生Webview(本文比较长)
1.新建一个Flutter项目
2.在项目根目录下右键new>module>flutter plugin
3.自动生成如下图所示项目结构
其中webview_plugin就是我们新建的WebView插件,名字可以自由命名。
展开webview_plugin文件夹的结构如下:
我们目前只关注Android的,所以我们只需要在android文件下写,其他不变,android文件下有个WebviewPlugin.java文件,以下是我修改完的代码:
/** * WebviewPlugin */public class WebviewPlugin implements MethodCallHandler { private Activity activity; private WebView webView; private Result result; /** * Plugin registration. */ public static void registerWith(Registrar registrar) { final MethodChannel channel = new MethodChannel(registrar.messenger(), "webview_plugin"); channel.setMethodCallHandler(new WebviewPlugin(registrar.activity())); } @TargetApi(Build.VERSION_CODES.ECLAIR_MR1) public WebviewPlugin(Activity activity) { this.activity = activity; webView=new WebView(activity); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setLoadWithOverviewMode(true); webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); webView.getSettings().setLoadsImagesAutomatically(true);// 加载网页中的图片 webView.getSettings().setUseWideViewPort(true); //设置使用视图的宽端口 webView.getSettings().setAllowFileAccess(true);// 可以读取文件缓存(manifest生效) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } } @Override public void onMethodCall(MethodCall call, Result result) { this.result=result; switch (call.method) { case "load": FrameLayout.LayoutParams params = buildLayoutParams(call); LinearLayout linearLayout=new LinearLayout(activity); linearLayout.setOrientation(LinearLayout.VERTICAL); final TextView titleView=new TextView(activity); titleView.setSingleLine(true); titleView.setEllipsize(TextUtils.TruncateAt.END); titleView.setTextColor(Color.parseColor("#FFFFFF")); titleView.setPadding(dp2px(activity,10),dp2px(activity,20),dp2px(activity,10),0); ViewGroup.LayoutParams titleViewParams= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT); titleView.setLayoutParams(titleViewParams); titleView.setText("我是标题"); titleView.setGravity(Gravity.CENTER); ViewGroup.LayoutParams layoutParams= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,dp2px(activity,70)); titleView.setLayoutParams(layoutParams); titleView.setBackgroundColor(Color.parseColor("#4876FF")); linearLayout.addView(titleView); linearLayout.addView(webView); activity.addContentView(linearLayout, params); webView.setWebViewClient(new MyWebViewClient(activity, (title, isError) -> titleView.setText(title))); webView.loadUrl(call.argument("url").toString()); break; } } public class MyWebViewClient extends WebViewClient { private final WebClientLoadListener loadListener; Activity activity; private boolean isError; public MyWebViewClient(Activity activity, WebClientLoadListener loadListener) { this.activity = activity; this.loadListener = loadListener; } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return super.shouldOverrideUrlLoading(view, url); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); isError = false; } @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); isError = true; } /** * 界面加载完后回调 * * @param view * @param url */ @Override public void onPageFinished(WebView view, String url) { String title = view.getTitle(); // 获取网页标题 loadListener.loadFinished(title, isError); super.onPageFinished(view, url); } } public interface WebClientLoadListener { void loadFinished(String title, boolean isError); } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) private FrameLayout.LayoutParams buildLayoutParams(MethodCall call) { Map rc = call.argument("rect"); FrameLayout.LayoutParams params; if (rc != null) { params = new FrameLayout.LayoutParams( dp2px(activity, rc.get("width").intValue()), dp2px(activity, rc.get("height").intValue())); params.setMargins(dp2px(activity, rc.get("left").intValue()), dp2px(activity, rc.get("top").intValue()), 0, 0); } else { Display display = activity.getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; int height = size.y; params = new FrameLayout.LayoutParams(width, height); } return params; } private int dp2px(Context context, float dp) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); }}
其中registerWith(Registrar registrar)就是注册插件的方法,这个不用改
public static void registerWith(Registrar registrar) { final MethodChannel channel = new MethodChannel(registrar.messenger(), "webview_plugin"); channel.setMethodCallHandler(new WebviewPlugin(registrar.activity())); }
最主要的是onMethodCall(MethodCall call, Result result)方法,以下call.method是约定的方法名,外面通过此方法与原生的进行通讯,在此plugin里我只约定了“load”这个方法,其他方法照写,我这个plugin比较简单,就一个LinnearLayout里包含一个Textview(标题)和WebView,详情代码见如下,最后执行activity.addContentView(linearLayout, params)来实现创建布局。
@Override public void onMethodCall(MethodCall call, Result result) { this.result=result; switch (call.method) { case "load": FrameLayout.LayoutParams params = buildLayoutParams(call); LinearLayout linearLayout=new LinearLayout(activity); linearLayout.setOrientation(LinearLayout.VERTICAL); final TextView titleView=new TextView(activity); titleView.setSingleLine(true); titleView.setEllipsize(TextUtils.TruncateAt.END); titleView.setTextColor(Color.parseColor("#FFFFFF")); titleView.setPadding(dp2px(activity,10),dp2px(activity,20),dp2px(activity,10),0); ViewGroup.LayoutParams titleViewParams= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT); titleView.setLayoutParams(titleViewParams); titleView.setText("我是标题"); titleView.setGravity(Gravity.CENTER); ViewGroup.LayoutParams layoutParams= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,dp2px(activity,70)); titleView.setLayoutParams(layoutParams); titleView.setBackgroundColor(Color.parseColor("#4876FF")); linearLayout.addView(titleView); linearLayout.addView(webView); activity.addContentView(linearLayout, params); webView.setWebViewClient(new MyWebViewClient(activity, (title, isError) -> titleView.setText(title))); webView.loadUrl(call.argument("url").toString()); break; } }
接下来就是写plugin包下的的WebviewPlugin.dart文件,这个文件才是相当于一个桥梁,连接着我们的Android代码,也提供了供flutter代码调用的方法,以下我们只声明了一个 launch(String url,Function callback, { Rect rect,}) 方法,url和callback(相当于java中的接口)和可选参数Rect(设置plugin的位置和大小),完成这几个文件,我们就基本完成了Plugin的构建了。
import 'dart:async';import 'dart:ui';import 'package:flutter/services.dart';class WebviewPlugin { static const MethodChannel _channel = const MethodChannel('webview_plugin'); static Future get platformVersion async { final String version = await _channel.invokeMethod('getPlatformVersion'); return version; } //声明plugin加载的方法,参数为url,callback回调,和可选参数Rect(控制)plugin的大小 Future launch( String url, Function callback, { Rect rect, }) async { Map args = { "url": url, }; if (rect != null) { args["rect"] = { "left": rect.left, "top": rect.top, "width": rect.width, "height": rect.height }; } final String result = await _channel.invokeMethod('load', args); if (callback != null) { callback(result); } }}
下面我们开始来示范如何调用我们完成的了的这个webview_plugin,以下看我们主项目中lib文件夹下的main.dart的代码
首先声明final flutterWebviewPlugin = new WebviewPlugin();
然后点击事件触发以下flutterWebviewPlugin.launch方法,参数依次是url,callbacl和Rect
flutterWebviewPlugin.launch( "https://blog.csdn.net/qq_16247851/article/details/81210771", (data) { setState(() { title = data; }); },rect: new Rect.fromLTWH(0.0, 0.0, MediaQuery.of(context).size.width, MediaQuery.of(context).size.height));
import 'dart:async';import 'package:flutter/material.dart';import 'package:webview_plugin/webview_plugin.dart';void main() => runApp(new MyApp());class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Webview', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), ); }}class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); @override initState() {}}class _MyHomePageState extends State { StreamSubscription _back; var title = "demo"; final flutterWebviewPlugin = new WebviewPlugin(); @override Widget build(BuildContext context) { return new Scaffold(body: new Center( child: new RaisedButton(onPressed: () { flutterWebviewPlugin.launch( "https://blog.csdn.net/qq_16247851/article/details/81210771", (data) { setState(() { title = data; }); }, rect: new Rect.fromLTWH(0.0, 0.0, MediaQuery.of(context).size.width, MediaQuery.of(context).size.height)); }), )); } @override void initState() { // TODO: implement initState super.initState(); }}
这样下来整个项目基本完成了,效果图如下(蓝色部分为头部,内容为网页的title,下面部分是webview):
更多相关文章
- Android(安卓)Context简介
- 手把手教你打造一个Material Design风格的App(一)
- Android中关于SQLite数据库的一些知识
- Android超精准计步器开发-Dylan计步
- Android(安卓)Activity生命周期
- 详细讲解Android(安卓)View的绘制流程
- android edittext 设置只允许输入整数,(设置输入类型)
- 如何加快Android(安卓)Studio 编译app 的速度
- PreferenceActivity-Android的设置界面