I have a list of data to be displayed in a recycler view and that is working fine. Now I have another dynamic list of data to be displayed inside the parent recycler-view. So I tried using another recycler-view inside the parent recycler-view, that is not working fine. It will be good if I get some idea of using recycler-view inside another one. Thanks in advance..!
I have illustrated my problem with an example:
For eg: I have a parent recyclerView with five linearLayout and I have created a child recyclerView inside the Linearlayout with visibility GONE. Now when I click the first Linearlayout I am changing the visibility of child recyclerView for the first Linearlayout to VISIBLE and attaching a separate view to it and same concept for all the other Linearlayouts. What happens is when I click first, second, third and fourth linearLayout the child recyclerView is not displaying date which I pass to it, all those first, sec, third and fourth data are accumulated and displayed in the last (i.e) inside fifth linearLayout.
Here is my parent recyclerview code:
class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyViewHolder>
{
RecyclerView insideCardRecyclerView,recyclerView;
List<String> monthsWeek = new ArrayList<>();
List<String> dealers = new ArrayList<>();
List<String> dealersList = new ArrayList<>();
List<String> date = new ArrayList<>();
HashSet<String> dealersListHash = new HashSet<>();
public CardAdapter(List<String> monthsWeek,List<String> dealers,List<String> date)
{
this.monthsWeek = monthsWeek;
this.dealers = dealers;
this.date = date;
}
public class MyViewHolder extends RecyclerView.ViewHolder
{
ProgressBar progressBar;
RecyclerView recyclerView;
TextView period;
LinearLayout linearLayoutParent,linearLayoutCardDetails;
public MyViewHolder(View view)
{
super(view);
linearLayoutParent = (LinearLayout) view.findViewById(R.id.card_view_linear_parent_layout);
linearLayoutCardDetails = (LinearLayout) view.findViewById(R.id.linear_card_layout_details);
period = (TextView) view.findViewById(R.id.period_summary_graph_card);
insideCardRecyclerView = (RecyclerView) view.findViewById(R.id.summary_graph_card_view_recycler_view);
}
}
#Override
public CardAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(getActivity())
.inflate(R.layout.summary_card_view,parent,false);
recyclerView = (RecyclerView) parent;
return new CardAdapter.MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position)
{
holder.period.setText(monthsWeek.get(position));
holder.linearLayoutParent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
if(searchClick)
{
for (String date1 : date)
{
if(Objects.equals(date1,monthsWeek.get(position)))
{
Log.e("Summary123 date..///", date1);
dealersList.add(dealers.get(date.indexOf(date1)));
}
}
searchClick = false;
holder.linearLayoutCardDetails.setVisibility(View.VISIBLE);
dealersListHash.addAll(dealersList);
dealersList.clear();
dealersList.addAll(dealersListHash);
//if the condition is true i am attaching another recyclerview inside this.
cardAdapterList = new CardAdapterList(dealersList);
LinearLayoutManager mLayoutManager1 = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL,true);
mLayoutManager1.setReverseLayout(false);
insideCardRecyclerView.setLayoutManager(mLayoutManager1);
insideCardRecyclerView.setItemAnimator(new DefaultItemAnimator());
insideCardRecyclerView.setAdapter(cardAdapterList);
}
else
{
searchClick = true;
holder.linearLayoutCardDetails.setVisibility(View.GONE);
}
}
});
}
#Override
public int getItemCount() {
return monthsWeek.size();
}
}
I've been in trouble sometimes with RecyclerView when multiple lists are needed to be shown in a single page of the application. Its not a very good idea actually to have multiple lists in a single layout but however, the idea of having a ScrollView and the lists inside that ScrollView is even worse.
I had to implement a ListView inside a ScrollView once and yes it was not a very good experience. Firstly, my list was not scrolling at all. Then I had to add some code to disable the scrolling when the touch is detected inside the list. It was not a very good idea of solving the actual problem. I had another problem of having a fixed height of the ListView. In case of list items with dynamic heights, the solution failed.
Having two lists in the layout, one after one is not a good idea either. As the first list need to have a fixed height.
So, after searching for suggestions about how can I implement two lists in a single layout file, I found most of the developers suggests of having a single list with a header and footer if necessary. Later, I could manage to show two lists in a single RecyclerView using my custom Adapter. I thought I should save some of my code for future use and hence, you see this note.
You can refer this sample code.
Related
I am trying to get viewholder of a particular position from recycle view.
I had found to do so by doing this in MainActivity :
viewHolderRec = recyclerViewInstance.findViewHolderForAdapterPosition(vHID);
It works well inside my MainActivity.
But, when I tried to implement the same inside my recycle view cursor adapter like this :
I had changed the cursor to take recycleview :
In my MainActivity :
final RecyclerView recyclerView = findViewById(R.id.recycle_view);
LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(manager);
recyclerView.setNestedScrollingEnabled(true);
mCursorAdapter = new RecycleCursorAdapter(this, null, recyclerView);
recyclerView.setAdapter(mCursorAdapter);
In RecycleCursorAdapter :
RecyclerView recyclerViewInstance;
public RecycleCursorAdapter(Context context, Cursor cursor, RecyclerView recyclerView) {
super(context, cursor);
recyclerViewInstance = recyclerView;
}
#Override
public void onBindViewHolder(ChatCursorAdapter3.ViewHolder viewHolder, Cursor cursor) {
ChatListItem myListItem = ChatListItem.fromCursor(cursor);
int vHID = viewHolder.getAdapterPosition();
viewHolderRec = (ViewHolder) recyclerViewInstance.findViewHolderForAdapterPosition(vHID - 1);
}
But viewHolderRec is null.
I want to get the viewholder of the view which is above the current viewHolder.
I just want to know how can I get it work.
What I want is to use findViewHolderForAdapterPosition() method inside my recycle view cursor adapter.
Yes, I know that I can use
viewHolder.getOldPosition()
But It does not fulfill my needs at the time of scrolling the recycle view.
Thanks In Advance :-)
I had searched a lot and find that, Instead of using recyclerViewInstance.findViewHolderForAdapterPosition(vHID - 1);
I can find the viewholder of any position by storing the viewholders in a List<> like
List<ViewHolder> vList = new ArrayList<>();
and whenever i want the viewholder of any posstion than i can find by
int vHID = viewHolder.getAdapterPosition();
viewHolderRec = vList.get(vHID - 1);
As what i need to do.
But, I know this effects lots of memory when the list is very large.
So what I did is removing the viewholders from memory which are not visible right now.
For this, I get the height in display pixels(dp) and get the number of visible items by it. and remove all the other viewholders.
This trick works for me. May Be this will be helpful for someone so I posting this answer.
I have got CardView with Views inside a RecyclerView. I have created adapter in which assets are attached to Views and everything works. Now, I would like to change these Views from my activity. Is it any simple way to do that?
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private List<Offer>offers;
TextView timer; //timer inside CardView
private void getViewReferences() {
recyclerView = (RecyclerView)findViewById(R.id.mainRecyclerView);
timer = (TextView)findViewById(R.id.timer);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getViewReferences();
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
initializeData();
initializeAdapter();
timer.setText("13:16"); //NullPointerException here
}
private void initializeData(){
offers = new ArrayList<>();
offers.add(new Offer("godzina", R.drawable.zdj, 200));
offers.add(new Offer("godzina", R.drawable.zdj));
}
private void initializeAdapter() {
RecyclerViewAdapter adapter = new RecyclerViewAdapter(offers);
recyclerView.setAdapter(adapter);
}
}
So you don't want to change the Views but you want to update the text values of the views in your list.
You can't directly do that by trying to find the Views in the recyclerView and change the text. The adapter is responsible for giving values to your list. So you need to update the dataset in the adapter and call one of the notifyDataSetChanged methods on the adapter to update the recyclerView.
The big advantage of this design is that it places Views in Cache in order to reuse them multiple times without having to inflate and construct them again. This is a huge performance improvement.
You need to read the docs and some tutorials to understand the adapter design pattern better.
I want to display many [CardViews] within a [RecyclerView].
Therefore I am using an adapter that extends
RecyclerView.Adapter < RecyclerView.ViewHolder >
Everything works fine so far.
But now, I want to populate the CardView with different widgets(checkboxes, textviews, buttons.. ) in different order, dependent on the data of the current dataset position. Defining a ViewHolder class for every possible combination of the widgets order is not an option due too many possibilities.
I managed it already with two RecyclerView-Adapters. One for the RecyclerView "list" and one for populating the CardView inside each item.
But that leads to bad performance.
So I came back to use only one RecyclerView and one Adapter.
But, how to populate each CardView in a good way then? Thank you for your help!
Update1:
This is my current and result updated_screenshot. Looks "fine" so far but is achieved not in a proper way.
I did it as following:
Setting an Adapter (OuterAdapter) to the recyclerView, that holds each CardView:
public class OuterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
.
.
.
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
rviewHolder = (RviewHolder) holder;
llm = new LinearLayoutManager(context);
rviewHolder.rv.setLayoutManager(llm);
innerAdapter = new VHAdapter(context, questionList);
rviewHolder.rv.setAdapter(innerAdapter);
}
.
.
.
}
Now, every Item has its own "VHAdapter" which fills the CardViews like:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder h, final int position) {
switch (h.getItemViewType()) {
case ONLY_HEADER_TEXT:
HeaderTextHolder holder = (HeaderTextHolder) h;
holder.tv.setText(q.getText());
break;
case ANSWER_TYPE_NUMBER:
NumberHolder nh = (NumberHolder) h;
nh.tv.setText(q.getText());
nh.et.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
tempAnswer.setNumber(Integer.parseInt(v.getText().toString()));
tempAnswer.setQuestionType(Question.ANSWER_TYPE_NUMBER);
return false;
}
});
break;
.
.
.
What I am trying to do now is getting rid the VHAdapter. But that means, I'd have to manually add widgets to ViewHolders rootLayout at OuterAdapter's onBindViewHolder.. Is that correct?
I have created a recyclerview for displaying data which is fetching from the server. I had used only single layout for displaying the data.
Now my requirement is like when I upload images or videos, then the uploading status should be displayed on top of the data which is displaying from the server. i-e on 0th position of recyclerview. I can add any number of images or videos.
after image or video successfully upload i also want to remove that row from recyclerview. I thought of doing using getItemViewType(). In this using two layout. I don't know this method is correct or not. I am not getting any solution to this,
Please.....
any help...
You can easily achieve that using the library SectionedRecyclerViewAdapter.
You should first create a section class:
class MySection extends StatelessSection {
String title;
List<String> list;
public MySection(String title, List<String> list) {
// call constructor with layout resources for this Section header, footer and items
super(R.layout.section_header, R.layout.section_footer, R.layout.section_item);
this.title = title;
this.list = list;
}
#Override
public int getContentItemsTotal() {
return list.size(); // number of items of this section
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
#Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new SimpleHeaderViewHolder(view);
}
#Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
MyHeaderViewHolder headerHolder = (MyHeaderViewHolder) holder;
// bind your header view here
headerHolder.tvItem.setText(title);
}
}
Then you set up the RecyclerView with your Sections:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
MySection uploadsSection = new MySection("Uploads", uploadList);
MySection downloadsSection = new MySection("Downloads", downloadList);
// Add your Sections
sectionAdapter.addSection(uploadsSection);
sectionAdapter.addSection(downloadsSection);
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
This way you manage the items from your upload and download lists separately by removing them from uploadList/downloadList then notifying the changes to the adapter. The upload items will always be displayed at the top of the RecyclerView because they are in the first section added to the adapter.
If you have different layouts for uploads and downloads you can create a different Section class for each.
Make two viewTypes like:
private static final int REGULAR_HOLDER = 1;
private static final int LOADING_HOLDER = 2;
Override getItemViewType and return LOADING_HOLDER for position 0, and REGULAR for all others. Also have state if you are loading or not. If you are not loading anything you will return REGULAR_HOLDER for all rows (positions).
Then in onCreate check if you have REGULAR or LOADING viewType, and create proper Holder. Important: make your Adapter implement RecyclerView.Adapter<VH> not your custom implementation of ViewHolder.
onBind executes next. There you will have to check if your viewHolder object you get is instance of RegularViewHolder or instance of LoadingViewHolder, like:
if (holder instance of RegularViewHolder) {
holder.doStuff();
} else if (holder instance of LoadingViewHolder) {
holder.showLoading();
}
Now, before this you should made two layouts. One is for your regular rows, and other is for row that will show loading. Make two classes that implement ViewHolder, in example above i called them RegularViewHolder and LoadingViewHolder.
EDIT: few things to keep in mind. I told you to keep a loading state (loading or not loading), so if you want to remove LOADING row, you could make that change and call notifyDataSetChanged();. Now, getItemViewType should return all REGULAR rows if you did it right.
Also you should keep in mind that if you want to show 10 rows of your data. Your getItemCount() should return 11 (10 + loading row) if there is loading happening. Also, in that case your data rows start from second row (position 1).
I'm using a list to populate a ListView (). The user is able to add items to the list. However, I need the items to be displayed at the top of the ListView. How do I insert an item at the beginning of my list in order to display it in reverse order?
By default list adds elements at bottom. That is why all new elements you add will show at bottom. If you want it in reverse order, may be before setting to listadapter/view reverse the list
Something like:
Collections.reverse(yourList);
Another solution without modifying the original list, override getItem() method in the Adapter
#Override
public Item getItem(int position) {
return super.getItem(getCount() - position - 1);
}
Updated: Example
public class ChatAdapter extends ArrayAdapter<ChatItem> {
public ChatAdapter(Context context, List<ChatItem> chats) {
super(context, R.layout.row_chat, chats);
}
#Override
public Item getItem(int position) {
return super.getItem(getCount() - position - 1);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = inflater.inflate(R.layout.row_chat, parent, false);
}
ChatItem chatItem = getItem(position);
//Other code here
return convertView;
}
}
You should probably use an ArrayAdapter and use the insert(T, int) method.
Ex:
ListView lv = new ListView(context);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, R.id...);
lv.setAdapter(adapter);
...
adapter.insert("Hello", 0);
The ListView displays the data as it is stored in your data source.
When you are adding in your database, it must be adding the elements in the end. So, when you are getting all the data via the Cursor object and assigning it to the ArrayAdapter, it is in that order only. You should basically be trying to put data in the beginning of the database, rather that in the end, by having some time-stamp maybe.
Using ArrayList, you can do it by Collections.reverse(arrayList) or if you are using SQLite, you can use order by.
You can add element at the beginning of the list: like
arraylist.add(0, object)
then it will always display the new element at the top.
mBlogList is a recycler view...
mBlogList=(RecyclerView) findViewById(R.id.your xml file);
mBlogList.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);
mBlogList.setLayoutManager(mLayoutManager);//VERTICAL FORMAT
You could always have a datestamp in your objects and sort your listview based on that..
public class CustomComparator implements Comparator<YourObjectName> {
public int compare(YourObjectName o1, YourObjectName o2) {
return o1.getDate() > o2.getDate() // something like that.. google how to do a compare method on two dates
}
}
now sort your list
Collections.sort(YourList, new CustomComparator());
This should sort your list such that the newest item will go on top
You can always use a LinkedList instead and then use addFirst() method to add elements to your list and it will have the desired behaviour (new items at the top of the ListView).