【问题标题】:Codename One - BrowserComponent: use the back button to go back in history代号一 - BrowserComponent:使用后退按钮返回历史
【发布时间】:2017-09-01 23:20:01
【问题描述】:

更新

我尝试在 Android 设备上使用 BrowserComponent:如果我触摸设备的后退按钮,应用程序什么也不做(使用以下代码)。

在这个问题中,我复制了相关的代码部分。

我的问题是,是否有人可以为我提供一个工作代码示例,该代码在按下后退按钮时执行页面中声明的 javascript 函数(特别是转到前一页的函数)。请注意我需要setBrowserNavigationCallback(如下代码)。

如果有人可以更正代码以正确使用setURLHierarchy,我也很感激,因为它会使模拟器崩溃并且不会在真实设备上加载“/community.html”。

非常感谢您的帮助。


更新:这是我的代码的最后一个版本

public void start() {
    if (current != null) {
        current.show();
        return;
    }
    Form hi = new Form("Community", new BorderLayout());

    // Suppress Android native indicator
    Display.getInstance().setProperty("WebLoadingHidden", "true");

    // Create the BrowserComponent
    browser = new BrowserComponent();

    // Set user-agent
    browser.setProperty("useragent", appAgent);

    // Set the start page - TO CORRECT, it's messy
    if (Display.getInstance().isSimulator()) {
        // I added this condition because opening a local html page
        // causes that the Simulator crashes
        browser.setURL(startPage_development);
        Log.p("BrowserComponet setUrl: " + startPage_development);
    } else {

        try {
            if (!Display.getInstance().isSimulator()) {
                browser.setURLHierarchy("/community.html");
            } else {
                browser.setURLHierarchy("/testing.html");
            }
            Log.p("BrowserComponet setURLHierarchy successfully");
        } catch (Exception err) {
            Log.e(err);
            browser.setURL(startPage);
            Log.p("BrowserComponet setUrl: " + startPage);
        }
    }

    // Javascript Bridge for back command
    JavascriptContext context = new JavascriptContext(browser);
    // JSObject document = (JSObject) context.get("document");
    JSObject window = context.getWindow();
    hi.setBackCommand(new Command("Back") {
        @Override
        public void actionPerformed(ActionEvent evt) {
            //window.call("goBackButton()");
            window.call("eval", new Object[]{"history.go(-1);"});
        }
    });

    // Allow browsing only inside my domains
    browser.setBrowserNavigationCallback((url) -> {
        if (url.startsWith("javascript")
                || url.startsWith(url_Root_development)
                || url.startsWith(url_Root_production)
                || url.startsWith(loginRoot)) {
            return true; // the BrowserComponent should navigate
        } else {
            Display.getInstance().callSerially(() -> {
                // it opens the url with the native platform
                Boolean can = Display.getInstance().canExecute(url);
                if (can != null && can) {
                    Display.getInstance().execute(url);
                }
            });
            return false; // the BrowserComponent shouldn't navigate
        }
    });

    hi.add(BorderLayout.CENTER, browser);
    hi.show();
}

【问题讨论】:

    标签: codenameone


    【解决方案1】:

    使用 setBackCommand 覆盖后退按钮行为并设置您可能拥有的任何业务逻辑,包括对 JavaScript 代码的调用。

    【讨论】:

    • 感谢您的建议,但我需要更多帮助。我刚刚更新了问题,添加了我尝试过但不起作用的代码。
    • 什么不起作用?它仍然最小化吗?代码到达了吗?您可以通过按 Escape 在 Android 皮肤中重现此内容
    • 经过数小时的尝试和错误,我完全更新了问题和代码。按后退按钮对我的代码没有任何作用:您能阅读我的问题的更新版本吗?谢谢
    • 表示返回命令码到达。如果您在模拟器中放置一个断点,它应该可以工作。为什么不直接在浏览器组件上调用back()
    • 我建议归档问题。这不是一个问题的答案,它是一个咆哮,我怀疑除了我之外的任何人都会看到它。只需在代号一设置中使用“更新客户端库”即可解决其中一个问题,因为我们最近更新了资源文件格式,但我无法“回答您的答案”,所以这并没有真正的帮助。按下退出按钮应该会生成触发后退命令,但浏览器小部件可能正在抓取键输入,代号一只能走这么远...... Web 内容不像我们的代码那样可移植,如果它行为不端,那么对什么有限制我们可以做到。
    【解决方案2】:

    我感谢 Shai 的建议,但它们不足以获得工作代码和工作模拟器(在其中按 Esc 没有任何作用,而且我不理解在这种情况下使用断点的含义)。只是在浏览器组件上调用back() 不是我需要的,因为我需要使用更复杂的方法,即调用我编写的javascript 函数,使浏览器只有在Internet 连接可用时才返回(或者它显示一条消息,表明 Internet 不可用)。

    所以,这就是我尝试自己回答问题的原因,将问题分解为几个步骤。我花了几天时间和很多精力来解决一个如此简单的问题(调用 javascript 按下后退按钮),主要是因为缺乏文档(Codename One JS Bridge package 包含 API 中的几个示例,但不是这种情况)以及 Codename One 模拟器的崩溃(和奇怪的行为),如下文所述。

    我希望帮助其他人分享我所做的事情。

    第 1 步:使用 BrowserComponent 加载本地页面

    我用 Netbeans 创建了一个新的空 Codename One 项目,我给它命名为“myBrowser”,包名“it.galgani.demos.mybrowser”,主类名“MyBrowser”,主题“native”,模板“Hello”世界(裸露的骨头)”。

    我修改了默认的start() 方法:

       public void start() {
            if(current != null){
                current.show();
                return;
            }
            Form hi = new Form("MyBrowser", new BorderLayout());
    
            // Create the BrowserComponent
            BrowserComponent browser = new BrowserComponent();
    
            try {
                // Load a web page placed in the "html" package
                browser.setURLHierarchy("/index.html");
                Log.p("setURLHierarchy executed");
            } catch (IOException ex) {
                Log.e(ex);
            }
    
            hi.add(BorderLayout.CENTER, browser);
            hi.show();
        }
    

    然后我在“源包”中创建了一个名为“html”的新“Java 包”。在我的情况下,创建的文件夹是:“/home/francesco/NetBeansProjects/myBrowser/src/html”

    在 html 文件夹中,我创建了这个index.html(选择“新建文件”,类别“其他”,文件类型“HTML”,文件名“索引”):

    <!DOCTYPE html>
    <html>
        <head>
            <title>BrowserComponent setURLHierarchy test</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
        <body>
            <div>Hello world</div>
        </body>
    </html>
    

    然后我点击“清理和构建项目”(没有错误,很好),然后点击“运行项目”:它工作正常,我的“Hello world”显示正确。现在下一步...

    第 2 步:创建其他 html 页面

    我将 index.html 修改为:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
        <body>
            <a href="page1.html">Page 1</a><br />
            <a href="page2.html">Page 2</a><br />
            <a href="page3.html">Page 3</a><br />
        </body>
    </html>
    

    然后我用这段代码创建了page1.html、page2.html和page3.html(根据页面改变h1):

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
        <body>
            <h1>Page 1</h1>
        </body>
    </html>
    

    我在模拟器中都试过了,没问题。要从一个页面返回到上一个页面,在模拟器中有一个选项可以通过在页面上单击鼠标右键来访问。

    日志报告了两个java异常,第一个是模拟器打开的时候,第二个是关闭的时候,但是不影响模拟器的功能:

    java.io.UTFDataFormatException: malformed input around byte 64
        at java.io.DataInputStream.readUTF(DataInputStream.java:656)
        at java.io.DataInputStream.readUTF(DataInputStream.java:564)
        at com.codename1.ui.util.Resources.loadTheme(Resources.java:1270)
        at com.codename1.ui.util.Resources.openFileImpl(Resources.java:303)
        at com.codename1.ui.util.Resources.openFile(Resources.java:269)
        at com.codename1.ui.util.Resources.<init>(Resources.java:189)
        at com.codename1.ui.util.Resources.open(Resources.java:768)
        at com.codename1.ui.util.Resources.open(Resources.java:688)
        at com.codename1.impl.javase.JavaSEPort$4.run(JavaSEPort.java:1720)
        at com.codename1.ui.Display.processSerialCalls(Display.java:1056)
        at com.codename1.ui.Display.mainEDTLoop(Display.java:873)
        at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)
        at com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
    [EDT] 0:0:1,396 - Codename One revisions: 14b404a993fcb91cfe25e26ce4d64ee044952b39
    
    [EDT] 0:0:1,444 - setURLHierarchy executed
    Exception in thread "JavaFX Application Thread" java.lang.IncompatibleClassChangeError
        at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
        at com.sun.glass.ui.gtk.GtkApplication.lambda$null$49(GtkApplication.java:139)
        at java.lang.Thread.run(Thread.java:748)
    

    唯一真正的坏事是文本在使用 iPhone3gs.skin 时具有可读的大小,但在使用其他皮肤(如 SamsungS7.skin)时却非常小(非常不可读)。我不知道为什么,我希望文本在真实设备上的大小是正确的,并且具有可读性好的默认字体大小。

    第 3 步:将命令与返回按钮关联

    在这种情况下,为了简化和测试后退按钮,我没有使用 Javascript Bridge,而是使用 BrowserComponent 类的 back() 方法。我在接下来的步骤中尝试了 Javascript Bridge。我在 MyBrowser 的start() 方法的末尾(就在hi.show() 之前)添加了这段代码:

    hi.setBackCommand(new Command("BackButton") {
            @Override
            public void actionPerformed(ActionEvent evt) {
                browser.back();
            }
        });
    

    在模拟器中,硬件后退按钮是非反应性的,并且工具栏中没有显示后退箭头。经过几次尝试,我意识到setBackCommand方法是在Form类和Toolbar类中实现的,行为不同。这篇文章解释了区别: https://www.codenameone.com/blog/toolbar-back-easier-material-icons.html

    以下是此步骤的最终代码,在模拟器和我的真实 Android 设备(我在其中测试了硬件后退按钮和材料图标后退箭头)中都能正常工作:

       public void start() {
            if (current != null) {
                current.show();
                return;
            }
            Form hi = new Form("MyBrowser", new BorderLayout());
    
            // Create the BrowserComponent
            BrowserComponent browser = new BrowserComponent();
    
            try {
                // Load a web page placed in the "html" package
                browser.setURLHierarchy("/index.html");
                Log.p("setURLHierarchy executed");
            } catch (IOException ex) {
                Log.e(ex);
            }
    
            hi.add(BorderLayout.CENTER, browser);
    
            Command backCommand = new Command("BackButton") {
                @Override
                public void actionPerformed(ActionEvent evt) {
                    browser.back();
                }
            };
    
            hi.getToolbar().setBackCommand(backCommand);
            hi.show();
        }
    

    第 4 步:在 Javascript Bridge 中使用后退按钮

    我将backCommand 更改为如下调用一个简单的writeln 函数,但新代码只运行了几次,其他时候它使模拟器崩溃或者它什么也没做:

       // Javascript Bridge
        JavascriptContext context = new JavascriptContext(browser);
        JSObject document = (JSObject)context.get("document");
    
        Command backCommand = new Command("BackButton") {
            @Override
            public void actionPerformed(ActionEvent evt) {
                // browser.back();
                document.call("writeln", new Object[]{"Hello world"});
            }
        };
    

    我怀疑 JavascriptContext 始终未正确加载,但我不确定。日志对理解问题没有帮助:

    [EDT] 0:0:0,18 - setURLHierarchy executed
    #
    # A fatal error has been detected by the Java Runtime Environment:
    #
    #  SIGSEGV (0xb) at pc=0x00007f3a04f944d1, pid=3730, tid=0x00007f3a07cdf700
    #
    # JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
    # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode linux-amd64 compressed oops)
    # Problematic frame:
    # C  [libjfxwebkit.so+0x11334d1]  checkJSPeer(long, int, OpaqueJSValue*&, OpaqueJSContext const*&)+0x41
    #
    # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
    #
    # An error report file with more information is saved as:
    # /home/francesco/NetBeansProjects/myBrowser/hs_err_pid3730.log
    #
    # If you would like to submit a bug report, please visit:
    #   http://bugreport.java.com/bugreport/crash.jsp
    # The crash happened outside the Java Virtual Machine in native code.
    # See problematic frame for where to report the bug.
    #
    Java Result: 134
    

    经过(非常长的)尝试和错误列表来解决这种奇怪的行为,最后我以在模拟器和我的真实 Android 设备中正常工作的方式更改了代码(请注意,我不再使用JavascriptContext 类):

    public void start() {
        if (current != null) {
            current.show();
            return;
        }
        Form hi = new Form("MyBrowser", new BorderLayout());
    
        // Create the BrowserComponent
        BrowserComponent browser = new BrowserComponent();
    
        try {
            // Load a web page placed in the "html" package
            browser.setURLHierarchy("/index.html");
            Log.p("setURLHierarchy executed");
        } catch (IOException ex) {
            Log.e(ex);
        }
    
        hi.add(BorderLayout.CENTER, browser);
    
        // Create a command for the Back button
        Command backCommand = new Command("BackButton") {
            @Override
            public void actionPerformed(ActionEvent evt) {
                 browser.execute("document.writeln('Hello world!');");
            }
        };
    
        hi.getToolbar().setBackCommand(backCommand);
        hi.show();
    }
    

    诀窍是使用 BrowserComponent 的(很少记录)execute 方法,而不是 JavascriptContext + JSObject 类。 StackOverflow 问题中对此方法的简要说明:Clarification on Codename One's BrowserComponent execute(String javaScript) method

    第 5 步:使用我的 goBackButton() javascript 函数

    我已经在我打算在我的应用程序中使用的网页中实现了goBackButton() 函数。我只想在 javascript 引擎已加载该方法时才调用该方法,因为:«BrowserComponent.execute(String) 将在您进行调用时在浏览器的当前页面中执行提供 JS sn-p。如果您的 sn-p 引用了尚未加载的内容,则 javascript 将导致错误。» (citation)

    这就是我更改代码的原因:

    // Create a command for the Back button
    String jsCode = "function goBack() {                        "
            + "    if (typeof goBackButton == 'function') {     "
            + "        window.goBackButton();                   "
            + "    return 'I\\'m invoking goBackButton()';      "
            + "    } else {                                     "
            + "        window.history.go(-1);                   "
            + "        return 'goBackButton() is not available';"
            + "    }                                            "
            + "}                                                "
            + "goBack();                                        ";
    Command backCommand = new Command("BackButton") {
        @Override
        public void actionPerformed(ActionEvent evt) {
            Log.p(browser.executeAndReturnString(jsCode));
        }
    };
    

    为了做一个基本的测试,我在 page1.html 中实现了一个假的 goBackButton():

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <script>
                function goBackButton() {
                    // My js code
                    // ...
                    history.go(-1);
                }
            </script>
        </head>
        <body>
            <h1>Page 1</h1>
        </body>
    </html>
    

    然后我点击 page1.html 和 page2.html 上的后退按钮。结果日志符合预期:

    [EDT] 0:0:0,28 - setURLHierarchy executed
    [EDT] 0:0:4,395 - I'm invoking goBackButton()
    [EDT] 0:0:7,736 - goBackButton() is not available
    

    第6步:使用setBrowserNavigationCallback

    我使用setBrowserNavigationCallback 只允许在我的域内浏览:在我原来的错误代码中,我体验到如果我使用JavascriptContext + JSObject 类,那么我必须使用条件if(url.startsWith("javascript")),因为通过点击后退按钮调用 NavigationCallback(不要问我为什么,我不知道)。相反,使用BrowserComponent.execute(String) 我不需要检查 url 是否以“javascript”开头,因为 NavigationCallback 永远不会被 javascript 调用。

    我添加了两个指向 index.html 的链接来做一些检查:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
        <body>
            <a href="page1.html">Page 1</a><br />
            <a href="page2.html">Page 2</a><br />
            <a href="page3.html">Page 3</a><br />
            <a href="https://www.google.com/">WebPage 1 (domain not allowed)</a><br />
            <a href="https://www.utiu-students.net/">WebPage2 (domain allowed)</a>
        </body>
    </html>
    

    下面的代码看起来是正确的,但实际上每次我点击“WebPage 1”链接(即谷歌页面)时它都会使模拟器崩溃:

    public void start() {
        if (current != null) {
            current.show();
            return;
        }
        Form hi = new Form("MyBrowser", new BorderLayout());
    
        // Create the BrowserComponent
        BrowserComponent browser = new BrowserComponent();
    
        try {
            // Load a web page placed in the "html" package
            browser.setURLHierarchy("/index.html");
            Log.p("setURLHierarchy executed");
        } catch (IOException ex) {
            Log.e(ex);
        }
    
        hi.add(BorderLayout.CENTER, browser);
    
        // Create a command for the Back button
        String jsCode = "function goBack() {                        "
                + "    if (typeof goBackButton == 'function') {     "
                + "        window.goBackButton();                   "
                + "    return 'I\\'m invoking goBackButton()';      "
                + "    } else {                                     "
                + "        window.history.go(-1);                   "
                + "        return 'goBackButton() is not available';"
                + "    }                                            "
                + "}                                                "
                + "goBack();                                        ";
        Command backCommand = new Command("BackButton") {
            @Override
            public void actionPerformed(ActionEvent evt) {
                Log.p(browser.executeAndReturnString(jsCode));
            }
        };
    
        hi.getToolbar().setBackCommand(backCommand);
    
        // Allow browsing only inside my domains
        String myDomain1 = "127.0.0.1"; // for local server (using simulator)
        String myDomain2 = "utiu-students.net";
        String myDomain3 = "login.uninettunouniversity.net";
        browser.setBrowserNavigationCallback((url) -> {
            Log.p("URL loaded: " + url);
            String domain = ""; // the domain of the url loaded by BrowserComponent
            if (url.startsWith("http")) {
                try {
                    domain = (new URI(url)).getHost();
                    domain = domain.startsWith("www.") ? domain.substring(4) : domain;
                } catch (URISyntaxException ex) {
                    Log.e(ex);
                }
            }
            Log.p("Domain of the URL: " + domain);
            if (url.startsWith("file") 
                    || domain.equals(myDomain1)
                    || domain.equals(myDomain2)
                    || domain.equals(myDomain3)) {
                Log.p("the BrowserComponent can navigate");
                return true; // the BrowserComponent can navigate
            } else {
                Display.getInstance().callSerially(() -> {
                    // it opens the url with the native platform
                    Boolean can = Display.getInstance().canExecute(url);
                    if (can != null && can) {
                        Display.getInstance().execute(url);
                    }
                });
                Log.p("the BrowserComponent cannot navigate");
                return false; // the BrowserComponent cannot navigate
            }
        });
    
        hi.show();
    }
    

    这是日志:

    [JavaFX Application Thread] 0:0:2,86 - URL loaded: https://www.google.com/
    [JavaFX Application Thread] 0:0:2,87 - Domain of the URL: google.com
    [JavaFX Application Thread] 0:0:2,88 - the BrowserComponent cannot navigate
    [JavaFX Application Thread] 0:0:3,815 - URL loaded: https://www.google.it/?gfe_rd=cr&ei=avGmWa-YNKnS8AfqlIGoDw
    [JavaFX Application Thread] 0:0:3,815 - Domain of the URL: google.it
    [JavaFX Application Thread] 0:0:3,815 - the BrowserComponent cannot navigate
    #
    # A fatal error has been detected by the Java Runtime Environment:
    #
    #  SIGSEGV (0xb) at pc=0x00007f3ec48044cf, pid=5754, tid=0x00007f3ec7be4700
    #
    # JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
    # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode linux-amd64 compressed oops)
    # Problematic frame:
    # C  [libjfxwebkit.so+0xa9e4cf]  WebCore::ResourceLoader::willSendRequestInternal(WebCore::ResourceRequest&, WebCore::ResourceResponse const&)+0x53f
    #
    # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
    #
    # An error report file with more information is saved as:
    # /home/francesco/NetBeansProjects/myBrowser/hs_err_pid5754.log
    #
    # If you would like to submit a bug report, please visit:
    #   http://bugreport.java.com/bugreport/crash.jsp
    # The crash happened outside the Java Virtual Machine in native code.
    # See problematic frame for where to report the bug.
    #
    Java Result: 134
    

    这很糟糕。奇怪的是谷歌页面被加载了两次,我猜是因为重定向。如果将 Google 的链接替换为 Codename One 网站的链接,模拟器会停止崩溃……但它会打开页面!所以BrowserNavigationCallBack() 不起作用。

    因为这个问题,我做了两次检查。 第一个是对原生浏览器的支持:

    // Check if the native browser is supported (only for logging)
    if (BrowserComponent.isNativeBrowserSupported()) {
        Log.p("Native Browser Supported");
    } else {
        Log.p("Native Browser NOT Supported");
    }
    

    第二个是删除BrowserNavigationCallBack()的代码并替换为:

    browser.setBrowserNavigationCallback((new BrowserNavigationCallback() {
        @Override
        public boolean shouldNavigate(String url) {
            Log.p("URL: " + url);
            return false;
        }
    }));
    

    糟糕的结果是 CodenameOne 网站仍然打开:shouldNavigate 的返回值被模拟器忽略(这是一个错误吗?)。这是日志:

    [EDT] 0:0:0,44 - Native Browser Supported
    [EDT] 0:0:0,59 - setURLHierarchy executed
    [JavaFX Application Thread] 0:0:0,90 - URL: file:///home/francesco/NetBeansProjects/myBrowser/build/classes/html/index.html
    [JavaFX Application Thread] 0:0:2,535 - URL: https://www.codenameone.com/
    

    但是,经过几次尝试,我找到了一个小解决方法,我认为它适用于大多数用例。

    我在setBrowserNavigationCallback末尾添加了这段代码:

    Log.p("the BrowserComponent cannot navigate");
    if (Display.getInstance().isSimulator()) {
        // small workaround because the return value is ignored by the simulator
        browser.setURL("jar:///goback.html");
    }
    return false; // the BrowserComponent cannot navigate
    

    我把goback.html放在了html包的外面,在/src:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script>window.history.go(-1);</script>
        </head>
        <body>
            The link will be opened with an external browser.<br />
        <a href="javascript:window.history.go(-1);">Click here to go back</a>
        </body>
    </html>
    

    现在模拟器(显然)可以正常工作:它似乎没有打开指向不允许域的链接......实际上它打开了很短的时间,然后自动返回(并以外部浏览器,如我所愿)。

    还有一个非常好的消息:你还记得点击下面的链接会导致模拟器崩溃吗(我报告了上面的日志)?我的解决方法也解决了这个问题:没有崩溃,没有奇怪的行为,并且链接是在外部浏览器中打开的。

    <a href="https://www.google.com/">WebPage 1 (domain not allowed)</a><br />
    

    该应用程序在我真正的 Android 设备上也能正常运行。

    这是最终代码:

    public void start() {
        if (current != null) {
            current.show();
            return;
        }
        Form hi = new Form("MyBrowser", new BorderLayout());
    
        // Create the BrowserComponent
        BrowserComponent browser = new BrowserComponent();
    
        // Check if the native browser is supported (only for logging)
        if (BrowserComponent.isNativeBrowserSupported()) {
            Log.p("Native Browser Supported");
        } else {
            Log.p("Native Browser NOT Supported");
        }
    
        try {
            // Load a web page placed in the "html" package
            browser.setURLHierarchy("/index.html");
            Log.p("setURLHierarchy executed");
        } catch (IOException ex) {
            Log.e(ex);
        }
    
        hi.add(BorderLayout.CENTER, browser);
    
        // Create a command for the Back button
        String jsCode = "function goBack() {                        "
                + "    if (typeof goBackButton == 'function') {     "
                + "        window.goBackButton();                   "
                + "    return 'I\\'m invoking goBackButton()';      "
                + "    } else {                                     "
                + "        window.history.go(-1);                   "
                + "        return 'goBackButton() is not available';"
                + "    }                                            "
                + "}                                                "
                + "goBack();                                        ";
        Command backCommand = new Command("BackButton") {
            @Override
            public void actionPerformed(ActionEvent evt) {
                Log.p(browser.executeAndReturnString(jsCode));
            }
        };
    
        hi.getToolbar().setBackCommand(backCommand);
    
        // Allow browsing only inside my domains
        String myDomain1 = "127.0.0.1"; // for local server (using simulator)
        String myDomain2 = "utiu-students.net";
        String myDomain3 = "login.uninettunouniversity.net";
    
        browser.setBrowserNavigationCallback((url) -> {
    
            Log.p("URL loaded: " + url);
            String domain = ""; // the domain of the url loaded by BrowserComponent
            if (url.startsWith("http")) {
                try {
                    domain = (new URI(url)).getHost();
                    domain = domain.startsWith("www.") ? domain.substring(4) : domain;
                } catch (URISyntaxException ex) {
                    Log.e(ex);
                }
            }
            Log.p("Domain of the URL: " + domain);
            if (url.startsWith("file")
                    || domain.equals(myDomain1)
                    || domain.equals(myDomain2)
                    || domain.equals(myDomain3)) {
                Log.p("the BrowserComponent can navigate");
                return true; // the BrowserComponent can navigate
            } else {
                Display.getInstance().callSerially(() -> {
                    // it opens the url with the native platform
                    Boolean can = Display.getInstance().canExecute(url);
                    if (can != null && can) {
                        Display.getInstance().execute(url);
                    }
                });
                Log.p("the BrowserComponent cannot navigate");
                if (Display.getInstance().isSimulator()) {
                    // small workaround because the return value is ignored by the simulator
                    browser.setURL("jar:///goback.html");
                }
                return false; // the BrowserComponent cannot navigate
            }
    
        }
        );
    
        hi.show();
    }
    

    作为参考,这个(非常小的)浏览应用程序的最终完整源代码在这里: https://github.com/jsfan3/CodenameOne-Apps/tree/master/Browsing/myBrowser

    第 7 步:使用真实网页内容测试应用

    现在是时候使用真实内容测试应用了。我删除了 html 包中的文件并复制了我想要使用的文件。结果:我的 javascript 代码在模拟器中没有按预期运行(可能是因为 JavaFX 限制),但是该应用程序最初在我的真实 Android 设备中按预期运行良好。然后,几分钟后,硬件返回按钮停止工作,并且材料左箭头图标消失了......非常沮丧,杀死应用程序并重新启动手机并没有恢复返回按钮:我不得不删除并重新安装应用程序让它再次工作(我将在另一集中调查这个问题,目前我无法复制它......)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-31
      • 2015-10-11
      • 1970-01-01
      • 1970-01-01
      • 2020-05-12
      • 1970-01-01
      • 2015-10-13
      • 1970-01-01
      相关资源
      最近更新 更多