在 Kotlin 中使用 MVVM 实现双向数据绑定 WebView
Implementing two way data-binding WebView using MVVM in Kotlin
我正在尝试在我所有的应用程序中实现 MVVM,我对此很陌生,所以我想通过我的 ViewModel 加载一个 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 是空的。
有没有想过实现这个的最佳方法?
您可能不会在 viewmodel
中使用 webview
看https://developer.android.com/topic/libraries/data-binding/binding-adapters.html
MVVM 规则
第一条规则:视图中不应该有任何逻辑,完全没有!甚至不是一个简单的 if 条件。视图的所有逻辑都发生在 ViewModel 中。
第二条规则:在响应事件时,视图除了通过调用方法通知视图模型外什么都不做。视图不会将任何视图相关 类 传递给视图模型。
规则三:ViewModel 使用实时数据作为与视图通信的主要方式!
MVVM 的一个好处是 ViewModel 不需要知道任何关于 View 的信息,也不需要引用 View 类!相反,它使用反应式编程范例,因此 View 仍然可以观察数据并收到有关更改的通知。
规则四:视图可以在需要时调用视图模型。 ViewModel 可以为视图提供辅助方法。
我认为您最好的选择是创建一个如下所示的绑定适配器:
@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 或类似的事情。
我找到了加载 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 Class
感谢大家的评论
我正在尝试在我所有的应用程序中实现 MVVM,我对此很陌生,所以我想通过我的 ViewModel 加载一个 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 是空的。
有没有想过实现这个的最佳方法?
您可能不会在 viewmodel
webview
看https://developer.android.com/topic/libraries/data-binding/binding-adapters.html
MVVM 规则
第一条规则:视图中不应该有任何逻辑,完全没有!甚至不是一个简单的 if 条件。视图的所有逻辑都发生在 ViewModel 中。
第二条规则:在响应事件时,视图除了通过调用方法通知视图模型外什么都不做。视图不会将任何视图相关 类 传递给视图模型。
规则三:ViewModel 使用实时数据作为与视图通信的主要方式!
MVVM 的一个好处是 ViewModel 不需要知道任何关于 View 的信息,也不需要引用 View 类!相反,它使用反应式编程范例,因此 View 仍然可以观察数据并收到有关更改的通知。
规则四:视图可以在需要时调用视图模型。 ViewModel 可以为视图提供辅助方法。
我认为您最好的选择是创建一个如下所示的绑定适配器:
@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 或类似的事情。
我找到了加载 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 Class
感谢大家的评论