I researched around 20 - 30 answers and still not found a working way to:
- get the image and text from --> view --> viewHolder --> recyclerview --> Fragment ( one image and one text fills 1 screen). ( NullPointerException)
- I need them as quote: String and imgRes: Int to input them in a viewModel DAO method to save them to favourites.
- Ideally if the user clicks the heart button, the present image text goes in the favourites. If it is not possible, or overly complicated, then if the user clicks anywhere in the screen the image text should go to favourites.
- In the adapter I need the context to get a random quote from the database (I understand that Context in Adapter is a big no-no) - can you recommend a cleaner way to do it?
- Please advise on how to implement the clicklistener so it gets the image and text! Thank you!
Adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.data.DataSource
import com.example.data.DataSource.prepareManualQuote
import com.example.databinding.SlidingItemBinding
class SlidingItemAdapter(context: Context) :
RecyclerView.Adapter<SlidingItemAdapter.SlidingItemViewHolder>() {
// does NOT WORK TO PASS CONTEXT HERE, NEITHER IN DATABASE, OR VIEWMODEL,
// but it is needed to get the drawable resources programatically
// that is why we pass a private constructor
private var imgList: MutableList<Int> = DataSource.getThemAll(context)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SlidingItemViewHolder {
val binding = SlidingItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SlidingItemViewHolder(binding)
}
// the prepareManualQuote is still a random quote - but passed through Database this time,
// not viewmodel
override fun onBindViewHolder(holder: SlidingItemViewHolder, position: Int) {
// we take a random quote and a random image
val randomPos: Int = (imgList.indices).random()
val imgRes: Int = imgList[randomPos]
val manualQuote: String = prepareManualQuote(holder.itemView.context)
holder.binding.imageViewManual.setImageResource(imgRes)
holder.binding.quoteManual.setText(manualQuote)
}
class SlidingItemViewHolder(val binding: SlidingItemBinding):
RecyclerView.ViewHolder(binding.root)
override fun getItemCount(): Int {
return imgList.size
}
}
SlidingFragment
class SlidingPhotosFragment : Fragment() {
private var _bindingManual: FragmentManualSlidingRecyclerviewBinding? = null
private val bindingManual get() = _bindingManual!!
// Anything used in onCreate and OnviewCreated, etc - declare it here
lateinit var isAuto: String
/*** Standard implementation to work with databases */
private val viewModel: SpiritViewModel by activityViewModels {
SpiritViewModelFactory(
(requireActivity().application as ConnectRoomDatabaseApplication).roomDatabase.itemDao()
)
}
var randomPos: Int = 5
var quote: String = ""
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_bindingManual = FragmentManualSlidingRecyclerviewBinding.inflate(inflater, container,false)
return bindingManual.root
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindingManual.lifecycleOwner = viewLifecycleOwner
// slidesrecyclerView is the binding name gave to recyclerview id in fragment_manual_sliding_recyclerview
bindingManual.slidesRecyclerview.adapter = context?.let { SlidingItemAdapter(it) }
// Extracting stuff from the recyclerview
val linearManager = LinearLayoutManager(context)
bindingManual.heartFavouriteButton.setOnClickListener {
val position: Int = linearManager.findFirstCompletelyVisibleItemPosition()
val row: View? = bindingManual.slidesRecyclerview.layoutManager!!.findViewByPosition(position)
val quoteManual: TextView = row!!.findViewById(R.id.quoteManual)
val imageViewManual: ImageView = row.findViewById(R.id.imageViewManual)
println("dra in manual sliders were extracted this quote and this imageViewId" quoteManual.text imageViewManual.getDrawable())
}
// Snap implementation
val pgSnaphelper: SnapHelper = PagerSnapHelper()
// By using a pagerSnap Helper the snap goes strictly to only 1 slide - not scrolling through many more
bindingManual.slidesRecyclerview.layoutManager?.let {
bindingManual.slidesRecyclerview.layoutManager!!.getChildAt(0)
?.let { it1 -> pgSnaphelper.calculateDistanceToFinalSnap(it, it1) }
}
pgSnaphelper.attachToRecyclerView(bindingManual.slidesRecyclerview)
}
}
XML fragment_manual_sliding_recyclerview
<?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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:navigationBarColor="@color/nav_icons_default"
tools:context=".ui.fragments.SlidingPhotosFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@ id/slides_recyclerview"
android:clipToPadding="false"
android:layout_width="0dp"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@ id/heartFavouriteButton"
style="@android:style/Widget.Holo.ImageButton"
android:layout_width="33dp"
android:layout_height="32dp"
android:layout_marginEnd="250dp"
android:adjustViewBounds="true"
android:background="@color/material_on_primary_disabled"
app:layout_constraintBottom_toTopOf="@ id/heartRedFavouriteButton"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_heart_svgrepo_com"
android:contentDescription="@string/save_slider_to_favourites" />
<ImageButton
android:id="@ id/heartRedFavouriteButton"
style="@android:style/Widget.Holo.ImageButton"
android:layout_width="33dp"
android:layout_height="32dp"
android:layout_marginEnd="250dp"
android:layout_marginBottom="100dp"
android:adjustViewBounds="true"
android:background="@color/material_on_primary_disabled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_heart_svgrepo_com2"/>
<!-- tools:visibility="visible" />-->
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<!-- tools:visibility="visible"-->
CodePudding user response:
Like Tenfour04 says in the comments, really your Adapter
is just there to display a set of data and pass back UI events to a listener. It shouldn't need to be compiling stuff like text, image URLs etc - that information should already be part of the data that was passed to the Adapter
for display.
It should be enough to just pass that item to the "hey this item was clicked" listener. That could call a setFavourite(item)
function in your ViewModel
, which can pass it to the DAO for handling. Since there's a database involved, you probably need some kind of itemId
in Item
so you can tell which item in the database it corresponds to. If you're just passing that Item
around, everything has all the info it needs by looking at it.
Nobody can write you a full implementation for all this, but here's the basics to get you started:
// In your adapter
var favClickedListener: ((Item) -> Unit)? = null
// In onBindViewHolder
val item = items[position]
holder.favButton.setOnClickListener {
favClickedListener?.invoke(item)
}
// In your fragment or whatever
adapter.favClickedListener = { item ->
// or maybe this should be a toggle - click the button, flip its status
viewModel.setFavourite(item)
}
and then your ViewModel handles setting that favourite status, maybe pushing a new set of items with this changed status so it can be redisplayed, etc