Контекстное меню / окно в WebView внутри PopupWindow приводит к сбою приложения?
У меня есть приложение с PopupWindow, содержащее WebView, которое открывает страницу Facebook, любое контекстное меню, например: автозаполнение текстовых полей в WebView или даже длительное нажатие, в котором должны отображаться параметры, которые пользователь может копировать / вставлять / вырезать, сразу же завершает работу приложения с следующая ошибка:
FATAL EXCEPTION: main
android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W@418cdab0 is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:700)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:345)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
at android.view.Window$LocalWindowManager.addView(Window.java:554)
at android.widget.PopupWindow.invokePopup(PopupWindow.java:1013)
at android.widget.PopupWindow.showAtLocation(PopupWindow.java:856)
at android.widget.PopupWindow.showAtLocation(PopupWindow.java:820)
at android.webkit.WebViewClassic$PastePopupWindow.show(WebViewClassic.java:971)
at android.webkit.WebViewClassic.showPasteWindow(WebViewClassic.java:7037)
at android.webkit.WebViewClassic.access$10300(WebViewClassic.java:235)
at android.webkit.WebViewClassic$PrivateHandler.handleMessage(WebViewClassic.java:11376)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4921)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
at dalvik.system.NativeStart.main(Native Method)
Обратите внимание: мое понимание проблемы состоит в том, что контекстное меню / окно фактически отображаются в области самого приложения, а не в WebView, использующем другое PopWindow (автоматически сгенерированное классом Core WebView), этот класс неправильно ссылается на контекст.
Мой код выглядит следующим образом:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/openpopup"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Open Popup Window" />
</LinearLayout>
Popup.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/background_light"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="1dp"
android:background="@android:color/darker_gray"
android:orientation="vertical" >
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="20dp"
android:orientation="vertical" >
<WebView
xmlns:ptr="http://schemas.android.com/apk/res-auto"
android:id="@+id/webviewActionView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="200dp"
android:minWidth="200dp"
android:scrollbars="none" >
</WebView>
<Button
android:id="@+id/dismiss"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Dismiss" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
MainActivity.java
import android.os.Bundle;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.PopupWindow;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button btnOpenPopup = (Button) findViewById(R.id.openpopup);
btnOpenPopup.setOnClickListener(new Button.OnClickListener() {
@SuppressLint("NewApi")
@Override
public void onClick(View arg0) {
LayoutInflater layoutInflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
View popupView = layoutInflater.inflate(R.layout.popup, null);
final PopupWindow popupWindow = new PopupWindow(popupView,
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
popupWindow.setTouchable(true);
popupWindow.setFocusable(true);
WebView popupWebview = (WebView) popupView.findViewById(R.id.webviewActionView);
popupWebview.loadUrl("https://m.facebook.com");
Button btnDismiss = (Button) popupView
.findViewById(R.id.dismiss);
btnDismiss.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
popupWindow.dismiss();
}
});
popupWindow.showAsDropDown(btnOpenPopup, 50, -30);
}
});
}
}
3 ответа
Как указано выше в моем ответе, вы можете использовать настраиваемое диалоговое окно для ваших требований.. я создал образец PopupDialog.. пожалуйста, проверьте и дайте мне знать.. если это решит вашу цель.. Автозаполнение и ContextMenus будут работать как положено..
MyActivity.java
package com.example.testapp;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button btnOpenPopup = (Button) findViewById(R.id.openpopup);
btnOpenPopup.setOnClickListener(new Button.OnClickListener() {
@SuppressLint("NewApi")
@Override
public void onClick(View arg0) {
final PopupDialog popupDialog = new PopupDialog(MyActivity.this);
popupDialog.setContentView(R.layout.popup);
WebView popupWebview = (WebView) popupDialog.findViewById(R.id.webviewActionView);
popupWebview.setWebChromeClient(new WebChromeClient());
popupWebview.setWebViewClient(new WebViewClient());
popupWebview.loadUrl("http://m.facebook.com");
Button btnDismiss = (Button) popupDialog
.findViewById(R.id.dismiss);
btnDismiss.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
popupDialog.dismiss();
}
});
// popupDialog.showAtLocation(50,50);
popupDialog.showAsDropDown(findViewById(R.id.openpopup));
}
});
}
}
PopupDialog.java
package com.example.testapp;
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public class PopupDialog extends Dialog{
private final Context mContext;
public PopupDialog(Context context) {
super(context);
mContext=context;
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
public void showAtLocation(int x,int y)
{
WindowManager.LayoutParams wmlp = getWindow().getAttributes();
wmlp.gravity = Gravity.TOP | Gravity.LEFT;
wmlp.x = x;
wmlp.y = y;
show();
}
public void showAsDropDown(View view)
{
float density = mContext.getResources().getDisplayMetrics().density;
WindowManager.LayoutParams wmlp = getWindow().getAttributes();
int[] location = new int[2];
view.getLocationInWindow(location);
wmlp.gravity = Gravity.TOP | Gravity.LEFT;
wmlp.x = location[0]+(int)(view.getWidth()/density);
wmlp.y = location[1]+(int)(view.getHeight()/density);
show();
}
}
редактировать: добавлен popup.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="400dp"
android:minWidth="200dp"
android:background="@android:color/background_light"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="1dp"
android:background="@android:color/darker_gray"
android:orientation="vertical" >
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="20dp"
android:orientation="vertical" >
<WebView
xmlns:ptr="http://schemas.android.com/apk/res-auto"
android:id="@+id/webviewActionView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="200dp"
android:minWidth="200dp"
android:scrollbars="none" >
</WebView>
<Button
android:id="@+id/dismiss"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Dismiss" />
</LinearLayout>
</LinearLayout>
Да, я думаю, что вы должны использовать имя вашей деятельности вместо "getBaseContext()". Я только что создал одну демонстрацию с вашим кодом. это нормально работает в моем компьютере. а когда вы получаете это исключение?
В вашем коде нет ничего плохого... похоже, это специфическая проблема для Android 4.1+. Webview также использует PopupWindow для отображения контекстного меню "Вставить" в поле ввода. Вложенные всплывающие окна через webview вызывают сбой WindowManager при добавлении в него нового представления.
Я пробовал Custom Dialog(вместо Popupwindow) со встроенным Webview, контекстное меню Вставить подходит в Android 4.1+.
Если вы не слишком конкретны в этом контекстном меню вставки, вы можете отключить его для конкретной версии Android, используя приведенный ниже код. Я пробовал это... Это предотвратит сбой приложения.
popupWebView.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (Build.VERSION.SDK_INT >= 16)
return true;
return false;
}
});
Также вы можете посмотреть класс Webview HitTestResult для отображения некоторых пользовательских контекстных меню в соответствии с вашими потребностями.