Reputation: 11035
In Android layout it has a RelativeLayout which contains a LinearLayout (ignored some other views in the layout).
< ...> //other layout
<NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fillViewport="false">
<RelativeLayout
android:id="@+id/view_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="0dp">
<LinearLayout
android:id="@+id/list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
/>
</RelativeLayout>
</NestedScrollView>
<...>//other layout
the list_container
could have a few views, one is webView.
What want to do is in the html loaded in the webview, there is a div element, so the android side could locate this div and float a view on top of that div element (the <div>
is treat as a location marker).
making it simpler, assuming there is only one webview in the list_container
, so the origin
of the webview view is same as the relativeLayout's left/top.
and there is some floating View that is child of the RelativeLayout (sibling of the list_container
), so the idea is set the floatingView's left/top margin to place the floatingView over of the <div>
element in the html.
here use the webview.evaluateJavascript to run a javascript, and get the element's location back through the callback(got from js's document.getElementById(elm.id).getBoundingClientRect(), after that, just apply the left/top to the floating view's margin.
val strJs = "(function() {\n" +
"var elementId = 'element_div_id';\n" +
"try {\n" +
" var elm = document.getElementById(elementId);\n" +
" var rect = document.getElementById(elm.id).getBoundingClientRect();\n" +
" return {\n" +
" 'elementId': elm.id,\n" +
" 'left': Math.floor(rect.left),\n" +
" 'top': Math.floor(rect.top)\n" +
" };\n" +
"} catch (e) {}\n" +
"return '';})();"
webview.evaluateJavascript(strJs) { data ->
if (!data.isNullOrBlank() && data.isNotEmpty()) {
try {
val obj: JSONObject = JSONObject(data)
val floatViewId = obj.optString("elementId")
val left = obj.optInt("left")
val top = obj.optInt("top")
val theFloatingView = lookupFloatingViewById(floatViewId)
var lp = (theFloatingView.layoutParams)
if (lp != null) {
if (lp.leftMargin != left || lp.topMargin != top) {
lp.setMargins(left, top, 0, 0)
theFloatingView.layoutParams = lp
}
}
} catch (ex: Throwable) {
}
}
}
But the result is that the floating view is not placed at the returned html <div>
element's left/top, it is off (on different device the off could be bigger or small) over different part of the html content.
Isn't it the webview and the html hosted in the webview both should have same origin (which actually same as the relativeLayout's lefty/top)? Why can't we use the left/top got from html dom's getBoundingClientRect() directly for the view's margin (for positioning the view)?
Upvotes: 1
Views: 682
Reputation: 11035
found a answer which solved similar problem. seems the Android view are on different coordinate than the html window. (could someone confirm or anyone has different opinion?)
the location return from getBoundingClientRect()
needs to remapped to android's coordinate.
final int px = (int) (x * ctx.getResources().getDisplayMetrics().density);
final int py = (int) (y * ctx.getResources().getDisplayMetrics().density);
Upvotes: 1