I've been trying for a week loading items inside some horizontal recyclerviews contained in one vertical recyclerview(parent). Both need to have endless scrolling the horizontal ones to the right and the vertical while descending.
Right now the endless scrolling and the loading works. However, this is not working correctly because the horizontal ones are loading crazy data from the other children (My guess is that I'm creating the presenters inside the ViewHolder and some how data gets mixed up)
Is there any other way of doing this. That could work better and faster than my approach. Also, some this I can notice the vertical loading is not as smooth as I've seen in some other apps.
Any help would be greatly appreciated
class EventCatalogAdapter(private val presenter: EventCatalogPresenter):
RecyclerView.Adapter<EventCatalogAdapter.ViewHolder>() {
override fun getItemCount() = presenter.getCategoryEventCount()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
EventCatalogAdapter.ViewHolder(parent.inflate(R.layout.item_event_catalog), viewType)
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
presenter.bind(holder, position)
override fun getItemViewType(position: Int) = presenter.getItemType(position)
class ViewHolder(itemView: View, type: Int): RecyclerView.ViewHolder(itemView),
EventCatalogItemView, EventViewList, OnItemClickListener<Event>,
EndlessScrollListener.DataLoader {
private lateinit var companyId: String
private lateinit var catId: String
private val eventPresenter = EventPresenterImpl(this)
override val titleFormat: String? get() = ""
init {
val layoutManager = HorizontalLinearLayoutManager(itemView.context,
LinearLayoutManager.HORIZONTAL, false)
itemView.rvEvents.layoutManager = layoutManager
itemView.rvEvents.addItemDecoration(HorizontalItemDecorator(itemView.context))
itemView.rvEvents.adapter = EventAdapter(eventPresenter, type, this)
itemView.rvEvents.addOnScrollListener(EndlessScrollListener(layoutManager, this))
}
override fun setTitleVisibility(b: Boolean) {
itemView.tvCategoryTitle.visibility = if(!b) View.GONE else View.VISIBLE
}
override fun setCategoryTitle(title: String) {
itemView.tvCategoryTitle.text = title
}
override fun eventsByCategory(companyId: String, categoryId: String) {
this.companyId = companyId
catId = categoryId
eventPresenter.getEventsByCategories(companyId, categoryId)
}
override fun loadMoreData(totalItems: Int) {
Log.d("LOAD", "Event presenter: $eventPresenter for category: $catId")
eventPresenter.getMoreEventsByCategories(companyId, catId, totalItems)
}
override fun setActiveEvent(event: Event?) {}
override fun showSettings() {}
override fun sendMessage(action: String, bundle: Bundle?) {}
override fun showStatus(status: Int) {}
override fun refresh() {
itemView.rvEvents.adapter.notifyDataSetChanged()
}
override fun showMessageTemplate(code: Int) {}
override fun hideMessageTemplate() {
refresh()
}
override fun onItemClick(item: Event) {
}
}
}
class EventAdapter(private val presenter: EventPresenter,
private val listener: OnItemClickListener<Event>):
RecyclerView.Adapter<EventAdapter.ViewHolderItemView>() {
private var type: Int = EVENT_STANDARD
constructor(presenter: EventPresenter, type: Int, listener: OnItemClickListener<Event>) :
this(presenter, listener) {
this.type = type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ViewHolderItemView(parent.inflate(type))
override fun onBindViewHolder(holder: ViewHolderItemView, position: Int) =
presenter.bind(holder, position)
override fun getItemCount() = presenter.getCount()
class ViewHolderItemView(itemView: View) : RecyclerView.ViewHolder(itemView), EventItemView {
override fun setEventDate(date: Date?) {
itemView.tvDate.text = Tools.formatDate(itemView.context, date)
}
override fun setName(name: String) {
itemView.tvName.text = name
}
override fun setScannerVisibility(scannerVisibility: Boolean) {
itemView.ibScanner.visibility = if (scannerVisibility) View.VISIBLE else View.INVISIBLE
}
override fun setEventPoster(posterUrl: String, transformation: Transformation?) {
Tools.loadImage(posterUrl, itemView.ivPoster, transformation, R.mipmap.portrait_test)
}
override fun setTotalRegistrants(totalRegistrants: Long) {
itemView.tvQtyRegs.text = totalRegistrants.toString()
}
override fun addScanAction(event: Event) {
itemView.ibScanner.setOnClickListener {
val auth = FirebaseAuth.getInstance()
val prefs = PreferenceHelper.customPrefs(itemView.context, auth.currentUser!!.uid)
prefs.edit().putString(EventInteractorImpl.FIELD_EVENT_ID, event.eventId).apply()
(itemView.context as SettingsActivity).launchScanner()
}
}
override fun addEventAction(event: Event) {
itemView.setOnClickListener {
(itemView.context as SettingsActivity).showRegistrants(event)
}
}
}
}
// Parent items:
<android.support.constraint.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="220dp">
<TextView
android:id="#+id/tvCategoryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/vertical_spacing"
android:paddingBottom="#dimen/vertical_spacing"
android:layout_marginEnd="#dimen/horizontal_spacing"
android:layout_marginStart="#dimen/horizontal_spacing"
android:textAlignment="center"
android:textSize="18sp"
android:textAllCaps="true"
android:fontFamily="sans-serif-light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#string/txt_cat_title" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rvEvents"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="#dimen/vertical_spacing"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvCategoryTitle" />
</android.support.constraint.ConstraintLayout>
// Children items
<android.support.constraint.ConstraintLayout
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"
android:layout_width="110dp"
android:layout_height="match_parent"
android:stateListAnimator="#animator/tile_elevation">
<ImageView
android:id="#+id/ivPoster"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="#string/txt_event_image"
android:src="#mipmap/portrait_test"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvName"
style="#style/SubTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textIsSelectable="false"
android:textAllCaps="true"
app:layout_constraintBottom_toTopOf="#+id/ibRegistrants"
app:layout_constraintStart_toStartOf="#+id/ibRegistrants"
tools:text="Washington D.C. " />
<TextView
android:id="#+id/tvDate"
style="#style/SubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
app:layout_constraintBottom_toTopOf="#+id/tvName"
app:layout_constraintStart_toStartOf="#+id/ibRegistrants"
tools:text="03/23" />
<TextView
android:id="#+id/tvQtyRegs"
style="#style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textIsSelectable="false"
app:layout_constraintBottom_toBottomOf="#+id/ibRegistrants"
app:layout_constraintStart_toEndOf="#+id/ibRegistrants"
app:layout_constraintTop_toTopOf="#+id/ibRegistrants"
tools:text="140" />
<ImageView
android:id="#+id/ibRegistrants"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:contentDescription="#string/txt_registrants"
android:src="#drawable/ic_registrants_48px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="#+id/ibScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="#string/txt_registrants"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_scan_action" />
</android.support.constraint.ConstraintLayout>
Related
Hello stackoverflow community,
i am working on recycler adaptor within fragment, on which i have created an interface on click listener for which i have a callback in fragment. the problem i am facing is its working some times but most of the time its not generating the callback. let me know what i am doing wrong in this..
is it that my View is not getting the click listener, as i have added ripple effect which is not shown on view?
or i need to put the listener in the activity instead of fragment, which i did but no result.
the code to my adatper class
class UserMoneyRequestsAdaptor(
postItems: ArrayList<UserMoneyRequest>?, recItemClick: RecItemClick
) : RecyclerView.Adapter<BaseViewHolder>() {
private var isLoaderVisible = false
var mPostItems: ArrayList<UserMoneyRequest>? = postItems
var recItemClick: RecItemClick? = null
init {
this.recItemClick = recItemClick
}
interface RecItemClick {
fun onAcceptReqest(position: Int, moneyRequestModel: UserMoneyRequest)
fun onCancelReqest(position: Int, moneyRequestModel: UserMoneyRequest)
}
companion object {
private const val VIEW_TYPE_LOADING = 0
private const val VIEW_TYPE_NORMAL = 1
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return when (viewType) {
VIEW_TYPE_NORMAL -> ViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.user_request_list_item, parent, false)
)
VIEW_TYPE_LOADING -> ProgressHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_loading, parent, false)
)
else -> ViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.user_request_list_item, parent, false)
)
}
}
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
holder.onBind(position)
holder.setIsRecyclable(false)
}
override fun getItemViewType(position: Int): Int {
return if (isLoaderVisible) {
if (position == mPostItems!!.size - 1) VIEW_TYPE_LOADING else VIEW_TYPE_NORMAL
} else {
VIEW_TYPE_NORMAL
}
}
override fun getItemCount(): Int {
return if (mPostItems == null) 0 else mPostItems!!.size
}
fun addItems(postItems: ArrayList<UserMoneyRequest>) {
mPostItems!!.addAll(postItems!!)
notifyDataSetChanged()
}
fun addLoading() {
isLoaderVisible = true
mPostItems!!.add(
UserMoneyRequest(
"-1", "", "", "", "", "", "", "", "", "", "", UserAccount(
"",
"",
"",
"",
Currency("", "", ""),
User("", "")
)
)
)
notifyItemInserted(mPostItems!!.size - 1)
}
fun addData(listItems: ArrayList<UserMoneyRequest>) {
val size = mPostItems!!.size
mPostItems!!.addAll(listItems)
val sizeNew = this.mPostItems!!.size
notifyItemRangeChanged(size, sizeNew)
}
fun addAll(list: java.util.ArrayList<UserMoneyRequest>) {
mPostItems!!.addAll(list)
notifyDataSetChanged()
}
fun updateItem(pos: Int, paymentLinkListModel: UserMoneyRequest) {
notifyItemChanged(pos)
}
fun addOneItem(postItem: UserMoneyRequest) {
mPostItems!!.add(0, postItem)
notifyDataSetChanged()
}
fun removeOneItem(position: Int) {
mPostItems!!.removeAt(position)
notifyDataSetChanged()
}
fun getlist(positionIndex: Int): UserMoneyRequest {
return mPostItems!![positionIndex]
}
fun removeLoading() {
isLoaderVisible = false
val position = mPostItems!!.size - 1
val item: UserMoneyRequest = getItem(position)
if (item.id == "-1") {
mPostItems!!.removeAt(position)
notifyItemRemoved(position)
}
}
fun clear() {
mPostItems!!.clear()
notifyDataSetChanged()
}
fun getItem(position: Int): UserMoneyRequest {
return mPostItems!![position]
}
inner class ViewHolder internal constructor(itemView: View?) :
BaseViewHolder(itemView) {
private val tvStatus = itemView!!.findViewById(R.id.requestStatus) as TextView
val tvAmount = itemView!!.findViewById(R.id.requestAmount) as TextView
private val tvDate = itemView!!.findViewById(R.id.requestDateAndTime) as TextView
private val requestedFrom = itemView!!.findViewById(R.id.requestedFromId) as TextView
private val requestAccept = itemView!!.findViewById(R.id.requestAcceptStatus) as TextView
private val requestDecline = itemView!!.findViewById(R.id.requestDecline) as TextView
private val requestNotes = itemView!!.findViewById(R.id.requestNotes) as TextView
val context: Context = itemView!!.context
override fun clear() {}
override fun onBind(position: Int) {
super.onBind(position)
val moneyRequest = mPostItems!![position]
tvAmount.text = (moneyRequest.amount.toDouble() / 100).toString() + moneyRequest.user_account.currency.code
tvDate.text = getMonthDateTime(moneyRequest.created_at)
requestedFrom.text = moneyRequest.user_account.user!!.email
requestNotes.text = "Notes: " + moneyRequest.notes
setStatus(context, moneyRequest)
//first on clicklistener
requestAccept.setOnClickListener {
recItemClick!!.onAcceptReqest(position, moneyRequest)
}
//second on clicklistener
requestDecline.setOnClickListener {
recItemClick!!.onCancelReqest(position, moneyRequest)
}
}
#RequiresApi(Build.VERSION_CODES.O)
private fun setStatus(context: Context, moneyRequest: UserMoneyRequest) {
when (moneyRequest.status) {
"0" -> {
tvStatus.text = moneyRequest.statusStr
tvStatus.setBackgroundResource(R.drawable.request_pending_text_bg)
requestAccept.visibility = View.VISIBLE
requestAccept.isClickable = true
requestDecline.visibility = View.VISIBLE
requestDecline.isClickable = true
}
"1" -> {
tvStatus.text = moneyRequest.statusStr
tvStatus.setBackgroundResource(R.drawable.request_active_background_text)
}
"2" -> {
tvStatus.text = moneyRequest.statusStr
tvStatus.setTextColor(
ContextCompat.getColor(
context,
R.color.color_status_rejected
)
)
tvStatus.setBackgroundResource(R.drawable.request_canceled_text_background)
}
"3" -> {
tvStatus.text = moneyRequest.statusStr
tvStatus.setBackgroundResource(R.drawable.request_rejected_text_background)
}
"4" -> {
tvStatus.text = moneyRequest.statusStr
tvStatus.setBackgroundResource(R.drawable.request_completed_text_background)
}
}
}
}
inner class ProgressHolder internal constructor(itemView: View?) :
BaseViewHolder(itemView) {
override fun clear() {}
}
}
my root element of the recycler view
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:id="#+id/relPayoutRequestItem"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/detailsLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginTop="#dimen/_10sdp"
android:layout_marginBottom="#dimen/_4sdp"
android:layout_toStartOf="#+id/amountLayout"
android:orientation="vertical"
android:padding="#dimen/_6sdp">
<TextView
android:id="#+id/requestedFromId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="#font/spartanmbbold"
android:lineSpacingExtra="#dimen/_4sdp"
android:lines="1"
android:text="#string/dummy_text_transfers"
android:textColor="#color/text_color"
android:textSize="#dimen/_10sdp"
android:textStyle="bold" />
<TextView
android:id="#+id/requestNotes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_2sdp"
android:fontFamily="#font/spartanmb_semibold"
android:lineSpacingExtra="#dimen/_4sdp"
android:text="Notes: NIL"
android:textColor="#color/text_color"
android:textSize="#dimen/_10sdp"
android:textStyle="normal" />
</LinearLayout>
<LinearLayout
android:id="#+id/amountLayout"
android:layout_marginTop="#dimen/_14sdp"
android:layout_marginBottom="#dimen/_4sdp"
android:layout_marginLeft="#dimen/_10sdp"
android:layout_marginRight="#dimen/_10sdp"
android:layout_alignParentEnd="true"
android:orientation="vertical"
android:layout_width="#dimen/_68sdp"
android:layout_height="wrap_content">
<TextView
android:id="#+id/requestAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/spartan_mb_medium"
android:textStyle="bold"
android:lines="1"
android:ellipsize="end"
android:textSize="#dimen/_10sdp"
android:layout_gravity="center_vertical|end"
android:textColor="#color/text_color"
android:lineSpacingExtra="#dimen/_4sdp"
android:text="#string/dummy_transaction_amount"/>
</LinearLayout>
<RelativeLayout
android:id="#+id/bottomlayout"
android:layout_below="#+id/detailsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="#+id/requestDateAndTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="#dimen/_6sdp"
android:layout_marginTop="#dimen/_12sdp"
android:ellipsize="end"
android:fontFamily="#font/spartan_mb_medium"
android:lineSpacingExtra="#dimen/_6sdp"
android:lines="1"
android:text="#string/dummy_transfer_time"
android:textColor="#color/transaction_time_text_color"
android:textSize="#dimen/_8sdp"
android:textStyle="normal" />
<TextView
android:layout_centerVertical="true"
android:id="#+id/requestStatus"
android:layout_toStartOf="#+id/acceptRipple"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/spartanmbbold"
android:textStyle="bold"
android:layout_marginEnd="#dimen/_4sdp"
android:lines="1"
android:gravity="center"
android:layout_gravity="end"
android:textSize="#dimen/_8sdp"
android:textColor="#color/white"
android:lineSpacingExtra="#dimen/_6sdp"
android:text="#string/dummy_payment_method"/>
<!-- first view for clicklistener-->
<com.balysv.materialripple.MaterialRippleLayout
android:id="#+id/acceptRipple"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="#dimen/_6sdp"
android:layout_marginEnd="#dimen/_4sdp"
app:mrl_rippleAlpha="0.2"
android:layout_toStartOf="#+id/requestRippleDecline"
app:mrl_rippleColor="#color/ripple_black"
app:mrl_rippleHover="true"
app:mrl_rippleOverlay="true"
app:mrl_rippleRoundedCorners="#dimen/_12sdp">
<TextView
android:id="#+id/requestAcceptStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/spartanmbbold"
android:textStyle="bold"
android:visibility="gone"
android:lines="1"
android:gravity="center"
android:layout_gravity="end"
android:background="#drawable/user_request_active_background_text"
android:textSize="#dimen/_8sdp"
android:textColor="#color/white"
android:lineSpacingExtra="#dimen/_6sdp"
android:text="#string/accept"/>
</com.balysv.materialripple.MaterialRippleLayout>
<!-- second view for clicklistener-->
<com.balysv.materialripple.MaterialRippleLayout
android:id="#+id/requestRippleDecline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginTop="#dimen/_6sdp"
android:layout_marginEnd="#dimen/_10sdp"
app:mrl_rippleAlpha="0.2"
app:mrl_rippleColor="#color/ripple_black"
app:mrl_rippleHover="true"
app:mrl_rippleOverlay="true"
app:mrl_rippleRoundedCorners="#dimen/_12sdp">
<TextView
android:id="#+id/requestDecline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/spartanmbbold"
android:textStyle="bold"
android:lines="1"
android:background="#drawable/user_request_rejected_text_background"
android:gravity="center"
android:visibility="gone"
android:layout_gravity="end"
android:textSize="#dimen/_8sdp"
android:textColor="#color/white"
android:lineSpacingExtra="#dimen/_6sdp"
android:text="#string/decline"/>
</com.balysv.materialripple.MaterialRippleLayout>
</RelativeLayout>
<View
android:layout_marginStart="#dimen/_20sdp"
android:layout_marginEnd="#dimen/_15sdp"
android:layout_marginTop="#dimen/_10sdp"
android:layout_below="#+id/bottomlayout"
android:layout_width="match_parent"
android:layout_height="#dimen/_1sdp"
android:background="#color/settings_screen_seperator" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
Please check the below code, it is written on kotlin if you want it in java please let me know I will share.
class LocationAdapter (
private val list: List<Any>,
private val listener: ClickItemListener
) :
RecyclerView.Adapter<LocationAdapter.ViewHolder>() {
interface ClickItemListener {
fun onClicked(model: Any,position:Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(ItemLocationBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ViewHolder, parentPosition: Int) {
Timber.d("onBindViewHolder" + parentPosition)
val model = list.get(parentPosition)
holder.bind.name.text = model.name
holder.itemView.setOnClickListener {
listener.onClicked(model,parentPosition)
}
}
inner class ViewHolder(private var binding: ItemLocationBinding) :
RecyclerView.ViewHolder(binding.root) {
val bind = binding
}
}
//call in Activity or Fragment
locationList.adapter = LocationAdapter(sharedViewModel.listOfPlace,
object : LocationAdapter.ClickItemListener {
override fun onClicked(model: Any, position: Int) {
Timber.d("$position")
}
})
You can try this way :
class YourAdapter(private val onItemClick: (String) -> Unit) {
...
}
Then in your onBindViewHolder
override fun onBindViewHolder(holder: YourViewHolder, position: Int) {
holder.bind(
...,
onItemClick,
...,
)
}
Then in your view holder bind method
fun bind(onItemClick: (String) -> Unit) {
onItemClick("Whatever")
}
Then where you create your adapter have this :
val yourAdapter =
YourAdapter { test ->
//Here you'd get "Whatever" as string since is what you have in your ViewHolder
}
In case you need to notify the Activity then you have to have a callback between your Fragment and Activity, or just do the casting (not recommended) and call the method you want.
Example of doing this (there are more ways)
https://gist.github.com/zacharymikel/40aa61b2ff4d0b1ae267212d7dd965e5
https://tutorial.eyehunts.com/android/activity-and-fragments-communication/
the problem was within the view, i was using nested scroll view which was not generating the click listener
In some devices the recyclerview are flashing items and nothing more, after that, appear from items in row. I can't debug that because in emulator and my phone it doesn't happen.
Has anyone ever experienced that?
The correct thing would be to keep as the follow img:
I'm creating the RecyclerView as below, in SubItemsActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
subMainList = ArrayList()
subMainList = db.listSubItems("AND id_main_item=$mainId")
layoutManagerRecycler = LinearLayoutManager(this)
subMainItemsRecycler.layoutManager = layoutManagerRecycler
subMainItemsRecycler.addItemDecoration(DividerItemDecoration(this, 1))
var subMainAdapter = SubMainAdapter(this, subMainList, this, activate)
subMainAdapter.setHasStableIds(true)
subMainItemsRecycler.adapter = subMainAdapter
}
The recyclerView layout is here:
I have two ConstraintLayouts, the main is the first, with id "prioritize_edit", the other layout with id "prioritize_sub" will not appear in the image example.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/prioritize_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/counter"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:textSize="17sp"
android:textStyle="bold"
android:textColor="#color/white"
android:paddingStart="4dp"
android:paddingEnd="2dp"
android:paddingVertical="7.5dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="1dp"
android:background="#BFBFBF"
android:layout_marginStart="8dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/arrows"
app:layout_constraintTop_toTopOf="parent" >
</TextView>
<ImageView
android:id="#+id/arrows"
android:src="#drawable/icon_arrow_bottom"
android:layout_height="0dp"
android:layout_width="15dp"
android:paddingEnd="1dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/counter"
app:layout_constraintEnd_toStartOf="#+id/check_item"
app:layout_constraintTop_toTopOf="parent">
</ImageView>
<CheckBox
android:id="#+id/check_item"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_gravity="start|center_vertical"
android:gravity="start|center_vertical"
android:layout_marginStart="3dp"
android:layout_marginEnd="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/arrows"
app:layout_constraintEnd_toStartOf="#+id/color_picker"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="#+id/color_picker"
android:layout_width="35dp"
android:layout_height="wrap_content"
android:background="#null"
android:gravity="center"
android:layout_gravity="center"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:layout_marginLeft="13dp"
android:layout_marginStart="13dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/check_item"
app:layout_constraintEnd_toStartOf="#+id/list_subitem"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/list_subitem"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
android:textSize="17sp"
android:fontFamily="sans-serif-black"
android:textColor="#color/text_gray"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/color_picker"
app:layout_constraintEnd_toStartOf="#+id/menu_subitem"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/menu_subitem"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:paddingEnd="8dp"
android:src="#drawable/round_more_vert_black_36"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/list_subitem"
app:layout_constraintEnd_toStartOf="#+id/draggable_subitem"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
android:id="#+id/draggable_subitem"
android:layout_width="42dp"
android:layout_height="42dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_marginStart="0dp"
android:layout_marginEnd="10dp"
android:layout_marginVertical="0dp"
android:src="#drawable/icon_draggable"
android:background="#null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/menu_subitem"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/prioritize_sub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
android:layout_marginBottom="5dp">
<Button
android:id="#+id/prioritizer_button"
android:layout_width="0dp"
android:layout_height="45dp"
android:fontFamily="sans-serif-black"
android:text="#string/prioritize"
android:textColor="#color/white"
android:background="#color/colorPrimary"
android:alpha="0.80"
android:paddingHorizontal="15dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="15dp"
android:textSize="16sp"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/counter_invisible"/>
<TextView
android:id="#+id/counter_invisible"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="17sp"
android:textStyle="bold"
android:textColor="#color/white"
android:paddingHorizontal="10dp"
android:paddingVertical="7.5dp"
android:layout_marginTop="7dp"
android:background="#BFBFBF"
android:layout_marginStart="8dp"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/prioritizer_button"/>
</androidx.constraintlayout.widget.ConstraintLayout>
My Adapter is this:
class SubMainAdapter(
var activity: SubMainActivity,
var items: MutableList<SubItems>,
var clickListener: OnSubMainItemClickListener,
var activate: Boolean
) : RecyclerView.Adapter<SubMainViewHolder>(){
override fun getItemCount(): Int {
return items.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubMainViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(
R.layout.layout_subitem_view,
parent,
false
)
val viewHolder = SubMainViewHolder(itemView)
return viewHolder
}
#SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: SubMainViewHolder, position: Int) {
holder.initialize(
activity, items[position], clickListener, items.size,
items,
position
)
if (activate) {
holder.dragButton.visibility = View.VISIBLE
holder.check_item.visibility = View.GONE
holder.menuButton.visibility = View.GONE
} else {
holder.dragButton.visibility = View.GONE
holder.check_item.visibility = View.VISIBLE
holder.menuButton.visibility = View.VISIBLE
}
holder.dragButton.setOnTouchListener { v, event ->
if(event.actionMasked== MotionEvent.ACTION_DOWN){
v.performClick()
activity.touchHelper?.startDrag(holder)
}
false
}
}
override fun getItemId(position: Int): Long {
val subItems: SubItems = items[position]
return subItems.id.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
}
class SubMainViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var subMainItem = itemView.list_subitem
var counter = itemView.counter
var arrows = itemView.arrows
var counterInvisible = itemView.counter_invisible
var prioritizeSub = itemView.prioritize_sub
var prioritizerButton = itemView.prioritizer_button
var prioritizeEdit = itemView.prioritize_edit
var check_item = itemView.check_item
var deleteButton = R.id.trash_subitem
var menuButton = itemView.menu_subitem
var dragButton = itemView.draggable_subitem
var color_picker = itemView.color_picker
fun initialize(
activity: SubMainActivity,
item: SubItems,
action: OnSubMainItemClickListener,
sizeArrayItems: Int,
listItems: MutableList<SubItems>,
position: Int
){
// var counterValue: String = item.list_order.toString()
// var digitsNumber: Int = sizeArrayItems.toString().length
// while (counterValue.length<digitsNumber){
// counterValue = "0$counterValue"
// }
var counterValue: String = item.path_index
subMainItem.setText(item.sub_item)
counterInvisible.text = "$counterValue"
counter.text = "$counterValue"
counter.setBackgroundColor(Color.parseColor("#a5a5a5"))
counter.background.alpha = if(item.max_level==0) 0 else (255*item.level)/item.max_level
arrows.setBackgroundColor(Color.parseColor("#a5a5a5"))
arrows.background.alpha = if(item.max_level==0) 0 else (255*item.level)/item.max_level
val prevItem = if(position <= listItems.size){listItems[position]}else{if(position-1 <= listItems.size){listItems[position]}else{listItems[position - 1]}}
val childs = item.direct_childs
val nextItem = if(position >= listItems.size){listItems[position]}else{if(position+1 >= listItems.size){listItems[position]}else{listItems[position + 1]}}
if(childs==0){
arrows.setImageResource(R.drawable.icon_arrow_right)
}else{
if(nextItem.visibility == 0){
arrows.setImageResource(R.drawable.icon_arrow_right)
}else {
arrows.setImageResource(R.drawable.icon_arrow_bottom)
}
arrows.setOnClickListener {
action.toggleSubItems(item, adapterPosition, itemView)
}
counter.setOnClickListener {
action.toggleSubItems(item, adapterPosition, itemView)
}
}
if (item.prioritizeButton){
prioritizeSub.visibility = View.VISIBLE
val prioritizeString = activity.getString(R.string.prioritize)
val finalStr = String.format(prioritizeString, item.path_index)
prioritizerButton.text = finalStr
prioritizerButton.setOnClickListener{
action.onPrioritizeItemsClick(item)
}
prioritizeEdit.visibility = View.GONE
}else{
prioritizeSub.visibility = View.GONE
prioritizeEdit.visibility = View.VISIBLE
}
val param = itemView.layoutParams as RecyclerView.LayoutParams
if (item.visibility==1) {
param.height = LinearLayout.LayoutParams.WRAP_CONTENT
param.width = LinearLayout.LayoutParams.MATCH_PARENT
itemView.visibility = View.VISIBLE
} else {
itemView.visibility = View.GONE
param.height = 0
param.width = 0
}
itemView.layoutParams = param
if (item.checked==Constants.TRUE){
check_item.isChecked = true
subMainItem.setTextColor(ContextCompat.getColor(activity, (R.color.checked)))
counter.setTextColor(ContextCompat.getColor(activity, (R.color.checked)))
subMainItem.apply {
paintFlags = paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
}
counter.apply {
paintFlags = paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
}
}else{
check_item.isChecked = false
subMainItem.setTextColor(ContextCompat.getColor(activity, (R.color.text_grayer)))
counter.setTextColor(ContextCompat.getColor(activity, (R.color.text_gray_darker)))
subMainItem.apply {
paintFlags = paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
counter.apply {
paintFlags = paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
}
check_item.setOnClickListener{
action.onCheckItemClick(item, adapterPosition, itemView)
}
menuButton.setOnClickListener{
val popup = PopupMenu(activity, menuButton)
popup.inflate(R.menu.submain_options)
popup.setOnMenuItemClickListener(object : PopupMenu.OnMenuItemClickListener {
override fun onMenuItemClick(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
R.id.trash_subitem -> {
action.onExcludeItemClick(item, adapterPosition)
return true
}
R.id.add_sub_subitem -> {
action.onAddChildItemClick(item, adapterPosition)
return true
}
R.id.prioritize_sub_subitem -> {
action.manageDragButtons(true, item.id, item.direct_childs)
return true
}
else -> return false
}
}
})
popup.show()
}
val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(
text: CharSequence?,
start: Int,
before: Int,
count: Int
) {
action.onKeyDownItem(item, itemView, text)
}
}
subMainItem.addTextChangedListener(textWatcher)
color_picker.adapter = CustomSpinnerAdapter(
color_picker.context,
listOf(
SpinnerData(R.drawable.white_arrow),
SpinnerData(R.drawable.red_arrow),
SpinnerData(R.drawable.orange_arrow),
SpinnerData(R.drawable.yellow_arrow),
SpinnerData(R.drawable.green_arrow),
SpinnerData(R.drawable.blue_arrow),
SpinnerData(R.drawable.turquoise_arrow),
SpinnerData(R.drawable.purple_arrow)
)
)
// var checkSpinner: Int = 0
color_picker.isSelected = false;
color_picker.setSelection(item.color, false)
color_picker.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View,
spinnerPosition: Int,
l: Long
) {
// if(++checkSpinner > 1) {
action.onSpinnerChange(item, spinnerPosition)
// }
}
override fun onNothingSelected(adapterView: AdapterView<*>?) {
return
}
}
}
}
interface OnSubMainItemClickListener{
fun onCheckItemClick(item: SubItems, position: Int, view: View)
fun onExcludeItemClick(item: SubItems, position: Int)
fun onAddChildItemClick(item: SubItems, position: Int)
fun onPrioritizeItemsClick(item: SubItems)
fun onKeyDownItem(item: SubItems, view: View, text: CharSequence?)
fun onChangeItem(item: SubItems, view: View, text: CharSequence?)
fun onSpinnerChange(item: SubItems, spinnerPosition: Int)
fun manageDragButtons(activate: Boolean, parentId: Int, childs: Int)
fun toggleSubItems(item: SubItems, position: Int, view: View)
}
As I'm using setHasStableIds(true) I've tried to change the getItemId function in adapter, but the problem remains. Has anyone had a similar problem?
It was a problem in OnItemSelectedListener from my spinner. The function inside it was always calling the database list, because of OnItemSelectedListener are called one first time when it is being created, I've uncommented the checkSpinner variable in Adapter to prevent this first call and now it has been solved.
var checkSpinner: Int = 0
color_picker.isSelected = false;
color_picker.setSelection(item.color, false)
color_picker.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View,
spinnerPosition: Int,
l: Long
) {
if(++checkSpinner > 1) {
action.onSpinnerChange(item, spinnerPosition)
}
}
override fun onNothingSelected(adapterView: AdapterView<*>?) {
return
}
}
onBindViewHolder() will draw the views. You are having a lot of views visible in default case in the xml. All views that are going to be toggled with visibility must have visibility as gone or invisible by default in the xml. onBindViewHolder() can control the visibility once views are loaded.
The issue is recycler inflates the view in onCreateViewHolder(), the views are visible. After this the data is set and notifyDataSetChanged is called. This will update the views as per the conditions in the onBindViewHolder() function. But, till this happens, you see the views. Since this takes small amount of time, you see flashing.
This will happen in devices which are having low memory or slower processing.
I hope is all well with you.
I have constructed my code and build according the MVP style as well pulling JSON data but when I run the the code the horizontal recycle view and the JSON data is not being displayed. I tried going through every line of code and watching other tutorials but still no results.
Here is below my main activity:
class ProductCategoryActivity : BaseMvpActivity<ProductCategoryActivityView, ProductCategoryActivityPresnter> (),
ProductCategoryActivityView, CategoryAdapter.onItemClickListener{
private lateinit var binding: FragmentProductCategoryBinding
val data :MutableList<CateogryResponse> = ArrayList()
val adapter= CategoryAdapter(data, this)
#Inject
lateinit var presenter: ProductCategoryActivityPresnter
#Inject
lateinit var progressDialog: ProgressDialog
override fun onCreateComponent() {
userComponent.plus(CategoryActivityModule(this)).inject(this)
}
override fun providePresenter(): ProductCategoryActivityPresnter {
return presenter
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentProductCategoryBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
presenter.fetchcatogries()
showdata(ArrayList())
}
override fun showError(message: String) {
}
override fun showProgress() {
}
override fun hideProgress() {
}
override fun showdata(data: ArrayList<CateogryResponse>) {
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerViewPrimary.layoutManager = layoutManager
recyclerViewPrimary.setHasFixedSize(true)
recyclerViewPrimary.adapter = adapter
for(i in data)
data.add(CateogryResponse("product"))
}
override fun onItemClick(position: Int) {
Toast.makeText(this, "Item $position clicked", Toast.LENGTH_SHORT).show()
val clickedItem = data[position]
adapter.notifyItemChanged(position)
}
}
Here is my presenter:
#ActivityScope
class ProductCategoryActivityPresnter #Inject constructor(
private val stringProvider: StringProvider,
#AndroidScheduler private val observeOnScheduler: Scheduler,
#IOScheduler private val subscribeOnScheduler: Scheduler,
private val getCategoryUseCase: CategoryUseCase
) : BasePresenter<ProductCategoryActivityView>() {
lateinit var catogriesservice: CategoryRepositoryImpl
val catogriesLoadError = MutableLiveData<Boolean>()
val loading = MutableLiveData<Boolean>()
var catogries: ArrayList<CateogryResponse> = arrayListOf()
override fun onCreatePresenter(savedInstanceState: Bundle?) {
}
override fun onSaveInstanceState(outState: Bundle?) {
}
override fun onLoadData(arguments: Bundle?) {
fetchcatogries()
}
fun fetchcatogries() {
getCategoryUseCase.execute()
.observeOn(observeOnScheduler)
.subscribeOn(subscribeOnScheduler)
.subscribe(
SingleRequestSubscriber(
{
it
if (it != null && it.size > 0) {
catogries.removeAll(catogries)
catogries.addAll(it)
}
},
onFailure = { appException ->
view?.showError(
ErrorHandler.getErrorMessage(
appException,
stringProvider
)
)
},
onApiError = { apiException ->
view?.showError(
ErrorHandler.getErrorMessage(
apiException,
stringProvider
)
)
},
onAuthenticationError = { requestxception ->
view?.showError(
ErrorHandler.getErrorMessage(
requestxception,
stringProvider
)
)
},
onShowProgress = {
if (it) {
view?.showProgress()
} else {
view?.hideProgress()
}
},
onSubscribed = {
disposable.add(it)
})
)
}
}
Here is my view:
interface ProductCategoryActivityView {
fun showError(message: String)
fun showProgress()
fun hideProgress()
fun showdata ( data: ArrayList<CateogryResponse>)
}
Here is my adapter:
class CategoryAdapter(
private val data: List<CateogryResponse>,
private val listener: onItemClickListener
) : RecyclerView.Adapter<CategoryAdapter.ViewHolder>() {
private val items: MutableList<CardView>
init {
this.items = ArrayList()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_category_adapter, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvTitle.text = data[position].product
items.add(holder.card)
}
override fun getItemCount(): Int {
return data.size
}
inner class ViewHolder(itemView: View
) : RecyclerView.ViewHolder(itemView),
View.OnClickListener {
val tvTitle: TextView = itemView.featured_title
val card: CardView = itemView.CardView
init {
itemView.setOnClickListener(this)
}
override fun onClick(v: View?) {
val position: Int = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position)
}
}
}
interface onItemClickListener {
fun onItemClick(position: Int)
}
}
Image:
Fragment Category Adapter:
android:id="#+id/CardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:cardCornerRadius="2dp"
app:cardElevation="8dp">
<!-- We Will Add here the card & ImageViews -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<ImageView
android:id="#+id/featured_image"
android:layout_width="match_parent"
android:layout_height="140dp"
android:scaleType="centerCrop" />
<TextView
android:id="#+id/featured_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineHeight="23dp"
android:text="Chairs"
android:textColor="#color/colorAccent"
android:textSize="20sp" />
<TextView
android:id="#+id/featured_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="asbkd asudhlasn saudnas jasdjasl hisajdl asjdlnas" />
</LinearLayout>
</androidx.cardview.widget.CardView>
Fragment Product Category:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafcfe"
tools:context="ui.category.ProductCategoryActivity">
<ImageView
android:id="#+id/imageView3"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="#+id/imageView"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewPrimary"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginTop="20dp"
android:text="#string/topselling"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/constraintLayout" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toTopOf="#+id/imageView3"
app:layout_constraintEnd_toEndOf="#+id/constraintLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewSecondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="#+id/imageView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="#+id/imageView3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
/>
<TextView
android:id="#+id/textViewCategories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="15dp"
android:text="#string/catogries"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="#+id/constraintLayout"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteX="39dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
Not sure what went wrong.
If someone can point it out or show me what needs to be done to be able to display the data in horzintal recycle view, I would be really thanlful
You should update your recycler view adapter once your fetch call is executed, not before it ends.
Modify your fetchcatogries declaration so it accepts a callback method, which will be executed after the data are loaded.
This method will accept a list object as a parameter, which you will manage in your ProductCategoryActivity to populate the RecyclerView.
fun fetchcatogries(callback: (MutableList<CateogryResponse>) -> Unit) {
getCategoryUseCase.execute()
.observeOn(observeOnScheduler)
.subscribeOn(subscribeOnScheduler)
.subscribe(
SingleRequestSubscriber(
{ categories ->
callback(categories)
},
...
)
}
You can now edit your onCreate and showdata methods in ProductCategoryActivity like this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentProductCategoryBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
presenter.fetchcatogries { categories ->
showdata(categories as ArrayList<CateogryResponse>)
}
}
override fun showdata(data: ArrayList<CateogryResponse>) {
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerViewPrimary.layoutManager = layoutManager
recyclerViewPrimary.setHasFixedSize(true)
recyclerViewPrimary.adapter = CategoryAdapter(data.toList(), this)
}
I have created a recycle view and inside that using card view for items. I have a delete button inside a card view whenever I click on that button my item is deleted from SQLite database. But to reflect it on UI, app need to restart. How can I notify adpater that item is deleted?
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="57dp"
android:layout_height="64dp"
android:layout_marginEnd="40dp"
android:layout_marginBottom="40dp"
android:clickable="true"
android:onClick="addNewCredentials"
app:backgroundTint="#270867"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="#android:drawable/ic_menu_add" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" >
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
list_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/urlView"
android:layout_width="300dp"
android:layout_height="30dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:text="url"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#+id/userNameView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="#+id/userNameView"
android:layout_width="300dp"
android:layout_height="25dp"
android:layout_marginBottom="16dp"
android:text="userName"
app:layout_constraintBottom_toTopOf="#+id/passwordView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/passwordView"
android:layout_width="300dp"
android:layout_height="25dp"
android:layout_marginBottom="16dp"
android:text="password"
app:layout_constraintBottom_toTopOf="#+id/noteView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/noteView"
android:layout_width="300dp"
android:layout_height="30dp"
android:layout_marginBottom="16dp"
android:text="note"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/delButton"
android:layout_width="78dp"
android:layout_height="40dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="36dp"
android:background="#E6360F"
android:text="#string/delete_credential_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
MainActivity.kt
package com.example.passwordmanager
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL,false)
val db = DataBaseHandler(this)
val detailsData = db.readCredentials()
val adapter = CredentialAdapter(detailsData,this,{credentialsModel: CredentialsModel->deleteClick(credentialsModel)})
recyclerView.adapter = adapter
}
fun deleteClick(credential: CredentialsModel){
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
//adapter.notifyItemRemoved(position)
Toast.makeText(applicationContext,"Deleted", Toast.LENGTH_SHORT).show()
}
}
fun addNewCredentials(view : View){
print("hello world")
val intent = Intent(this, AddDetailActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
}
}
CredentialAdapter.kt
package com.example.passwordmanager
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.list_item_layout.view.*
class CredentialAdapter(
private val items: List<CredentialsModel>,
ctx: Context, val clickListener: (CredentialsModel) -> Unit
): RecyclerView.Adapter<CredentialAdapter.ViewHolder>() {
var context = ctx
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
fun bind(credential: CredentialsModel,clickListener: (CredentialsModel) -> Unit){
itemView.urlView.text = credential.url
itemView.userNameView.text = credential.userName
itemView.passwordView.text = credential.password
itemView.noteView.text = credential.note
itemView.delButton.setOnClickListener{clickListener(credential)}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_layout,parent,false))
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val credential:CredentialsModel = items[position]
holder.bind(credential,clickListener)
}
}
add remove setOnClickListener in your onBindViewHolder.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.remove.setOnClickListener {
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
notifyItemRemoved(holder.getAdapterPosition())
}
}
}
The best way to handle these kinds of situations is to use LiveData.
LiveData is basically an observable class which reads data only when there is a change.
What you can do is create a set function in your adapter like:
internal fun setData(data: List<Data>) {
this.data= dataList //this datalist is a list defined in your adapter
notifyDataSetChanged()
}
now in your main activity/fragment, create a LiveData List outside the onCreate function like this:
private lateinit var allData:LiveData<List<Data>>
Now inside your onCreate function, use can observe the livedata and set the data for recyclerview like this:
allData.observe(this, Observer { data->
data?.let { adapter.setData(it) }
})
You are deleting the item from database but not from the list inside recyclerview adapter.
class CredentialAdapter(
private val items: ArrayList<CredentialsModel>, // Change list to arraylist
ctx: Context, val clickListener: (CredentialsModel, Int) -> Unit
): RecyclerView.Adapter<CredentialAdapter.ViewHolder>() {
...
...
fun remove(position: Int) {
// Remove and notify the adapter to reload
items.removeAt(position)
notifyItemRemoved(position)
}
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView) {
fun bind(credential: CredentialsModel,clickListener: (CredentialsModel, Int) -> Unit){
...
...
// Pass adapter item position so that we can update the list after delete
itemView.delButton.setOnClickListener{clickListener(credential, adapterPosition)
}
}
...
...
}
Inside MainActivity.kt
fun deleteClick(credential: CredentialsModel, position: Int) {
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
adapter.remove(position)
Toast.makeText(applicationContext,"Deleted", Toast.LENGTH_SHORT).show()
}
}
use ListAdpater
class AdapterMain(var onClickListener: (Int) -> Unit) :
ListAdapter<Note, AdapterMain.NoteViewHolder>(DIFFCALBACK) {
companion object DIFFCALBACK : DiffUtil.ItemCallback<Note>() {
override fun areItemsTheSame(oldItem: Note, newItem: Note): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean {
return oldItem.title == newItem.title &&
oldItem.description == newItem.description &&
oldItem.priority == newItem.priority
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.note_item, parent, false)
return NoteViewHolder(view)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
holder.txtTitle.text = getItem(position).title
holder.txtDesc.text = getItem(position).description
holder.txtPriority.text = getItem(position).priority.toString()
}
inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var txtTitle: TextView = itemView.txt_title
var txtDesc: TextView = itemView.txt_desc
var txtPriority: TextView = itemView.txt_priority
init {
itemView.setOnClickListener { onClickListener(adapterPosition) }
}
}
fun getNoteAt(position: Int): Note {
return getItem(position)
}
}
you can see complete code of simple NoteApp with kotlin , recyclerView , MVVM and..
class coba : AppCompatActivity() {
private lateinit var recycleView :RecyclerView
private lateinit var datalis :ArrayList
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_coba)
recycleView = findViewById(R.id.rcycoba)
datalis = ArrayList()
val dtnama = arrayOf(
"Danial Sanganus",
"Bijonia Skolin",
"Alianes Pertoli",
"Sivanian Pertici",
"Olehsan alausi"
)
for (i in dtnama.indices){
datalis.add(
dataCoba(
dtnama[i]
)
)
populateData()
}
}
private fun populateData(){
val linearManager = LinearLayoutManager(this)
linearManager.reverseLayout=true
linearManager.stackFromEnd=true
recycleView.layoutManager=linearManager
val adp =adpCoba(this,datalis)
recycleView.adapter=adp
}
}
I'm using Recyclerview inside a Fragment Following Google's sample of MVP Android architecture and I tried to make the View part passive as possible following this article , which makes the whole Recyclerview Adapter passive of the Data Models and the presenter handles it.
Here is my code of the Fragment:
class OrderHistoryFragment : Fragment(), OrderHistoryContract.View {
lateinit var mPresenter: OrderHistoryContract.Presenter
lateinit var rvOrderHistory: RecyclerView
lateinit var orderHistoryAdapter : OrderHistoryAdapter
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val root = inflater!!.inflate(R.layout.order_history_fragment, container, false)
rvOrderHistory = root.findViewById<RecyclerView>(R.id.rvOrderHistory)
rvOrderHistory.layoutManager = LinearLayoutManager(context, LinearLayout.VERTICAL, false)
orderHistoryAdapter = OrderHistoryAdapter(mPresenter, object : HistoryItemListener {
override fun onReorder(orderHistory: OrderHistory) {
}
override fun onOpenOrder(orderHistory: OrderHistory) {
val orderIntent = Intent(activity, OrderDetailActivity::class.java)
orderIntent.putExtra("orderId", orderHistory.id)
startActivity(orderIntent)
}
})
rvOrderHistory.adapter = orderHistoryAdapter
return root
}
override fun onResume() {
super.onResume()
mPresenter.start()
}
override fun setPresenter(presenter: OrderHistoryContract.Presenter) {
mPresenter = checkNotNull<OrderHistoryContract.Presenter>(presenter)
}
override fun showLoadingIndicator(load: Boolean?) {
}
override fun updateOrdersAdapter() {
orderHistoryAdapter.notifyDataSetChanged()
}
override fun showSnackBar(Message: String) {
val parentLayout = activity.findViewById<View>(android.R.id.content)
val snackBar = Snackbar
.make(parentLayout, Message, Snackbar.LENGTH_INDEFINITE)
snackBar.setAction("Dismiss") { snackBar.dismiss() }
snackBar.setActionTextColor(Color.RED)
snackBar.show()
}
interface HistoryItemListener {
fun onReorder(orderHistory: OrderHistory)
fun onOpenOrder(orderHistory: OrderHistory)
}
companion object {
fun newInstance(): OrderHistoryFragment {
return OrderHistoryFragment()
}
}
fun OrderHistoryFragment() {
}
}
And this is my RecyclerView Adapters code
class OrderHistoryAdapter(internal var orderHistoryPresenter: OrderHistoryContract.Presenter, private val listener: OrderHistoryFragment.HistoryItemListener) : RecyclerView.Adapter<OrderHistoryAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.order_history_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
orderHistoryPresenter.onBindOrdersRow(position, holder)
holder.bReOrder!!.setOnClickListener { v -> listener.onReorder(orderHistoryPresenter.getOrderHistoryItem(position)) }
holder.cvOrderItem!!.setOnClickListener { v -> listener.onOpenOrder(orderHistoryPresenter.getOrderHistoryItem(position)) }
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return orderHistoryPresenter.getOrdersCount()
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), OrderHistoryContract.orderRowView {
internal var ivOrderVendor: ImageView? = null
internal var tvOrderId: TextView? = null
internal var tvOrderItems: TextView? = null
internal var tvOrderDate: TextView? = null
internal var tvOrderPrice: TextView? = null
internal var bReOrder: Button? = null
internal var cvOrderItem: CardView? = null
init {
ivOrderVendor = itemView.findViewById<ImageView>(R.id.ivOrderVendor)
tvOrderId = itemView.findViewById<TextView>(R.id.tvOrderId)
tvOrderItems = itemView.findViewById<TextView>(R.id.tvOrderItems)
tvOrderDate = itemView.findViewById<TextView>(R.id.tvOrderDate)
tvOrderPrice = itemView.findViewById<TextView>(R.id.tvOrderPrice)
bReOrder = itemView.findViewById<Button>(R.id.bReOrder)
cvOrderItem = itemView.findViewById<CardView>(R.id.cvOrderItem)
}
override fun setOrderImage(url: String) {
Glide.with(itemView.context).load(url).into(ivOrderVendor!!)
}
override fun setOrderDate(orderDate: String) {
tvOrderDate!!.text = orderDate
}
override fun setOrderId(orderId: String) {
tvOrderId!!.text = orderId
}
override fun setOrderItems(orderItems: ArrayList<String>) {
val stringBuilder = StringBuilder()
for (item in orderItems) {
stringBuilder.append(item)
}
tvOrderItems!!.text = stringBuilder.toString()
}
override fun setOrderPrice(orderPrice: String) {
tvOrderPrice!!.text = R.string.price.toString() + " " + orderPrice + " " + R.string.egp
}
}
}
And Here is code of the presenter which handles the Adapter data and it's binding to the ViewHolder
class OrderHistoryPresenter internal constructor(mDataRepository: DataRepository, mOrdeHistoryView: OrderHistoryContract.View) : OrderHistoryContract.Presenter {
private val mDataRepository: DataRepository
//refrence of the View to trigger the functions after proccessing the task
private val mOrdeHistoryView: OrderHistoryContract.View
private var orderHistoryItems = ArrayList<OrderHistory>()
init {
this.mDataRepository = checkNotNull(mDataRepository, "tasksRepository cannot be null")
this.mOrdeHistoryView = checkNotNull<OrderHistoryContract.View>(mOrdeHistoryView, "tasksView cannot be null!")
mOrdeHistoryView.setPresenter(this)
}
override fun start() {
mOrdeHistoryView.showLoadingIndicator(true)
mDataRepository.getCurrentUser(object : LocalDataSource.userRequestCallback {
override fun onUserRequestSuccess(botitUser: BotitUser) {
val urlParams = HashMap<String, String>()
urlParams.put(Endpoints.USER_ID_KEY, botitUser.userId!!)
val url = Endpoints.getUrl(Endpoints.urls.ORDER_HISTORY, urlParams)
mDataRepository.buildEndPointRequest(url, " ", Endpoints.requestsType.GET, object : EndpointDataSource.RequestCallback {
override fun onRequestSuccess(Body: String) {
try {
mOrdeHistoryView.showLoadingIndicator(false)
orderHistoryItems = JSONParser.parseData(JSONParser.parsers.ORDER_HISTORY, JSONObject(Body)) as ArrayList<OrderHistory>
mOrdeHistoryView.updateOrdersAdapter()
} catch (e: JSONException) {
e.printStackTrace()
}
}
override fun onRequestError(Body: String) {
mOrdeHistoryView.showLoadingIndicator(false)
mOrdeHistoryView.showSnackBar("Cannot load data")
}
})
}
override fun onUserRequestError(Body: String) {
}
})
}
override fun refreshData() {
}
override fun getOrdersCount(): Int {
return orderHistoryItems.size
}
override fun onBindOrdersRow(position: Int, orderViewHolder: OrderHistoryContract.orderRowView) {
if (orderHistoryItems.isNotEmpty()) {
val orderHistory = orderHistoryItems[position]
// orderViewHolder.setOrderDate(orderHistory.orderDate!!)
orderViewHolder.setOrderId(orderHistory.orderId!!)
orderViewHolder.setOrderImage(orderHistory.orderImage!!)
orderViewHolder.setOrderItems(orderHistory.orderItems)
orderViewHolder.setOrderPrice(orderHistory.orderPrice!!)
}
}
override fun getOrderHistoryItem(position: Int): OrderHistory {
return orderHistoryItems[position]
}
override fun actionReOrder(ordreId: String) {
}
}
Here is the Fragment XML
<android.support.v7.widget.RecyclerView android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/rvOrderHistory"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
</android.support.v7.widget.RecyclerView>
and Here is the RecyclerView Item XML order_history_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cvOrderItem"
android:layout_margin="4dp"
android:orientation="vertical">
<android.support.constraint.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:id="#+id/ivOrderVendor"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/mac" />
<TextView
android:id="#+id/tvOrderId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Order #2123"
android:textAppearance="#style/TextAppearance.AppCompat.Body2"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toRightOf="#+id/ivOrderVendor"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvOrderItems"
android:layout_width="242dp"
android:layout_height="35dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="MacDonald’s: Big Mac Beef, Big Tasty Beef. El Ezaby: Signal 2, Pantene Shampoo"
android:textAppearance="#style/TextAppearance.AppCompat.Small"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/tvOrderId"
app:layout_constraintLeft_toRightOf="#+id/ivOrderVendor"
android:layout_marginLeft="8dp"
app:layout_constraintHorizontal_bias="0.0" />
<TextView
android:id="#+id/tvOrderDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:text="03:22 PM 23/2/2017"
app:layout_constraintBottom_toTopOf="#+id/tvOrderItems"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintHorizontal_bias="1.0" />
<TextView
android:id="#+id/tvOrderPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:text="Price: 225.50 LE"
android:textAppearance="#style/TextAppearance.AppCompat.Body2"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvOrderItems" />
<Button
android:id="#+id/bReOrder"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_margin="4dp"
android:background="#drawable/chip_accent"
android:foreground="?attr/selectableItemBackground"
android:orientation="vertical"
android:padding="8dp"
android:text="Order Again"
android:textAllCaps="false"
android:textColor="#color/colorAccent"
android:textSize="15sp"
app:layout_constraintHorizontal_bias="0.937"
app:layout_constraintLeft_toRightOf="#+id/tvOrderPrice"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvOrderItems"
tools:layout_editor_absoluteY="74dp" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
The issue is that when I start the Activity showing the Fragment, the RecyclerView doesn't show the Item. It only appears if I scrolled the empty RecyclerView or by leaving the App on the foreground and go back to it again.
At the initialization the data of the Adapter is empty but onResume() I make a request which updates the data at the Presenter, Then I notifyDataChange() on the Adapter but nothing updates.
When I debugged I found that onBindViewHolder() isn't called after notifyDataChange() on the Adapter so i don't know why the notifyDataChange() doesn't notify the Adapter that the data is changed.
Anyone has an idea or any solution that might fix this issue?
You need to use runOnUiThread.
if(activity != null) {
activity!!.runOnUiThread {
root.Recycleview.adapter = Adapter(Array)
Adapter(Array).notifyDataSetChanged()
}
}
have a look at this answer
As stupid as it might sound, calling this line of code after setting data for recyclerView, helped me for this issue:
recyclerView.smoothScrollToPosition(0)
PS: technologies that I was using that may have something to do with this were: RJava, Retrofit2, NavigationUI, Fragments, LiveData, and Databinding.
EDIT:
I followed #SudoPlz comment and answer on another question and it also worked too, you have to extend RecyclerView and override requestLayout:
private boolean mRequestedLayout = false;
#SuppressLint("WrongCall")
#Override
public void requestLayout() {
super.requestLayout();
// We need to intercept this method because if we don't our children will never update
// Check https://stackoverflow.com/questions/49371866/recyclerview-wont-update-child-until-i-scroll
if (!mRequestedLayout) {
mRequestedLayout = true;
this.post(() -> {
mRequestedLayout = false;
layout(getLeft(), getTop(), getRight(), getBottom());
onLayout(false, getLeft(), getTop(), getRight(), getBottom());
});
}
}
while still, I would have preferred this to fixed after 4, 5 years, however, this was a good workaround, and you won't forget about them in your view.
I noticed in your item xml your constraintLayout height is match_parent right?
I recommend you to use it as wrap_content