I think the main problem happened during adding foodInfo into foodLists, and also the part where I pass data to listAdapter. I did check with Log.d for foodData.foodName and it works fine, but when I try Log.d to get the foodList it doesn't work.
here are my code for the moment
EDITED SOLVED
My Adapter
class listAdapter(val food : ArrayList<Foods>) : RecyclerView.Adapter<listAdapter.ViewHolder>() {
val unfoldedIndexes = HashSet<Int>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.cell, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return food.count()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(position: Int) = with(itemView) {
// folding_cell.setBackgroundColor(resources.getColor(R.color.colorPrimary))
folding_cell.backSideColor = resources.getColor(R.color.White)
if (unfoldedIndexes.contains(position)) {
cell_title_view.visibility = View.GONE
cell_content_view.visibility = View.VISIBLE
} else {
cell_content_view.visibility = View.GONE
cell_title_view.visibility = View.VISIBLE
}
itemView.setOnClickListener {
// toggle clicked cell state
folding_cell.toggle(false)
// register in adapter that state for selected cell is toggled
registerToggle(position)
}
}
private fun registerToggle(position: Int) {
if (unfoldedIndexes.contains(position))
registerFold(position)
else
registerUnfold(position)
}
private fun registerFold(position: Int) {
unfoldedIndexes.remove(position)
}
private fun registerUnfold(position: Int) {
unfoldedIndexes.add(position)
}
}
}
My Activity for the recyclerView
class FoodListActivity : BaseActivity(1) {
private val TAG = "FoodListActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_food_list)
setupBottomNavigation()
Log.d(TAG,"onCreate")
getFromFirebase()
}
private fun getFromFirebase() {
val currentUserUID = FirebaseAuth.getInstance().uid.toString()
val ref = FirebaseDatabase.getInstance().reference
ref.child("users").child(currentUserUID).child("foods").addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
setHasFixedSize(true)
recyclerview.layoutManager = LinearLayoutManager(this#FoodListActivity)
p0.children.forEach {
Log.d("getFood", it.toString())
val foodData = it.getValue(Foods::class.java)
if (foodData != null) {
val foodList = ArrayList<Foods>()
val adapter = ListAdapter(foodList)
foodList.add(foodData)
recyclerview.adapter = adapter
}
}
}
})
}}
Related
In my scenario, I have two fragments. In my first Fragment I have a RecyclerView with SelectionTracker. After selecting an item in my first fragment I can navigate to the second fragment. But from my second fragment if I navigate back to the first fragment using back button all my previous selection is lost. How can I prevent this?
I am using Jetpack Navigation Component to handle Fragment navigation.
Here is my fragment
class SelectVideoForCourseFragment : Fragment(R.layout.fragment_select_video_for_course) {
.....
..
private val adapter by lazy { MediaListAdapter() }
private lateinit var selectionTracker: SelectionTracker<String>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initializeListeners()
initializeRecyclerView(savedInstanceState)
fetchMedias()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
selectionTracker.onSaveInstanceState(outState)
}
private fun fetchMedias() {
val mediaRepo = MediaRepo()
mediaRepo.getVideos(contentResolver).observe(viewLifecycleOwner) {
adapter.submitItems(it)
}
}
private fun initializeRecyclerView(savedInstanceState: Bundle?) {
binding.mediaList.layoutManager = GridLayoutManager(requireContext(), 3)
binding.mediaList.addItemDecoration(GridSpacingItemDecoration(3, 5.dp, false))
binding.mediaList.adapter = adapter
selectionTracker = SelectionTracker.Builder(
"tracker-select-video-for-course",
binding.mediaList,
MediaItemKeyProvider(adapter),
MediaItemDetailsLookup(binding.mediaList),
StorageStrategy.createStringStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectSingleAnything()
).build()
if (savedInstanceState != null) {
selectionTracker.onRestoreInstanceState(savedInstanceState)
}
adapter.tracker = selectionTracker
}
......
...
}
My RecyclerView Adapter:
class MediaListAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val differ = AsyncListDiffer(this, DIFF_CALLBACK_MEDIA)
var tracker: SelectionTracker<String>? = null
fun submitItems(updatedItems: List<Any>) {
differ.submitList(updatedItems)
}
fun getSelections(): List<MediaEntity> {
....
..
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == 1) {
val binding = ListItemImageBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
ViewHolderImage(binding)
} else {
val binding = ListItemVideoBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
ViewHolderVideo(binding)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ViewHolderImage) {
tracker?.let {
val item = differ.currentList[position] as ImageEntity
holder.bind(item , it.isSelected(item.uri.toString()))
}
} else {
holder as ViewHolderVideo
val item = differ.currentList[position] as VideoEntity
tracker?.let {
holder.bind(item, it.isSelected(item.uri.toString()))
}
}
}
override fun getItemCount(): Int = differ.currentList.size
override fun getItemViewType(position: Int): Int {
return if (differ.currentList[position] is ImageEntity) {
1
} else {
2
}
}
inner class ViewHolderImage(
private val binding: ListItemImageBinding): RecyclerView.ViewHolder(binding.root
) {
fun bind(item: ImageEntity, isSelected: Boolean) {
.....
...
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<String> =
object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition(): Int = absoluteAdapterPosition
override fun getSelectionKey(): String {
val item = differ.currentList[absoluteAdapterPosition] as ImageEntity
return item.uri.toString()
}
override fun inSelectionHotspot(e: MotionEvent): Boolean = true
}
}
inner class ViewHolderVideo(
private val binding: ListItemVideoBinding,
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: VideoEntity, isSelected: Boolean) {
.....
...
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<String> =
object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition(): Int = absoluteAdapterPosition
override fun getSelectionKey(): String {
val item = differ.currentList[absoluteAdapterPosition] as VideoEntity
return item.uri.toString()
}
override fun inSelectionHotspot(e: MotionEvent): Boolean = true
}
}
companion object {
val DIFF_CALLBACK_MEDIA = object: DiffUtil.ItemCallback<Any>() {
.....
...
}
}
}
I have add an some condition in viewholder of recycleradapter of recyclerview to hide some items
but it shows empty spaces of removed items in recyclerview which I hide how can I solve these problem to remove empty spaces.is there any another way to hide the items in recyclerview if is then share us.
Bookadapter.kt
class bookadapter(
private var booklist: ArrayList<Booksmodel>,
private val itemClickListener: OnBookItemClicklistner
) : RecyclerView.Adapter<bookadapter.bookholder>() {
var searchText: String = ""
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): bookholder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.singlebook, parent, false)
return bookholder(view)
}
fun filterlist(filterlist: ArrayList<Booksmodel>, searchText: String) {
this.searchText = searchText
booklist = filterlist
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: bookholder, position: Int) {
val view = booklist[position]
holder.dind(view, itemClickListener)
}
override fun getItemCount(): Int {
return booklist.size
}
inner class bookholder(view: View) : RecyclerView.ViewHolder(view) {
val bookname: TextView = view.findViewById(R.id.recbooknametxt)
val bookpublication = view.findViewById<TextView>(R.id.recbookpubtxt)
val bookdept = view.findViewById<TextView>(R.id.recbookdepttxt)
val bookimage = view.findViewById<ImageView>(R.id.recbookimg)
val bookview = view.findViewById<CardView>(R.id.bookcardView)
fun bind(book: Booksmodel, clicklistner: OnBookItemClicklistner) {
val database = FirebaseDatabase.getInstance()
val auth = FirebaseAuth.getInstance()
database.getReference("Users").child(book.UserUID.toString())
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.value != null) {
val usercollege = snapshot.child("College").value.toString()
database.getReference("Users")
.child(auth.currentUser!!.uid)
.addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.value != null) {
val mycollege =
snapshot.child("College").value.toString()
if (usercollege == mycollege) {
if (searchText.isNotBlank()) {
val highlightedText = book.BookName!!.replace(
searchText,
"<font color='red'>$searchText</font>",
true
)
bookname.text =
HtmlCompat.fromHtml(
highlightedText,
HtmlCompat.FROM_HTML_MODE_LEGACY
)
} else {
bookname.text = book.BookName
}
//bookname.text=book.BookName
bookpublication.text = book.BookPublication
bookdept.text = book.Department
Picasso.get().load(book.BookImage).into(bookimage)
} else {
bookview.visibility = View.GONE
}
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
}
override fun onCancelled(error: DatabaseError) {
}
})
itemView.setOnClickListener {
clicklistner.onBookItemclick(book)
}
}
}
interface OnBookItemClicklistner {
fun onBookItemclick(books: Booksmodel)
}
}
Are you using Android studio?. It should have caught such errors.
override fun onBindViewHolder(holder: bookholder, position: Int) {
val view = booklist[position]
//holder.dind(view, itemClickListener) typo
holder.bind(view, itemClickListener)
}
If you want to hide the item completely
bookview.visibility = View.GONE
bookview.layoutParams = ViewGroup.LayoutParams(0,0) //sets width and height of the view
I'm working on a firebase realtime database android project. I build a feed system that shows all the users posts but it sorts all data at once. I need to show 10 posts then show progress bar then load 10 others etc...
Explore Fragment
class ExploreFragment : Fragment() {
private var postAdapter:PostAdapter? = null
private var postList: MutableList<Post>? = null
override fun onCreateView(
recyclerView.layoutManager = linearLayoutManager
postList= ArrayList()
postAdapter = context?.let { PostAdapter(it,postList as ArrayList<Post>) }
recyclerView.adapter = postAdapter
retrievePosts()
return view
}
private fun retrievePosts() {
val postRef = FirebaseDatabase.getInstance().reference.child("Posts")
postRef.addValueEventListener(object :ValueEventListener{
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()){
postList!!.clear()
for (snapshot in p0.children){
val post = snapshot.getValue(Post::class.java)!!
(postList as ArrayList<Post>).add(post)
}
postAdapter?.notifyDataSetChanged()
}
}
override fun onCancelled(p0: DatabaseError) {
}
})
}}
PostAdapter
class PostAdapter(private val mContext: Context,private val mPost: List<Post>
):RecyclerView.Adapter<PostAdapter.ViewHolder>()
{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(mContext).inflate(R.layout.post_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = mPost[position]
Picasso.get().load(post.getPost()).into(holder.postImg)
publisherInfo(holder.profileImg, holder.userName, post.getPublisher())
}
override fun getItemCount(): Int {return mPost.size}
inner class ViewHolder(#NonNull itemView: View): RecyclerView.ViewHolder(itemView)
{
var profileImg:CircleImageView
var postImg:ImageView
var userName:TextView
init {
profileImg = itemView.findViewById(R.id.profile_image_post)
postImg = itemView.findViewById(R.id.post_image)
userName = itemView.findViewById(R.id.username_post)
}
}
private fun publisherInfo(profileImg: CircleImageView, userName: TextView, publisherID: String) {
val userRef = FirebaseDatabase.getInstance().reference.child("Users").child(publisherID)
userRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
val user = p0.getValue<User>(User::class.java)
Picasso.get().load(user!!.getImage()).into(profileImg)
userName.text = user!!.getUsername()
}
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
}
I remove some lines to make the code simple
So my main problem at the moment is trying to add data to my recycler view. Essentially I created a button that adds the string "hello" to a list and then tries to call mAdapter.notifyItemInserted(dataSet.size). It adds perfectly to the end of the list in the logs, but I just can't figure out how to update the UI for it.
The only items I can manage to load at the moment are the items fakeItems that are created when I start the app.
class MainActivity : AppCompatActivity() {
var dataSet = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
insertFakeItems()
setUpRecycler()
}
private fun insertFakeItems() {
for (i in 0..5) {
dataSet.add("Item #$i")
}
}
private fun setUpRecycler() {
val mAdapter = MyAdapter(dataSet)
val mList = list
mList.layoutManager = LinearLayoutManager(this)
mList.adapter = mAdapter
mList.orientation =
DragDropSwipeRecyclerView.ListOrientation.VERTICAL_LIST_WITH_VERTICAL_DRAGGING
//mList.disableSwipeDirection(DragDropSwipeRecyclerView.ListOrientation.DirectionFlag.RIGHT)
// button
val onItemSwipeListener = object : OnItemSwipeListener<String> {
override fun onItemSwiped(
position: Int,
direction: OnItemSwipeListener.SwipeDirection,
item: String
): Boolean {
Log.d("Main", "Position = $position, Direction = $direction, Item = $item")
when (direction) {
OnItemSwipeListener.SwipeDirection.RIGHT_TO_LEFT -> {
Toast.makeText(applicationContext, "Item ${position+1} deleted", Toast.LENGTH_SHORT).show()
//todo: add deleted code here
}
OnItemSwipeListener.SwipeDirection.LEFT_TO_RIGHT -> {
Toast.makeText(applicationContext, "Item ${position+1} archived", Toast.LENGTH_SHORT).show()
//todo: add archived code here
}
}
return false
}
}
mList.swipeListener = onItemSwipeListener
fab_add.setOnClickListener {
Log.d("Main", "Button pressed")
dataSet.add(dataSet.size,"hello")
mAdapter.notifyItemInserted(dataSet.size)
println(dataSet)
}
}
}
And this is the adapter:
class MyAdapter(dataSet: MutableList<String>)
: DragDropSwipeAdapter<String, MyAdapter.ViewHolder>(dataSet) {
class ViewHolder(itemView: View) : DragDropSwipeAdapter.ViewHolder(itemView) {
val itemText: TextView = itemView.findViewById(R.id.item_text)
val dragIcon: ImageView = itemView.findViewById(R.id.drag_icon)
}
override fun getViewHolder(itemLayout: View) = MyAdapter.ViewHolder(itemLayout)
override fun onBindViewHolder(item: String, viewHolder: MyAdapter.ViewHolder, position: Int) {
viewHolder.itemText.text = item
}
override fun getViewToTouchToStartDraggingItem(item: String, viewHolder: MyAdapter.ViewHolder, position: Int): View? {
return viewHolder.dragIcon
}
override fun onDragFinished(item: String, viewHolder: ViewHolder) {
super.onDragFinished(item, viewHolder)
println("$dataSet")
}
}
The problem is that in Java or Kotlin you pass by value not by reference. So until you change the value of the dataSet you are using in RecyclerView, you won't get any effect.
Do something like this (:
class MyAdapter(dataSet: MutableList<String>)
: DragDropSwipeAdapter<String, MyAdapter.ViewHolder>(dataSet) {
private var dataSet: MutableList<String>
init {
this.dataSet = dataSet
}
class ViewHolder(itemView: View) : DragDropSwipeAdapter.ViewHolder(itemView) {
val itemText: TextView = itemView.findViewById(R.id.item_text)
val dragIcon: ImageView = itemView.findViewById(R.id.drag_icon)
}
override fun getViewHolder(itemLayout: View) = MyAdapter.ViewHolder(itemLayout)
override fun onBindViewHolder(item: String, viewHolder: MyAdapter.ViewHolder, position: Int) {
viewHolder.itemText.text = item
}
override fun getViewToTouchToStartDraggingItem(item: String, viewHolder: MyAdapter.ViewHolder, position: Int): View? {
return viewHolder.dragIcon
}
override fun onDragFinished(item: String, viewHolder: ViewHolder) {
super.onDragFinished(item, viewHolder)
println("$dataSet")
}
fun updateItem(item: String) {
this.dataSet.add(item)
notifyItemInserted(dataSet.size - 1)
}
}
In activity:
fab_add.setOnClickListener {
Log.d("Main", "Button pressed")
mAdapter.updateItem("hello")
println(dataSet)
}
You must call notifyDatasetChanged after changing recycleView items list.
There are two Recyclerview in same activity rv1 and rv2. Selected item of rv1 will show in rv2
rv1:
Here I select multiple items
rv2:
it should shows like this
Adapter class of rv1:
class ServiceJobAdapter(val context: Context, var list:ArrayList<JobRespo>,var listener:OnItemClick):
RecyclerView.Adapter<ServiceJobAdapter.ItemsCarrier>(){
var currentSelectedIndex= -1
var selectedItem = SparseBooleanArray()
var animationItemsIndex = SparseBooleanArray()
private val reverseAllAnimations = false
var holder:ItemsCarrier? =null
class ItemsCarrier(itemView: View) : RecyclerView.ViewHolder(itemView)
{
var jobName = itemView.findViewById<TextView>(R.id.job_name)
var jobPrice = itemView.findViewById<TextView>(R.id.job_price)
var iconBack = itemView.findViewById(R.id.icon_back) as RelativeLayout
var iconFront = itemView.findViewById(R.id.icon_front) as RelativeLayout
var iconContainer = itemView.findViewById(R.id.icon_container) as CardView
fun binde(jobRespo: JobRespo) {
jobName.text = jobRespo.repDesc
jobPrice.text="₹"+jobRespo.repRice
if (iconFront.visibility == View.VISIBLE)
{
jobRespo.setChecked(false)
}else{
jobRespo.setChecked(true)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemsCarrier {
val view = LayoutInflater.from(context).inflate(R.layout.job_row,parent,false)
return ItemsCarrier(view)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ItemsCarrier, position: Int) {
this.holder = holder
holder.itemView.isActivated = selectedItem[position,false]
holder.binde(list[position])
applyIconAnimation(holder, position)
// apply click events
applyClickEvents(holder, position)
}
private fun applyClickEvents(holder: ItemsCarrier, position: Int) {
holder.iconContainer.setOnClickListener {
listener.onIconClicked(position)
listener.AllItem(list[position]) }
}
private fun applyIconAnimation(holder: ItemsCarrier, position: Int)
{
if (selectedItem.get(position, false)) {
holder.iconFront!!.visibility = View.GONE
resetIconYAxis(holder.iconBack)
holder.iconBack.visibility = View.VISIBLE
holder.iconBack.alpha = 1f
if (currentSelectedIndex == position) {
FlipAnimator.flipView(context, holder.iconBack, holder.iconFront, true)
resetCurrentIndex()
}
} else {
holder.iconBack.visibility = View.GONE
resetIconYAxis(holder.iconFront)
holder.iconFront.visibility = View.VISIBLE
holder.iconFront.alpha = 1f
if (reverseAllAnimations && animationItemsIndex.get(
position,
false
) || currentSelectedIndex == position
) {
FlipAnimator.flipView(context, holder.iconBack, holder.iconFront, false)
resetCurrentIndex() } } }
private fun resetIconYAxis(view: RelativeLayout?)
{
if (view!!.rotationY != 0f) {
view.rotationY = 0f
}
}
private fun resetCurrentIndex() {
currentSelectedIndex = -1
}
fun toggleSelection(pos: Int) {
currentSelectedIndex = pos
if (selectedItem.get(pos, false)) {
selectedItem.delete(pos)
animationItemsIndex.delete(pos)
} else {
selectedItem.put(pos, true)
animationItemsIndex.put(pos, true)
}
notifyItemChanged(pos)
}
fun getSelectedItems(): ArrayList<JobRespo>
{
val selectItems = ArrayList<JobRespo>()
for(item in list)
{
if (!item.isChecked())
{
selectItems.add(item)
}
}
return selectItems}}
Using Interface
interface OnItemClick{
fun onIconClicked(position: Int)
fun AllItem(jobs:JobRespo)}
Implement interface in Activity
override fun onIconClicked(position: Int) {
mAdapter!!.toggleSelection(position)
}
override fun AllItem(jobs: JobRespo) {
val selectedItems:ArrayList<JobRespo>
=mAdapter!!.getSelectedItems()
selectIte.addAll(selectedItems)
}
Adapter class of 2nd Recyclerview:
lass SelectItemAdapter(val context: Context, var list:List<JobRespo>): RecyclerView.Adapter<SelectItemAdapter.ItemsCarriers>(){
class ItemsCarriers(itemView: View) : RecyclerView.ViewHolder(itemView) {
var itemName = itemView.findViewById<TextView>(R.id.item_name)
var itemPrice= itemView.findViewById<TextView>(R.id.item_peice)
var itmDelet =itemView.findViewById<LinearLayout>(R.id.item_delete)
fun bind(job: JobRespo) {
itemName.text = job.repDesc
itemPrice.text = job.repRice }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemsCarriers {
val root = LayoutInflater.from(context).inflate(R.layout.select_item_row,parent,false)
return ItemsCarriers(root)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ItemsCarriers, position: Int) {
holder.bind(list[position])}}
Now how can i pass multiselected data from one recyclerview to anathor recyclerview of same activity?
Thanks your consideration and guidded to me :)
You can create a method in a second adapter to update the selected value like below and use it when you getting selected items.
fun setSelectedData(data:ArrayList<JobRespo>){
this.list.clear()
this.list.addAll(data)
notifyDataSetChanged ();
}