I have RecycleView to show Chat from local database use Room and it works fine but when I try to scroll fast it does not respond for a couple seconds.
I already try to make simple logic onBindViewHolder but still the same. its because I use View.GONE and View.Visible in time group? how can I make this faster when scroll and I load data from local with limit 100.
this my Adapter using SortedList
class ContentAdapter(private val userUid: String, private val mListener: (code: Int, data: Content)->Unit): ExperimentalAdapter<Content, ContentAdapter.Holder>() {
private var lastDateTime: DateTime = DateFactory().getDateTimeDefaultTimezone()
private var lastPosition = 0
init {
mData = SortedListAsync(
Content::class.java,
object : SortedListAsyncAdapterCallback<Content>(this) {
override fun areContentsTheSame(oldItem: Content?, newItem: Content?): Boolean {
if(oldItem!!.dateTime == null){
oldItem.calculateDateTime()
}
if(newItem!!.dateTime == null){
newItem.calculateDateTime()
}
return oldItem.content == newItem.content &&
oldItem.status == newItem.status &&
oldItem.createdAt == newItem.createdAt
}
override fun areItemsTheSame(item1: Content?, item2: Content?): Boolean {
if(item1!!.dateTime == null){
item1.calculateDateTime()
}
if(item2!!.dateTime == null){
item2.calculateDateTime()
}
return item1.uid == item2.uid
}
override fun compare(o1: Content?, o2: Content?): Int {
if(o1!!.dateTime == null){
o1.calculateDateTime()
}
if(o2!!.dateTime == null){
o2.calculateDateTime()
}
return o2.createdAt.toDateTime().millis
.compareTo(o1.createdAt.toDateTime().millis)
}
})
setHasStableIds(true)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val timeExec = measureNanoTime {
var item: Content = mData.get(position)
var isShowTime = false
var timeString = ""
var itemBefore: Content? = null
if (position != 0) {
itemBefore = mData.get(position - 1)
}
if (itemBefore != null) {
val nowDate = item.dateTime!!
val beforeDate = itemBefore.dateTime!!
if(item.beforeDateTime == null || item.beforeDateTime != beforeDate){
d("do checking time")
if (!DateFactory().isStillInOneDay(nowDate, beforeDate)) {
when {
DateFactory().isStillInOneDay(lastDateTime, beforeDate) -> {
isShowTime = true
timeString = "Today"
}
DateFactory().isYesterday(lastDateTime, beforeDate) -> {
isShowTime = true
timeString = "Yesterday"
}
DateFactory().isTheDayBeforeYesterday(lastDateTime, beforeDate) -> {
isShowTime = true
timeString = "${beforeDate.dayOfWeek().asShortText}, ${beforeDate.dayOfMonth().get()}/${beforeDate.monthOfYear().get()}/${beforeDate.year().get()}"
}
else -> {
isShowTime = true
timeString = "${beforeDate.dayOfWeek().asShortText}, ${beforeDate.dayOfMonth().get()}/${beforeDate.monthOfYear().get()}/${beforeDate.year().get()}"
}
}
}
mData.get(position).apply {
this.timeString = timeString
this.isShowTime = isShowTime
this.beforeDateTime = beforeDate
}
item = mData.get(position)
}else{
d("Skip checking time")
}
lastPosition = position
}
holder.bind(item, item.isShowTime, item.timeString)
}
d("Time for exec ${mData.get(position).content} is $timeExec")
}
override fun getItemViewType(position: Int): Int {
val type = mData.get(position)
if(type.type == GlobalConfig.CONTENT_TYPE_IMAGE){
return if (type.createdBy == userUid){
10
}else{
11
}
}else if(type.type == GlobalConfig.CONTENT_TYPE_TEXT){
return if (type.createdBy == userUid){
20
}else{
22
}
}
return 99
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
d("inflate calling")
when(viewType){
10 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_image_mine, parent, false)
return HolderImageMine(view, mListener)
}
11 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_image_other, parent, false)
return HolderImageOther(view, mListener)
}
20 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_text_mine, parent, false)
return HolderTextMine(view, mListener)
}
22 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_text_other, parent, false)
return HolderTextOther(view, mListener)
}
else -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_text_mine, parent, false)
return HolderTextMine(view, mListener)
}
}
}
abstract class Holder(v: View): RecyclerView.ViewHolder(v){
private val mTimeLastRead: TextView = v.findViewById(R.id.victTVLastRead)
private val mTimeLastIndex: TextView = v.findViewById(R.id.victTVTimeIndex)
private val mHolderView: ConstraintLayout = v.findViewById(R.id.victRoot) ?: v.findViewById(R.id.include)
open fun bind(data: Content, isShowTime: Boolean, timeString: String){
if (mTimeLastRead.visibility != View.GONE) mTimeLastRead.visibility = View.GONE
if(isShowTime){
if (mTimeLastIndex.visibility != View.VISIBLE) mTimeLastIndex.visibility = View.VISIBLE
if(mTimeLastIndex.text != timeString) mTimeLastIndex.text = timeString
}else{
if(mTimeLastIndex.visibility != View.GONE) mTimeLastIndex.visibility = View.GONE
if(mTimeLastIndex.text != timeString) mTimeLastIndex.text = timeString
}
if(mTimeLastRead.visibility == View.GONE && mTimeLastIndex.visibility == View.GONE){
if(mHolderView.visibility != View.GONE) mHolderView.visibility = View.GONE
}else{
if(mHolderView.visibility != View.VISIBLE) mHolderView.visibility = View.VISIBLE
}
}
}
class HolderTextMine(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatTextMineTime)
private val mReadText: TextView = v.findViewById(R.id.viChatTextMineRead)
private val mContentText: TextView = v.findViewById(R.id.viChatTextMineContent)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
mContentText.text = data.content
mReadText.text = data.statusText
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
class HolderTextOther(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatTextTime)
private val mContentText: TextView = v.findViewById(R.id.viChatTextContent)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
mContentText.text = data.content
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
class HolderImageMine(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatImageTime)
private val mReadText: TextView = v.findViewById(R.id.viChatImageRead)
private val mImageView: ImageView = v.findViewById(R.id.viChatImageView)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
if(data.status != 0) {
val link = MediaApi().createUrlPictureContent(data.content)
val token = SharedUtils(itemView.context).getToken()!!
Glide.with(itemView).load(Connection.headerUrl(link, token))
.thumbnail(0.1f).apply(
RequestOptions().dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).override(18, 18)
).into(mImageView)
}else{
Glide.with(itemView).load(data.content).thumbnail(0.1f).apply(
RequestOptions().dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).override(18, 18)
).into(mImageView)
}
mReadText.text = data.statusText
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
class HolderImageOther(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatImageOtherTime)
private val mImageView: ImageView = v.findViewById(R.id.viChatImageOtherView)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
val link = MediaApi().createUrlPictureContent(data.content)
val token = SharedUtils(itemView.context).getToken()!!
Glide.with(itemView).load(Connection.headerUrl(link, token)).thumbnail(0.1f).apply(
RequestOptions().dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).override(18, 18)
).into(mImageView)
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
companion object {
const val ON_CLICK = 1
}
}
and this my RecycleView in Fragment
mRecycleView.apply {
layoutManager = object :LinearLayoutManager(context, RecyclerView.VERTICAL, true){
override fun supportsPredictiveItemAnimations(): Boolean {
return false
}
}
this.layoutManager?.isItemPrefetchEnabled = true
adapter = ContentAdapter(SharedUtils(context!!).getUid()!!){ code, data ->
when(code){
ContentAdapter.ON_CLICK -> {
if(data.type == GlobalConfig.CONTENT_TYPE_IMAGE){
val bundle = bundleOf(
Pair("image", data.content)
)
view?.findNavController()?.navigate(R.id.action_chatFragment_to_viewImageFragment, bundle)
}
}
}
}
clearOnScrollListeners()
addOnScrollListener(object :EndlessRecyclerOnScrollListener(layoutManager as LinearLayoutManager) {
override fun onLoadMore(current_page: Int, totalItem: Int) {
rLaunch {
mLoading = true
withContext(Dispatchers.Main) {
viewModel.isLoading.value = true
}
viewModel.loadContent(totalItem)
}
d("Load content Chat!")
}
})
itemAnimator = null
setOnTouchListener { view, motionEvent ->
this#ChatFragment.activity?.hideKeyboardFrom(mTextContent)
return#setOnTouchListener false }
}
this is my XML for show image message
<?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:id="#+id/viChatRootImageMine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="#+id/viChatRoomImageCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardBackgroundColor="#dbe8ff"
app:cardCornerRadius="10dp"
app:cardElevation="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/viChatRootImageLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/viChatImageView"
android:layout_width="100dp"
android:layout_height="150dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatImageTime"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#color/white" />
<TextView
android:id="#+id/viChatImageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
android:text="07:00"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatImageRead" />
<TextView
android:id="#+id/viChatImageRead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:text="R"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<include
android:id="#+id/include"
layout="#layout/view_item_chat_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/space4" />
<Space
android:id="#+id/space4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/viChatRoomImageCard" />
</androidx.constraintlayout.widget.ConstraintLayout>
and this for show text
<?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:id="#+id/viChatTextMineRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent">
<androidx.cardview.widget.CardView
android:id="#+id/viChatTextMineCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:padding="5dp"
app:cardBackgroundColor="#dbe8ff"
app:cardCornerRadius="10dp"
app:cardElevation="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/viChatTextMineLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/viChatTextMineTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
android:text="07:00"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatTextMineRead" />
<TextView
android:id="#+id/viChatTextMineRead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="R"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="#+id/viChatTextMineTime"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="#+id/viChatTextMineContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:scrollbarStyle="outsideOverlay"
android:text="Rofie Sagara"
android:textColor="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatTextMineTime"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedWidth="true" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="22dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<include
android:id="#+id/include"
layout="#layout/view_item_chat_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/space" />
<Space
android:id="#+id/space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/viChatTextMineCard" />
</androidx.constraintlayout.widget.ConstraintLayout>
Thanks
Thanks #FarshidABZ
Change ConstraintLayout to Linear make more fast in RecycleView
see this article
Related
enter image description here
When I click on cardview I want the other cardview color to turn white
here design is that i show in recyclerview
<?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="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:backgroundTint="#android:color/transparent"
android:elevation="0dp"
android:paddingBottom="10dp">
<androidx.cardview.widget.CardView
android:id="#+id/mainCard"
android:layout_width="80dp"
android:layout_height="100dp"
android:layout_marginLeft="1dp"
android:layout_marginTop="1dp"
android:layout_marginBottom="10dp"
app:cardCornerRadius="10dp"
app:cardElevation="15dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/cardImage"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_gravity="center"
android:layout_marginBottom="20dp"
app:srcCompat="#drawable/cate1" />
<TextView
android:id="#+id/mainText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="10dp"
android:text="Telefon, Tablet\nve Aksesuarlar"
android:textColor="#302E2E"
android:textSize="10sp" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="#+id/colorCard"
android:layout_width="82dp"
android:layout_height="102dp"
android:visibility="invisible"
app:cardBackgroundColor="#color/orange"
app:cardCornerRadius="10dp"
app:cardElevation="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The block with adapter codes for the recyclerview that I have listed
I couldn't understand the logic in setOnclickListener
and
I don't know how to make an algorithm
class CategoryMainAdapter(val mContext: Context) :
RecyclerView.Adapter<CategoryMainAdapter.ViewHolderClass>() {
var categoryMainList: List<CategoryMain> = listOf()
inner class ViewHolderClass(view: View) : RecyclerView.ViewHolder(view) {
val mainCard: CardView
val cardImage: ImageView
val mainText: TextView
val colorCard: CardView
init {
mainCard = view.findViewById(R.id.mainCard)
cardImage = view.findViewById(R.id.cardImage)
mainText = view.findViewById(R.id.mainText)
colorCard = view.findViewById(R.id.colorCard)
}
}
#SuppressLint("NotifyDataSetChanged")
fun setList(list: List<CategoryMain>) {
categoryMainList = list
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderClass {
val design =
LayoutInflater.from(mContext).inflate(R.layout.main_category_design, parent, false)
return ViewHolderClass(design)
}
override fun onBindViewHolder(holder: ViewHolderClass, position: Int) {
val categoryMain = categoryMainList[position]
holder.mainText.text = categoryMain.categoryName
holder.cardImage.setImageResource(categoryMain.categoryPhoto)
holder.mainCard.setOnClickListener() {
holder.colorCard.visibility = View.VISIBLE
holder.mainText.setTextColor(ContextCompat.getColor(mContext, R.color.orange))
}
}
override fun getItemCount(): Int {
return categoryMainList.size
}
}
You can use a multi-select functionality in recycler view like this: How to implement multi-select in RecyclerView? or Multiple selected items RecyclerView in Activity.java
i solved my problem
now when i click on which cardview its color changes
class CategoryMainAdapter(val mContext: Context) :
RecyclerView.Adapter<CategoryMainAdapter.ViewHolderClass>() {
var categoryMainList: List<CategoryMain> = listOf()
var clikedPosition : Int = -1
inner class ViewHolderClass(view: View) : RecyclerView.ViewHolder(view) {
val mainCard: CardView
val cardImage: ImageView
val mainText: TextView
val colorCard: CardView
init {
mainCard = view.findViewById(R.id.mainCard)
cardImage = view.findViewById(R.id.cardImage)
mainText = view.findViewById(R.id.mainText)
colorCard = view.findViewById(R.id.colorCard)
}
}
#SuppressLint("NotifyDataSetChanged")
fun setList(list: List<CategoryMain>) {
categoryMainList = list
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderClass {
val design =
LayoutInflater.from(mContext).inflate(R.layout.main_category_design, parent, false)
return ViewHolderClass(design)
}
override fun onBindViewHolder(holder: ViewHolderClass, position: Int) {
val categoryMain = categoryMainList[position]
holder.mainText.text = categoryMain.categoryName
holder.cardImage.setImageResource(categoryMain.categoryPhoto)
if (categoryMain.isSelected == true){
categoryMainList[position].isSelected = false
holder.colorCard.visibility = View.INVISIBLE
holder.mainText.setTextColor(ContextCompat.getColor(mContext, R.color.blacklow))
}
holder.mainCard.setOnClickListener() {
if (clikedPosition != -1){
notifyItemChanged(clikedPosition)
}
clikedPosition = position
categoryMainList[position].isSelected = true
holder.colorCard.visibility = View.VISIBLE
holder.mainText.setTextColor(ContextCompat.getColor(mContext, R.color.orange))
}
}
override fun getItemCount(): Int {
return categoryMainList.size
}
}
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.
So I am developing and android app in Kotlin with coroutines and no matter what change I make, I still keep getting the message:
I/Choreographer: Skipped 59 frames! The application may be doing too much work on its main thread.
How can I get rid of it. I mean like I am only displaying nine photos... Below is my code
Model:
data class Food (
val id: String,
val name: String,
val price: String,
#Json(name = "img_url") val imgSrcUrl: String,
val type: String,
val description: String,
val average_rating: String,
val number_of_raters: String,
val special_price: String
)
data class FoodCategory(
val id: String,
val title: String,
val foods: List<Food>
)
ViewModel:
enum class NetworkStatus {LOADING, DONE, FAILED}
enum class FontFamily (#FontRes val fontRes: Int) {
POPPINS_BOLD(R.font.poppins_bold),
POPPINS(R.font.poppins)
}
class FoodOverviewViewModel(private val foodRepository: FoodRepository): ViewModel() {
private lateinit var foodProducts: List<Food>
//This is the data that is gonna be exposed to the viewmodel
//It will be submitted to a ListAdapter
private val _foodCategory = MutableLiveData<List<FoodCategory>>()
val foodCategory: LiveData<List<FoodCategory>>
get() = _foodCategory
//Used to display a progress bar for network status
private val _status = MutableLiveData<NetworkStatus>()
val status: LiveData<NetworkStatus>
get() = _status
init {
getOverviewProducts()
}
private fun getOverviewProducts() {
viewModelScope.launch(Dispatchers.Default) {
_status.postValue(NetworkStatus.LOADING)
try {
getUpdatedFood()
Log.i("getOverviewProducts","I am running on tread: $coroutineContext")
_status.postValue(NetworkStatus.DONE)
}catch (e: Exception) {
_status.postValue(NetworkStatus.FAILED)
}
}
}
private suspend fun getUpdatedFood() {
//withContext(Dispatchers.Default) {
val limiter = 6 //Number of items I want to get from the server
val foodCategory = arrayListOf<FoodCategory>()
Log.i("getUpdatedFood","I am running on tread: $coroutineContext")
val getRecommended = foodRepository.getRecommendedFood(limiter.toString())
foodCategory += FoodCategory(id = 0.toString(), title = "Recommended for you", foods = getRecommended)
val getSpecials = foodRepository.getSpecials(limiter.toString())
foodCategory += FoodCategory(id = 1.toString(), title = "Specials", foods = getSpecials)
_foodCategory.postValue(foodCategory)
//}
}
}
Repository:
class FoodRepository {
suspend fun getRecommendedFood(limiter: String) = withContext(Dispatchers.IO) {
Log.i("Resp-getRecommended","I am running on tread: $coroutineContext")
return#withContext ProductApi.retrofitService.getRecommended(limiter)
}
suspend fun getSpecials(limiter: String) = withContext(Dispatchers.IO) {
Log.i("Resp-getSpecials","I am running on tread: $coroutineContext")
return#withContext ProductApi.retrofitService.getSpecials(limiter)
}
}
BindingAdapters:
//Load image using Glide (in Food item recycleview)
#BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView , imgUrl: String?) {
imgUrl?.let {
val imgUri = imgUrl.toUri().buildUpon().scheme("http").build()
Glide.with(imgView.context)
.load(imgUri)
.apply(
RequestOptions()
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image))
.into(imgView)
}
}
//set raters count (in Food item recycleview)
#BindingAdapter("ratersCount")
fun bindText(txtView: TextView, number_of_raters: String?) {
number_of_raters?.let {
val ratersCount = "(${number_of_raters})"
txtView.text = ratersCount
}
}
//update the progressbar visibilty (in outer-parent recycleview)
#BindingAdapter("updateStatus")
fun ProgressBar.updateStatus(status: NetworkStatus?) {
visibility = when (status) {
NetworkStatus.LOADING -> View.VISIBLE
NetworkStatus.DONE -> View.GONE
else -> View.GONE
}
}
//Hide or view an imageview based in the network Status. When network Error, an error image
//will show (in outer-parent recycleview)
#BindingAdapter("setNoInternet")
fun ImageView.setNoInternet(status: NetworkStatus?) {
when(status) {
NetworkStatus.LOADING -> {
visibility = View.GONE
}
NetworkStatus.DONE -> {
visibility = View.GONE
}
NetworkStatus.FAILED -> {
visibility = View.VISIBLE
setImageResource(R.drawable.ic_connection_error)
}
}
}
//Submit the list of FoodCatergory item to the outer-parent recycleview
#BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: List<FoodCategory>?) {
(recyclerView.adapter as FoodCategoryAdapter).submitList(data)
}
//Submit list the the Food item recyclew view (child recycleView)
#BindingAdapter("setProducts")
fun RecyclerView.setProducts(foods: List<Food>?) {
if (foods != null) {
val foodAdapter = FoodItemAdapter()
foodAdapter.submitList(foods)
adapter = foodAdapter
}
}
I have a Recycleview of Food Item and a Recycleview Pool of FoodCategory. If I comment out
_foodCategory.postValue(foodCategory)
in ViewModel: getUpdatedFood() than I do not get the message. However, when I submit the list to the outer recycleview (The one the hold the viewpool), than I get this answer. Please help. I been stuck on it for a while tryna get rid of that message.
Thank you..
Updated
Below is the adapeters and its view holders
FoodItem layout
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View"/>
<variable
name="foodItem"
type="com.example.e_commerceapp.models.Food"/>
<variable
name="font"
type="com.example.e_commerceapp.products.overview.FontFamily"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/child_item_main_layout"
android:background="#drawable/search_background"
android:layout_marginTop="10dp"
android:layout_marginStart="10dp"
android:layout_marginBottom="10dp"
android:layout_width="150dp"
android:layout_height="250dp">
<ImageView
android:layout_width="120dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:id="#+id/burger_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_height="160dp"
/>
<!-- app:imageUrl="#{foodItem.imgSrcUrl}"-->
<TextView
android:layout_width="match_parent"
android:layout_height="34dp"
android:layout_marginStart="5dp"
android:id="#+id/burger_title"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="5dp"
android:singleLine="true"
android:textColor="#B4000000"
app:layout_constraintTop_toBottomOf="#id/burger_image"
android:text="#{foodItem.name}"
android:textSize="12sp"
android:fontFamily="#font/poppins"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="35dp"
app:layout_constraintTop_toBottomOf="#id/burger_title"
android:layout_marginEnd="5dp"
android:gravity="center"
android:id="#+id/burger_price"
android:layout_marginStart="5dp"
app:layout_constraintStart_toEndOf="#id/special_price"
android:textColor="#D0000000"/>
<!-- app:price="#{foodItem.price}"-->
<!-- app:specialPrice="#{foodItem.special_price}"-->
<!-- app:fontRes="#{foodItem.special ? font.POPPINS : font.POPPINS_BOLD}"-->
<TextView
android:layout_width="wrap_content"
android:layout_height="35dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/burger_title"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:layout_marginStart="5dp"
android:id="#+id/special_price"
android:textColor="#D0000000"
android:visibility="gone"
android:textStyle="normal"
android:fontFamily="#font/poppins_bold"/>
<!-- app:setSpecialPrice="#{foodItem.special_price}"-->
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
app:layout_constraintTop_toBottomOf="#id/burger_price"
android:src="#drawable/ic_baseline_star_24"
android:visibility="#{foodItem.hasRating ? View.GONE : View.VISIBLE}"
android:id="#+id/rating_star"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="5dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="15dp"
android:layout_marginStart="5dp"
android:gravity="center"
android:textSize="12sp"
android:visibility="#{foodItem.hasRating ? View.GONE : View.VISIBLE}"
android:id="#+id/rating_count"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#id/burger_price"
android:text="#{foodItem.average_rating}"
android:layout_marginBottom="10dp"
app:layout_constraintStart_toEndOf="#id/rating_star"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="15dp"
android:id="#+id/number_of_raters"
android:textSize="12sp"
android:visibility="#{foodItem.hasRating ? View.GONE : View.VISIBLE}"
android:layout_marginStart="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#id/rating_count"
app:ratersCount="#{foodItem.number_of_raters}"
android:layout_marginBottom="10dp"
app:layout_constraintTop_toBottomOf="#id/burger_price"/>
<ImageView android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
FoodItem Adapter
class FoodItemAdapter: ListAdapter<Food ,
FoodItemAdapter.ItemFoodViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup , viewType: Int): ItemFoodViewHolder {
return ItemFoodViewHolder(
FoodItemBinding.inflate(LayoutInflater.from(parent.context),
parent, false))
}
override fun onBindViewHolder(holder: ItemFoodViewHolder , position: Int) {
val currentFood = getItem(position)
holder.bind(currentFood)
}
class ItemFoodViewHolder(private var binding: FoodItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(food: Food) {
binding.foodItem = food
binding.executePendingBindings()
}
}
object DiffCallback: DiffUtil.ItemCallback<Food>() {
override fun areItemsTheSame(oldItem: Food , newItem: Food): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Food , newItem: Food): Boolean {
return oldItem.id == newItem.id
}
}
}
FoodCategory layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="foodCategory"
type="com.example.e_commerceapp.models.FoodCategory"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:background="#fff"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:id="#+id/category_title"
android:layout_marginTop="16dp"
android:text="#{foodCategory.title}"
android:textColor="#2B2A2A"
android:fontFamily="#font/poppins_bold"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/nestedRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:setProducts="#{foodCategory.foods}"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/category_title"
tools:itemCount="4"
tools:listitem="#layout/food_item"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
FoodCategory Adapter
class FoodCategoryAdapter: ListAdapter<FoodCategory,
FoodCategoryAdapter.CategoryFoodViewHolder>(Companion) {
private val viewPool = RecyclerView.RecycledViewPool()
override fun onCreateViewHolder(parent: ViewGroup , viewType: Int): CategoryFoodViewHolder {
return CategoryFoodViewHolder(FoodCategoryBinding.inflate(LayoutInflater.from(parent.context),
parent, false))
}
override fun onBindViewHolder(holder: CategoryFoodViewHolder , position: Int) {
val currentFoodCategory = getItem(position)
holder.bind(currentFoodCategory)
}
inner class CategoryFoodViewHolder(private var binding: FoodCategoryBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(currentFoodCategory: FoodCategory?) {
binding.foodCategory = currentFoodCategory
binding.nestedRecyclerView.setRecycledViewPool(viewPool)
binding.executePendingBindings()
}
}
companion object: DiffUtil.ItemCallback<FoodCategory>() {
override fun areItemsTheSame(oldItem: FoodCategory , newItem: FoodCategory): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: FoodCategory, newItem: FoodCategory): Boolean {
return oldItem.id == newItem.id
}
}
}
The parent recycleView
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".products.overview.FoodOverviewFragment">
<data>
<variable
name="foodOverview"
type="com.example.e_commerceapp.products.overview.FoodOverviewViewModel"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:background="#color/grey"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:id="#+id/relative_layout"
android:layout_height="105dp"
android:elevation="8dp"
android:layout_marginBottom="5dp"
android:background="#fff"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:layout_width="200dp"
android:layout_marginTop="10dp"
android:layout_height="35dp"
android:id="#+id/logo_and_name"
android:src="#drawable/compony_logo_and_name"
android:layout_alignParentStart="true"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_marginTop="10dp"
android:id="#+id/notifications"
android:src="#drawable/ic_baseline_notifications_24"
android:layout_alignParentEnd="true"
android:paddingEnd="20dp"
android:paddingStart="20dp"/>
<TextView
android:layout_width="match_parent"
android:id="#+id/search"
android:layout_marginTop="10dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="#drawable/search_background"
android:layout_below="#id/logo_and_name"
android:gravity="center_vertical"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
android:paddingEnd="10dp"
android:paddingStart="10dp"
android:text="#string/search_text"
tools:ignore="RtlSymmetry"
app:drawableEndCompat="#drawable/ic_baseline_search_24"/>
</RelativeLayout>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:updateStatus="#{foodOverview.status}"
app:layout_constraintTop_toBottomOf="#id/relative_layout"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:id="#+id/progressbar"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/relative_layout"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:id="#+id/noInternetImage"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="0dp"
android:id="#+id/foodCategory"
android:clipToPadding="false"
tools:itemCount="4"
tools:listitem="#layout/food_category"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_height="0dp"
app:listData="#{foodOverview.foodCategory}"
app:layout_constraintTop_toBottomOf="#id/relative_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
The skipping frames likely has nothing to do with the code you posted: it sounds like a misconfiguration of RecyclerViews / Adapters to me. You'll need to post that code for it be more clear though.
However, even though what you posted likely isn't the culprit you can still optimize the coroutines code you have:
class FoodOverviewViewModel(private val foodRepository: FoodRepository): ViewModel() {
private lateinit var foodProducts: List<Food>
private val _foodCategory = MutableLiveData<List<FoodCategory>>()
val foodCategory: LiveData<List<FoodCategory>>
get() = _foodCategory
private val _status = MutableLiveData<NetworkStatus>()
val status: LiveData<NetworkStatus>
get() = _status
init {
getOverviewProducts()
}
private fun getOverviewProducts() {
viewModelScope.launch { // <------- Don't apply a custom scope here
_status.value = NetworkStatus.LOADING // <--- Don't call "postValue" here
try {
val food = getUpdatedFood() // <------ This is already using a background dispatcher
_foodCategory.value = food // <------- Emit this value here
_status.value = NetworkStatus.DONE
} catch (e: Exception) {
_status.value = NetworkStatus.FAILED
}
}
}
private suspend fun getUpdatedFood(): List<FoodCategory> { // <---- Return a value here
val limiter = 6 //Number of items I want to get from the server
val foodCategory = arrayListOf<FoodCategory>()
Log.i("getUpdatedFood","I am running on tread: $coroutineContext")
val getRecommended = foodRepository.getRecommendedFood(limiter.toString())
foodCategory += FoodCategory(id = 0.toString(), title = "Recommended for you", foods = getRecommended)
val getSpecials = foodRepository.getSpecials(limiter.toString())
foodCategory += FoodCategory(id = 1.toString(), title = "Specials", foods = getSpecials)
return foodCategories
}
}
The key ideas here:
Use viewModelScope.launch { ... } rather than applying a scope. You want everything in there that is not a coroutine to run on the main thread. See, for example, https://developer.android.com/kotlin/coroutines/coroutines-best-practices#main-safe
Return a value from getUpdatedFood so you can just update the _foodCategory value on the main thread in the viewModelScope
I am implementing android architecture component to view buyers list and select one.
Here is my Buyer entity
#Entity
data class Buyer(#PrimaryKey var id: Long = 0, var name: String = "", var photo: String = "", var address: String = "",
#Ignore var isSelected: Boolean = false,
#SerializedName("last_update_time") var lastUpdateTime: Long = 0L) {
}
I have inserted and displayed it in recyclerview.
What I want is to know how can I display particular buyer is selected, when click on one buyer.
If I click on one buyer previous selected buyer must deselect.
Please help me to implement this.
EDIT
class BuyerAdapter(private var buyers: ArrayList<Buyer>, private val listener: View.OnClickListener) : RecyclerView.Adapter<BuyerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BuyerViewHolder {
val v = LayoutInflater.from(parent.context).inflate(
R.layout.item_buyer, parent, false)
return BuyerViewHolder(v)
}
override fun onBindViewHolder(holder: BuyerViewHolder, position: Int) {
bindView(holder, position)
}
private fun bindView(holder: BuyerViewHolder, position: Int) {
val buyer = buyers[position]
holder.setName(buyer.name)
holder.setAddress(buyer.address)
holder.loadImage(ServiceHandler.BASE_URL + buyer.photo)
if (buyer.isSelected) {
holder.setCardColor(R.color.waveBlue)
holder.setNameColor(R.color.white)
holder.setAddressColor(R.color.white)
} else {
holder.setCardColor(R.color.white)
holder.setNameColor(R.color.contentGrey)
holder.setAddressColor(R.color.contentGreyDesc)
}
holder.itemView.tag = buyer
holder.itemView.setOnClickListener(listener)
}
override fun getItemCount(): Int = buyers.size
fun refresh(newBuyers: ArrayList<Buyer>) {
this.buyers = newBuyers
notifyDataSetChanged()
}
}
And here is my adapter xml item
<?xml version="1.0" encoding="utf-8"?><!--<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"--><!--xmlns:card_view="http://schemas.android.com/apk/res-auto"--><!--android:layout_width="match_parent"--><!--android:layout_height="191dp"--><!--android:paddingTop="13dp"--><!-->-->
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/buyer_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="4dp"
android:clickable="true"
card_view:cardCornerRadius="2dp"
card_view:cardElevation="4dp"
card_view:cardPreventCornerOverlap="true">
<android.support.constraint.ConstraintLayout
android:id="#+id/rlBuyerBack"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/ivLogo"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerCrop"
android:src="#drawable/placeholder_profile_photo"
card_view:layout_constraintBottom_toBottomOf="parent"
card_view:layout_constraintLeft_toLeftOf="parent"
card_view:layout_constraintTop_toTopOf="parent" />
<in.motiontech.wave.helper.WaveTextView
android:id="#+id/tvName"
style="#style/semiBoldFont"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="Name"
android:textColor="#color/contentGrey"
android:textSize="#dimen/tSizeHeader"
card_view:layout_constraintLeft_toRightOf="#+id/ivLogo"
card_view:layout_constraintTop_toTopOf="#+id/ivLogo" />
<in.motiontech.wave.helper.WaveTextView
android:id="#+id/tvAddress"
style="#style/regularFont"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="#id/tvName"
android:layout_below="#+id/tvName"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:text="Address"
android:textColor="#color/contentGrey"
android:textSize="#dimen/tSizeDesc"
card_view:layout_constraintLeft_toRightOf="#+id/ivLogo"
card_view:layout_constraintTop_toBottomOf="#+id/tvName" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
Edit 2
Here how I added data in recyclerview
viewModel.getBuyers().observe(this, Observer<List<Buyer>> {
if (it != null) {
if (it.isEmpty()) {
showProgress()
if (CommonUtils.isInNetwork(this)) {
viewModel.getBuyerList()
} else {
CommonUtils.showNoInternetDialog(this)
}
} else {
hideProgress()
buyerAdapter?.refresh(ArrayList(it))
}
}
})
What I have done is I have updated value of livedata. You can see below code:
fun selectBuyer(buyer: Buyer?) {
if (buyer == null)
return
buyers.value?.filter { it != buyer }?.forEach { it.isSelected = false }
buyers.value?.get(buyers.value!!.indexOf(buyer))?.isSelected = true
newBuyer = buyer
}
I notice there is no need to notifyupdate, as I am using observer pattern