使用Kotlin中的MVVM实现双向数据绑定WebView

4
我很新手,正在尝试在我的应用程序中实现MVVM。我想通过我的ViewModel加载一个Webview,但我不知道最好的方法如何做到这一点,如果还有一个进度条来显示我的webview正在加载时会更好。
以下是代码:
    class NewWebViewFragment : Fragment() {

    private lateinit var viewModel: NewWebViewViewModel

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

        val binding: FragmentWebViewBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_web_view, container, false)

        val webViewModelo = ViewModelProviders.of(this).get(NewWebViewViewModel::class.java)

        binding.webViewModel = webViewModelo
        binding.lifecycleOwner = this


        return binding.root
    }

}

现在是我的ViewModel:
class NewWebViewViewModel : ViewModel() {

//Implement in MVVM

private fun showWebView(webView: WebView, progressBar: ProgressBar) {

        webView.webViewClient = WebViewClient()
        webView.webChromeClient = WebChromeClient()

        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true

        webView.loadUrl("https://brand.randombrand.com/en/")

        webView.webViewClient = object : WebViewClient() {
            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                progressBar.visibility = View.VISIBLE
                super.onPageStarted(view, url, favicon)
            }

            override fun onPageFinished(view: WebView?, url: String?) {
                progressBar.visibility = View.GONE
                super.onPageFinished(view, url)
            }
        }
    }
}

还有我的XML:

<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">


    <data>
        <variable
            name="webViewModel"
            type="com.example.navigations.viewmodel.webview.NewWebViewViewModel" />
    </data>

    <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".fragments.WebViewFragment">


    <!-- TODO: Update blank fragment layout -->
        <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/webView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">


    </WebView>

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"
        android:layout_gravity="center"/>


    </FrameLayout>
</layout>

我收到的错误是一个 nullpointerexception,因为我传递的 Webview 是 null。有什么最佳实现方式的想法吗?
3个回答

5

我认为对于你来说,最好的选择是创建一个Binding Adapter,代码如下:

@BindingAdapter("loadUrl")
fun WebView.setUrl(url: String) {
    this.loadUrl(url)
}

在您的ViewModel中,您可以通过Fragment参数(如果它们来自另一个Fragment)接收数据,或者直接从ViewModel中修改它们。
在VM中,数据将是要加载的url,可能类似于val webViewUrl = MutableLiveData<String>().apply{ value = "initial url to be loaded .com" }
在您的xml文件中:
<WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/webView"
        app:loadUrl="@{viewModel.webViewUrl}"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

是的,你绝不能在ViewModel中执行UI相关的任务,例如实例化WebView等。


嗨,感谢留言!这些bindingAdapter应该放在哪里实现呢?它们应该属于View Fragment吗? - Diogo Sequeira
你可以创建一个 Utils 类,只有 Binding Adapters,这通常是我做的方式 :) - Victor Motogna

2

ViewModel中不应该使用WebView

请查看https://developer.android.com/topic/libraries/data-binding/binding-adapters.html

MVVM的规则

第一条规则:视图不应该有任何逻辑,甚至不是简单的if条件语句。所有视图的逻辑都在ViewModel中处理。

第二条规则:响应事件时,视图只需通过调用一个方法通知ViewModel。视图不会向ViewModel传递任何与视图相关的类。

第三条规则:ViewModel使用LiveData作为与视图通信的主要方式!

MVVM的一个好处是ViewModel不需要了解View的任何信息,也没有对View类的引用!相反,它使用了响应式编程范例,因此View仍然可以观察数据并被通知其更改。

第四条规则:视图可以在需要时调用ViewModel。ViewModel可以为视图提供帮助方法。


这是一个非常非常好的评论 :) 感谢您的见解和良好实践。 - Diogo Sequeira

0

我找到了一种加载webView的解决方案。这是我所做的:

 import android.webkit.WebView
import androidx.databinding.BindingAdapter

object Utilities {

    @BindingAdapter("loadUrl")
    @JvmStatic
    fun WebView.setUrl(url: String) {
        this.loadUrl(url)
    }
}

1# 创建了一个 Utils 对象

<WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/webView"
            app:loadUrl="@{webViewModel.webViewUrl}"
            app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
            >

2 # 更新后的 XML 如下所示

3 # val webViewUrl = MutableLiveData().apply{ value = "https://google.com/en/" }

我将这段代码添加到了我的 ViewModel 类中

感谢大家的评论


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接