29

遗憾的是,科学和数学在 Android 平台上从未得到足够的重视。也许这是一个简单的问题,但我完全是 javascript 的花花公子,所以我很难理解如何解决这个问题。

我想做的很简单:我只需要使用渲染数学方程。它可以是 MathJax 或 JqMath 或任何小而快的东西。如果我必须在 webview 中执行此操作,如何在同一个 webview 中显示段落中的纯文本和格式化方程?

(任何人都提出了其他显然有效的方法,我也会认为这是解决方案)

到目前为止我做了什么:

我开始使用 MathJax 将公式放入 webview (我不知道那里是否有任何成功的 jqMath 用例?有人愿意分享他们的经验吗?)我可以像独立的MathJax App一样重现所有功能。在该应用程序中,用户在 EditText 字段中键入字符串,一旦用户按下按钮进行确认,MathJax 将在 Latex 中呈现公式。

我不需要用户确认。我认为这应该更容易,因为没有用户交互,但不知何故,方程式永远不会显示。我知道我一定遗漏了一些东西,因为独立的 MathJax 中的代码是用于用户输入的。我应该修改代码的哪一部分以直接从字符串渲染方程,例如 \sqrt{b^2-4ac} ?这是 webview 的初始标题:

WebView w = (WebView) findViewById(R.id.webview);
w.getSettings().setJavaScriptEnabled(true);
w.getSettings().setBuiltInZoomControls(true);
w.loadDataWithBaseURL("http://bar", "<script type='text/x-mathjax-config'>"
                        +"MathJax.Hub.Config({ showMathMenu: false, "
                        +"jax: ['input/TeX','output/HTML-CSS'], "
                        +"extensions: ['tex2jax.js'], " 
                        +"TeX: { extensions: ['AMSmath.js','AMSsymbols.js',"
                        +"'noErrors.js','noUndefined.js'] } "
                        +"});</script>"
                        +"<script type='text/javascript' "
                        +"src='file:///android_asset/MathJax/MathJax.js'"
                        +"></script><span id='math'></span>","text/html","utf-8","");

以下是用户在输入edittext后按下按钮时原始webview的加载方式:

WebView w = (WebView) findViewById(R.id.webview);
EditText e = (EditText) findViewById(R.id.edit);
w.loadUrl("javascript:document.getElementById('math').innerHTML='\\\\["
                  +doubleEscapeTeX(e.getText().toString())
                  +"\\\\]';");
w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");

private static String doubleEscapeTeX(String s) {
    String t="";
    for (int i=0; i < s.length(); i++) {
        if (s.charAt(i) == '\'') t += '\\';
        if (s.charAt(i) != '\n') t += s.charAt(i);
        if (s.charAt(i) == '\\') t += "\\";
    }
    return t;
}

我试图用硬编码的字符串替换

w.loadUrl("javascript:document.getElementById('math').innerHTML='\\\\["
                  +doubleEscapeTeX("sample string")
                  +"\\\\]';");

但它不起作用,文本"sample string"甚至不会出现在 webview 中。

[解决方案] 见乔的回答

为了详细说明他的答案,这是一个关于如何以纯文本设置句子,然后以显示形式显示格式化方程式的示例(全部在单个 webview 中):

将上面的最后一行替换为

+"></script><span id='math'></span>","text/html","utf-8","");

+"></script><span id='text'>This is plain text.</span> <span id='math'></span>", "text/html", "utf-8", "");

然后打电话

w.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
        if (!url.startsWith("http://bar"))
            return;
        w.loadUrl("javascript:document.getElementById('math').innerHTML='\\\\["
                    +doubleEscapeTeX("\\sqrt{b^2-4ac}") + "\\\\]';");
        w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
    }
});

这将设置一个字符串这是普通字体的纯文本富+酒吧,并在 webview 中居中显示公式。

编辑(Android 4.4 问题)

从 Android KitKat 开始,您必须将所有行替换loadUrl(...)evaluateJavascript(...). 但这仅适用于 KitKat(API 19 或更高版本),因此您需要先进行 SDK 版本检查。例如:

if (android.os.Build.VERSION.SDK_INT < 19) {
    w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
} else {
    w.evaluateJavascript("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);",null);
}

更新(从 MATHJAX 2.5 开始)

由于存在文件夹更改,以前的代码将无法在最新版本的 MathJax 上运行。此外,最小化本地 MathJax 大小的推荐方法现在是使用Grunt。可以在此处找到减小 MathJax 大小的模板。

假设您已经成功减小了 MathJax 的大小,并且假设输出的是 HTML-CSS,下面是一个可以加载 MathJax 的简单版本:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final WebView w = (WebView) findViewById(R.id.webview);
    w.getSettings().setJavaScriptEnabled(true);
    w.getSettings().setBuiltInZoomControls(true);

    String Url = "</style>"
                 + "<script type='text/x-mathjax-config'>"
                 + "  MathJax.Hub.Config({" + "    showMathMenu: false,"
                 + "             jax: ['input/TeX','output/HTML-CSS', 'output/CommonHTML'],"
                 + "      extensions: ['tex2jax.js','MathMenu.js','MathZoom.js', 'CHTML-preview.js'],"
                 + "         tex2jax: { inlineMath: [ ['$','$'] ], processEscapes: true },"
                 + "             TeX: {" + "               extensions:['AMSmath.js','AMSsymbols.js',"
                 + "                           'noUndefined.js']" + "             }"
                 + "  });"
                 + "</script>"
                 + "<script type='text/javascript' src='file:///android_asset/MathJax/MathJax.js'>"
                 + "</script>"
                 + "<p style=\"line-height:1.5; padding: 16 16\" align=\"justify\">"
                 + "<span >";

    // Demo display equation
    url += "This is a display equation: $$P=\frac{F}{A}$$";

    url += "This is also an identical display equation with different format:\\[P=\\frac{F}{A}\\]";

    // equations aligned at equal sign
    url += "You can also put aligned equations just like Latex:";
    String align = "\\begin{aligned}"
                + "F\\; &= P \\times A \\\\ "
                + "&= 4000 \\times 0.2\\\\"
                + "&= 800\\; \\text{N}\\end{aligned}"; 
    url += align;

    url += "This is an inline equation \\sqrt{b^2-4ac}.";

    // Finally, must enclose the brackets
    url += "</span></p>";

    mWebView.loadDataWithBaseURL("http://bar", url, "text/html", "utf-8", "");
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (!url.startsWith("http://bar")) return;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                view.loadUrl(JAVASCRIPT);
            } else {
                view.evaluateJavascript(JAVASCRIPT, null);
            }
        }
    });
}

与乔斯的回答不同,不需要分隔文本或数学类型,MathJax 只会相应地格式化它们。

但是,似乎存在一个错误,即 KitKat 设备中的 webview 无法呈现大写字母“A”。欢迎任何知道方法的人为此提供解决方法。

4

6 回答 6

23

loadDataWithBaseURL()如果我正确理解您的问题,则问题似乎是由于在您调用附加loadUrl()s 以显示公式时MathJax 库尚未完成加载(来自调用)。最简单的解决方法是等待onPageFinished()回调进行调用。

例如,以下代码似乎在我的身上运行良好:

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final WebView w = (WebView) findViewById(R.id.webview);
    w.getSettings().setJavaScriptEnabled(true);
    w.getSettings().setBuiltInZoomControls(true);
    w.loadDataWithBaseURL("http://bar", "<script type='text/x-mathjax-config'>" 
            + "MathJax.Hub.Config({ "
            + "showMathMenu: false, " 
            + "jax: ['input/TeX','output/HTML-CSS'], " 
            + "extensions: ['tex2jax.js'], "
            + "TeX: { extensions: ['AMSmath.js','AMSsymbols.js'," 
            + "'noErrors.js','noUndefined.js'] } "
            + "});</script>" 
            + "<script type='text/javascript' " 
            + "src='file:///android_asset/MathJax/MathJax.js'"
            + "></script><span id='math'></span>", "text/html", "utf-8", "");
    w.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (!url.startsWith("http://bar")) return;
            w.loadUrl("javascript:document.getElementById('math').innerHTML='\\\\["
                    + doubleEscapeTeX("sample string") + "\\\\]';");
            w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
        }
    });
}

更新:为了适应 WebView 中的额外输出,我建议在初始loadDataWithBaseURL()调用中添加 HTML 元素。因此,对于上面的示例,而不是最后的这一行:

            + "></script><span id='math'></span>", "text/html", "utf-8", "");

我们可以执行以下操作:

            + "></script><span id='text'>Formula:</span>"
            + "<span id='math'></span>", "text/html", "utf-8", "");

此外,如果您之后需要以交互方式更新该部分,您可以使用我们用于“数学”部分的相同机制,例如:

            w.loadUrl("javascript:document.getElementById('text').innerHTML='"
                    + newText + "';");

<span>如果您想拥有特定的样式/格式,您还可以使用其他 HTML 元素(而不是我们上面使用的)。

于 2013-06-14T21:41:21.900 回答
5

与其使用 javascript 和 javascript 库来呈现 LaTeX 客户端,不如执行以下操作?

  1. 编写一个服务器端函数,该函数接受一些 LaTeX 字符串并生成一个图像文件。您可以使用“开箱即用”的标准 LaTeX 命令行工具来执行此操作。(这似乎是你能够做的事情,问题在于让渲染发生在客户端。)

  2. 在您的网站/服务上创建一个端点,如下所示:
    GET /latex?f={image format}&s={latex string}

  3. 在您的网页上,使用一个简单的 HTML<img/>标签来呈现您的 LaTeX。例如:
    <img src="/latex?f=jpeg&s=f%40x%41%61x%942"/>
    f(x)=x^2方程渲染为 JPEG。

  4. (可选)为对创建一个简单的服务器端缓存,(f,s)以便您仅在对特定字符串(由任何客户端)发出的第一个请求时呈现特定的 LaTeX 字符串。作为第一个原型,您可能只有一个服务器目录,其中包含名为 的文件{hash(s)}.{f},其中hash只是一些哈希函数,例如SHA1.

这使您无需尝试使用 javascript 使所有内容都在客户端工作。而且,我会争辩(尽管有些人可能不同意),这要干净得多。您发送给客户的所有内容是:

  1. HTML<img/>标记
  2. 浏览器解析src属性时的实际图像内容

非常轻巧和快速!

于 2013-06-14T19:31:48.733 回答
2

“大写 A”问题是 Android WebView 上 MathJax 的一个已知错误。您可以用调整过的字体文件替换字体文件。该修复将在使用带有 TeX 字体的 HTML-CSS 输出时起作用。除了 MathJax,你还可以试试KaTeX,它不那么漂亮但速度更快。

实际上我已经将它们都打包到了一个 Android 自定义视图中,看看MathView.

于 2015-12-02T10:29:04.053 回答
1

聚会迟到了,但我认为我有比乔更好的解决方案。原来的问题问:

如何在同一个 webview 中显示段落中的纯文本和格式化的等式?

在原始示例中,我们有:

w.loadUrl("javascript:document.getElementById('math').innerHTML='\\\\["
                  +doubleEscapeTeX("sample string")
                  +"\\\\]';");

你注意到\\\\[and了\\\\]吗?这四个反斜杠被 LaTeX 解释为只有一个,\[并且\]在 LaTeX中是一种简写, \begin{equation}... \end{equation}它产生一个显示的数学块。如果我们想要一段文本中包含一些方程式,我们需要摆脱\\\\[并使用 LaTeX 命令\(\). 具有段落数学和显示数学的示例的代码将是:

w.loadUrl("javascript:document.getElementById('math').innerHTML='"
   +doubleEscapeTeX(
           "This is a second degree equation \\( \\small ax^2+bx+c=0\\) and its "
          +"roots are \\[x=\\frac{-b\\pm \\sqrt{b^2-4ac}}{2a}. \\]"
    )+"';");

WebView 变为: 在此处输入图像描述 注意:

  • 我们只需要两个反斜杠而不是四个,这是因为我们在里面doubleEscapeTeX()并且这个函数复制了反斜杠。

  • <span id='math'>数学环境之外的文本也使用 HTML(在标签内)呈现。

  • LaTeX 字体比 html 字体大,因此\\small在每个数学环境中添加 a 会使它们的大小大致相同。在这里,我\\small仅将命令添加到第一个等式以显示差异。

于 2014-04-13T12:55:23.470 回答
0

jqMath 是一个类似于 MathJax 的 JavaScript 库,但更小、更简单、更快。如果你不需要所有的 LaTeX,你可能会喜欢它。它应该在 Android 上运行良好。

这只是一个建议,我不知道如果您使用 MathJax 可能会有什么错误或优势。

MathJax 比 jqMath 更大、更复杂,并且速度大约慢 5 倍

工作 JSFIDDLE:http: //jsfiddle.net/sinhayash/gWWB5/1/

因为\sqrt{b^2-4ac}你可以有这个代码:√(b^2-4ac)

您可以轻松地使用 javascript 中的字符串处理将字符串转换为 jqMath 格式并轻松显示

于 2013-06-17T07:20:01.980 回答
0

您可以使用以下代码在 android 中轻松使用数学公式。

public void setView(String data,WebView w){
    WebSettings webSettings = w.getSettings();
    webSettings.setJavaScriptEnabled(true);
    String path="file:///android_asset/v/";
    String js="<html><head>"
       + "<link rel='stylesheet' href='file:///android_asset/v/jqmath.css'>"
       + "<script src = 'file:///android_asset/v/jquery-3.0.0.min.js'></script>"
       + "<script src = 'file:///android_asset/v/jqmath-etc-0.4.3.min.js'></script>"
       + "<script src = 'http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'></script>"
       + "</head><body>"
       + "<script>var s ='"+data+"';M.parseMath(s);document.write(s);</script> </body>";
    w.loadDataWithBaseURL("", js, "text/html", "UTF-8", "");
}
于 2016-07-02T09:19:27.253 回答