In case anybody's wondering, here's a rough working solution. The element in the page has a left margin of 30%. After you scroll the content into view, you tap the txt
div element in the WebView
, and the PopupWindow
will display where the element is, relative to the WebView
.
src/main/your.package/MainActivity.java:
This attaches the javascript interface to the WebView with the name "Android".
public class MainActivity extends AppCompatActivity {
private WebView webview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webview = (WebView) findViewById(R.id.webview);
webview.getSettings().setJavaScriptEnabled(true);
webview.addJavascriptInterface(new MyJavaScriptInterface(this), "Android");
try {
String content = new Scanner(getAssets().open("sample.html")).useDelimiter("\\A").next();
webview.loadData(content, "text/html", null);
} catch (IOException e) {
e.printStackTrace();
}
}
private void managePopup(int px, int py) {
View view = MainActivity.this.getLayoutInflater().inflate(R.layout.view_popup, null);
PopupWindow popup = new PopupWindow(view,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popup.setOutsideTouchable(true);
popup.showAsDropDown(webview, px, -webview.getHeight() + py);
}
private class MyJavaScriptInterface {
private Context ctx;
public MyJavaScriptInterface(Context ctx) {
this.ctx = ctx;
}
@JavascriptInterface
public void showMenu(int x, int y) {
final int px = (int) (x * ctx.getResources().getDisplayMetrics().density);
final int py = (int) (y * ctx.getResources().getDisplayMetrics().density);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = new Runnable() {
@Override
public void run() {
managePopup(px, py);
}
};
mainHandler.post(myRunnable);
}
}
}
Kotlin version src/main/your.package/MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById<View>(R.id.webview) as WebView
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(MyJavaScriptInterface(this), "Android")
webView.loadUrl("file:///android_asset/sample.html")
}
private fun managePopup(px: Int, py: Int) {
val view = this@MainActivity.layoutInflater.inflate(R.layout.view_popup, null)
val popup = PopupWindow(
view,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
popup.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
popup.isOutsideTouchable = true
popup.showAsDropDown(webView, px, -webView!!.height + py)
}
private inner class MyJavaScriptInterface(private val ctx: Context) {
@JavascriptInterface
fun showMenu(x: Int, y: Int) {
val px = (x * ctx.resources.displayMetrics.density).toInt()
val py = (y * ctx.resources.displayMetrics.density).toInt()
val mainHandler = Handler(Looper.getMainLooper())
val myRunnable = Runnable { managePopup(px, py) }
mainHandler.post(myRunnable)
}
}
}
src/main/assets/sample.html:
Here is some sample HTML that gets loaded into the WebView
from the assets directory. It has a click listener on the div.
<html>
<style>
#txt {
margin-left: 30%;
margin-top: 400px;
margin-bottom: 1000px;
border: 1px red solid;
display: inline-block;
}
</style>
<body>
<div id="txt">some text</div>
<script>
var element = document.getElementById("txt");
element.addEventListener('click', function (event) {
var rect = element.getBoundingClientRect();
Android.showMenu(rect.left, rect.top + rect.height);
});
</script>
</body>
</html>
src/main/res/layout/view_popup.xml:
This is a sample menu. I'm not using PopupMenu
in this example, preferring PopupWindow#showAsDropDown(View,int,int)
instead.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#000">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#FFF"
android:text="Menu Item 1" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#FFF"
android:text="Menu Item 2" />
</LinearLayout>
Result:
Here is a GIF of it:
