I've tried to make a RecyclerView with an Adapter but I cannot see the layout? - kotlin

I've tried to build a recyclerview with an adapter class and cardview layout however even though the code runs I cannot see the list with cardview (I just get a blank recyclerview)?
SpeakerAdapter.kt
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val speakerName: TextView = itemView.findViewById(R.id.name)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.speakerName.text = speakers[position]
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val View = LayoutInflater.from(parent.context).inflate(R.layout.speaker_list, parent, false)
return ViewHolder(View)
}
override fun getItemCount() = speakers.size
}
Speaker.kt
class Speaker : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity_speaker)
val speakers : ArrayList<String> = ArrayList()
for (i in 1..100){
speakers.add("Speaker 1 #$i")
recyclerView_speaker.layoutManager = LinearLayoutManager(this)
recyclerView_speaker.adapter = SpeakerAdapter(speakers)
}
}}

It's a bit hard to debug without the xml. But here goes nothing:
I think it'd be worth a shot modifying your view holder class. The reference for the text view is being set when you init the view holder and the layout may not be inflated yet. Try finding the reference when you bind the data to the view to avoid this issue.
Try this:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(name: String?) {
itemView. name.text = name
}
}
and then modify your adapter to call this method
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(speakers[position])
}
}
PS.: it'd be a good idea to call your view holder something else. ViewHolder is a name that is already used by the android SDK. Ex.: SpeakerViewholder

Related

How do I initialize a lazy variable for a RecyclerView adapter

I have an adapter that takes in a list of items derived from a data class that I need to use for settings the items in the recyclerView.
The adapter looks something like this:
class MyAdapter(private var adapterItems: MutableList<MyDataClass>): RecyclerView.Adapter<ViewHolder>(){
override fun OnCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder{
//Todo
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int): ViewHolder {
//Todo
}
override fun getItemCount(): Int {
//Todo
}
inner class ViewHolder(
itemView: View)
: RecyclerView.ViewHolder(itemView)
Next I have my data class as
data class MyDataClass(
val id: Int,
val title: String,
val subTitle: String,
}
I am using the following way to create a variable for my adapter in my activity along with other member parameters that I have not mentioned here.
class Activity #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int): RecyclerView(context, attrs, defStyle){
private val myAdapter: MyAdapter by lazy {
MyAdapter(mutableListOf()
}
init{
this.adapter = myAdapter
//Along with layout manager and inflating the recyclerView
}
}
I have a Fragment class that is responsible for creating the items to be supplied to the adapter and check the working of the recycler view.
Thus doing so, I am facing the following error and have no clue on resolving it:
Attempt to invoke virtual method 'boolean
androids.recyclerview.widget.RecyclerView$ViewHolder.shouldIgnore()'
on a null object reference
I have duplicated my code over here for privacy. Any help is much appreciated.

How can I change recyclerview item background color permanent and transact new Fragment?

I want to change the color of recyclervView item that I clicked(so user can understand that already checked the detail of the item) and go detail fragment page. However this background color change must be permanent should I store it in livedata of recycler view items. I share my codes at the end I am new to android programming so please explain your solution for beginner and my english level is not good. Thanks for everything.
class AdapterRecycler() :
RecyclerView.Adapter<AdapterRecycler.ViewHolder>()
class ViewHolder(view: View, listener: onItemClickListener) : RecyclerView.ViewHolder(view) {
val name: TextView = view.findViewById(R.id.gameId)
val score: TextView = view.findViewById(R.id.scoreId)
val genre: TextView = view.findViewById(R.id.genres)
val layout1: RelativeLayout = view.findViewById(R.id.rowlayout)
init {
// Define click listener for the ViewHolder's View.
//val textView : TextView = view.findViewById(R.id.gameId)
itemView.setOnClickListener {
layout1.setBackgroundColor(Color.rgb(224,224,224))
listener.onItemClick(adapterPosition)
}
}
}
// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
// Create a new view, which defines the UI of the list item
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.text_row_item, viewGroup, false)
return ViewHolder(view, listenerItems)
}
class Games : Fragment() {
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
adapter.setOnItemClickListener(object : AdapterRecycler.onItemClickListener {
override fun onItemClick(position: Int) {
// Fragment transaction to detail page
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainerView2, Details()).commit()
}
})
}
Should I store a boolean value in here to save item checked status?
data class Game(val name : String, val score : Int, val genres : Array<String>)
I tried solution at my codes and I get transaction but not the color changes of item layout.
I think it will be good if you'll update your Game model, after it became checked. Something like this:
data class Game(.., var isChecked: Boolean = false)
And inside your AdapterRecycler.onItemClickListener realization call ViewModel function to update isChecked value:
fun onItemChecked(position: Int) {
val item = TODO("get item by position from your live data")
item.isChecked = !item.isChecked
}
In addition, you should override your ViewHolder to setup a background rely on your isCheked value (it will be the same as must be after ViewHolder notify calls):
class ViewHolder(view: View, listener: onItemClickListener) : RecyclerView.ViewHolder(view) {
...
fun bind(item: Game) {
val backColor = if (item.isChecked) Color.GREEN else Color.BLUE
layout1.setBackgroundColor(Color.rgb(224,224,224))
}
}
Call bind function from your AdapterRecycler.onBindViewHolder this way:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position), callback)
}
Don't forget to notify your adapter.
Guys thanks to your replies I reviewed my code and got solution.
Here is my edit at the code:
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
...
val color = if (_data[position].isChecked) Color.rgb(224,224,224) else Color.WHITE
viewHolder.layout1.setBackgroundColor(color)
...
}
That way I change the color of recyclerview item color since I change the fragments make the viewmodel at activity based and used it shared viewModel between fragments
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: GamesViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
viewModel = ViewModelProvider(this)[GamesViewModel::class.java]
...
}
Then I added the boolean variable to my model.
data class Game(val name : String, val score : Int, val genres : Array<String>, var isChecked : Boolean = false)
Then I change the liveData's attribute of isChecked at the click listener.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter.setOnItemClickListener(object : AdapterRecycler.onItemClickListener {
override fun onItemClick(position: Int) {
viewModel= ViewModelProvider(requireActivity()).get(GamesViewModel::class.java)
val item = viewModel.liveData.value!!.get(position)
item.isChecked = true
// toGo next fragment
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainerView2, Details()).commit()
}
})
VoilĂ  we got the perfect result thanks for helping me, have a great day.

Kotlin OnItemClickListener for a RecyclerView inside a Fragment

The problem is related to the fact that I don't manage to implement a OnItemClick listener for a RecyclerView that is located in a "mainFragment" by implementing the OnClickListener in the Adapter.
I would like my application (kotlin) to launch another fragment ("deletePage" in the code bellow) everytime an itemView (an ImageView) from the RecyclerView is clicked, this fragment would display the same photo in big.
My Adapter code is the following one:
class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private var photo = emptyList<Photo>()
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.row_layout, parent, false)
)
}
override fun getItemCount(): Int {
return photo.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.itemView.longitude.text = photo[position].latitude
holder.itemView.latitude.text = photo[position].longitude
holder.itemView.imageView.load(photo[position].photo)
}
fun setData(photo: List<Photo>) {
this.photo = photo
notifyDataSetChanged()
}
}
And my mainFragment code is the following one:
class MainFragment : Fragment(){
private lateinit var myView: MyViewModel
private val adapter by lazy { MyAdapter() }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_main, container, false)
//Recyclerview
val adapter = MyAdapter()
val recyclerView = view.recycler_view
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
myView = ViewModelProvider(this).get(MyViewModel::class.java)
myView.readPhoto.observe(viewLifecycleOwner, Observer {photo ->
adapter.setData(photo)
})
setHasOptionsMenu(true)
return view
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_database, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when {
item.itemId == R.id.deleteAll -> findNavController().navigate(R.id.deletePage)
item.itemId == R.id.refresh -> {
Toast.makeText(activity, "yep", Toast.LENGTH_SHORT).show()
instertDataToDatabase()
}
}
if (item.itemId == R.id.deleteAll) {
}
return super.onOptionsItemSelected(item)
}
}
The objective is that when you click on an item of the RecyclerView the "findNavController().navigate(R.id.deletePage)" fragment is displayed but everytime I try to implement a solution the application crashes when clicking at an item of the RecyclerView. Right now the navigation works by the click on a button in the Menu at the toolbar but is not the ideal solution.
Any sort of help or advice would be very much appreciated!
Firstly you need to give click listener of your itemView in onBindViewHolder
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.itemView.longitude.text = photo[position].latitude
holder.itemView.latitude.text = photo[position].longitude
holder.itemView.imageView.load(photo[position].photo)
holder.itemView.setOnClickListener { navigateToFragment() }
}
For the navigation somehow you should have a context instance.
Here is the possible solutions that came up to my mind:
Give navigator instance to adapter
class Navigator(fragment: Fragment) {
private val fragmentRef = WeakReference(fragment)
fun navigate(){
// make your navigations here with using fragmentRef.get()
}
}
In your fragment:
private val navigator = Navigator(this)
...
val adapter = MyAdapter(navigator)
In your adapter:
class MyAdapter(private val navigator: Navigator) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
fun navigateToFragment(){
navigator.navigate()
}
Make interface for callback
2.1. Implement that interface in fragment and give adapter the interface instance
interface Navigable{
fun navigate()
}
In your fragment:
class MainFragment : Fragment(), Navigable{
...
override fun navigate(){
// make your navigation
}
val adapter = MyAdapter(navigator)
In your adapter:
class MyAdapter(private val navigable: Navigable) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
fun navigateToFragment(){
navigable.navigate()
}
2.2. Make the interface, but do not pass reference, use findFragment in adapter
In your adapter: override onAttachedToRecyclerView to find fragment
private lateinit var navigable: Navigable
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
navigable = recyclerView.findFragment<Fragment>() as Navigable
}
fun navigateToFragment(){
navigable.navigate()
}

How to create custom Adapter with for 3 recycleViews

Hello All,
i want to ask if i can add to my 3 recycle Views each recycle view hase interface to optimise my code i tried to add only 1 adapter for the 3 recycle View, as you can see my code below but i find my self stuck with this adapter, any 1 have idea how add custom adapter to adapt 3 recycle View? Thanx.
class CustomAdapter(private val contexte: Context) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val context: Context = contexte
inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
inner class PagesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
inner class CorpusViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == VIEW_TYPE_CORPUS)
return CorpusViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.corpus_item_layout, parent, false)
)
if (viewType == VIEW_TYPE_FOLDER)
return FolderViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.folder_item_layout, parent, false)
)
return PagesViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.page_item_layout, parent, false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
TODO("Not yet implemented")
}
override fun getItemCount(): Int {
return 20
}
companion object {
internal val VIEW_TYPE_CORPUS = 1
internal val VIEW_TYPE_FOLDER = 2
internal val VIEW_TYPE_PAGES = 2
}
Instead of doing this, I suggest you to use base class that takes layout id and initialize your common adapter with that.
open class AdapterItem(val layoutId: Int)
data class Corpus(val id: Int): AdapterItem(id)
then init your adapter like
CustomAdapter<AdapterItem>(...)
in your adapter, override getView
#Override
fun getView(position: Int, convertView: View, parent: ViewGroup): View {
val item = list[position]
return if(converView != null){
convertView
} else {
LayoutInflater.from(parent.context).inflate(item.layoutId, parent, false)
}
}

I am trying to create a RecyclerView in Kotlin but having what seems a simple issue

So im trying to create a RecyclerView but im getting errors on "class RecyclerAdapter" and "override fun onBindViewHolder" Reference photo down below
On the first it states "Class 'RecyclerAdapter' is not abstract and does not implement abstract base class member public abstract fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int): Unit defined in androidx.recyclerview.widget.RecyclerView.Adapter"
The second error states "onBindViewHolder' overrides nothing"
im am following this tutorial https://www.youtube.com/watch?v=ai9rSGcDhyQ&ab_channel=CodePalace Here the snippet from the tutorial that Im having problems with
I copied the tutorial almost word for word but I have no idea why im still getting these erros and trust me I have tried researching here and elsewhere including android documentation for solutions but to no avail. Here is what I tried to try to fix it:When I added RecyclerView.Viewholder both errors disappear but then the variables become unresolved
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemTitle.text=titles[position]
holder.itemDetail.text=details[position]
holder.itemPicture.setImageResource(images[position])
}
I would be very grateful if you can find a solution to this issue.
thank you
class RecyclerAdapter (private var titles:List<String>,private var details: List<String>,private var
images:List<Int>):
RecyclerView.Adapter<RecyclerView.ViewHolder>(){
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemTitle: TextView = itemView.findViewById(R.id.title)
val itemDetail:TextView=itemView.findViewById(R.id.description)
val itemPicture: ImageView =itemView.findViewById(R.id.iv_image)
init {
// Define click listener for the ViewHolder's View.
itemView.setOnClickListener{v:View->
val position:Int =adapterPosition
Toast.makeText(itemView.context," You clicked on item #
${position+1}",Toast.LENGTH_SHORT)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v =LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemTitle.text=titles[position]
holder.itemDetail.text=details[position]
holder.itemPicture.setImageResource(images[position])
}
override fun getItemCount(): Int {
return titles.size
}
}
RecyclerAdapter.ViewHolder, not RecyclerView.ViewHolder
class RecyclerAdapter (private var titles:List<String>,private var details: List<String>,private var
images:List<Int>):
RecyclerView.Adapter<RecyclerAdapter.ViewHolder>()