Please help kotlin error 'No value passed for parameter' - kotlin

I have a chat application. But I am getting such error in adapters.
Error: No value passed for parameter 'itemView2' What I'm trying to do is to display the item named itemView2 in the xml page. I am not very proficient in Kotlin language. That's why I need help.
This is MessagingAdapter.kt
class MessagingAdapter(private val messages: OrderedRealmCollection<Message>, autoUpdate: Boolean,
private val context: Context, private val lifecycleOwner: LifecycleOwner, var user: User, private val myThumbImg: String,
private val selectedItems: LiveData<List<Message>>,
private val progressMap: LiveData<Map<String, Int>>, private val audibleState: LiveData<Map<String, AudibleState>>)
: RealmRecyclerViewAdapter<Message, RecyclerView.ViewHolder>(messages, autoUpdate)
, StickyHeaderAdapter<RecyclerView.ViewHolder> {
private val interaction = context as? Interaction?
private val contactHolderInteraction = context as? ContactHolderInteraction?
private val audibleHolderInteraction = context as? AudibleInteraction?
//timestamps to implement the date header
var timestamps = HashMap<Int, Long>()
var lastTimestampPos = 0
//date header
override fun getHeaderId(position: Int): Long {
return if (timestamps.containsKey(position)) {
timestamps[position] ?: 0
} else 0
}
//date header
override fun onCreateHeaderViewHolder(parent: ViewGroup): RecyclerView.ViewHolder? {
val view = LayoutInflater.from(parent.context).inflate(R.layout.row_day, parent, false)
return HeaderHolder(view)
}
//date header
override fun onBindHeaderViewHolder(viewholder: RecyclerView.ViewHolder?, position: Int) {
val mHolder = viewholder as HeaderHolder?
//if there are no timestamps in this day then hide the header
//otherwise show it
val headerId = getHeaderId(position)
if (headerId == 0L) mHolder?.header?.visibility = View.GONE else {
val formatted = TimeHelper.getChatTime(headerId)
mHolder?.header?.text = formatted
}
}
override fun getItemCount() = messages.size
override fun getItemViewType(position: Int): Int {
val message = messages[position]
return message.type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
// check the type of view and return holder
return getHolderByType(parent, viewType)
}
override fun onBindViewHolder(mHolder: RecyclerView.ViewHolder, position: Int) {
//get itemView type
val type = getItemViewType(position)
val message = messages[position]
when (type) {
MessageType.SENT_TEXT -> {
val sentTextHolder = mHolder as SentTextHolder
initHolder(sentTextHolder)
sentTextHolder.bind(message, user)
}
MessageType.SENT_IMAGE -> {
val sentImageHolder = mHolder as SentImageHolder
initHolder(sentImageHolder)
sentImageHolder.bind(message, user)
}
MessageType.SENT_VOICE_MESSAGE -> {
val sentVoiceMessageHolder = mHolder as SentVoiceMessageHolder
initHolder(sentVoiceMessageHolder)
initAudibleHolder(sentVoiceMessageHolder)
sentVoiceMessageHolder.bind(message, user)
}
MessageType.SENT_VIDEO -> {
val sentVideoMessageHolder = mHolder as SentVideoMessageHolder
initHolder(sentVideoMessageHolder)
sentVideoMessageHolder.bind(message, user)
}
MessageType.SENT_FILE -> {
val sentFileHolder = mHolder as SentFileHolder
initHolder(sentFileHolder)
sentFileHolder.bind(message, user)
}
MessageType.SENT_AUDIO -> {
val sentAudioHolder = mHolder as SentAudioHolder
initHolder(sentAudioHolder)
initAudibleHolder(sentAudioHolder)
sentAudioHolder.bind(message, user)
}
MessageType.SENT_CONTACT -> {
val sentContactHolder = mHolder as SentContactHolder
initHolder(sentContactHolder)
initContactHolder(sentContactHolder)
sentContactHolder.bind(message, user)
}
MessageType.SENT_LOCATION -> {
val sentLocationHolder = mHolder as SentLocationHolder
initHolder(sentLocationHolder)
sentLocationHolder.bind(message, user)
}
MessageType.SENT_STICKER -> {
val sentStickerHolder = mHolder as SentStickerHolder
initHolder(sentStickerHolder)
sentStickerHolder.bind(message, user)
}
MessageType.RECEIVED_TEXT -> {
val holder = mHolder as ReceivedTextHolder
initHolder(holder)
holder.bind(message, user)
}
MessageType.RECEIVED_IMAGE -> {
val receivedImageHolder = mHolder as ReceivedImageHolder
initHolder(receivedImageHolder)
receivedImageHolder.bind(message, user)
}
MessageType.RECEIVED_VOICE_MESSAGE -> {
val receivedVoiceMessageHolder = mHolder as ReceivedVoiceMessageHolder
initHolder(receivedVoiceMessageHolder)
initAudibleHolder(receivedVoiceMessageHolder)
receivedVoiceMessageHolder.bind(message, user)
}
MessageType.RECEIVED_VIDEO -> {
val receivedVideoMessageHolder = mHolder as ReceivedVideoMessageHolder
initHolder(receivedVideoMessageHolder)
receivedVideoMessageHolder.bind(message, user)
}
MessageType.RECEIVED_FILE -> {
val receivedFileHolder = mHolder as ReceivedFileHolder
initHolder(receivedFileHolder)
receivedFileHolder.bind(message, user)
}
MessageType.RECEIVED_AUDIO -> {
val receivedAudioHolder = mHolder as ReceivedAudioHolder
initHolder(receivedAudioHolder)
initAudibleHolder(receivedAudioHolder)
receivedAudioHolder.bind(message, user)
}
MessageType.RECEIVED_CONTACT -> {
val receivedContactHolder = mHolder as ReceivedContactHolder
initHolder(receivedContactHolder)
initContactHolder(receivedContactHolder)
receivedContactHolder.bind(message, user)
}
MessageType.RECEIVED_LOCATION -> {
val receivedLocationHolder = mHolder as ReceivedLocationHolder
initHolder(receivedLocationHolder)
receivedLocationHolder.bind(message, user)
}
MessageType.SENT_DELETED_MESSAGE -> {
val sentDeletedMessageHolder = mHolder as SentDeletedMessageHolder
sentDeletedMessageHolder.bind(message, user)
}
MessageType.RECEIVED_DELETED_MESSAGE -> {
val receivedDeletedMessageHolder = mHolder as ReceivedDeletedMessageHolder
receivedDeletedMessageHolder.bind(message, user)
}
MessageType.GROUP_EVENT -> {
val groupEventHolder = mHolder as GroupEventHolder
groupEventHolder.bind(message, user)
}
MessageType.RECEIVED_STICKER -> {
val receivedStickerHolder = mHolder as ReceivedStickerHolder
initHolder(receivedStickerHolder)
receivedStickerHolder.bind(message, user)
}
else -> {
val notSupportedTypeHolder = mHolder as? NotSupportedTypeHolder
notSupportedTypeHolder?.bind(message, user)
}
}
}
private fun initHolder(baseHolder: BaseHolder) {
baseHolder.selectedItems = selectedItems
baseHolder.progressMap = progressMap
baseHolder.lifecycleOwner = lifecycleOwner
baseHolder.interaction = interaction
}
private fun initAudibleHolder(audibleBase: AudibleBase) {
audibleBase.audibleInteraction = audibleHolderInteraction
audibleBase.audibleState = audibleState
}
private fun initContactHolder(contactHolderBase: ContactHolderBase) {
contactHolderBase.contactHolderInteraction = contactHolderInteraction
}
private fun distinctMessagesTimestamps() {
for (i in messages.indices) {
val timestamp = messages[i].timestamp.toLong()
if (i == 0) {
timestamps[i] = timestamp
lastTimestampPos = i
} else {
val oldTimestamp = messages[i - 1].timestamp.toLong()
if (!TimeHelper.isSameDay(timestamp, oldTimestamp)) {
timestamps[i] = timestamp
lastTimestampPos = i
}
}
}
}
//update timestamps if needed when a new message inserted
fun messageInserted() {
val index = messages.size - 1
val newTimestamp = messages[index].timestamp.toLong()
if (timestamps.isEmpty()) {
timestamps[index] = newTimestamp
lastTimestampPos = index
return
}
val lastTimestamp = timestamps[lastTimestampPos]!!
if (!TimeHelper.isSameDay(lastTimestamp, newTimestamp)) {
timestamps[index] = newTimestamp
lastTimestampPos = index
}
}
private fun getHolderByType(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
MessageType.DAY_ROW -> return TimestampHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_day, parent, false))
MessageType.SENT_DELETED_MESSAGE -> return SentDeletedMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_deleted_message, parent, false))
MessageType.RECEIVED_DELETED_MESSAGE -> return ReceivedDeletedMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_deleted_message, parent, false))
MessageType.SENT_TEXT -> return SentTextHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_message_text, parent, false))
MessageType.SENT_IMAGE -> return SentImageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_message_img, parent, false))
MessageType.RECEIVED_TEXT -> return ReceivedTextHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_text, parent, false)) //ERROR HERE
MessageType.RECEIVED_IMAGE -> return ReceivedImageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_img, parent, false))
MessageType.SENT_VOICE_MESSAGE -> return SentVoiceMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_voice_message, parent, false), myThumbImg)
MessageType.RECEIVED_VOICE_MESSAGE -> return ReceivedVoiceMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_voice, parent, false))
MessageType.RECEIVED_VIDEO -> return ReceivedVideoMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_video, parent, false))
MessageType.SENT_VIDEO -> return SentVideoMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_message_video, parent, false))
MessageType.SENT_FILE -> return SentFileHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_file, parent, false))
MessageType.RECEIVED_FILE -> return ReceivedFileHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_file, parent, false))
MessageType.SENT_AUDIO -> return SentAudioHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_audio, parent, false))
MessageType.RECEIVED_AUDIO -> return ReceivedAudioHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_audio, parent, false))
MessageType.SENT_CONTACT -> return SentContactHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_contact, parent, false))
MessageType.RECEIVED_CONTACT -> return ReceivedContactHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_contact, parent, false))
MessageType.SENT_LOCATION -> return SentLocationHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_location, parent, false))
MessageType.RECEIVED_LOCATION -> return ReceivedLocationHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_location, parent, false))
MessageType.GROUP_EVENT -> return GroupEventHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_group_event, parent, false))
MessageType.SENT_STICKER -> return SentStickerHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_sticker, parent, false))
MessageType.RECEIVED_STICKER -> return ReceivedStickerHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_sticker, parent, false))
}
return NotSupportedTypeHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_not_supported, parent, false))
}
init {
distinctMessagesTimestamps()
}
}
This is ReceivedTextHolder.kt
class ReceivedTextHolder(context: Context, itemView: View,itemView2: View) : BaseReceivedHolder(context,itemView) {
private var tvMessageContent: AXEmojiTextView = itemView.findViewById(R.id.tv_message_content)
private val circleImg: CircleImageView = itemView2.findViewById<View>(R.id.voice_circle_img_text) as CircleImageView
override fun bind(message: Message,user: User) {
super.bind(message,user)
tvMessageContent.text = message.content
loadUserPhoto(user,message.fromId, circleImg)
}
private fun loadUserPhoto(user:User,fromId: String, imageView: ImageView) {
//if it's a group load the user image
if (user.isGroupBool && user.group.users != null) {
val mUser = ListUtil.getUserById(fromId, user.group.users)
if (mUser != null && mUser.thumbImg != null) {
Glide.with(context).load(mUser.thumbImg).into(imageView)
}
} else {
if (user.thumbImg != null) Glide.with(context).load(user.thumbImg).into(imageView)
}
}
}
Please help guys :(
What I'm trying to do is to display the item named itemView2 in the xml page.

What the error means is that you're trying to call a function that asks for a parameter named itemView2, but you didn't pass that parameter. Usually this happens when you provide too few arguments when you call the method.
In this specific case, the method in question is actually the constructor of ReceivedTextHolder. As you can see, the constructor is declared to take 3 parameters (context, itemView, and itemView2):
class ReceivedTextHolder(context: Context, itemView: View,itemView2: View)
But when you call this constructor, you only give 2 arguments:
return ReceivedTextHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_text, parent, false)) //ERROR HERE
Here, the first argument (context) receives the value context, and the second argument (itemView) receives the value of the expression:
LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_text, parent, false)
But you're missing the 3rd argument. The 3rd would probably look like the 2nd one. You just need to find and inflate the correct view for that.

Related

Filter searchView from RecycleView with Adapter

Adapter class
class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) : RecyclerView.Adapter<AppListAdapter.AppViewHolder>() {
public val appList = arrayListOf<ApplicationInfo>()
private val checkedAppList = arrayListOf<Boolean>()
private val packageManager: PackageManager = context.packageManager
init {
context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA).sortedBy { it.loadLabel(packageManager).toString() }.forEach { info ->
if (info.packageName != context.packageName) {
if (info.flags and ApplicationInfo.FLAG_SYSTEM == 0) {
appList.add(info)
checkedAppList.add(initialChecked.contains(info.packageName))
}
}
}
}
inner class AppViewHolder(private val item: ItemAppBinding) : RecyclerView.ViewHolder(item.root) {
fun bind(data: ApplicationInfo, position: Int) {
item.txApp.text = data.loadLabel(packageManager)
item.imgIcon.setImageDrawable(data.loadIcon(packageManager))
item.cbApp.isChecked = checkedAppList[position]
item.cbApp.setOnCheckedChangeListener { _, checked ->
checkedAppList[position] = checked
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
return AppViewHolder(ItemAppBinding.inflate(LayoutInflater.from(context), parent, false))
}
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
holder.bind(appList[position], position)
}
override fun getItemCount(): Int {
return appList.size
}
on MainActivity
binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
binding.searchView2.clearFocus()
// how to write code filtered by query?
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
// how to write code filtered by newText?
return false
}
})
I'm newbie in kotlin..anyone can help?
I believe you want to filter items with "ApplicationInfo.txApp", so i will write the code for it.
First you need your adapter class to extend Filterable like below, and add one more list to hold all items:
class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) : RecyclerView.Adapter<AppListAdapter.AppViewHolder>(), Filterable {
public val appList = arrayListOf<ApplicationInfo>()
public val appListFull = ArrayList<ApplicationInfo>(appList)
// This full list because of when you delete all the typing to searchView
// so it will get back to that full form.
Then override it's function and write your own filter to work, paste this code to your adapter:
override fun getFilter(): Filter {
return exampleFilter
}
private val exampleFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val filteredList: ArrayList<ApplicationInfo> = ArrayList()
if (constraint == null || constraint.isEmpty()) {
// when searchview is empty
filteredList.addAll(appListFull)
} else {
// when you type something
// it also uses Locale in case capital letters different.
val filterPattern = constraint.toString().lowercase(Locale.getDefault()).trim()
for (item in appListFull) {
val txApp = item.txApp
if (txApp.lowercase(Locale.getDefault()).contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
#SuppressLint("NotifyDataSetChanged") #Suppress("UNCHECKED_CAST")
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
appList.clear()
appList.addAll(results!!.values as ArrayList<ApplicationInfo>)
notifyDataSetChanged()
}
}
And finally call this filter method in your searchview:
binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
yourAdapter.filter.filter(query)
yourAdapter.notifyDataSetChanged()
binding.searchView2.clearFocus()
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
yourAdapter.filter.filter(newText)
yourAdapter.notifyDataSetChanged()
return false
}
})
These should work i'm using something similar to that, if not let me know with the problem.

Can't Update child item in NestedRecyclerView

How do I update the child when clicking on the categories item or when doing a search like the one in the picture
I am updating the data from Viewmodel but the child element does not update
I also tried calling the adapter child and updating it directly, but to no avail
This is the code, if you need any additional details, I will add it down
In ParentAdapter
class BaseAdapter(private val listener: OnItemClickListener
): RecyclerView.Adapter<BaseViewHolder>() {
var items : MutableList<HomeItem<Any>> = mutableListOf()
lateinit var adapter : SubjectsAdapter
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return when (viewType){
VIEW_ITEM_SEARCH ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_search_view,
parent,false
)
BaseViewHolder.SearchItemViewHolder(view)
}
VIEW_ITEM_STUDENT ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_student, parent,false
)
BaseViewHolder.StudentItemViewHolder(view)
}
VIEW_ITEM_COLOR ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_chip_list, parent,false
)
BaseViewHolder.YearsItemViewHolder(view)
}
VIEW_ITEM_SUBJECT ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_subject_list, parent,false
)
BaseViewHolder.SubjectsItemViewHolder(view)
}
else -> throw Exception("UnKnow View Type")
}
}
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
when (holder){
is BaseViewHolder.SearchItemViewHolder -> bindSearch(holder,position)
is BaseViewHolder.StudentItemViewHolder -> bindStudent(holder,position)
is BaseViewHolder.YearsItemViewHolder -> bindYears(holder,position)
is BaseViewHolder.SubjectsItemViewHolder -> bindSubjects(holder,position)
}
}
fun bindYears(holder: BaseViewHolder.YearsItemViewHolder,position: Int){
val p = items[position].item as List<SubjectsColor>
val adapterchip = ChipAdapter { chip ->
listener.onSubjectColorClick(chip)
}
holder.binding.apply {
recyclerChipList.layoutManager = LinearLayoutManager(root.context,
LinearLayoutManager.HORIZONTAL,
false)
recyclerChipList.adapter = adapterchip
recyclerChipList.setHasFixedSize(true)
}
adapterchip.submitList(p)
}
fun bindSubjects(holder :BaseViewHolder.SubjectsItemViewHolder,position: Int){
val p = items[position].item as List<SubjectStudent>
adapter = SubjectsAdapter{ subject->
listener.onSubjectStudentClick(subject)
}
holder.binding.apply {
recyclerSubjectList.layoutManager = LinearLayoutManager(root.context,
LinearLayoutManager.VERTICAL,
false)
recyclerSubjectList.adapter = adapter
recyclerSubjectList.setHasFixedSize(true)
}
Log.d("BindSubjects","BindSubjects : $p")
adapter.submitList(p)
}
In ViewModel
#HiltViewModel
class HomeViewModel #Inject constructor(
private val subDao : SubjectsDao,
//state : SavedStateHandle
) : ViewModel(){
private val colorId = MutableLiveData(1)
val colorIdResult = colorId.asFlow().flatMapLatest(){ id ->
id.let {
subDao.getAllSubjectStudentByColor(id)
}
}.stateIn(viewModelScope, SharingStarted.Lazily, null)
fun getAllSubjectsColor() :Flow<List<SubjectsColor>> =
subDao.getAllSubjectColors()
fun onSubjectColorClick(colorI:Int){
colorId.postValue(colorI)
}}
In Fragment
adapterBase = BaseAdapter(this)
val itemsList: MutableList<HomeItem<Any>> = mutableListOf()
itemsList.add(HomeItem("Google Home", HomeItemType.TYPE_SEARCH))
itemsList.add(HomeItem("Student", HomeItemType.TYPE_PROFILE))
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.getAllSubjectsColor().collect {
itemsList.add(HomeItem(it, HomeItemType.TYPE_COLORS))
}
}
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.colorIdResult.collect { id ->
val result = id ?: return#collect
itemsList.add(HomeItem(result.subjectStudent, HomeItemType.TYPE_SUBJECTS))
//adapterBase.notifyDataSetChanged()
}
}
binding.apply {
recyclerBaseFragment.layoutManager = LinearLayoutManager(requireContext())
recyclerBaseFragment.setHasFixedSize(true)
recyclerBaseFragment.adapter = adapterBase
}
adapterBase.items = itemsList
}
enum class HomeItemType {
TYPE_SEARCH,
TYPE_PROFILE,
TYPE_COLORS,
TYPE_SUBJECTS
}
data class HomeItem<T>(
var item :T,
val type: HomeItemType
)

Problem saving data into room db from paging library api response

I have an application built using Jetpack Compose , where i also use paging library 3 to fetch data from db , i have multiple remote mediator where i fetch data and save it directly into database , the issue is that sometimes data gets saved , sometimes not , it goes to the point that sometimes one of the two only gets data stored.
Remote Mediator 1:
#ExperimentalPagingApi
class PopularClothingRemoteMediator #Inject constructor(
private val clothingApi: ClothingApi,
private val clothingDatabase: ClothingDatabase
) : RemoteMediator<Int, Clothing>(){
private val clothingDao = clothingDatabase.clothingDao()
private val clothingRemoteKeysDao = clothingDatabase.clothingRemoteKeysDao()
override suspend fun initialize(): InitializeAction {
val currentTime = System.currentTimeMillis()
val lastUpdated = clothingRemoteKeysDao.getRemoteKeys(clothingId = 1)?.lastUpdated ?: 0L
val cacheTimeout = 1440
val diffInMinutes = (currentTime - lastUpdated) / 1000 / 60
return if (diffInMinutes.toInt() <= cacheTimeout) {
// Log.d("RemoteMediator", "UP TO DATE")
InitializeAction.SKIP_INITIAL_REFRESH
} else {
// Log.d("RemoteMediator", "REFRESH")
InitializeAction.LAUNCH_INITIAL_REFRESH
}
}
override suspend fun load(loadType: LoadType, state: PagingState<Int, Clothing>): MediatorResult {
return try {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextPage?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
val prevPage = remoteKeys?.prevPage
?: return MediatorResult.Success(
endOfPaginationReached = remoteKeys != null
)
prevPage
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
val nextPage = remoteKeys?.nextPage
?: return MediatorResult.Success(
endOfPaginationReached = remoteKeys != null
)
nextPage
}
}
val response = clothingApi.getPopularClothing(page = page)
if (response.popularClothing.isNotEmpty()) {
clothingDatabase.withTransaction {
if (loadType == LoadType.REFRESH) {
clothingDao.deleteAllClothing()
clothingRemoteKeysDao.deleteAllRemoteKeys()
}
val prevPage = response.prevPage
val nextPage = response.nextPage
val keys = response.popularClothing.map { clothing ->
ClothingRemoteKeys(
clothingId = clothing.clothingId,
prevPage = prevPage,
nextPage = nextPage,
lastUpdated = response.lastUpdated
)
}
// When i debug this code , it works fine and the last line is executed
// the issue data sometimes gets saved , sometimes not
clothingRemoteKeysDao.addAllRemoteKeys(clothingRemoteKeys = keys)
clothingDao.addClothing(clothing = response.popularClothing)
}
}
MediatorResult.Success(endOfPaginationReached = response.nextPage == null)
} catch (e: Exception) {
return MediatorResult.Error(e)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Clothing>
): ClothingRemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.clothingId?.let { clothingId ->
clothingRemoteKeysDao.getRemoteKeys(clothingId = clothingId)
}
}
}
private suspend fun getRemoteKeyForFirstItem(
state: PagingState<Int, Clothing>
): ClothingRemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { clothing ->
clothingRemoteKeysDao.getRemoteKeys(clothingId = clothing.clothingId)
}
}
private suspend fun getRemoteKeyForLastItem(
state: PagingState<Int, Clothing>
): ClothingRemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { clothing ->
clothingRemoteKeysDao.getRemoteKeys(clothingId = clothing.clothingId)
}
}
}
Remote Mediator 2:
class OuterwearRemoteMediator #Inject constructor(
private val clothingApi: ClothingApi,
private val clothingDatabase: ClothingDatabase
) : RemoteMediator<Int, Clothing>() {
private val clothingDao = clothingDatabase.clothingDao()
private val clothingRemoteKeysDao = clothingDatabase.clothingRemoteKeysDao()
override suspend fun initialize(): InitializeAction {
val currentTime = System.currentTimeMillis()
val lastUpdated = clothingRemoteKeysDao.getRemoteKeys(clothingId = 1)?.lastUpdated ?: 0L
val cacheTimeout = 1440
val diffInMinutes = (currentTime - lastUpdated) / 1000 / 60
return if (diffInMinutes.toInt() <= cacheTimeout) {
// Log.d("RemoteMediator", "UP TO DATE")
InitializeAction.SKIP_INITIAL_REFRESH
} else {
// Log.d("RemoteMediator", "REFRESH")
InitializeAction.LAUNCH_INITIAL_REFRESH
}
}
override suspend fun load(loadType: LoadType, state: PagingState<Int, Clothing>): MediatorResult {
return try {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextPage?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
val prevPage = remoteKeys?.prevPage
?: return MediatorResult.Success(
endOfPaginationReached = remoteKeys != null
)
prevPage
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
val nextPage = remoteKeys?.nextPage
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
nextPage
}
}
val response = clothingApi.getOuterwear(page = page)
if (response.outerwear.isNotEmpty()) {
clothingDatabase.withTransaction {
if (loadType == LoadType.REFRESH) {
clothingDao.deleteAllClothing()
clothingRemoteKeysDao.deleteAllRemoteKeys()
}
val prevPage = response.prevPage
val nextPage = response.nextPage
val keys = response.outerwear.map { clothing ->
ClothingRemoteKeys(
clothingId = clothing.clothingId,
prevPage = prevPage,
nextPage = nextPage,
lastUpdated = response.lastUpdated
)
}
// the same thing here
// When i debug this code , it works fine and the last line is executed
// the issue data sometimes gets saved , sometimes not
clothingRemoteKeysDao.addAllRemoteKeys(clothingRemoteKeys = keys)
clothingDao.addClothing(clothing = response.outerwear)
}
}
MediatorResult.Success(endOfPaginationReached = response.nextPage == null)
} catch (e: Exception) {
return MediatorResult.Error(e)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Clothing>): ClothingRemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.clothingId?.let { clothingId ->
clothingRemoteKeysDao.getRemoteKeys(clothingId = clothingId)
}
}
}
private suspend fun getRemoteKeyForFirstItem(
state: PagingState<Int, Clothing>): ClothingRemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { clothing ->
clothingRemoteKeysDao.getRemoteKeys(clothingId = clothing.clothingId)
}
}
private suspend fun getRemoteKeyForLastItem(
state: PagingState<Int, Clothing>
): ClothingRemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { clothing ->
clothingRemoteKeysDao.getRemoteKeys(clothingId = clothing.clothingId)
}
}

Method param require nothing instead of typed param

There's superclass with typed parameter:
abstract inner class CalendarViewHolder<T : CalendarItem>(view: View) :
RecyclerView.ViewHolder(view) {
open fun bindData(data: T) {}
}
I expect that the method bindData will accept params which are objects of CalendarItem subclasses.
But in onBindViewHolder I got the following error:
Type mismatch.
Required:
Nothing
Found:
CalendarItem
Here's the implementation of the method:
override fun onBindViewHolder(holder: CalendarViewHolder<out CalendarItem>, position: Int) {
val item = items[position]
holder.bindData(item)
if (item is DayItem) {
if (holder is OnClickStrategy) {
holder.onClickListener = {
selectedDayOfYear = item.date?.get(Calendar.DAY_OF_YEAR) ?: 0
notifyItemChanged(position)
if (previousClickedPosition != -1) {
notifyItemChanged(previousClickedPosition)
}
previousClickedPosition = holder.adapterPosition
}
}
}
}
Here's the full code of adapter:
class CalendarAdapter :
RecyclerView.Adapter<CalendarAdapter.CalendarViewHolder<out CalendarItem>>() {
private val items = mutableListOf<CalendarItem>()
private var selectedDayOfYear = -1
private var previousClickedPosition = -1
abstract inner class CalendarViewHolder<T : CalendarItem>(view: View) :
RecyclerView.ViewHolder(view) {
open fun bindData(data: T) {}
}
abstract inner class CalendarDateViewHolder(view: View) : CalendarViewHolder<DayItem>(view) {
protected lateinit var tvDate: AppCompatTextView
protected lateinit var tvMonth: AppCompatTextView
protected lateinit var tvDayDescription: AppCompatTextView
protected lateinit var clRoot: ConstraintLayout
#CallSuper
override fun bindData(data: DayItem) {
tvDate = itemView.findViewById(R.id.tv_day_number)
tvMonth = itemView.findViewById(R.id.tv_month)
tvDayDescription = itemView.findViewById(R.id.tv_day_description)
clRoot = itemView.findViewById(R.id.cl_root)
}
}
interface OnClickStrategy {
var onClickListener: () -> Unit
}
inner class CalendarEmptyViewHolder(view: View) : CalendarViewHolder<EmptyDayItem>(view)
inner class CalendarWithDateViewHolder(view: View) :
CalendarDateViewHolder(view), OnClickStrategy {
override lateinit var onClickListener: () -> Unit
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.setOnClickListener {
onClickListener.invoke()
}
clRoot.background = ContextCompat.getDrawable(
itemView.context,
when (selectedDayOfYear) {
data.date?.get(Calendar.DAY_OF_YEAR) -> R.drawable.item_selected_background
else -> R.drawable.item_default_background
}
)
tvDate.setTextColor(
ContextCompat.getColor(
itemView.context,
if (selectedDayOfYear == data.date?.get(Calendar.DAY_OF_YEAR)) R.color.white
else R.color.black
)
)
}
}
inner class CalendarTodayDateViewHolder(view: View) : CalendarDateViewHolder(view) {
override fun bindData(data: DayItem) {
super.bindData(data)
tvDayDescription.text = "hoy"
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.background = ContextCompat.getDrawable(
itemView.context,
R.drawable.item_today_background
)
val monthName = when (data.date?.get(Calendar.MONTH)) {
0 -> "January"
1 -> "February"
else -> "March"
}
tvMonth.text = monthName
tvDate.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
tvDayDescription.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
tvMonth.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
}
}
inner class CalendarNewMonthDateViewHolder(view: View) : CalendarDateViewHolder(view),
OnClickStrategy {
override lateinit var onClickListener: () -> Unit
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
val monthName = when (data.date?.get(Calendar.MONTH)) {
0 -> "January"
1 -> "February"
else -> "March"
}
tvMonth.text = monthName
clRoot.setOnClickListener {
onClickListener.invoke()
}
tvDate.setTextColor(
ContextCompat.getColor(
itemView.context,
if (selectedDayOfYear == data.date?.get(Calendar.DAY_OF_YEAR)) R.color.white
else R.color.black
)
)
clRoot.background = ContextCompat.getDrawable(
itemView.context,
when (selectedDayOfYear) {
data.date?.get(Calendar.DAY_OF_YEAR) -> R.drawable.item_selected_background
else -> R.drawable.item_default_background
}
)
}
}
inner class CalendarDisabledDateViewHolder(view: View) : CalendarDateViewHolder(view) {
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.background = ContextCompat.getDrawable(
itemView.context,
R.drawable.item_disabled_background
)
tvDate.setTextColor(ContextCompat.getColor(itemView.context, R.color.silver))
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
) = when (viewType) {
CALENDAR_EMPTY_ITEM_TYPE -> onCreateCalendarEmptyViewHolder(parent)
CALENDAR_TODAY_DATE_ITEM_TYPE -> onCreateCalendarTodayDateViewHolder(parent)
CALENDAR_NEW_MONTH_DATE_ITEM_TYPE -> onCreateCalendarNewMonthDateViewHolder(parent)
CALENDAR_WITH_DATE_ITEM_TYPE -> onCreateCalendarWithDateViewHolder(parent)
else -> onCreateCalendarDisabledDateViewHolder(parent)
}
override fun getItemViewType(position: Int) = when (val item = items[position]) {
EmptyDayItem -> CALENDAR_EMPTY_ITEM_TYPE
is DayItem -> when {
item.isToday() -> CALENDAR_TODAY_DATE_ITEM_TYPE
item.isNewMonth() -> CALENDAR_NEW_MONTH_DATE_ITEM_TYPE
item.isDateEnabled -> CALENDAR_WITH_DATE_ITEM_TYPE
else -> CALENDAR_DISABLED_DATE_ITEM_TYPE
}
else -> UNKNOWN_ITEM_TYPE
}
private fun onCreateCalendarWithDateViewHolder(parent: ViewGroup) = CalendarWithDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarEmptyViewHolder(parent: ViewGroup): CalendarViewHolder<EmptyDayItem> =
CalendarEmptyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_empty, parent, false)
)
private fun onCreateCalendarTodayDateViewHolder(parent: ViewGroup) =
CalendarTodayDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarNewMonthDateViewHolder(parent: ViewGroup) =
CalendarNewMonthDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarDisabledDateViewHolder(parent: ViewGroup) =
CalendarDisabledDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
override fun onBindViewHolder(holder: CalendarViewHolder<out CalendarItem>, position: Int) {
val item = items[position]
holder.bindData(item)
if (item is DayItem) {
if (holder is OnClickStrategy) {
holder.onClickListener = {
selectedDayOfYear = item.date?.get(Calendar.DAY_OF_YEAR) ?: 0
notifyItemChanged(position)
if (previousClickedPosition != -1) {
notifyItemChanged(previousClickedPosition)
}
previousClickedPosition = holder.adapterPosition
}
}
}
}
override fun getItemCount() = items.size
fun setItems(items: MutableList<DayItem>) {
val localItems: MutableList<CalendarItem> = items.toMutableList()
if (!items.isNullOrEmpty()) {
val firstDayNumber = items[0].date?.get(Calendar.DAY_OF_WEEK) ?: 0
for (i in 1 until firstDayNumber) {
localItems.add(0, EmptyDayItem)
}
this.items.clear()
this.items.addAll(localItems)
}
}
companion object {
private const val CALENDAR_EMPTY_ITEM_TYPE = 0
private const val CALENDAR_TODAY_DATE_ITEM_TYPE = 1
private const val CALENDAR_NEW_MONTH_DATE_ITEM_TYPE = 2
private const val CALENDAR_WITH_DATE_ITEM_TYPE = 3
private const val CALENDAR_DISABLED_DATE_ITEM_TYPE = 4
private const val CALENDAR_HEADER_ITEM_TYPE = 5
private const val UNKNOWN_ITEM_TYPE = -1
}
}
sealed class CalendarItem
class DayItem(
val date: Calendar? = null,
val isDateEnabled: Boolean = true
): CalendarItem() {
fun isToday() = if (date != null) isSameDay(date, Calendar.getInstance()) else false
fun isNewMonth() = date?.get(Calendar.DAY_OF_MONTH) == 1
private fun isSameDay(cal1: Calendar, cal2: Calendar) =
cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)
}
object EmptyDayItem : CalendarItem()
What's the reason of the problem?
You specified the type as out CalendarItem, but the bind function consumes the item of type T, so it is in an in position, not out. So by marking it as out at the class declaration, you have restricted it to Nothing wherever T appears as a function parameter type.
There's no way to make generics work for you here, as I'm assuming you're trying to get it to allow you to bind items without casting. onCreateViewHolder has your ViewHolder in an out position, which means your particular ViewHolder cannot have invariant or contravariant (in) type if you want to be able to return different types of your ViewHolder subclass from onCreateViewHolder.
It's just not logically possible for generics to safely allow you to pass these items to the appropriate types, and you will have to use casting.
I suggest you eliminate the generic type from your abstract ViewHolder, and make each subclass throw an error if it gets the wrong type. Then you will quickly get exceptions when you test your app if there are any type mismatches and you will know the problem lies in either getItemViewType or onCreateViewHolder, where you've hooked up your classes and types incorrectly.
I also suggest using View Binding, because it will make these classes much more streamlined. No lateinit view properties or findViewById calls.
For example:
abstract class CalendarViewHolder(view: View): RecyclerView.ViewHolder(view) {
abstract fun bindData(data: CalendarItem): Any
// Any return type here allows subclasses to use "= with(binding)"
// to reduce nesting.
}
class CalendarDateViewHolder(private val binding: ItemCalendarDayBinding): MyViewHolder(binding.root) {
override fun bind(item: CalendarItem) = with(binding) {
item as? DayItem ?: error("Wrong item type.")
// item is now smart cast as DayItem
// bind the DayItem data to the views.
// can access the views directly by name in with block.
}
}
private fun onCreateCalendarWithDateViewHolder(parent: ViewGroup) = CalendarWithDateViewHolder(
ItemCalendarDayBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
And I suggest also getting rid of all those onCreate functions and adding constructors that use the parent ViewGroup, just to streamline things, for example:
class CalendarDateViewHolder private constructor(private val binding: ItemCalendarDayBinding): MyViewHolder(binding.root) {
constructor(parent: ViewGroup): this(ItemCalendarDayBinding.inflate(LayoutInflater.from(parent.context), parent, false))
//...
}

Observing MediatorLiveData Issue

I have the following LiveData variables in my ViewModel (simplified example):
val currentUser : LiveData<UserObject>
val allSites : LiveData<ArrayList<SiteObject>>
val filterSitesForUser : LiveData<Boolean>
val orderSitesByField : LiveData<String>
val orderSitesDirection : LiveData<Query.Direction>
val searchFilterSitesText : LiveData<String>
I'm trying to use MediatorLiveData to have one 'stream' of data connecting to my RecyclerView.
I therefore also have the following code in the ViewModel, which is observed in the Fragment:
fun sitesList() : LiveData<ArrayList<SiteObject>> {
val result = MediatorLiveData<ArrayList<SiteObject>>()
result.addSource(currentUser) {
result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
}
result.addSource(allSites) {
result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
}
result.addSource(filterSitesForUser) {
result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
}
result.addSource(orderSitesByField) {
result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
}
result.addSource(orderSitesDirection) {
result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
}
result.addSource(searchFilterSitesText) {
result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
}
return result
}
..along with this, also in the ViewModel:
private fun combineSitesData (
currentUser: LiveData<UserObject>,
allSites: LiveData<ArrayList<SiteObject>>,
filterSitesForUser: LiveData<Boolean>,
orderSitesByField: LiveData<String>,
orderSitesDirection: LiveData<Query.Direction>,
searchFilterSitesText: LiveData<String>
) : ArrayList<SiteObject> {
var sitesList = ArrayList<SiteObject>()
val userId = currentUser.value?.userID
if (userId == null || allSites.value == null) {
Log.d(TAG, "combineSitesData() - currentUser or allSites value null")
return sitesList
}
when (filterSitesForUser.value) {
true -> sitesList.addAll(allSites.value!!.filter { site -> site.users.contains(userId) })
false -> sitesList.addAll(allSites.value!!)
}
if (orderSitesDirection.value == Query.Direction.ASCENDING){
when (orderSitesByField.value) {
DATE_CREATED -> sitesList.sortBy { it.dateCreatedTimestamp }
DATE_EDITED -> sitesList.sortBy { it.dateEditedTimestamp }
SITE_TASKS -> sitesList.sortBy { it.siteTask }
SITE_RATING -> sitesList.sortBy { it.siteRating }
else -> sitesList.sortBy {it.siteReference}
}
}
if (orderSitesDirection.value == Query.Direction.DESCENDING){
when (orderSitesByField.value) {
DATE_CREATED -> sitesList.sortByDescending { it.dateCreatedTimestamp }
DATE_EDITED -> sitesList.sortByDescending { it.dateEditedTimestamp }
SITE_TASKS -> sitesList.sortByDescending { it.siteTask }
SITE_RATING -> sitesList.sortByDescending { it.siteRating }
else -> sitesList.sortByDescending {it.siteReference}
}
}
if (!searchFilterSitesText.value.isNullOrEmpty()) {
var filteredList = ArrayList<SiteObject>()
var filterPattern = searchFilterSitesText.value.toString().toLowerCase().trim()
for (site in sitesList) {
if(site.siteReference.toLowerCase().contains(filterPattern) || site.siteAddress.toLowerCase().contains(filterPattern)) {
filteredList.add(site)
}
}
Log.d(TAG, "combineSitesData() - returned filteredList (size = ${filteredList.size})")
return filteredList
}
Log.d(TAG, "combineSitesData() - returned sitesList (size = ${sitesList.size})")
return sitesList
}
This is the code in the Fragment which observes the list of Sites:
// Observe ViewModel SitesList
businessViewModel.sitesList().observe(viewLifecycleOwner, Observer { sitesList ->
if (sitesList != null) {
businessViewModel.currentUser.value?.userID?.let { sitesAdapter.setList(it, sitesList) }
sitesAdapter.notifyDataSetChanged()
Log.d (TAG, "setupViewModelObservers(): businessViewModel.sitesList().size = ${sitesList.size}" )
}
})
It seems to be working apart from the fact that when it is first observes, it fires off six times, which therefore updates my RecylcerView six times - when this is a large list, I see this being a bit of an issue!
After it initialises, it only updates once when any of the LiveData's change, so that's ok.
I'm not sure how to prevent this from happening, without holding a list in the Fragment or ViewModel to compare observed list to, which I am trying to avoid (as trying to follow MVVM architecture)..
EDIT: for clarity, I have added my RecyclerViewAdapter to show what the function setList does:
class SitesRecyclerViewAdapter(
private var currentUserId: String,
private val onItemClickedListener: (SiteObject) -> Unit,
private val onItemLongClickedListener: (SiteObject) -> Boolean
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var sitesList = ArrayList<SiteObject>()
class SiteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val listItemBinding = SitesListItemBinding.bind(itemView)
fun bind(
userId: String,
site: SiteObject,
clickListener: (SiteObject) -> Unit,
longClickListener: (SiteObject) -> Boolean
) {
// Set text fields
listItemBinding.sitesItemTitleText.text = site.siteReference
// listItemBinding.sitesItemProjectsText.text = site.recentProjectsText TODO: Create way of showing recent projects with arrays
//Reset Icons visibility
listItemBinding.sitesItemTaskImageView.visibility = View.INVISIBLE
listItemBinding.sitesItemRating1ImageView.visibility = View.INVISIBLE
listItemBinding.sitesItemRating2ImageView.visibility = View.INVISIBLE
listItemBinding.sitesItemRating3ImageView.visibility = View.INVISIBLE
listItemBinding.sitesItemFavouriteImageView.visibility = View.GONE
//Set sitePriority Icon visibility
if (site.siteTask) listItemBinding.sitesItemTaskImageView.visibility = View.VISIBLE
//Set siteRating Icon visibility
when(site.siteRating){
1 -> listItemBinding.sitesItemRating1ImageView.visibility = View.VISIBLE
2 -> listItemBinding.sitesItemRating2ImageView.visibility = View.VISIBLE
3 -> listItemBinding.sitesItemRating3ImageView.visibility = View.VISIBLE
}
//Set siteFavourite Icon visibility
if (site.users.contains(userId)) listItemBinding.sitesItemFavouriteImageView.visibility = View.VISIBLE
// Set Listeners
listItemBinding.sitesItemCardview.setOnClickListener { clickListener(site) }
listItemBinding.sitesItemCardview.setOnLongClickListener { longClickListener(site) }
// Not set map listener?
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
// LayoutInflater: takes ID from layout defined in XML.
// Instantiates the layout XML into corresponding View objects.
// Use context from main app -> also supplies theme layout values!
val inflater = LayoutInflater.from(parent.context)
// Inflate XML. Last parameter: don't immediately attach new view to the parent view group
val view = inflater.inflate(R.layout.sites_list_item, parent, false)
return SiteViewHolder(view)
}
override fun getItemCount(): Int = sitesList.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
// Populate ViewHolder with data that corresponds to the position in the list
// which we are told to load
(holder as SiteViewHolder).bind(
currentUserId,
sitesList[position],
onItemClickedListener,
onItemLongClickedListener
)
}
fun setList(userID: String, observedSites: ArrayList<SiteObject>) {
currentUserId = userID
sitesList = observedSites
notifyDataSetChanged()
Log.d(TAG, "setList(), observedSites size = ${observedSites.size}")
}
}
You can use postValue instead of setValue
If you called this method multiple times before a main thread executed a posted task, only
the last value would be dispatched.