My app is working fine when using the data class that i'm currently using from an online exercise: https://android-kotlin-fun-mars-server.appspot.com/. But when I try to change it to my own data class and json file (https://opendata.visitflanders.org/tourist/activities/breweries.json)
This is the data class that works fine:
@Parcelize
data class MarsProperty (
val id: String,
// used to map img_src from the JSON to imgSrcUrl in our class
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) : Parcelable {
val isRental
get() = type == "rent"
}
This is the one i'm trying to use: data class that doesn't work
@Parcelize
data class MarsProperty (
@Json(name = "Bottom Fermentation")val Bottom_Fermentation: Int,
val Changed_time: Int,
val Contact_email: String,
val Contact_name: String,
@Json(name = "Contact_phone/mobile")val Contact_phone_mobile: String,
val Deleted: Int,
@Json(name = "Mixed Fermentation")val Mixed_Fermentation: String,
val Name: String,
val Opening_times_en: String,
val Opening_times_nl: String,
@Json(name = "Possibility for individuals to join groups_en") val groups_en: String,
@Json(name = "Possibility for individuals to join groups_nl") val groups_nl: String,
@Json(name = "Related links") val Related_links: String,
@Json(name = "Sponaneuous Fermentation") val Sponaneuous_Fermentation: String,
@Json(name = "Top Fermentation") val Top_Fermentation: Int,
val Website: String,
val box_number: String,
val city_name: String,
val copyrights: String,
val description_en: String,
val description_nl: String,
@Json(name = "e-mail") val email: String,
val fax: String,
val house_number: Int,
val imagesURL: String,
val mobile: String,
val phone: String,
@Json(name = "postal_ code")val postal_code: Int,
val province: String,
val street: String,
val videoURL: String): Parcelable {
val joinGroup
get() = groups_nl == "Ja"
}
My OverviewFragment
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.inflate(inflater)
binding.lifecycleOwner = this
binding.viewModel = viewModel
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})
viewModel.navigateToSelectedProperty.observe(this, Observer {
if ( null != it ) {
// Must find the NavController from the Fragment
this.findNavController().navigate(OverviewFragmentDirections.actionShowDetail(it))
// Tell the ViewModel we've made the navigate call to prevent multiple navigation
viewModel.displayPropertyDetailsComplete()
}
})
return binding.root
}
}
My OverviewViewModel
class OverviewViewModel : ViewModel() {
private val _properties = MutableLiveData<List<MarsProperty>>()
val properties: LiveData<List<MarsProperty>>
get() = _properties
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
get() = _navigateToSelectedProperty
init {
getMarsRealEstateProperties()
}
private fun getMarsRealEstateProperties() {
viewModelScope.launch {
try {
_properties.value = MarsApi.retrofitService.getProperties()
} catch (e: Exception) {
_properties.value = ArrayList()
}
}
}
fun displayPropertyDetails(marsProperty: MarsProperty) {
_navigateToSelectedProperty.value = marsProperty
}
fun displayPropertyDetailsComplete() {
_navigateToSelectedProperty.value = null
}
}
activity_main.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@ id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
My fragment_overview.xml
<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="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.marsrealestate.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@ id/photos_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="6dp"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:listData="@{viewModel.properties}"
app:spanCount="1"
tools:itemCount="16"
tools:listitem="@layout/grid_view_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
My photogrid adapter
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
class MarsPropertyViewHolder(private var binding: GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
// This is important, because it forces the data binding to execute immediately,
// which allows the RecyclerView to make the correct view size measurements
binding.executePendingBindings()
}
}
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
return oldItem.Name == newItem.Name
}
}
override fun onCreateViewHolder(parent: ViewGroup,
viewType: Int): MarsPropertyViewHolder {
return MarsPropertyViewHolder(GridViewItemBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(marsProperty)
}
holder.bind(marsProperty)
}
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
}
If you need any more files let me know.
CodePudding user response:
So what you are trying to do is in your viewModel
_properties.value = MarsApi.retrofitService.getProperties()
Use appropriate methods:
setValue()
Sets the value. If there are active observers, the value will be dispatched to them. This method must be called from the main thread.
_properties.setValue(MarsApi.retrofitService.getProperties())
postValue()
If you need set a value from a background thread, you can use postValue(Object)
Posts a task to a main thread to set the given value. If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
_properties.postValue(MarsApi.retrofitService.getProperties())
CodePudding user response:
try this:
viewModelScope.launch(Dispatchers.IO) {
try {
_properties.postValue(MarsApi.retrofitService.getProperties())
} catch (e: Exception) {
_properties.postValue(ArrayList())
}
}