Android技术知识Android知识点和文章分享Android知识

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

2017-10-14  本文已影响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# 的某些语法习惯 😂

上一篇 下一篇

猜你喜欢

热点阅读