Json 使用 Retrofit 和 moshi 解析失败 -Android
Json Parsing failed using Retrofit and moshi -Android
这是我收到的错误(这是次要错误,我在使用 ListItemView 进行数据绑定时收到此错误)
import com.example.bookkotlin.databinding.ListViewItemBindingImpl;
^
symbol: class ListViewItemBindingImpl
location: package com.example.bookkotlin.databinding
> Task :app:kaptDebugKotlin FAILED
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
> java.lang.reflect.InvocationTargetException (no error message)
build.gradle(项目:应用程序)
buildscript {
ext {
kotlin_version = "1.4.20"
version_glide = "4.8.0"
version_lifecycle = "2.2.0"
version_moshi = "1.9.3"
version_navigation = "1.0.0"
version_retrofit = "2.9.0"
version_recyclerview = "1.2.0-alpha05"
}
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
build.gradle(模块:应用程序)
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-parcelize'
id 'kotlin-kapt'
}
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 30
buildFeatures {
dataBinding true
}
buildToolsVersion "30.0.1"
defaultConfig {
applicationId "com.example.bookkotlin"
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// Glide
implementation "com.github.bumptech.glide:glide:$version_glide"
// RecyclerView
implementation "androidx.recyclerview:recyclerview:$version_recyclerview"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
// Moshi
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$version_lifecycle"
// Navigation
implementation "android.arch.navigation:navigation-fragment-ktx:$version_navigation"
implementation "android.arch.navigation:navigation-ui-ktx:$version_navigation"
implementation "org.jetbrains.kotlin:kotlin-reflect:1.1.0"
}
这是 BookApiService
package com.example.bookkotlin.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
private const val BASE_URL = "https://www.googleapis.com/books/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface BookApiService {
@GET("v1/volumes")
suspend fun getProperties(@Query("q") type : String) : BookResponse
}
object BookApi {
val retrofitService : BookApiService by lazy { retrofit.create(BookApiService::class.java) }
}
这是 BookResponse
package com.example.bookkotlin.network
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class BookResponse( val items : Array<Items> ) : Parcelable
@Parcelize
data class Items (val id: String,
val volumeInfo : BookProperty) : Parcelable
@Parcelize
data class BookProperty( val authors: Array<String>?, val title: String,
val imageLinks: ImageLink) : Parcelable
@Parcelize
data class ImageLink( val smallThumbnail: String) : Parcelable
这是 OverviewFragment
package com.example.bookkotlin.overview
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.bookkotlin.R
import com.example.bookkotlin.databinding.FragmentOverviewBinding
class OverviewFragment : Fragment() {
private val viewModel: OverviewViewModel by lazy {
ViewModelProvider(this).get(OverviewViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val binding: FragmentOverviewBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_overview, container, false)
binding.lifecycleOwner = this
binding.viewModel = viewModel
binding.recyclerList.adapter = RecyclerviewAdapter()
return binding.root
}
}
我正在从我的 OverviewViewModel
调用 API
package com.example.bookkotlin.overview
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.bookkotlin.network.BookApi
import com.example.bookkotlin.network.BookResponse
import kotlinx.coroutines.launch
class OverviewViewModel : ViewModel() {
// Internally, we use a MutableLiveData, because we will be updating the List of MarsProperty
// with new values
private val _properties = MutableLiveData<BookResponse> ()
// The external LiveData interface to the property is immutable, so only this class can modify
val properties: LiveData<BookResponse>
get() = _properties
init {
getBookProperties()
}
private fun getBookProperties() {
viewModelScope.launch {
try {
_properties.value =BookApi.retrofitService.getProperties("Harry Potter")
} catch (e : Exception) {
}
}
}
}
RecyclerviewAdapter
package com.example.bookkotlin.overview
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.bookkotlin.databinding.ListViewItemBinding
import com.example.bookkotlin.network.Items
class RecyclerviewAdapter() : ListAdapter<Items, RecyclerviewAdapter.BookPropertyViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookPropertyViewHolder {
return BookPropertyViewHolder.from(parent)
}
override fun onBindViewHolder(holder: BookPropertyViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item)
}
class BookPropertyViewHolder private constructor(val binding: ListViewItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(books: Items) {
binding.property = books
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): BookPropertyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListViewItemBinding.inflate(layoutInflater, parent, false)
return BookPropertyViewHolder(binding)
}
}
}
}
class DiffCallback : DiffUtil.ItemCallback<Items>() {
override fun areItemsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem.volumeInfo.id == newItem.volumeInfo.id
}
}
绑定适配器
package com.example.bookkotlin
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.bookkotlin.network.Items
import com.example.bookkotlin.overview.RecyclerviewAdapter
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: BookResponse) {
val adapter = recyclerView.adapter as RecyclerviewAdapter
adapter.submitList(data.items.toList())
}
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String) {
Glide.with(imgView)
.load(imgUrl)
.into(imgView)
}
这是list_view_item
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<data>
<variable
name="property"
type="com.example.bookkotlin.network.Items" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="150dp"
android:orientation="horizontal"
android:paddingStart="@dimen/margin_16_dp"
android:paddingLeft="@dimen/margin_16_dp"
android:paddingEnd="@dimen/margin_16_dp"
android:paddingRight="@dimen/margin_16_dp"
android:id="@+id/container">
<ImageView
android:id="@+id/cover_image"
android:layout_width="@dimen/image_view_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerInside"
tools:src="@mipmap/ic_launcher"
app:imageUrl ="@{property.volumeInfo.imageLinks.smallThumbnail}"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/margin_16_dp"
android:layout_marginLeft="@dimen/margin_16_dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:maxLines="1"
android:textAllCaps="true"
android:textColor="@color/textColorAuthor"
android:textSize="@dimen/author_text_size"
android:text="@{property.volumeInfo.authors.toString()}" />
<TextView
android:id="@+id/book_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elegantTextHeight="true"
android:ellipsize="end"
android:lineSpacingMultiplier="1.1"
android:maxLines="2"
android:textColor="@color/textColorBookTitle"
android:textSize="@dimen/title_text_size"
android:text="@{property.volumeInfo.title}"
/>
</LinearLayout>
</LinearLayout>
</layout>
fragment_overview
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.bookkotlin.overview.OverviewViewModel" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context = "com.example.bookkotlin.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/searchSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:orientation="horizontal">
<SearchView
android:id="@+id/search_view_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:iconifiedByDefault="false"
android:queryHint="Enter a Book Title"
tools:queryHint="Enter a Title" />
<Button
android:id="@+id/search_book_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Search" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:listData="@{viewModel.properties}"
tools:listitem="@layout/list_view_item"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</LinearLayout>
<TextView
android:id="@+id/empty_view"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:textAppearanceMedium" />
<ProgressBar
android:id="@+id/loading_progress"
style="?android:progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
</layout>
请帮我修复错误,我对 Kotlin 编程还是个新手。
我在 kotlin 中解析 Json 并将 List<ObjectType>
替换为 Array<ObjectType>
时遇到了类似的问题。
尝试以下方法
@Parcelize
data class BookResponse( val items : Array<Items> ) : Parcelable
@Parcelize
data class Items (val volumeInfo : BookProperty) : Parcelable
// the volumeinfo returns a JsonObject and that object doesn't contain an **id** field, so remove the **id** member variable from BookProperty
@Parcelize
data class BookProperty( val authors: Array<String>?, val title: String,
val imageLinks: ImageLink
) : Parcelable
@Parcelize
data class ImageLink( val smallThumbnail: String) : Parcelable
此外,API 只返回一个对象而不是列表,因此还要进行以下更改
interface BookApiService {
@GET("v1/volumes")
suspend fun getProperties(@Query("q") type : String) : BookResponse
}
编辑:
你这个 formatter 来可视化 Json 响应。例如,当您为 Harry Potter
查询 API 时,您得到的是 单个 对象而不是对象列表。
OverviewViewModel
//change the datatype to hold a single object instead of a list
private val _properties = MutableLiveData<BookResponse>()
val properties: LiveData<BookResponse>
get() = _properties
BindingUtils
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: BookResponse) {
val adapter = recyclerView.adapter as RecyclerviewAdapter
adapter.submitList(data.items.toList())
}
项目
@Parcelize
data class Items(val volumeInfo: BookProperty, val id: String) : Parcelable
DiffCallback
class DiffCallback : DiffUtil.ItemCallback<Items>() {
override fun areItemsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem == newItem
}
}
data parsed successfully
这是我收到的错误(这是次要错误,我在使用 ListItemView 进行数据绑定时收到此错误)
import com.example.bookkotlin.databinding.ListViewItemBindingImpl;
^
symbol: class ListViewItemBindingImpl
location: package com.example.bookkotlin.databinding
> Task :app:kaptDebugKotlin FAILED
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
> java.lang.reflect.InvocationTargetException (no error message)
build.gradle(项目:应用程序)
buildscript {
ext {
kotlin_version = "1.4.20"
version_glide = "4.8.0"
version_lifecycle = "2.2.0"
version_moshi = "1.9.3"
version_navigation = "1.0.0"
version_retrofit = "2.9.0"
version_recyclerview = "1.2.0-alpha05"
}
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
build.gradle(模块:应用程序)
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-parcelize'
id 'kotlin-kapt'
}
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 30
buildFeatures {
dataBinding true
}
buildToolsVersion "30.0.1"
defaultConfig {
applicationId "com.example.bookkotlin"
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// Glide
implementation "com.github.bumptech.glide:glide:$version_glide"
// RecyclerView
implementation "androidx.recyclerview:recyclerview:$version_recyclerview"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
// Moshi
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$version_lifecycle"
// Navigation
implementation "android.arch.navigation:navigation-fragment-ktx:$version_navigation"
implementation "android.arch.navigation:navigation-ui-ktx:$version_navigation"
implementation "org.jetbrains.kotlin:kotlin-reflect:1.1.0"
}
这是 BookApiService
package com.example.bookkotlin.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
private const val BASE_URL = "https://www.googleapis.com/books/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface BookApiService {
@GET("v1/volumes")
suspend fun getProperties(@Query("q") type : String) : BookResponse
}
object BookApi {
val retrofitService : BookApiService by lazy { retrofit.create(BookApiService::class.java) }
}
这是 BookResponse
package com.example.bookkotlin.network
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class BookResponse( val items : Array<Items> ) : Parcelable
@Parcelize
data class Items (val id: String,
val volumeInfo : BookProperty) : Parcelable
@Parcelize
data class BookProperty( val authors: Array<String>?, val title: String,
val imageLinks: ImageLink) : Parcelable
@Parcelize
data class ImageLink( val smallThumbnail: String) : Parcelable
这是 OverviewFragment
package com.example.bookkotlin.overview
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.bookkotlin.R
import com.example.bookkotlin.databinding.FragmentOverviewBinding
class OverviewFragment : Fragment() {
private val viewModel: OverviewViewModel by lazy {
ViewModelProvider(this).get(OverviewViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val binding: FragmentOverviewBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_overview, container, false)
binding.lifecycleOwner = this
binding.viewModel = viewModel
binding.recyclerList.adapter = RecyclerviewAdapter()
return binding.root
}
}
我正在从我的 OverviewViewModel
调用 API package com.example.bookkotlin.overview
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.bookkotlin.network.BookApi
import com.example.bookkotlin.network.BookResponse
import kotlinx.coroutines.launch
class OverviewViewModel : ViewModel() {
// Internally, we use a MutableLiveData, because we will be updating the List of MarsProperty
// with new values
private val _properties = MutableLiveData<BookResponse> ()
// The external LiveData interface to the property is immutable, so only this class can modify
val properties: LiveData<BookResponse>
get() = _properties
init {
getBookProperties()
}
private fun getBookProperties() {
viewModelScope.launch {
try {
_properties.value =BookApi.retrofitService.getProperties("Harry Potter")
} catch (e : Exception) {
}
}
}
}
RecyclerviewAdapter
package com.example.bookkotlin.overview
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.bookkotlin.databinding.ListViewItemBinding
import com.example.bookkotlin.network.Items
class RecyclerviewAdapter() : ListAdapter<Items, RecyclerviewAdapter.BookPropertyViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookPropertyViewHolder {
return BookPropertyViewHolder.from(parent)
}
override fun onBindViewHolder(holder: BookPropertyViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item)
}
class BookPropertyViewHolder private constructor(val binding: ListViewItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(books: Items) {
binding.property = books
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): BookPropertyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListViewItemBinding.inflate(layoutInflater, parent, false)
return BookPropertyViewHolder(binding)
}
}
}
}
class DiffCallback : DiffUtil.ItemCallback<Items>() {
override fun areItemsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem.volumeInfo.id == newItem.volumeInfo.id
}
}
绑定适配器
package com.example.bookkotlin
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.bookkotlin.network.Items
import com.example.bookkotlin.overview.RecyclerviewAdapter
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: BookResponse) {
val adapter = recyclerView.adapter as RecyclerviewAdapter
adapter.submitList(data.items.toList())
}
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String) {
Glide.with(imgView)
.load(imgUrl)
.into(imgView)
}
这是list_view_item
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<data>
<variable
name="property"
type="com.example.bookkotlin.network.Items" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="150dp"
android:orientation="horizontal"
android:paddingStart="@dimen/margin_16_dp"
android:paddingLeft="@dimen/margin_16_dp"
android:paddingEnd="@dimen/margin_16_dp"
android:paddingRight="@dimen/margin_16_dp"
android:id="@+id/container">
<ImageView
android:id="@+id/cover_image"
android:layout_width="@dimen/image_view_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerInside"
tools:src="@mipmap/ic_launcher"
app:imageUrl ="@{property.volumeInfo.imageLinks.smallThumbnail}"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/margin_16_dp"
android:layout_marginLeft="@dimen/margin_16_dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:maxLines="1"
android:textAllCaps="true"
android:textColor="@color/textColorAuthor"
android:textSize="@dimen/author_text_size"
android:text="@{property.volumeInfo.authors.toString()}" />
<TextView
android:id="@+id/book_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elegantTextHeight="true"
android:ellipsize="end"
android:lineSpacingMultiplier="1.1"
android:maxLines="2"
android:textColor="@color/textColorBookTitle"
android:textSize="@dimen/title_text_size"
android:text="@{property.volumeInfo.title}"
/>
</LinearLayout>
</LinearLayout>
</layout>
fragment_overview
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.bookkotlin.overview.OverviewViewModel" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context = "com.example.bookkotlin.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/searchSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:orientation="horizontal">
<SearchView
android:id="@+id/search_view_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:iconifiedByDefault="false"
android:queryHint="Enter a Book Title"
tools:queryHint="Enter a Title" />
<Button
android:id="@+id/search_book_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Search" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:listData="@{viewModel.properties}"
tools:listitem="@layout/list_view_item"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</LinearLayout>
<TextView
android:id="@+id/empty_view"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:textAppearanceMedium" />
<ProgressBar
android:id="@+id/loading_progress"
style="?android:progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
</layout>
请帮我修复错误,我对 Kotlin 编程还是个新手。
我在 kotlin 中解析 Json 并将 List<ObjectType>
替换为 Array<ObjectType>
时遇到了类似的问题。
尝试以下方法
@Parcelize
data class BookResponse( val items : Array<Items> ) : Parcelable
@Parcelize
data class Items (val volumeInfo : BookProperty) : Parcelable
// the volumeinfo returns a JsonObject and that object doesn't contain an **id** field, so remove the **id** member variable from BookProperty
@Parcelize
data class BookProperty( val authors: Array<String>?, val title: String,
val imageLinks: ImageLink
) : Parcelable
@Parcelize
data class ImageLink( val smallThumbnail: String) : Parcelable
此外,API 只返回一个对象而不是列表,因此还要进行以下更改
interface BookApiService {
@GET("v1/volumes")
suspend fun getProperties(@Query("q") type : String) : BookResponse
}
编辑:
你这个 formatter 来可视化 Json 响应。例如,当您为 Harry Potter
查询 API 时,您得到的是 单个 对象而不是对象列表。
OverviewViewModel
//change the datatype to hold a single object instead of a list
private val _properties = MutableLiveData<BookResponse>()
val properties: LiveData<BookResponse>
get() = _properties
BindingUtils
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: BookResponse) {
val adapter = recyclerView.adapter as RecyclerviewAdapter
adapter.submitList(data.items.toList())
}
项目
@Parcelize
data class Items(val volumeInfo: BookProperty, val id: String) : Parcelable
DiffCallback
class DiffCallback : DiffUtil.ItemCallback<Items>() {
override fun areItemsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem == newItem
}
}
data parsed successfully