kotlin中ViewModel + ViewBinding使用实例

android使用androidx后可以使用viewbinding了,因为是内生库,也蛮好用的。

butterknife感觉已经在退环境了。

文章列出ViewModel + ViewBinding,是MVVM模式的简单使用,没有涉及到太复杂的环境,如果要在复杂环境下使用,还有待研究。

在Kotlin中使用ViewBinding需要的在build.gradle(APP)中启用:

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

这里使用的是一个使用Fragment载入网页的例子,使用ViewModel来给Fragment提供数据。涉及到的数据简单的认为有2个,一个是网页的URL,另外一个是网页的Title。我们用一个java bean来存放该数据,如下:

data class DataWeb(var title: String, var url: String)

接下来是用ViewMod提供数据的实现类:

class WebViewModel : ViewModel() {

    private val _general = MutableLiveData<Constants>().apply {
        value = DataWeb("baidu", "https://www.baidu.com")
    }
    val general get() = _general

    /** 改变*/
    fun update(title: String?, url: String) {
        val constants = general.value
        constants?.title = title ?: ""
        constants?.url = url
        general.value = constants
    }
}

接下来是使用Fragment载入数据:

// 布局layout_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <RelativeLayout
            android:id="@+id/actionBar"
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:background="@color/actionBarBackground"
            android:elevation="4dp">

            <TextView
                android:id="@+id/tvActionBarTitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginStart="20dp"/>
        </RelativeLayout>

        <com.scwang.smartrefresh.layout.SmartRefreshLayout
            android:id="@+id/refreshLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <android.webkit.WebView
                android:id="@+id/webView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </com.scwang.smartrefresh.layout.SmartRefreshLayout>
    </LinearLayout>
</layout>

// Fragment实现
class WebViewFragment2 : Fragment(), TheWebViewClientCallback, TheWebViewChromeClientCallback {

    companion object {
        const val TAG = "WebViewFragment"
    }

    private var _binding: LayoutFragmentBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    val binding get() = _binding!!

    private lateinit var webViewModel: WebViewModel

    private var loadService: LoadService<Any>? = null

    /** 是否记载出错*/
    private var isLoadError = false

    /** 支持下拉刷新*/
    var canNativeRefresh = true

    /** 是否需要重新载入URL*/
    var isNeedLoadUrl = true

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        webViewModel = ViewModelProvider(this).get(WebViewModel::class.java)
        _binding = LayoutFragmentBinding.inflate(inflater, container, false)
        val webView = binding.webView.apply {
            // 监控WebView载入的过程
            webViewClient = TheWebViewClient().apply {
                callback = this@WebViewFragment2
            }
            // 仅接受Title
            webChromeClient = TheWebViewChromeClient().apply {
                callback = this@WebViewFragment2
            }
            settings.javaScriptEnabled = true
        }
        binding.refreshLayout.apply {
            // 无需加载更多
            setEnableLoadMore(false)
            // 支持下拉刷新
            setEnableRefresh(canNativeRefresh)
            setOnRefreshListener {
                webView.reload()
            }
        }
        webViewModel.general.observe(viewLifecycleOwner) {
            binding.tvActionBarTitle.text = it.title
            if (isNeedLoadUrl) {
                isNeedLoadUrl = false
                webView.loadUrl(it.url)
            }
            Log.d(TAG, "<---->observe called<---->")
        }
        loadService = LoadSir.getDefault().register(binding.refreshLayout) {
            // 用户点击按钮的reload事件
            loadService?.showCallback(LoadingCallback::class.java)
            Log.d(TAG, "<>register loadService<>")
            webView.reload()
        }
        return binding.root
    }

    override fun pageStarted(url: String?) {
        Log.d(TAG, "<>pageStarted<>")
        isLoadError = false
        loadService?.showCallback(LottieLoadingCallback::class.java)
    }

    override fun pageFinished(url: String?) {
        Log.d(TAG, "<>pageFinished<>")
        if (!isLoadError) {
            loadService?.showSuccess()
        }
        binding.refreshLayout.setEnableRefresh(canNativeRefresh)
        binding.refreshLayout.finishRefresh()
    }

    override fun onError() {
        Log.e(TAG, "<>onError<>")
        isLoadError = true
        loadService?.showCallback(ErrorCallback::class.java)
    }

    override fun onReceivedTitle(title: String?) {
        Log.d(TAG, "onReceivedTitle : $title")
        webViewModel.update(title, "")
    }
}

ViewModel对数据的监控使用的是观察者模式,代码中webViewModel.general.observe()就是启用观察者,而且加入了lifecycle中来保证在生命周期中能够接受到数据的变更,初始数据的载入,也在general.observe()方法中实现。而实时数据的变更使用的是model中的我们自己写的update()方法。

以上就是本文要介绍的内容。

最后提出其他边角的代码:

interface TheWebViewClientCallback {
    fun pageStarted(url: String?)
    fun pageFinished(url: String?)
    fun onError()
}

class TheWebViewClient : WebViewClient() {

    var callback: TheWebViewClientCallback? = null

    override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
        super.onPageStarted(view, url, favicon)
        if (null != callback) {
            callback!!.pageStarted(url)
        } else {
            Log.e("TAG", "onPageStarted callback is null")
        }
    }

    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)
        callback?.pageFinished(url)
    }

    override fun onReceivedError(
        view: WebView?,
        request: WebResourceRequest?,
        error: WebResourceError?
    ) {
        callback?.onError()
        super.onReceivedError(view, request, error)
    }
}

interface TheWebViewChromeClientCallback {
    fun onReceivedTitle(title: String?)
}

class TheWebViewChromeClient : WebChromeClient() {

    var callback: TheWebViewChromeClientCallback? = null

    override fun onReceivedTitle(view: WebView?, title: String?) {
        super.onReceivedTitle(view, title)
        callback?.onReceivedTitle(title)
    }
}

//三方框架

// loadSir
implementation 'com.kingja.loadsir:loadsir:1.3.8'
// lottie
implementation 'com.airbnb.android:lottie:5.2.0'
// 下拉刷新
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-andx-11'

 

posted @ 2022-11-08 10:43  swalka`x  阅读(1040)  评论(0编辑  收藏  举报