85

I am using Firebase Recycler Adapter (Firebase UI Library) to populate Recycler View. I want to hide an item(row) on a condition. I have a LinearLayout containing a recycler view.

I set linear layout visibility to Gone in populateViewHolder() method of recycler view adapter.

@Override
protected void populateViewHolder(UsersViewHolder viewHolder, User user, int position) {

    if (user.getUserEmail().equals(Utils.decodeEmail(userEmail))) {
        viewHolder.llMain.setVisibility(View.GONE);
        return;
    }

    viewHolder.tvUserEmail.setText(user.getUserEmail());
}

It hides the LinearLayout but the row remains there with empty space.

Is there any method I should override to overcome this or is there any way to achieve the result?

koceeng
  • 2,169
  • 3
  • 16
  • 37
Dhaval
  • 2,724
  • 2
  • 24
  • 33
  • 6
    It would be better to populate data without not needed items – Selvin Dec 19 '16 at 13:16
  • 2
    off-topic comment: also your `if` has no `else` ... it will you kick in the ass later(releted with view reusing) – Selvin Dec 19 '16 at 13:17
  • I agree with Selvin, but maybe height=0 would do the trick – aclowkay Dec 19 '16 at 13:18
  • @Selvin Yes I will add the else part later. But I am using Firebase Recycler Adapter so I am only providing a reference of node to it not any data. Is there anything I am missing to implement ? – Dhaval Dec 19 '16 at 13:20
  • Check my answer on this post. https://stackoverflow.com/questions/46156902/make-firebaserecycleradapter-get-data-under-conditions-and-not-all-of-them/46384299#46384299 – gtzinos Sep 23 '17 at 21:16
  • Check out my answer [here](https://stackoverflow.com/a/54272566/5670752). – Taslim Oseni Jan 20 '19 at 00:35

11 Answers11

194

In some cases changing only visibility attribute might still end up as allocated blank space (because of parent view's padding, margins, inner elements etc). Then changing height of the parent view helps:

holder.itemView.setVisibility(View.GONE); 
holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(0, 0));

Then be sure that in the condition that it should be visible, also set:

holder.itemView.setVisibility(View.VISIBLE);
holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

You need to do that because the viewHolder is recycled as you scroll, if you change properties as this and never return them to their natural state, other elements will be already hidden in the event they reuse the same view.

htafoya
  • 18,261
  • 11
  • 80
  • 104
incognito
  • 2,243
  • 3
  • 14
  • 16
  • 3
    Although it's working great for linear recyclerview but not removing vacant space in grid recyclerview – Srinivas Nahak Mar 25 '18 at 23:07
  • 3
    This worked, but I am using a grid recyclerview, and there is a blank cell or two in between. – Justin Ebby May 24 '18 at 12:11
  • 6
    Don't forget to return Visibility of the view back to VISIBLE & LayoutParams to it's initial state in opposite case.In my case: holder.lin.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); – Ali.DM Nov 21 '18 at 16:16
  • 2
    this is perfect. – Nouman Ch Feb 15 '19 at 11:13
  • 3
    If you get a crash by doing this! Make sure you you try "params = itemView.getLayoutParams()" then set the width/height on that params object. After that: "itemView.setLayoutParams(params)". – MisseMask Aug 08 '19 at 12:47
  • That's so perfect! but pay attention, the design for your RecyclerView will hide too when set the holder is visible. to resolve this just delete line 2 in the last code. – Amr Jyniat Dec 13 '19 at 21:19
  • Sometimes even after doing this I still got white spaces. So, for that I did `recyclerView.requestLayout()` after changing layoutparams and it worked fine. – Yaswant Narayan Jun 17 '20 at 16:55
  • 1
    @KoustuvGanguly you are welcome. I'm surprised to see that after 4 years this issue is still relevant :) – incognito May 31 '21 at 19:55
39

You should hide all views or parent from UsersViewholder layout xml. You should hide entire viewholder or each view

Entire viewholder:

itemView.setVisibility(View.GONE);

or each element:

view.setVisibility(View.GONE);

But don't forget to set them VISIBLE otherwise, you will end up with some strange things from recycling

Cătălin Florescu
  • 5,012
  • 1
  • 25
  • 36
  • This looks promising. I will definitely try this. – Dhaval Dec 19 '16 at 13:32
  • You can add a method in ViewHolder, like show() / hide() that handle all that work of visibility. Don't forget that is a recycler view, so you need to show view holder if condition is not met, otherwise will hide unwanted items. – Cătălin Florescu Dec 19 '16 at 13:37
  • 3
    Sorry to say, But, once the size of items increased, the problem occurs again and the blank spaces again appears. What should I do to solve the issue? – Dhaval Dec 20 '16 at 10:50
  • 2
    Hide all items, including parents, cards. I had the same problem, but i resolved hinding all items. – Cătălin Florescu Dec 20 '16 at 12:00
  • I did as you suggested. But still problem seems to be exist. Here is my code. http://pastebin.com/8f4PAsh3 – Dhaval Dec 20 '16 at 12:32
  • 1
    When i said all element, i reffer to your LinearLayout, Relative, TextView and ImageView, not itemview. – Cătălin Florescu Dec 20 '16 at 12:39
  • Sorry, I misunderstood. Your solution works fine. Thank you. – Dhaval Dec 21 '16 at 17:31
  • 1
    @FlorescuCătălin - Sorry for bringing up the conversation again after 4 years... But how do you get the LinearLayout from the view holder? – Alaa M. Jul 16 '20 at 15:53
30

IF

view.setVisibility(View.GONE);

gives you a Blank view

Then follow This.

public static class Data_ViewHolder extends RecyclerView.ViewHolder {
    private final LinearLayout layout;
    final LinearLayout.LayoutParams params;

    public Show_Chat_ViewHolder(final View itemView) {
        super(itemView);
        .
        .
        .
        layout =(LinearLayout)itemView.findViewById(R.id.show_item_layout);
        params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
        ViewGroup.LayoutParams.WRAP_CONTENT);
        .
        .
        .
    }

    private void Layout_hide() {
        params.height = 0;
        //itemView.setLayoutParams(params); //This One.
        layout.setLayoutParams(params);   //Or This one.

    }
  }

Now Call from Adapter

mFirebaseAdapter = new FirebaseRecyclerAdapte......{
public void populateViewHolder.....{

if(model.getData().equals("..Something.."))
  {
      viewHolder.Layout_hide();
  }
else
      viewHolder.Person_Email(model.getEmail());
   }
 }
Community
  • 1
  • 1
Samin
  • 577
  • 6
  • 6
8

If you are hiding whole itemView and facing the problem of blank spaces.

Try this to hide the itemView.

holder.itemView.setVisibility(View.GONE);
ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
params.height = 0;
params.width = 0;
holder.itemView.setLayoutParams(params);

And this to show it.

holder.itemView.setVisibility(View.VISIBLE);

This is a recyclerView, so use both in if else block or you might encounter some unintended UI issues.

Suvajit Patra
  • 190
  • 1
  • 4
5

There is no built in way to hide a child in RecyclerView. But you can implement this feature in your Adapter.

public class MyAdapter extends RecyclerView.Adapter<...>{
    List<Object> items;
    Map<Integer,Object> deletedItems;
    ...

    public void hideItem(final int position) {
         deletedItems.add(position, items.get(position));
         items.remove(position);
         notifyItemRemoved(position);
    }

    ....
}
Fartab
  • 4,725
  • 2
  • 26
  • 39
  • But I haven't implemented custom RecyclerView Adapter. As I mentioned, I am using FirebaseRecyclerAdapter. – Dhaval Dec 19 '16 at 13:31
  • This works only if your itemView is a data in an array, because it may be a header or other kind of element that you want to delete that will mess all around if you do this. – htafoya Apr 30 '19 at 02:03
5

"GONE" will not remove the space occupied by the item ....you can use

if (condition) {
         item.layoutParams.height = 0
         item.layoutParams.width = 0
}

inside "onBindViewHolder"

Sreejesh K Nair
  • 563
  • 1
  • 6
  • 16
1

It seems like RV internally caches root view info so changing it's visibility does nothing to occuppied space by the item.

Wrap you RV item view with FrameLayout and set View.GONE to inner view. This way occupped space will be cleared correctly as well as item won't be shown at all.

Nexen
  • 1,663
  • 4
  • 16
  • 44
0
public class OfferViewHolder extends RecyclerView.ViewHolder {
    public TextView textViewOfferName;
    public LabelImageView labelImageView;
    public TextView textViewOldPrice;
    public TextView textViewNewPrice;
    public TextView textViewShopName;
    public TextView textViewTimeDate;
    public TextView textViewDistance;

    public LinearLayout linearLayoutMain;


    public OfferViewHolder(View view) {
        super(view);
        linearLayoutMain=(LinearLayout) view.findViewById(R.id.ll_main);
        textViewOfferName = (TextView) view.findViewById(R.id.textViewoffername);
        labelImageView=(LabelImageView) view.findViewById(R.id.labelImageView) ;
        textViewOldPrice=(TextView) view.findViewById(R.id.textViewOldPrice);
        textViewNewPrice=(TextView) view.findViewById(R.id.textViewNewPrice);
        textViewShopName=(TextView) view.findViewById(R.id.textViewShopName);
        textViewTimeDate=(TextView) view.findViewById(R.id.textViewDate);
        textViewDistance=(TextView) view.findViewById(R.id.textViewDistance);

        linearLayoutMain.setVisibility(View.GONE);
        textViewOfferName.setVisibility(View.GONE);
        labelImageView.setVisibility(View.GONE);
        textViewOldPrice.setVisibility(View.GONE);
        textViewNewPrice.setVisibility(View.GONE);
        textViewShopName.setVisibility(View.GONE);
        textViewTimeDate.setVisibility(View.GONE);
        textViewDistance.setVisibility(View.GONE);



    }


}`enter code here`

THEN IN YOUR ADAPTER

 if (a.equals(offer.getOfferCategory())) {


                        if (offer.getOfferCategory()==null){
//                            chatMessageViewHolder.getLinearLayoutMain().setVisibility(View.GONE);
//                            chatMessageViewHolder.linearLayoutMain.setLayoutParams(new RecyclerView.LayoutParams(0, 0));


                        }
                        else {
                            chatMessageViewHolder.itemView.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.textViewShopName.setText(offer.getOfferCategory());
                            chatMessageViewHolder.linearLayoutMain.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.textViewOfferName.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.labelImageView.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.textViewOldPrice.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.textViewNewPrice.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.textViewShopName.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.textViewTimeDate.setVisibility(View.VISIBLE);
                            chatMessageViewHolder.textViewDistance.setVisibility(View.VISIBLE);

                        }

Thank you lorescu George Cătălin and Dhalav

Goodlife
  • 3,822
  • 2
  • 24
  • 23
0

holder.itemView.setVisibility(View.VISIBLE); is not working now. I am using this

holder.itemView.findViewById(R.id.card).setVisibility(View.GONE);

you can easily send the ViewHolder value to your action function ..

0

I do not recommend answers with setting height and width of View to 0 because adapter still needs to render them and if there are too many hidden items this can cause lags, it is better to change the list itself and then send it to the adapter

hotMule
  • 25
  • 1
  • 6
  • Please try to improve the other answers or provide a real answer instead of writing a answer that only is a comment to them. – Fabian Mar 27 '21 at 15:02
0
private fun hideShowItemView(itemView: View, toShow: Boolean) {
        itemView.isVisible = toShow
        itemView.layoutParams.height = if (toShow) ViewGroup.LayoutParams.WRAP_CONTENT else 0
 }
  1. itemView is an ItemView of the ViewHolder
  2. toShow is a boolean to hide or show item of recyclerview

Use below line of code in onBindViewHolder block as per the requirement,

To hide Item : hideShowItemView(holder.itemView, false)

To show Item : hideShowItemView(holder.itemView, true)
Jinal Patel
  • 229
  • 2
  • 7