Kotlin+DataBinding+MVVM 做ImageView 图片加载 详细教程

一、 项目准备

1. 引入DataBinding
  • 在项目 build.gradle 文件 android 节点下添加
    buildFeatures {
        dataBinding = true // for data binding
    }
2. 引入一个MVVM 框架
    implementation 'com.github.AranAndroid009:CustomView:0ca395ae60'
    implementation 'com.github.AranAndroid009:Mvvm:97b52ee25b'
3. 引入一个图片浏览的框架 ImageSelector:

注: 该框架模仿微信UI 可以兼容android 10
项目地址: https://github.com/donkingliang/ImageSelector
根据项目文档,自己行导入 做好准备工作

    implementation 'com.github.donkingliang:ImageSelector:2.2.1'
4. 引入一个图片加载的框架 Glide
    implementation 'com.github.bumptech.glide:glide:4.12.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

二、 搭一个页面

1. 创建 一个空的Activity
2. 创建 ViewModel
  class MainVM : BaseViewModel() {
  }
3. 修改XML
  • 布局最外层用 layout 节点包裹 , 此时会自动生成 ActivityMainBinding
 <layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    
 </layout>
  • 添加数据绑定, 让改页面拥有MainVM 的一个实例化对象
 <layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
        <data>
        <import type="android.view.View" />
        <variable
            name="vm"
            type="com.winspread.waterrecord.main.MainVM" />
    </data>
 </layout>
  • 我们给页面添加 一个图片控件和按钮控件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="android.view.View" />
        <variable
            name="vm"
            type="com.winspread.waterrecord.main.MainVM" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".main.MainActivity">

        <ImageView
            android:id="@+id/iv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />

        <Button
            android:id="@+id/btn_single"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="按钮"/>
    </RelativeLayout>
</layout>
3. 修改 Activity
class MainActivity : BaseActivity<MainVM, ActivityMainBinding>(R.layout.activity_main) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 让页面vm 获取 MainVM 的实例  
        binding.vm = viewModel
        // 绑定activity生命周期, 如果不绑定可能会出现数据改变页面不变的情况
        binding.lifecycleOwner = this
    }
}

三、 编写逻辑

1. 先写个小功能, 点击按钮, 让按钮文字修改

1.binding.lifecycleOwner = this 上面如果没有写这句绑定会出现点击没有反应
2.btnText 其实变量可以理解为被观察对象,页面去订阅了他改变的事件

  • 在 MainVM 定义个变量和方法
    var btnText = MediatorLiveData<String>()
    fun btnLogin(v: View) {
    	btnText.value = "点击了"
    }
  • 将数据和事件绑定到页面上
        <Button
            android:id="@+id/btn_single"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:onClick="@{(v)->vm.btnLogin(v)}"
            android:text="@{vm.btnText}" />
2. 默认是加载一个网络图片, 点击按钮选择一张本地图片
  • 根据 ImageSelector 项目开发文档 自行导入, 别忘了添加必要的权限
  • 完整 MainVM
class MainVM : BaseViewModel() {

    var btnText = MediatorLiveData<String>()
    var url = MediatorLiveData<String>()
    var uri = MediatorLiveData<Uri>()
    var onClick = MediatorLiveData<String>()

    // 方式一
    fun btnLogin(v: View) {
	//  btnText.value = "点击了"
        onClick.postValue("")
    }
}
  • 完整 MainActivity

class MainActivity : BaseActivity<MainVM, ActivityMainBinding>(R.layout.activity_main) {

    val REQUEST_CODE = 1
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding.vm = viewModel
        binding.lifecycleOwner = this
        initView()
    }

    private fun initView() {
        // 默认显示一个网路图片
        viewModel.url.value = "https://t7.baidu.com/it/u=4162611394,4275913936&fm=193&f=GIF"
        ImageSelector.preload(this)
        // 方式一
        viewModel.onClick.observe(this, Observer {
            ImageSelector
                .builder()
                .useCamera(true) // 设置是否使用拍照
                .setSingle(true)  //设置是否单选
                .canPreview(true) //是否可以预览图片,默认为true
                .start(this, REQUEST_CODE); // 打开相册
        })

        // 方式二
        binding.btnSingle.setOnClickListener({
            ImageSelector
                .builder()
                .useCamera(false) // 设置是否使用拍照
                .setSingle(true)  //设置是否单选
                .canPreview(true) //是否可以预览图片,默认为true
                .start(this, REQUEST_CODE); // 打开相册
        })
    }

    override fun onActivityResult(
        requestCode: Int,
        resultCode: Int,
        data: Intent?
    ) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == REQUEST_CODE && data != null) {
            //获取选择器返回的数据
            val images: ArrayList<String>? =
                data.getStringArrayListExtra(ImageSelector.SELECT_RESULT)

            viewModel.btnText.value = images?.get(0)
            viewModel.uri.value = UriUtils.getImageContentUri(this, images?.get(0))
        }
    }
}
  • 完整的XML ( 如果使用方式一 需要添加onClick属性 如果使用方式二 请删掉onClick 这行 )
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="android.view.View" />
        <variable
            name="vm"
            type="com.winspread.waterrecord.main.MainVM" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".main.MainActivity">

        <ImageView
            android:id="@+id/iv"
            imageUri="@{vm.uri}"
            imageUrl="@{vm.url}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />

        <!--  如果使用方式一 需要添加onClick属性
                如果使用方式二 请删掉onClick 这行 -->
        <Button
            android:id="@+id/btn_single"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:onClick="@{(v)->vm.btnLogin(v)}"
            android:text="@{vm.btnText}" />

    </RelativeLayout>
</layout>
  • 完整的 自定义属性
    @BindingAdapter("imageUrl")
    fun imageUrl(view: ImageView, url: String?) {
        url?.let {
            Glide.with(view.context)
                .load(url)
                .into(view)
        }
    }
    
    @BindingAdapter("imageUri")
    fun imageUri(view: ImageView, uri: Uri?) {
        uri?.let {
            Glide.with(view.context)
                .load(uri)
                .into(view)
        }
    }
  • 千万别忘了添加插件 (后两个) 否则 BindingAdapter 会报错的
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    id 'kotlin-kapt'
}

四 总结关键点(自定义属性)

  • 通过DataBinding 自定义属性 完成图片自动加载

五 感谢

Databinding中ImageView的用法和坑(Java和Kotlin),附带Glide用法(https://blog.csdn.net/qq_33337504/article/details/98615593)
Android DataBinding (四) 自定义属性(https://blog.csdn.net/tianjf0514/article/details/75195124)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,我来回答你的问题。 首先,Kotlin 是一种基于 JVM 的静态类型编程语言,它的语法简洁易懂,支持函数式编程和面向对象编程。 协程是 Kotlin 中的一种轻量级线程,可以实现异步编程和并发执行。Retrofit 是一款网络请求库,它可以帮助我们轻松地实现网络请求和数据解析。MVVM 是一种软件架构模式,可以将应用程序分为三个部分:模型、视图和视图模型。 下面是一个基于 Kotlin + 协程 + Retrofit + MVVM 的网络请求的优雅实现: 1. 定义 API 接口 首先定义 API 接口,使用 Retrofit 注解来描述请求方法和参数。 ```kotlin interface ApiService { @GET("api/news") suspend fun getNews(@Query("category") category: String): NewsResponse } ``` 2. 创建数据模型 根据 API 接口的返回数据,我们可以创建一个数据模型。 ```kotlin data class News(val title: String, val content: String) data class NewsResponse(val code: Int, val message: String, val newsList: List<News>) ``` 3. 创建 ViewModel ViewModel 是连接数据模型和视图的中间层,它处理数据逻辑并提供可观察的数据。 ```kotlin class NewsViewModel : ViewModel() { private val _newsList = MutableLiveData<List<News>>() val newsList: LiveData<List<News>> = _newsList fun loadNews(category: String) { viewModelScope.launch { val response = retrofit.create(ApiService::class.java).getNews(category) if (response.code == 200) { _newsList.value = response.newsList } } } } ``` 4. 创建视图 视图负责展示数据,并与用户交互。 ```kotlin class NewsActivity : AppCompatActivity() { private val viewModel by viewModels<NewsViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_news) viewModel.newsList.observe(this, Observer { newsList -> // 更新视图 }) viewModel.loadNews("tech") } } ``` 通过使用 Kotlin + 协程 + Retrofit + MVVM,我们可以实现优雅地网络请求,代码简洁易懂,逻辑清晰。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值