我是靠谱客的博主 玩命电脑,最近开发中收集的这篇文章主要介绍【JetPack+Retrofit+Rxjava】获取Bing每日一图并显示ViewModel+LiveData+DataBinding+MVVM 补充笔记扉:一:效果图展示二:配置文件三:代码块7. ImageActivity,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
扉:
- 原文来自:Android官方架构组件ViewModel+LiveData+DataBinding架构属于自己的MVVM
- 很喜欢作者的思路,但是使用Kotlin需要配置的东西好多并且很多细节要重写,于是在原作基础上进行了Kotlin的二创和补充说明。而且没有对应的导包,自己学的时候还蛮伤脑筋的。
- 注意实现的接口,onChange的接口是lifecycle的,三态接口是Rxjava的(next,error,onComplete)
- 测试发现bean类的顺序可以乱,可以不写 但是名称和回调的参数名称必须得一致,那不想一致想自定义怎么办?可以参照这种写法,不一定有用哈【Android-Kotlin-Volley】图片画廊学习笔记
一:效果图展示
二:配置文件
1. 添加Glide、Retrofit、RxJava的依赖
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
implementation 'com.github.bumptech.glide:glide:4.6.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
2. 启用DataBinding
- 用手打吧,每次ctrl c/v有点憨憨。DataBing实现了ViewBind的所有功能,但他的效率也偏低。xml纠缠。
- App内的build.gradle的plugins加入,为了新版更好的支持BindingAdapter,不然xml中的引用会报错的
id 'kotlin-kapt'
- DataBinding报Null问题的解决方式
3. 添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
4. 接口信息
- 接口地址
https://cn.bing.com/HPImageArchive.aspx?format=js&idx=1&n=1
- postman测试,返回的Json结构,采用GsonFormat解析
{
"images": [
{
"startdate": "20210202",
"fullstartdate": "202102021600",
"enddate": "20210203",
"url": "/th?id=OHR.MountNemrut_ZH-CN4681788604_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp",
"urlbase": "/th?id=OHR.MountNemrut_ZH-CN4681788604",
"copyright": "内姆鲁特山上巨大的石灰岩雕像,土耳其阿德亚曼 (© Peerakit JIrachetthakun/Getty Images)",
"copyrightlink": "https://www.bing.com/search?q=%E5%86%85%E5%A7%86%E9%B2%81%E7%89%B9%E5%B1%B1&form=hpcapt&mkt=zh-cn",
"title": "",
"quiz": "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20210202_MountNemrut%22&FORM=HPQUIZ",
"wp": true,
"hsh": "8e96102b6ad68ddce2ffcd8732f8d6f2",
"drk": 1,
"top": 1,
"bot": 1,
"hs": []
}
],
"tooltips": {
"loading": "正在加载...",
"previous": "上一个图像",
"next": "下一个图像",
"walle": "此图片不能下载用作壁纸。",
"walls": "下载今日美图。仅限用作桌面壁纸。"
}
}
三:代码块
1. ImageBean(根据返回的json格式,使用GsonFormat生成)
- 原作者很细心:接口并没有返回图片url前缀信息,所以我在ImagesBean的内部手动添加了一个变量BASE_URL来存储图片url前缀信息。【刚好对应Retrofit】
class ImageBean {
var tooltips: TooltipsBean? = null
var images: List<ImagesBean>? = null
//静态类
class TooltipsBean {
var loading: String? = null
var previous: String? = null
var next: String? = null
var walle: String? = null
var walls: String? = null
}
//静态类
class ImagesBean {
companion object {
const val BASE_URL = "https://www.bing.com/"
}
var startdate: String? = null
var fullstartdate: String? = null
var enddate: String? = null
var url: String? = null
var urlbase: String? = null
var copyright: String? = null
var copyrightlink: String? = null
var quiz: String? = null
var isWp = false
var hsh: String? = null
var drk = 0
var top = 0
var bot = 0
var hs: List<*>? = null
}
}
2. Data类
- 由于项目采用MVVM架构,View层与ViewModel层的通信是通过LiveData这个架构组件实现的,不同于MVP架构中通过接口来通信,所以还要对数据加载的状态和错误信息进行维护。这里创建一个包装类来维护数据的状态和错误信息,以便View层可以对数据加载错误信息进行响应和处理。
- 泛型T,原作涉及到T的部分都有些问题
- 只有一个泛型T的成员mData来存储数据,和一个String类型的mErrorMsg来存储错误信息。这样View层就可以通过判断mErrorMsg是否为空来判断出数据加载成功与否。
class Data<T>(data: T?, errorMsg: String?) {
private var mData: T ?=null
var errorMsg: String?=null
var data: T?
get() = mData
set(data) {
mData = data
}
init {
if (data != null) {
mData = data
}
if (errorMsg != null) {
this.errorMsg = errorMsg
}
}
}
3. 创建数据访问接口ImageRepertory
- Retrofit+Rxjava作为网络访问框架。首先ImageRepertory内部有一个Retrofit实例,并且在构造函数中进行Retrofit的配置和创建。接着创建一个Service接口,其中的getImage方法用来获取图片信息,方法返回一个ImageBean的【Rxjava】Observable对象。
- 由于只有一个方法,所以作者没有另外抽取service
class ImageRepertory {
private val mRetrofit: Retrofit
private interface Service {
@GET("HPImageArchive.aspx")
fun getImage(
@Query("format") format: String?,
@Query("idx") idx: Int,
@Query("n") n: Int
): Observable<ImageBean>
}
fun getImage(format: String?, idx: Int, n: Int): Observable<ImageBean> {
return mRetrofit.create(Service::class.java).getImage(format, idx, n)
}
init {
mRetrofit = Retrofit.Builder()
.baseUrl("https://cn.bing.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
}
4. 编写ImageViewModel
- 类要继承自android.arch.lifecycle.ViewModel这个类,以便在创建时与View层的生命周期相关联。然后是三个成员变量:mImage这个变量的类型是MutableLiveData用来存放图片信息,以便当信息发生变化时及时通知View层来更新界面;mRepertory这个变量来负责数据访问;idx这个变量来记录当前的图片页码。这三个变量在构造函数中创建并初始化,接着为mImage添加了getter方法以便View层可以对其进行观察与响应。loadImage,nextImage和previousImage这三个方法分别对应图片的加载,下一张和上一张,并且内部通过访问mRepertory的方法来完成数据的访问,又对返回的数据进行判断处理并触发mImage的setValue方法来对数据进行更新。
class ImageViewModel : ViewModel() {
//定义了Data系的image对象
val image: MutableLiveData<Data<ImageBean.ImagesBean?>> = MutableLiveData()
private val mRepertory: ImageRepertory = ImageRepertory()
private var idx: Int = 0
fun loadImage() {
mRepertory.getImage("js", idx, 1)
.subscribeOn(Schedulers.io())//改变调用它之前代码的线程
.observeOn(AndroidSchedulers.mainThread())//改变调用它之后代码的线程
.subscribe(object : Observer<ImageBean> {
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {
image.value = Data(
null, e.message
)
}
override fun onComplete() {}
override fun onNext(t: ImageBean) {
image.setValue(
Data<ImageBean.ImagesBean?>(
t.images!![0] as ImageBean.ImagesBean?, null
)
)
}
})
}
fun nextImage() {
mRepertory.getImage("js", ++idx, 1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ImageBean?> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(imageBean: ImageBean) {
image.setValue(
Data(
imageBean.images!![0] as ImageBean.ImagesBean?, null
)
)
}
override fun onError(e: Throwable) {
image.value = Data(
null, e.message!!
)
idx--
}
override fun onComplete() {}
})
}
fun previousImage() {
if (idx <= 0) {
image.value = Data(
null, "已经是第一个了"
)
return
}
mRepertory.getImage("js", --idx, 1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ImageBean?> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(imageBean: ImageBean) {
image.setValue(
Data(
imageBean.images!![0] as ImageBean.ImagesBean?, null
)
)
}
override fun onError(e: Throwable) {
image.setValue(
Data(
null, e.message!!
)
)
idx++
}
override fun onComplete() {}
})
}
}
5. 编写页面
- 与正常的XML布局文件不同的是,根标签改成了layout标签,内部有data标签和具体的布局。dat标签内存放Databinding的数据类。除了需要用到的ImagesBean类之外,这里还声明了一个Presenter类用来对界面的用户行为做统一的管理。注意到ImageView的标签内声明了一个url属性,并且和data内的image的数据进行了绑定。然而ImageView并没有这个属性,这时就需要用到Databinding的自定义属性了。
- 注意这边转换data的时候,有些测试版本AS是不会自动出小灯泡的。快捷键是【Alt+回车】光标放在androidx上即可
<?xml version="1.0" encoding="utf-8"?>
<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="imageBean"
type="com.ywjh.retrofitgetbinding.ImageBean.ImagesBean" />
<variable
name="presenter"
type="com.ywjh.retrofitgetbinding.ImageActivity.Presenter" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
app:url="@{imageBean.BASE_URL+imageBean.url}"
android:layout_width="match_parent"
android:layout_height="300dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/btn_previous"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:onClick="@{presenter.onClick}"
android:layout_weight="1"
android:text="上一张" />
<Button
android:id="@+id/btn_load"
android:layout_width="0dp"
android:onClick="@{presenter.onClick}"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="加载" />
<Button
android:id="@+id/btn_next"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:onClick="@{presenter.onClick}"
android:layout_weight="1"
android:text="下一张" />
</LinearLayout>
</LinearLayout>
</layout>
6. 建立BindingAdapter绑定xml方式
- 以注解的方式联合,然后使用JvmStatic
object BindingAdapter {
@JvmStatic
@androidx.databinding.BindingAdapter("url")
fun setImageUrl(imageView: ImageView, url: String?) {
Glide.with(imageView.context)
.load(url)
.into(imageView)
}
}
7. ImageActivity
- 三个成员变量:mBinding数据绑定对象,用来实现数据绑定;mViewModel用来获取数据,实现与数据层的解耦;mProgressDialog用来弹出加载提示框。这三个变量在oncreate方法中初始化,mBinding用DataBindingUtil的setContentView方法实现视图层的绑定;mViewModel要使用ViewModelProvider的get方法完成创建。接着对ViewModel中的LiveData进行观察,在observe方法中处理错误和数据的绑定。内部类Presenter用来对点击事件进行响应,并且也要在oncreate方法里与mBinding进行绑定。
- 注意:xml文件中一定不能出现与业务相关的代码!比如直接将ViewModel的访问数据的方法在xml中与按钮的点击事件进行绑定,这种方做法是不可取的,因为XML文件的作用应该只是进行数据的显示和用户的交互,而访问数据这种和业务相关的操作不应出现在XML文件中。
class ImageActivity : AppCompatActivity() {
private var mViewModel: ImageViewModel? = null
private var mProgressDialog: ProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityImageBinding = DataBindingUtil.setContentView(this,R.layout.activity_image)
mViewModel = ViewModelProvider(
this, ViewModelProvider.AndroidViewModelFactory(application)
).get(ImageViewModel::class.java)
mProgressDialog = ProgressDialog(this)
mProgressDialog!!.setMessage("加载中")
mViewModel!!.image.observe(this, object : Observer<Data<ImageBean.ImagesBean?>?> {
override fun onChanged(@Nullable t: Data<ImageBean.ImagesBean?>?) {
if (t != null) {
if (t.errorMsg != null) {
Toast.makeText(this@ImageActivity, t.errorMsg, Toast.LENGTH_SHORT)
.show()
mProgressDialog!!.dismiss()
return
}
}
if (t != null) {
binding.setImageBean(t.data)
title = t.data?.copyright
}
mProgressDialog!!.dismiss()
}
})
binding.setPresenter(Presenter())
mProgressDialog!!.show()
mViewModel!!.loadImage()
}
inner class Presenter {
fun onClick(view: View) {
mProgressDialog!!.show()
when (view.getId()) {
R.id.btn_load -> mViewModel?.loadImage()
R.id.btn_previous -> mViewModel!!.previousImage()
R.id.btn_next -> mViewModel!!.nextImage()
else -> {
}
}
}
}
}
最后
以上就是玩命电脑为你收集整理的【JetPack+Retrofit+Rxjava】获取Bing每日一图并显示ViewModel+LiveData+DataBinding+MVVM 补充笔记扉:一:效果图展示二:配置文件三:代码块7. ImageActivity的全部内容,希望文章能够帮你解决【JetPack+Retrofit+Rxjava】获取Bing每日一图并显示ViewModel+LiveData+DataBinding+MVVM 补充笔记扉:一:效果图展示二:配置文件三:代码块7. ImageActivity所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复