I'm trying to implement recycler view inside some of my fragments, and i tried to do so on the first one. No issues are displayed in the IDE on compilation time, but on runtime I get this message on the console: E/RecyclerView: No layout manager attached; skipping layout. Also, data is not showing in my application.
Here is my Fragment:
var sandwiches = listOf<Sandwich>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = DataBindingUtil.inflate<FragmentSandwichesBinding>(
inflater,
R.layout.fragment_sandwiches, container, false
)
val application = requireNotNull(this.activity).application
val dataSource = NbaCafeDB.getInstance(application).sandwichDao
val viewModelFactory = SandwichViewModelFactory(dataSource, application)
val sandwichViewModel =
ViewModelProvider(this, viewModelFactory).get(SandwichViewModel::class.java)
sandwiches = sandwichViewModel.getAll()
val adapter = SandwichAdapter(sandwiches)
binding.sandwichRecycler.adapter = adapter
binding.setLifecycleOwner(this)
return binding.root
}
}
And here is my Adapter:
class SandwichAdapter (val sandwich: List<Sandwich>) : RecyclerView.Adapter<SandwichAdapter.SandwichHolder>() {
override fun getItemCount() = sandwich.size
class SandwichHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(sandwich: Sandwich) {
view.findViewById<TextView>(R.id.sandwichNom).text = sandwich.nomSandwich
view.findViewById<TextView>(R.id.sandwichDesc).text = sandwich.descSandwich
view.findViewById<TextView>(R.id.sandwichPreu).text = (sandwich.preuSandwich.toString()+" €")
}
companion object {
fun from(parent: ViewGroup): SandwichHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.sandwich_cell_layout, parent, false)
return SandwichHolder(view)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SandwichHolder {
return SandwichHolder.from(parent)
}
override fun onBindViewHolder(holder: SandwichHolder, position: Int) {
holder.bind(sandwich[position])
}
}
Also, I'm retrieving data from a room database and using viewModel and viewModelFactory, in case that changes anything.
Thanks!
You don't need to call RecyclerView.setLayoutManager(layoutManager).
Just add app:layoutManager to RecyclerView in your xml.
The default orientation of LinearLayoutManager is VERTICAL, but if you want to change orientation as HORIZONTAL, you can just add android:orientation="horizontal".
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical|horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
Related
I implemented a simple recyclerview using databinding using kotlin language.
In xml, recyclerview seems to be the default,
but when I run it, it doesn't show up as default.
Other buttons and views included in the fragment appear normally when executed, but only the recyclerview is not visible. Which part is wrong?
class WorkFragment : Fragment() {
private var _binding: FragmentWorkBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWorkBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val data = mutableListOf<WorkList>()
val adapter = WorkAdapter()
adapter.data = data
binding.recycler1.adapter = adapter
binding.recycler1.layoutManager = LinearLayoutManager(requireContext())
}
}
class WorkAdapter : RecyclerView.Adapter<WorkAdapter.ViewHolder>() {
var data = mutableListOf<WorkList>()
class ViewHolder(val binding: RecyclerviewWorkItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(workList: WorkList) {
binding.tvStarttime.text = workList.Starttime
binding.tvAdmin.text = workList.Admin
binding.tvPart.text = workList.Part
binding.tvStoptime.text = workList.Stoptime
binding.tvWorkers.text = workList.Workers.toString()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkAdapter.ViewHolder {
val binding = RecyclerviewWorkItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: WorkAdapter.ViewHolder, position: Int) {
holder.bind(data[position])
}
override fun getItemCount(): Int {
return data.size
}
// fun replaceList(newList: MutableList<WorkList>) {
// data = newList.toMutableList()
// notifyDataSetChanged()
// }
}
Is there something wrong with my code?
For reference, I just used a fragment, not an activity.
its typically for the RecyclerView to behave like its hidden but actually its not. the reason is you are passing an empty list so the recycler will not be extended or show because there is no items in the list.
I'm having trouble with Android RecyclerView in Kotlin inside a fragment who is in Home activity.
Here's my code:
myFragmentLayout:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvChapterList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
myFragmentCode:
val chaptersList: ArrayList<String> = ArrayList()
private lateinit var layoutManager: RecyclerView.LayoutManager
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.courses_layout, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
chaptersList.add("Android")
chaptersList.add("Kotlin")
chaptersList.add("RecyclerView")
layoutManager = LinearLayoutManager(context)
rvChapterList.layoutManager = layoutManager
rvChapterList.adapter = ChapterAdapter(Home(), chaptersList)
}
and myChapterAdapter:
class ChapterAdapter(private val context: Home, private val chaptersList: ArrayList<String>) : RecyclerView.Adapter<ChapterAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.list_item, parent, false))
}
override fun getItemCount(): Int {
return chaptersList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.chapterName?.text = chaptersList.get(position)
holder.itemView.setOnClickListener {
Toast.makeText(context, chaptersList.get(position), Toast.LENGTH_LONG).show()
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val chapterName = view.tvChapterName
}
}
It got this code from this tutorial, it's working inside an activity but not iside a fragment.
rvChapterList.adapter = ChapterAdapter(Home(), chaptersList)
Never create an Activity or other type of Context yourself. You always obtain them from the framework.
Replace that with:
rvChapterList.adapter = ChapterAdapter(requireActivity(), chaptersList)
and change the context property in ChapterAdapter to be a Context or Activity, not Home.
I know that there are plenty of similiar posts about similar issues as mine, I even followed this and this and other posts as well (I won't list every single post here though) and tried different things out, but I still can't make my recyclerview show anything on my Fragment.
I write an app mostly for learning purposes - something similar to this, just to get sense about how things work together. If you like to see the project, (at least what I've done so far), you can do this here.
I just want to show items on recyclerview. Currently, nothing from the recyclerview is shown in the Fragment a white background only :(.
I don't even get any errors sort of "No adapter attached; skipping layout" or "No LayoutManager attached; skipping layout". And I am sure this could be a really small issue, so could you please explain me what am I missing or doing wrong.
Thank you so much for your efforts.
Here is what I do:
The Adapter code:
class ActivitiesAdapter internal constructor(
context: Context
) : RecyclerView.Adapter<ActivitiesAdapter.ActivitiesViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var activities = emptyList<DoItAgainEntity>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ActivitiesViewHolder {
val itemView = inflater.inflate(R.layout.recyclerview_item, parent, false)
return ActivitiesViewHolder(itemView)
}
override fun onBindViewHolder(holder: ActivitiesViewHolder, position: Int) {
val current = activities[position]
holder.activityItemView.text = current.engagement
}
internal fun setActivities(activities: List<DoItAgainEntity>) {
this.activities = activities
notifyDataSetChanged()
}
override fun getItemCount() = activities.size
inner class ActivitiesViewHolder(itemview: View) : RecyclerView.ViewHolder(itemview) {
val activityItemView: TextView = itemview.findViewById(R.id.textView)
}
}
The Fragment code:
class ShowDBEntriesFragment : Fragment() {
private lateinit var viewModel: ShowDBEntriesViewModel
private lateinit var layout: View
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: ActivitiesAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
layout = inflater.inflate(R.layout.show_dbentries_fragment, container, false)
adapter = ActivitiesAdapter(context!!)
recyclerView = layout.findViewById(R.id.recyclerview)
recyclerView.addItemDecoration(
DividerItemDecoration(
context!!,
LinearLayoutManager.VERTICAL
)
)
recyclerView.layoutManager =
LinearLayoutManager(context!!, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = adapter
return layout
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(ShowDBEntriesViewModel::class.java)
fab.setOnClickListener {
val fragmentManager = (activity as MainActivity).supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val fragment = InsertNewEngagementFragment()
fragmentTransaction.replace(R.id.fragment_newEngagement, fragment)
fragmentTransaction.commit()
}
}
}
The xml code from recyclerview_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/textView"
style="#style/activity_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_orange_light" />
</LinearLayout>
You are not calling setActivities().
Your code will look like this:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
layout = inflater.inflate(R.layout.show_dbentries_fragment, container, false)
adapter = ActivitiesAdapter(context!!)
recyclerView = layout.findViewById(R.id.recyclerview)
recyclerView.addItemDecoration(
DividerItemDecoration(
context!!,
LinearLayoutManager.VERTICAL
)
)
recyclerView.layoutManager =
LinearLayoutManager(context!!, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = adapter
// Create activities variable
adapter.setActivities(activities)
return layout
}
In recyclerview_item.xml
Change
android:layout_height="match_parent"
to
android:layout_height="wrap_content"
The item layout is probably taking entire layout space.
This is the answer of this issue. I don't OWN this code. This code was written from a genious with huge heart (and also a friend of mine) who understands how things work and writes beautiful code. It's a great honor of mine to learn from you, bro - thank you so much for your ultimate kindness and god-like Android knowledge you have! Keep it up like this.
And here it is - the right way of doing things:
The Adapter code:
class ActivitiesAdapter : RecyclerView.Adapter<ActivitiesViewHolder>() {
private var activities = emptyList<DoItAgainEntity>()
internal fun setActivities(activities: List<DoItAgainEntity>) {
this.activities = activities
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ActivitiesViewHolder =
ActivitiesViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.recyclerview_item,
parent,
false
)
)
override fun onBindViewHolder(holder: ActivitiesViewHolder, position: Int) {
holder.bind(activities[position])
}
override fun getItemCount() = activities.size
}
class ActivitiesViewHolder(
override val containerView: View
) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(vo: DoItAgainEntity) {
itemView.textView.text = vo.engagement
}
}
The Fragment code:
class ShowDBEntriesFragment : Fragment() {
private lateinit var viewModel: ShowDBEntriesViewModel
private lateinit var activitiesAdapter: ActivitiesAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View = inflater.inflate(R.layout.show_dbentries_fragment, container, false)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(ShowDBEntriesViewModel::class.java)
activitiesAdapter = ActivitiesAdapter()
recyclerview.apply {
addItemDecoration(
DividerItemDecoration(
context!!,
LinearLayoutManager.VERTICAL
)
)
layoutManager = LinearLayoutManager(context!!, LinearLayoutManager.VERTICAL, false)
adapter = activitiesAdapter
}
// for testing purposes. could be deleted easily
activitiesAdapter.setActivities(
listOf(
DoItAgainEntity(1, "play guitar", 100),
DoItAgainEntity(2, "make breakfast", 2),
DoItAgainEntity(2, "go out with friends", 20)
)
)
fab.setOnClickListener {
val fragmentManager = (activity as MainActivity).supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val fragment = InsertNewEngagementFragment()
fragmentTransaction.replace(R.id.fragment_newEngagement, fragment)
fragmentTransaction.commit()
}
}
}
The xml code from recyclerview_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/textView"
style="#style/activity_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_orange_light" />
</LinearLayout>
I have PagedListAdapater:
class TrackedActivityAdapter constructor(diffUtilCallback: DiffUtil.ItemCallback<TrackedActivity>) :
PagedListAdapter<TrackedActivity, TrackedActivityAdapter.TrackedActivityHolder>(diffUtilCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackedActivityHolder {
val inflater = LayoutInflater.from(parent.context)
val view = DataBindingUtil.inflate<ActivityItemBinding>(inflater, R.layout.activity_item, parent, false)
return TrackedActivityHolder(view)
}
override fun onBindViewHolder(holder: TrackedActivityHolder, position: Int) {
getItem(position)?.let { holder.bind(it) }
}
class TrackedActivityHolder(var binding: ActivityItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(trackedActivity: TrackedActivity) {
binding.activity = trackedActivity
binding.executePendingBindings()
}
}
}
And fragment in which this adapter created:
class HistoryFragment : DaggerFragment() {
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = DataBindingUtil.inflate<HistoryFragmentViewBinding>(
inflater,
R.layout.history_fragment_view,
container,
false
)
val historyViewModel = ViewModelProviders.of(this, viewModelFactory).get(HistoryViewModel::class.java)
val adapter = TrackedActivityAdapter(TrackedActivity.DIFF_CALLBACK)
binding.trackedActivityRv.adapter = adapter
binding.trackedActivityRv.layoutManager = LinearLayoutManager(binding.root.context)
historyViewModel.getTrackedActivities().observe(this,
Observer<PagedList<TrackedActivity>> { t ->
adapter.submitList(t)
})
binding.executePendingBindings()
return binding.root
}
}
After the adapter.submitList (t) method has been called. Nothing happens, and the onCreateViewHolder and onBindViewHolder methods are not called, please tell me there could be a reason, I just can't understand (
Have you tried using HistoryFragmentViewBinding.inflate(inflater) instead of DataBindingUtil.inflate()?
I had the same problem and got stuck for days. Turns out I just returned the wrong view at the end. It wasn't like yours, but probably worth a try.
I had the same issue. It is only one line different from you, and the code is exactly the same. In my case
recyclerview.setHasFixedSize(true)
Was added. When I removed this, onCreateViewHolder() was called.
So, I don't know exactly which part of your code is the problem, but try to handle this part as well.
val adapter = TrackedActivityAdapter(TrackedActivity.DIFF_CALLBACK)
binding.trackedActivityRv.adapter = adapter
binding.trackedActivityRv.layoutManager = LinearLayoutManager(binding.root.context)
binding.executePendingBindings()
I have an app that returns data from an API to a fragment when the app is first opened but nothing shows up until I change orientation then the RecyclerView is populated. Is it something with the lifecycle methods? If so then which one should I override?
ListAdapter
class ListAdapter #Inject constructor(private val context: Context) : RecyclerView.Adapter<ListAdapter.ViewHolder>() {
var collection: ArrayList<ListEntity> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListAdapter.ViewHolder =
ListAdapter.ViewHolder(LayoutInflater.from(context).inflate(R.layout.lists, parent, false))
override fun getItemCount(): Int {
Timber.i("the size is ${collection.size}")
return collection.size
}
override fun onBindViewHolder(holder: ListAdapter.ViewHolder, position: Int) {
holder.bind(collection[position])
}
class ViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind(listEntity: ListEntity) {
val mytxt = view.findViewById<TextView>(R.id.txt)
mytxt.text = listEntity.by
}
}
}
ListFragment
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_list, container, false)
view.list_recyclerview.layoutManager = LinearLayoutManager(activity)
view.list_recyclerview.adapter = listAdapter
view.list_recyclerview.setHasFixedSize(false)
view.list_recyclerview.isNestedScrollingEnabled = true
Timber.i("OnCreateView called")
return view
}
//In AndroidMainfest
<activity
android:name="com.Activity"
android:configChanges="orientation|screenSize" >
</activity>