一个好用的WebView控件窗口组件

作者: 天兵公园 | 来源:发表于2017-10-14 16:46 被阅读0次

    在我们公司的一个项目中使用到了QQ微信微博等三方登陆,但是由于网站已经已成了这些登陆方式,为了简化app端的工作量以及最快的方式让app拥有三方登陆的功能,我们决定在app里使用web登陆的方式。

    早在之前我写过一篇文章介绍了一些简单的WebView控件的使用,不过WebView确实是很复杂的一个控件,这里说的复杂并不是说它有多难用,而是很多的状态的不确定性,在不同的手机上可能渲染的效果不一样等等,不过如果api level>=19问题就会相对好得多,所以如果webview需要大量用的话,不妨将api level设置大于19,或者使用Crosswalk,不过比较坑的是,Crosswalk已经不再维护了...

    不过眼下解决的问题是,app许多网页和本地代码交互的界面,如果保证不用放置太多的包含WebView的Activity才是关键,这里写了个简单的库 WebFrame,也许是重复造的轮子。

    WebFrame https://github.com/yahch/WebFrame

    用法:

    allprojects {
            repositories {
                ...
                maven { url 'https://www.jitpack.io' }
            }
    }
    
    dependencies {
                compile 'com.github.yahch:WebFrame:2017.09.27.2'
    }
    

    如果有需要在网页上调用的本地代码,需要继承WebFrameScriptInterface 这个抽象类。

    WebFrameSettings 是一个单例,用于对webview 的 activity 进行设置,它的成员如下:

    private String url;
    private HashMap<String, WebFrameScriptInterface> objs;
    private boolean noActionBar;
    

    第一个表示需要导航到的url,第二个表示这个页面和本地代码调用的对象类的集合,第三个设置是否需要ActionBar。

    在我的项目中,我点击 QQ 登录的图标后,导航至我们网站的 QQ 登录授权链接,对应在 Android 中的操作如下:

    Intent intentForQQLogin = new Intent(LoginActivity.this, WebFrameActivity.class);
    WebFrameSettings.instance.reset();
    WebFrameSettings.instance.setUrl("http://my.域名.com/oauth/qq?client=android");
    WebFrameSettings.instance.addObject("app", new WebInterface());
    WebFrameSettings.instance.setNoActionBar(true);
    startActivity(intentForQQLogin);
    

    可以看到所有交互传入的对象是 WebInterface 的实例,来看看 WebInterface 的内容。

    class WebInterface extends WebFrameScriptInterface {
        @JavascriptInterface
        public void oauth(String uid) {
            if (getWebFrameActivity() != null) getWebFrameActivity().finish();
        }
    }
    

    因为在我们网站授权登录后的回调时 app.oauth(qqopenid),所以方法签名一定要对应好。
    这里有个黑魔法是在调用方能关闭授权窗口的Activity,也就是能通过 getWebFrameActivity() 获取到打开的窗口的 “句柄”,然后进行操作。

    WebFrameScriptInterface 的设计如下:

    public abstract class WebFrameScriptInterface {
        private WebFrameActivity webFrameActivity;
    
        public WebFrameScriptInterface() {
        }
    
        public WebFrameActivity getWebFrameActivity() {
            return this.webFrameActivity;
        }
    
        public void setWebFrameActivity(WebFrameActivity webFrameActivity) {
            this.webFrameActivity = webFrameActivity;
        }
    }
    

    WebFrameScriptInterface 是一个抽象类,其实不写成抽象类也可以,因为也不需要子类重写或实现什么。它唯一作用就是保存着一个 WebFrame 对象实例,供 Java/JS 调用接口层使用。

    在 WebFrameScriptInterface 的 onCreate 方法中,会将当前的实例传递给抽象类的实现者。

    @SuppressLint("JavascriptInterface")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        ...
        
        HashMap<String, WebFrameScriptInterface> invokeObjects = WebFrameSettings.instance.getObjs();
        if (invokeObjects.size() > 0) {
            for (Map.Entry<String, WebFrameScriptInterface> entry : invokeObjects.entrySet()) {
                WebFrameScriptInterface ws = entry.getValue();
                ws.setWebFrameActivity(WebFrameActivity.this);
                webViewWebFrameModule.addJavascriptInterface(ws, entry.getKey());
            }
        }
        ...
    }
    

    到这里这个组件算是介绍完了,我是沿着我的思路是实现的,也可能网络上有更好的第三方库,写了多年的 C# 也可能还是保留有 C# 的某些语法习惯 😂

    相关文章

      网友评论

        本文标题:一个好用的WebView控件窗口组件

        本文链接:https://www.haomeiwen.com/subject/xewyyxtx.html