Kivy WebView Error: Cannot add to window, it already has a parent

Kivy WebView Error: Cannot add to window, it already has a parent

WebView代码

from kivy.uix.modalview import ModalView
from kivy.clock import Clock
from android.runnable import run_on_ui_thread
from jnius import autoclass, cast, PythonJavaClass, java_method

WebViewA = autoclass('android.webkit.WebView')
WebViewClient = autoclass('android.webkit.WebViewClient')
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
LinearLayout = autoclass('android.widget.LinearLayout')
KeyEvent = autoclass('android.view.KeyEvent')
ViewGroup = autoclass('android.view.ViewGroup')
DownloadManager = autoclass('android.app.DownloadManager')
DownloadManagerRequest = autoclass('android.app.DownloadManager$Request')
Uri = autoclass('android.net.Uri')
Environment = autoclass('android.os.Environment')
Context = autoclass('android.content.Context')
PythonActivity = autoclass('org.kivy.android.PythonActivity')


class DownloadListener(PythonJavaClass):
    #
    __javacontext__ = 'app'
    __javainterfaces__ = ['android/webkit/DownloadListener']

    @java_method('(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V')
    def onDownloadStart(self, url, userAgent, contentDisposition, mimetype,
                        contentLength):
        mActivity = PythonActivity.mActivity 
        context =  mActivity.getApplicationContext()
        visibility = DownloadManagerRequest.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
        dir_type = Environment.DIRECTORY_DOWNLOADS
        uri = Uri.parse(url)
        filepath = uri.getLastPathSegment()
        request = DownloadManagerRequest(uri)
        request.setNotificationVisibility(visibility)
        request.setDestinationInExternalFilesDir(context,dir_type, filepath)
        dm = cast(DownloadManager,
                  mActivity.getSystemService(Context.DOWNLOAD_SERVICE))
        dm.enqueue(request)


class KeyListener(PythonJavaClass):
    __javacontext__ = 'app'
    __javainterfaces__ = ['android/view/View$OnKeyListener']

    def __init__(self, listener):
        super().__init__()
        self.listener = listener

    @java_method('(Landroid/view/View;ILandroid/view/KeyEvent;)Z')
    def onKey(self, v, key_code, event):
        if event.getAction() == KeyEvent.ACTION_DOWN and\
           key_code == KeyEvent.KEYCODE_BACK: 
            return self.listener()
        

class WebView(ModalView):
    # https://developer.android.com/reference/android/webkit/WebView
    
    def __init__(self, url, enable_javascript = False, enable_downloads = False,
                 enable_zoom = False, width=None, height=None, **kwargs):
        super().__init__(**kwargs)
        self.url = url
        self.enable_javascript = enable_javascript
        self.enable_downloads = enable_downloads
        self.enable_zoom = enable_zoom
        self.webview = None
        self.enable_dismiss = True
        if width:
            self.width = width
        if height:
            self.height = height
        self.open()

    @run_on_ui_thread        
    def on_open(self):
        mActivity = PythonActivity.mActivity 
        webview = WebViewA(mActivity)
        webview.setWebViewClient(WebViewClient())
        webview.getSettings().setJavaScriptEnabled(self.enable_javascript)
        webview.getSettings().setBuiltInZoomControls(self.enable_zoom)
        webview.getSettings().setDisplayZoomControls(False)
        webview.getSettings().setAllowFileAccess(True) #default False api>29
        layout = LinearLayout(mActivity)
        layout.setOrientation(LinearLayout.VERTICAL)
        layout.addView(webview, self.width, self.height)
        mActivity.addContentView(layout, LayoutParams(-1,-1))
        webview.setOnKeyListener(KeyListener(self._back_pressed))
        if self.enable_downloads:
            webview.setDownloadListener(DownloadListener())
        self.webview = webview
        self.layout = layout
        try:
            webview.loadUrl(self.url)
        except Exception as e:            
            print('Webview.on_open(): ' + str(e))
            self.dismiss()  
        
    @run_on_ui_thread        
    def on_dismiss(self):
        if self.enable_dismiss:
            self.enable_dismiss = False
            parent = cast(ViewGroup, self.layout.getParent())
            if parent is not None: parent.removeView(self.layout)
            self.webview.clearHistory()
            self.webview.clearCache(True)
            self.webview.clearFormData()
            self.webview.destroy()
            self.layout = None
            self.webview = None
        
    @run_on_ui_thread
    def on_size(self, instance, size):
        if self.webview:
            params = self.webview.getLayoutParams()
            params.width = self.width
            params.height = self.height
            self.webview.setLayoutParams(params)

    def pause(self):
        if self.webview:
            self.webview.pauseTimers()
            self.webview.onPause()

    def resume(self):
        if self.webview:
            self.webview.onResume()       
            self.webview.resumeTimers()

    def downloads_directory(self):
        # e.g. Android/data/org.test.myapp/files/Download
        dir_type = Environment.DIRECTORY_DOWNLOADS
        context =  PythonActivity.mActivity.getApplicationContext()
        directory = context.getExternalFilesDir(dir_type)
        return str(directory.getPath())

    def _back_pressed(self):
        if self.webview.canGoBack():
            self.webview.goBack()
        else:
            self.dismiss()  
        return True
    
    def closeWebView(self, force=False, animation=True):
        self.dismiss(force=force, animation=animation)

显示 WebView

class App(APP):
    def build(self):
        #self.manager = ScreenManager()
        #login = Screen(name="login")
        #login.add_widget(LoginPage())
        #register = Screen(name="register")
        #register.add_widget(Register())
        #forgotLogin = Screen(name="forgot")
        #forgotLogin.add_widget(ForgotLogin())
        #chat = Screen(name="chat")
        #chat.add_widget(Chat())
        #self.manager.add_widget(login)
        #self.manager.add_widget(register)
        #self.manager.add_widget(forgotLogin)
        #self.manager.add_widget(chat)
        #self.manager.current = "login"
        return WebView(url="https://google.com/", enable_javascript=True, enable_zoom=True)

if __name__== "__main__":
   chat = App()
   chat.run()

我在 android 10 上编译和测试它强制关闭,在 运行 adb logcat 之后我得到:

05-29 23:37:14.410  1760  4712 D PowerManagerServiceEx: releaseWakeLockInternal: lock=42843022 [WindowManager], flags=0x0
05-29 23:37:14.469 27595 27630 I python  :  Traceback (most recent call last):
05-29 23:37:14.469 27595 27630 I python  :    File "/home/mike/kiv/.buildozer/android/app/main.py", line 66, in <module>05-29 23:37:14.469 27595 27630 I python  :    File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/app.py", line 949, in run
05-29 23:37:14.470 27595 27630 I python  :    File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/app.py", line 919, in _run_prepare
05-29 23:37:14.470 27595 27630 I python  :    File "/home/mike/kiv/.buildozer/android/app/main.py", line 26, in build
05-29 23:37:14.470 27595 27630 I python  :    File "/home/mike/kiv/.buildozer/android/app/browse.py", line 98, in __init__
05-29 23:37:14.470 27595 27630 I python  :    File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/uix/modalview.py", line 222, in open
05-29 23:37:14.470 27595 27630 I python  :    File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/core/window/__init__.py", line 1305, in add_widget
05-29 23:37:14.470 27595 27630 I python  :  kivy.uix.widget.WidgetException: Cannot add <browse.WebView object at 0xbedc50d0> to window, it already has a parent <__main__.App object at 0xbf7f1258>
05-29 23:37:14.470 27595 27630 I python  : Python for android ended.
05-29 23:37:14.521 27595 27622 E libEGL  : validate_display:91 error 3008 (EGL_BAD_DISPLAY)
05-29 23:37:14.521 27595 27622 I chatty  : uid=10479(ga.animechat.chatapp.ga.animechat.chatapp) imechat.chatapp identical 3 lines
05-29 23:37:14.521 27595 27622 E libEGL  : validate_display:91 error 3008 (EGL_BAD_DISPLAY)
05-29 23:37:14.521 27595 27622 F OpenGLRenderer: Failed to set damage region on surface 0xe6e60e20, error=EGL_BAD_DISPLAY
05-29 23:37:14.521 27595 27622 F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 27622 (RenderThread), pid 27595 (imechat.chatapp)

我不知道该怎么办 我尝试通过 parent=self 它仍然给出错误。知道该怎么做吗? Google 没有在任何地方提到 webview,所以希望你能指出一种修复它的方法。在实际代码中,webview 位于其单独的文件中,不确定是否是问题所在。也许我应该将它包装在另一个继承 Widget.

的 class 中

问题是 Webview (ModalView) 分配了自己的父级。这是ModalView的一个特点。通过在 build() 方法中返回一个 Webview 实例,您试图将 App 设置为 Webview 的父级。这会导致您看到的错误。您可以通过返回 Webview:

以外的内容来解决此问题
def build(self):
    WebView()
    return None