I have a use case...
I want to get layoutInflator to bind different Views for nested recyclerView. Is there any best way to get layoutInflator?
I was trying to get it from onBindViewHolder
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder. //something?
}
You shouldn't be inflating anything in onBindViewHolder - onCreateViewHolder is where you set up a ViewHolder object that displays a list item, including inflating its layout views. onBindViewHolder is where you interact with those views, to display the appropriate data for the current item.
Those created ViewHolders get reused, so instead of inflating views for every item, you just create a handful of ViewHolders that get swapped around as you scroll the list. That recycling of a pool of VHs is why it's called a RecyclerView.
Generally, you'd store references to the inflated views inside each ViewHolder, like in this example:
/**
* Provide a reference to the type of views that you are using
* (custom ViewHolder)
*/
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val textView: TextView
init {
// Define click listener for the ViewHolder's View
textView = view.findViewById(R.id.textView)
}
}
That way, the ViewHolder that gets passed into onBindViewHolder has a textView field you can access, e.g. to set its text contents to display data for the current item. (If you're using View Binding, you could store a reference to the binding object instead, and access the views through holder.binding.textView etc)
I don't know what you're doing with nested RecyclerViews, but the principle is the same - each ViewHolder in the main RV will contain another RV, with its own adapter and data set, created and initialised in onCreateViewHolder. If you want to change its contents (because you're displaying a particular item in the main RV list) you'd probably want to add a setData function on the nested RV's adapter that changes its data set and refreshes the display. Let the adapter handle inflating things when it needs to, inside onCreateViewHolder
Related
I have a nested recycler view that contains parent and child adapters. I am using Room DB for showing data on the recycler view.
When I, add or delete data from a parent or child list item or parent list item, it goes to the first position.
This is how I, collect data from the view model
This is how I, collect data from Room DB and emit for activity
These are some edit delete operations executing in the activity
I just want when I, execute any operation the position remains the same not scroll for the first position. There is also a solution called the smooth Scroll To Position method but I want a solution without using this function.
In your collect lambda, you are setting the recyclerView's adapter every time a new flow is collected, which is making your recyclerView go on the top.
You should move binding!!.parentRecyclerView.adapter = adapter to either onCreate if using inside an activity or onViewCreated if fragment.
You can also do a null check and set only if the recycler views adapter is null. This should be avoided as it is more idiomatic to set it when the view gets created.
I use a RecyclerView to display a list of items. The RecyclerView shows a single item at the time. By clicking a button I would like to change ViewHolders for all the items, including the one displayed. The data stays the same, only the list item layout changes.
I supposed I need to clear the cache pool, but it did not help. There are still views in the recycler pool.
recyclerView.recycledViewPool.clear()
RecyclerView keeps using the cached views.
Moreover, how to re-create the view with a new ViewHolder of the item displayed?
add type in your model class
var viewType : ViewType
make an enum of viewType
ViewType { VIEW_ONE, VIEW_TWO }
override ItemViewType function in your RecyclerViewAdapter. Make separate Layout files for each view type and create/inflate in onCreateViewHolder of RecyclerViewAdapter.
When button is being pressed. Change the ViewType in your model class and call notifyDatasetChanged()
I have a RecyclerView. Each ViewHolder in the RecyclerView contains two elements whose visibility I want to set to GONE, upon calling of a method. What I'm trying to do is to wait for the RecyclerView to finish laying out using recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(), change the visibilities for each item, and then remove the GlobalLayoutListener. Here is a part of my code:
public void deselectMessages() {
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
for (int i = 0; i < messages.size(); i++) {
recyclerView.findViewHolderForAdapterPosition(i).itemView.findViewById(R.id.frame_layout_selected).setVisibility(View.GONE);
recyclerView.findViewHolderForAdapterPosition(i).itemView.findViewById(R.id.selected_button).setVisibility(View.GONE);
}
recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
I get the following error:
java.lang.NullPointerException: Attempt to read from field 'android.view.View androidx.recyclerview.widget.RecyclerView$ViewHolder.itemView' on a null object reference
What can I do to make this work?
I have thought that maybe I can't modify the ViewHolders which are not currently being displayed. But the problem is that when I scroll to those Views, the elements of those ViewHolders have their visibility set to VISIBLE. If I don't modify the visibilities of views currently not on the screen, when I do scroll to those views, the elements of the ViewHolders will have their visibilities set to VISIBLE. What can I do about this? By the way, I am okay with modifying the visibilities in the xml files through code when the deselectMessages() method is called.
It is not guaranteed that the RecyclerView will return a ViewHolder for a given position. RecyclerView only creates some ViewModels that are enough to fill the RecyclerView and few buffer ones. What you need to do is basically maintain a state object within your Adapter. That adapter can then use this state information to render/update the item according to your need in onBindViewHolder function.
In your case you can keep a HashSet flaggedItems. And keep the position or ID of elements you want to show differently.
If you update this set, make sure to call notifyDataSetChanged in your adapter. Adapter will make sure the items that are updated.
And all your logic to decide how the item should look like should go in onBindViewHolder.
As per my understanding, the following things happen on a broad level when data is to be shown using the RecyclerView.
1) Recycler view calls the onCreateViewHolder() method which returns a ViewHolder object and then calls onBindViewHolder() which binds the data with individual views contained in the ViewHolder.
Doubt:
As RecyclerView object never gets the total inflated View of the XML file, It only gets the views wrapped inside the ViewHolder object. How is RecyclerView able to draw the ViewHolder views correctly? Do the individual Views present in the ViewHolder have sufficient information about their parent layout, padding, margin, where they should be placed in their parent layout?
RecyclerView contains some components.
Adapter (Provide data)
View Holder (Provide view for binding data)
LayoutManager (Locate position of each item)
and others.
You could read this article to deep inside RecyclerView. (Link)
The main point which I missed here is that the custom ViewHolder class which we define is an extended version of base class RecyclerView.ViewHolder class which holds the actual reference to the inflated View. The constructor of this class receives the View object reference which we created by inflating the XML file of the row to be displayed. Hence this View object which represents the entire hierarchy of the row file layout is saved as an instance member which is named as itemView in the android defined base class (RecylclerViewer.ViewHolder).
class MyViewHolder extends RecyclerView.ViewHolder
{
MyViewHolder(View itemView)
{
/* We are calling the base class constructor passing the Entire View hirerachy */
super(itemView)
}
}
When we extend the ViewHolder class, we add additional View members in this class (like TextView, Buttons etc.) which is an optimisation so that while binding the data in onBindViewHolder(), we can avoid calls to findViewById on the parent View object.
Thus our custom ViewHolder object not only wraps the sub view objects but also holds / wraps the reference to the entire inflated View. Because of this, RecyclerView is able to draw the row completely.
I have a requirement as shown in this image.
I have 2 show 2 object data on single row. Normally we show these information as one object in one row. How could we achieve it.
You can use,
GridView
RecyclerView with GridLayoutManager
You can achieve it customizing getView() method in listview adapter.
what getView() method do?
getView() normally generates view for each row in a general sense.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//inside this we populate data using position value
}
Here you need to show two object (sequence or random) data in a single row. You need to use another int variable to keep track about object size.
initialize it on top, int i = 0;
inside getView() you need to do something extra because you need to populate 2 object data on a single getView() method call.
Create 2 object and populate data using i.
increment i value after every object creation.
Put an if condition do check i value is less than your array size. Its because getView() method will call based on your array size and you need to populate data half / less of that.
****Populate data inside above if condition.
*Better you try Gridview for this type of needs.
*Custom layout GridView example
You need ListView xml, ListItem xml, ListItem class (object), ArrayAdaper class
Set a row view and items in that view in ListItem xml.
Create ListItem class (object) with variables (Such as String, int) you needed.
Bind the view and item in ArrayAdapter class.