Home > Software design >  Recyclerview empty when changing data class
Recyclerview empty when changing data class

Time:12-20

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())
            }
        }
  • Related