目标

通过一个小案例学习使用Android DataBinding和JectPack组件。
案例将实现加载Bing每日一图的功能。

成果

test.gif

过程

启用DataBinding

1、APP的gradle文件中配置

android{    ...    dataBinding {        enabled true    }    ...}

2、Kotlin启用kapt(Java不需要)

apply plugin: 'kotlin-kapt'

使用的库

    //图片加载    implementation 'com.github.bumptech.glide:glide:4.9.0'    //ViewModelActivity    implementation "android.arch.lifecycle:extensions:1.1.1"    //网络请求    implementation 'com.squareup.retrofit2:retrofit:2.5.0'    //GSON解析    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'

一些简单的封装

ViewModel需要使用到的数据的简单封装
这样简单封装下,可以通用到其他的模块中,当然不封装也是可以使用的,这是学习了,就按照工作使用的通用封装一下,方便其他时候直接拿来用

class Data(var data: T ?= null, var msg: String ?= null)

Activity绑定ViewModel的封装
不算是封装,只是简单的扩展,同理Fragment也可以这样用(因为每次加载ViewModel时写的太长,所以写个扩展方法)

inline fun  FragmentActivity.bindViewModel() = ViewModelProviders.of(this).get(T::class.java)

网络请求

Bing的每日一图的请求地址格式为
https://cn.bing.com/HPImageArchive.aspx?format=js&idx=1&n=1
简单的分析就是
BaseUrl ==> https://cn.bing.com/
请求的数据定位 HPImageArchive.aspx
请求格式 format(JSON,XML等等)
请求的ID idx
每次请求的个数 n

尝试再浏览器进行简单的请求后,返回的JSON如下

{"images":[{"startdate":"20190325","fullstartdate":"201903251600","enddate":"20190326","url":"/th?id=OHR.SakuraFes_ZH-CN1341601988_1920x1080.jpg&rf=NorthMale_1920x1080.jpg&pid=hp","urlbase":"/th?id=OHR.SakuraFes_ZH-CN1341601988","copyright":"目黑川上盛开的樱花,日本东京 (© taketan/Getty Images)","copyrightlink":"http://www.bing.com/search?q=%E6%A8%B1%E8%8A%B1&form=hpcapt&mkt=zh-cn","title":"","quiz":"/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20190325_SakuraFes%22&FORM=HPQUIZ","wp":true,"hsh":"070b68e02215a4d18ee44b6589b0f5be","drk":1,"top":1,"bot":1,"hs":[]}],"tooltips":{"loading":"正在加载...","previous":"上一个图像","next":"下一个图像","walle":"此图片不能下载用作壁纸。","walls":"下载今日美图。仅限用作桌面壁纸。"}}

这里我们所关注的只有两个值。
图片描述(copyright)和图片地址(url)。
所以根据返回的JSON定义两个数据类

data class ImgBean(    @SerializedName("url")    var url: String,    @SerializedName("copyright")    var copyright: String)data class BaseImg(    @SerializedName("images")    val mutableList: MutableList)

这样分析后,Retrofit就可以完成请求了

import retrofit2.Callimport retrofit2.Retrofitimport retrofit2.converter.gson.GsonConverterFactoryimport retrofit2.http.GETimport retrofit2.http.Query/** *  @author : JXD *  @date : 2019/3/26 星期二 */object RetrofitUtil {    const val BASE_URL = "https://cn.bing.com/"    private val retrofit: Retrofit =        Retrofit.Builder()            .baseUrl(BASE_URL)            .addConverterFactory(GsonConverterFactory.create())            .build()    private interface BingService {        @GET("HPImageArchive.aspx")        fun loadImg(            @Query("format") format: String,            @Query("idx") idx: Int,            @Query("n") n: Int        ): Call    }    fun loadImg(format: String = "js", idx: Int = 0, n: Int = 1) =        retrofit.create(BingService::class.java).loadImg(format, idx, n)}

ViewModel + LiveData

在Google发布的JectPack中,ViewModel + LiveData的组合很神奇,可以在数据变化时自动通知更改显示数据,这里就不写生命周期绑定之类的(我也是萌新,不懂),我的简单理解就是,Activity存在时,一直存在,帮忙保存数据,Activity销毁后,切断了异步回调的数据,也就避免内存泄漏啥的。

import android.arch.lifecycle.MutableLiveDataimport android.arch.lifecycle.ViewModelimport retrofit2.Callimport retrofit2.Callbackimport retrofit2.Response/** *  @author : JXD *  @date : 2019/3/26 星期二 */class ImgViewModel : ViewModel() {    private var mIdx = -1    val mData = MutableLiveData()    fun loadImg(idx: Int = -1) {        RetrofitUtil.loadImg(idx = idx)            .enqueue(object : Callback {                override fun onFailure(call: Call, t: Throwable) {                }                override fun onResponse(call: Call, response: Response) {                    val imgBean = response.body()                    if (imgBean != null) {                        val img = imgBean.mutableList[0]                        mData.value = Data(ImgBean(RetrofitUtil.BASE_URL + img.url, img.copyright))                    }                }            })    }    fun next() {        ++mIdx        if (mIdx > 7) {            --mIdx            mData.value = Data(msg = "已经是最后一张了")            return        }        loadImg(mIdx)    }    fun pre() {        --mIdx        if (mIdx < -1) {            mIdx = -1            mData.value = Data(msg = "已经是第一张了")            return        }        loadImg(mIdx)    }}

ViewModel定义了三个方法,
加载图片 loadImg(),
下一张 next(),
上一张 pre()

当返回数据,解析成功后,修改MutableLiveData的值,这样就能监控到数据被改变的状态了。

activity_main.xml

DataBinding需要以标签为根
可以理解为传入我们需要传入变量
name 就是变量名,type就是变量类型

<?xml version="1.0" encoding="utf-8"?>                                    

BindingAdapter

ImageView加载图片,我们习惯于使用Glide,加载并做处理等,那么DataBinding如何使用呢。
以上XML中ImageView的url并不存在于它的属性中,使我们定义的BindingAdapter的方法中。

import android.databinding.BindingAdapterimport android.widget.ImageViewimport com.bumptech.glide.Glide/** * @author : JXD * @date : 2019/3/26 星期二 */object BindingAdapter {    @BindingAdapter("url")    @JvmStatic    fun setImgUrl(img: ImageView, url: String?) {        if (null == url){            return        }        Glide.with(img.context)            .load(url)            .into(img)    }}

MainActivity

1、绑定ViewModel
2、绑定DataBinding
3、监听ViewModel的值变化
4、变化时修改Activity的title的值,并加载新图片
5、如果新值为空,那么加载提示信息

import android.arch.lifecycle.Observerimport android.content.Contextimport android.databinding.DataBindingUtilimport android.os.Bundleimport android.support.v7.app.AppCompatActivityimport android.view.Viewimport android.widget.Toastimport com.syxrobot.mvvmtest.databinding.ActivityMainBindingclass MainActivity : AppCompatActivity() {    private lateinit var mContext: Context    private lateinit var mBind: ActivityMainBinding    private lateinit var mViewModel: ImgViewModel    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        //1        mViewModel = bindViewModel()        //2        mBind = DataBindingUtil.setContentView(this, R.layout.activity_main)        mContext = this        //3        mViewModel.mData.observe(this, Observer {            //4            it?.data?.let { ig ->                mBind.img = ig                title = ig.copyright            }            //5            it?.msg?.let { msg -> log(msg) }        })        mViewModel.loadImg()    }    private fun log(msg: String) {        Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show()    }    fun mainClick(v: View) {        when (v.id) {            R.id.btn_next -> mViewModel.next()            R.id.btn_pre -> mViewModel.pre()        }    }}

更多相关文章

  1. 一句话锁定MySQL数据占用元凶
  2. Android(安卓)Fragment与Fragment之间数据获取
  3. Android(安卓)4.1 动态加载APK中的资源
  4. Android的Http网络请求模型初步
  5. Android(安卓)WebView加载Html文本不能适配,以及图片中间有空白的
  6. android usb连接读取卡片(android打卡机)非nfc读取卡片
  7. 专题一====Android五种数据存储方式
  8. 在android中使用Realm数据库框架
  9. ril

随机推荐

  1. 天气预报(二)
  2. android 更新失败
  3. Android(安卓)高仿知乎日报 (上)
  4. Android(安卓)Intent的几个主要用法
  5. 安卓SDK安装时出现的小问题
  6. Android.网络拨号脚本
  7. android 动态增加不同名称的按钮
  8. Android(安卓)Touch事件
  9. 在Android(安卓)Studio中导入jar包
  10. Android(安卓)获取LocationProvider以及