Android:在ScrollView内的WebView中使用锚链接

hwazgwia  于 2023-05-05  发布在  Android
关注(0)|答案(3)|浏览(147)

我有锚链接(例如:<a href="#P12"/>)在我的HTML中,我加载到WebView中,这个WebView在NestedScrollView中。当我暂时禁用NestedScrollView,因为每个人都建议这样做时,锚链接不能正常工作,这些锚链接工作得很好。然而,我的布局确实需要这个NestedScrollView,因为有多种原因:
1.它有一个CollapsingToolbarLayout,需要设置app:layout_behavior="@string/appbar_scrolling_view_behavior;

  1. WebView的上方和下方都有布局元素;
    1.这些元素必须作为一个整体一起滚动。
    预期的行为是,当点击锚链接时,内容跳转到网页的正确部分。实际的行为是,内容跳到一些随机的地方,你不能再滚动整个页面(顶部或底部被切断)。

是否有可能使这些锚链接在我的WebView中工作,该WebView位于NestedScrollView中?

在StackOverflow上,我发现了与此类似的one question,但不幸的是,它没有答案,而且它来自2017年,所以我非常怀疑有人会很快回答它。

ippsafx7

ippsafx71#

因为当我偶然发现一个没有答案的问题时,我会感到沮丧,所以我会回答我自己的问题,这样别人就会知道我是如何解决这个问题的。
坏消息是,这个问题无法解决。至少在2021年还没有。
我工作的公司和客户决定做的是改变这个屏幕的整个实现。整个屏幕现在都是WebView,而不是只有屏幕的一部分是WebView。

epfja78i

epfja78i2#

我不能用上面提供的方法来做,所以我尝试了一种diff方法
1.启用JS与您的webview webView.getSettings().setJavaScriptEnabled(true);
1.添加并定义javascript接口关键字,以便以后可以从javascript访问它

//Adding Javascript interface so that javascript can access annotated functions in this Class
webView.addJavascriptInterface ( this , "android" );`

1.然后创建一个扩展WebViewClient的类
1.启用html5功能并添加一些javascript代码

private void loadWebViewDatafinal(WebView wv) {
   WebSettings ws=wv.getSettings();

   ws.setJavaScriptEnabled(true);
   ws.setAllowFileAccess(true);

 //this try block enables html5

try {
    Log.e("WEB_VIEW_JS", "Enabling HTML5-Features");
    Method m1=WebSettings.class.getMethod("setDomStorageEnabled", new Class[]{Boolean.TYPE});
    m1.invoke(ws, Boolean.TRUE);

    Method m2=WebSettings.class.getMethod("setDatabaseEnabled", new Class[]{Boolean.TYPE});
    m2.invoke(ws, Boolean.TRUE);

    Method m3=WebSettings.class.getMethod("setDatabasePath", new Class[]{String.class});
    m3.invoke(ws, "/data/data/" + mContext.getPackageName() + "/databases/");

    Method m4=WebSettings.class.getMethod("setAppCacheMaxSize", new Class[]{Long.TYPE});
    m4.invoke(ws, 1024 * 1024 * 8);

    Method m5= WebSettings.class.getMethod("setAppCachePath", new Class[]{String.class});
    m5.invoke(ws, "/data/data/" + mContext.getPackageName() + "/cache/");

    Method m6=WebSettings.class.getMethod("setAppCacheEnabled", new Class[]{Boolean.TYPE});
    m6.invoke(ws, Boolean.TRUE);

    Log.e("WEB_VIEW_JS", "Enabled HTML5-Features");
} catch (Exception e) {
    Log.e("WEB_VIEW_JS", "Reflection fail", e);
}

 //this javascipt code give me the offset from the top of the link which
 //i send to the android java class using the keyword 'android' which was defined above .

  wv.loadUrl("javascript:(function(){\n"+
    // anchor tag and perform in click on anchor click

        "var anchors= document.getElementsByTagName(\"a\");\n" +
        "\n" +
        "for( let i = 0;i < anchors.length;i+=1)\n" +
        "{\n" +
        "    let currentanchor=anchors[i];\n" +
        "    \n" +
        "    currentanchor.onclick= function(){\n" +

                " if (this.hash !== \"\") {\n" +
                "\n" +
                "        event.preventDefault();\n" +
                "\n" +
                "        var hash = this.hash;\n" +
                "\n" +
                "        var div = document.getElementById(hash.replace('#',''));\n" +
                "\n" +

 //this is the line by which we can link javascript values from html and Java 

                "        android.OnAnchorClickScroll(div.offsetTop);\n"+
                "\n" +
                "                    };\n" +
                "\n" +

        "       }" +
        "}"+
  "})();");

}
1.现在,在类中添加一个函数,在这个函数中你定义了android关键字,并且可以访问scrollview来进行滚动

@JavascriptInterface
    public void OnAnchorClickScroll(float pos) {
       GlobalClassForFunctions.getInstance ( ).PrintMessageOnTheConsole ( "THIS IS Anchor pos --" + pos );
       int offsetInt = (int)getDensityIndependentPixels(pos, mContext);
       scrollView.smoothScrollTo(0,webView.getTop()+offsetInt);

   }

这就是我们使用的函数

// this function is to be used whenever you are getting pixels from javascript and want them to be converted in android dp
//We need this, because the measured pixels in the WebView don't use density of the screen
public float getDensityIndependentPixels(float pixels, Context context){
    return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            pixels,
            context.getResources().getDisplayMetrics()
    );
}
bnl4lu3b

bnl4lu3b3#

这是我的工作解决方案。单击锚链接将适当滚动ScrollView。单击“上一步”将滚动回顶部。

class FirstFragment : Fragment() {

    private var _binding: FragmentFirstBinding? = null

    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        _binding = FragmentFirstBinding.inflate(inflater, container, false)
        return binding.root

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val wvClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                if (url != null && url.contains("#")) {
                    val anchor = url.substring(url.lastIndexOf("#") + 1)
                    view?.evaluateJavascript(
                        "(function() { " +
                                "var element = document.getElementById('$anchor');" +
                                "if (element) {" +
                                "    var rect = element.getBoundingClientRect();" +
                                "    return rect.top;" +
                                "}" +
                                "return -1;" +
                                "})();"
                    ) { value ->
                        val anchorY = value.toFloat().toInt()
                        if (anchorY != -1) {
                            // scroll to to anchor target
                            binding.svContent.scrollTo(0, anchorY.toPx())
                        }
                    }
                } else {
                    // Scroll to top
                    binding.svContent.scrollTo(0, 0)
                }
            }
        }

        with (binding.myWebview) {
            settings.javaScriptEnabled = true
            webViewClient = wvClient
            loadUrl("https://html.com/anchors-links/")
        }
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        val callback: OnBackPressedCallback =
            object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                    if (binding.myWebview.canGoBack()){
                        binding.myWebview.goBack()
                    }
                }
            }
        requireActivity().onBackPressedDispatcher.addCallback(
            this,
            callback
        )
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

fun Int.toPx() : Int = (this * Resources.getSystem().displayMetrics.density).toInt()

相关问题