How to hide an item in a listview in Android
Asked Answered
P

12

29

I know, this question was asked before, but I haven't seen a working answer for it.

Is there any way to hide some items in a ListView without changing source data?

I tried to set visibility of the item view to gone, it won't be displayed anymore, but the place reserved for this item is still there.

I also set:

android:dividerHeight="0dp"
android:divider="#FFFFFF"

Without success.

Prevision answered 18/2, 2011 at 12:53 Comment(3)
AFAIK you can't do it without changing the data and notifyDataSetChanged()Constructive
i think Mohsen Afshin answer is the best answer and should be acceptedFlagstaff
I wrote a blogpost about this, here : vshivam.wordpress.com/2015/01/07/…Armure
S
25

You can either write your own ListAdapter or subclass one of the existing ones.

In your ListAdapter, you would simply filter out the items you do not want displayed by returning modified values for getCount(), getItem() and getItemId() as appropriate.

Subir answered 18/2, 2011 at 13:54 Comment(6)
Thank you. I think, I'll try to rewrite the existing Adapter, so it would be easier a bit for me, to do with it everything I wantPrevision
modified getCount(), getItem() together with notifyDataSetChanged() were solution. Thank youPrevision
This worked for me too; I just created a displayable list which by default contains everything in the backing list. When I want to filter things out, I can just remove them from the displayable list and then repopulate it from the backing list when the filter is cleared.Allegedly
Any examples? What should you return in getItem() for invisible rows? Null?Zola
If I return null in getItem() I've got a NPE. When I set setVisibility(View.GONE) to desired item it doesn't working too, it just shows an an empty place instead of item in the listGammadion
Though it asks for a bit of repeated bloatware, it's a perfect solution without need to additional bloatware code.Manutius
B
15

if you want to hide the item like this:

convertView.setLayoutParams(new AbsListView.LayoutParams(-1,1));
convertView.setVisibility(View.GONE);

can't be AbsListView.LayoutParams(-1,0);

if convertview are reused you should add this below to set it height back:

if(convertView.getVisibility() == View.GONE) {
            convertView.setVisibility(View.VISIBLE);
            convertView.setLayoutParams(new AbsListView.LayoutParams(-1,-2));
        }
Boatyard answered 6/1, 2015 at 2:58 Comment(2)
this trick will keep position or index and very easy to implementOuter
may I ask what -1,1 means in the first line of code?Pincushion
J
11

I tried several solutions including setVisibitlity(View.GONE) and inflating a default null view but all of them have a common problem and that's the dividers between hidden items are stacked up and make a bad visible gray space in large lists.

If your ListView is backed by a CursorAdapter then the best solution is to wrap it with a CursorWrapper.

So my solution (based on @RomanUsachev answer here) is this:

FilterCursorWrapper

   public class FilterCursorWrapper extends CursorWrapper {
    private int[] index;
    private int count = 0;
    private int pos = 0;

    public boolean isHidden(String path) {

      // the logic to check where this item should be hidden

      //   if (some condintion)
      //      return false;
      //    else {
      //       return true; 
      //   }

       return false;

    }

    public FilterCursorWrapper(Cursor cursor, boolean doFilter, int column) {
        super(cursor);
        if (doFilter) {
            this.count = super.getCount();
            this.index = new int[this.count];
            for (int i = 0; i < this.count; i++) {
                super.moveToPosition(i);
                if (!isHidden(this.getString(column)))
                    this.index[this.pos++] = i;
            }
            this.count = this.pos;
            this.pos = 0;
            super.moveToFirst();
        } else {
            this.count = super.getCount();
            this.index = new int[this.count];
            for (int i = 0; i < this.count; i++) {
                this.index[i] = i;
            }
        }
    }

    @Override
    public boolean move(int offset) {
        return this.moveToPosition(this.pos + offset);
    }

    @Override
    public boolean moveToNext() {
        return this.moveToPosition(this.pos + 1);
    }

    @Override
    public boolean moveToPrevious() {
        return this.moveToPosition(this.pos - 1);
    }

    @Override
    public boolean moveToFirst() {
        return this.moveToPosition(0);
    }

    @Override
    public boolean moveToLast() {
        return this.moveToPosition(this.count - 1);
    }

    @Override
    public boolean moveToPosition(int position) {
        if (position >= this.count || position < 0)
            return false;
        return super.moveToPosition(this.index[position]);
    }

    @Override
    public int getCount() {
        return this.count;
    }

    @Override
    public int getPosition() {
        return this.pos;
    }
}

when your Cursor is ready, feed to FilterCursorWrapper with your desired column index

FilterCursorWrapper filterCursorWrapper = new FilterCursorWrapper(cursor, true,DATA_COLUMN_INDEX);

dataAdapter.changeCursor(filterCursorWrapper);

and if you do filtering and sorting, don't forget to use FilterCursorWrapper everywhere:

    dataAdapter.setFilterQueryProvider(new FilterQueryProvider() {
        @Override
        public Cursor runQuery(CharSequence constraint) {
            String selection = MediaStore.Video.Media.DATA + " LIKE '%" + constraint.toString().toLowerCase() + "%'";
            return new FilterCursorWrapper(context.getContentResolver().query(videoMediaUri, columns, selection, null, null), true, DATA_COLUMN_INDEX);
        }
    });

and for refreshing the list, that's sufficient to query with empty filter:

dataAdapter.getFilter().filter("");

and you're done, simply by changing the logic of isHidden method, you control to show or hide hidden items. And the benefit is that you don't see undesired dividers stacked up. :-)

Juarez answered 26/8, 2013 at 15:59 Comment(0)
H
5

In some case you have an easy solution :

I have to hide a View in a list view because the items to populate the view are invalid, So I don't want to see the view :

In my list adapter :

public class PlanListAdapter extends BaseAdapter{

//some code here : constructor ......

    // my code that create the view from the item list (one view by item ...)
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {


        LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        convertView = inflater.inflate(R.layout.my_layout, null);

        if(my_data_are_not_valid) {
             //just create an empty view     
             convertView = new Space(context);  
        }
        else {
             //populate the view with data here     
             populate(convertView);
        }

        return convertView;
}

//some code here to populate the view ...


}
Harmonica answered 8/1, 2015 at 22:6 Comment(0)
N
2

If you create your own adapter you can do it in the public View getView(int position, View convertView, ViewGroup parent) method. This can be useful in case you are planning to show the invisible items at some point. For example:

if(item.getYourRequirement == undesiredVlue)
    convertView.setVisibility(View.GONE);
else
    convertView.setVisibility(View.VISIBLE);

I hope this helps

Nonparous answered 18/2, 2011 at 15:19 Comment(7)
No success. And I think, INVISIBLE is worse than GONE. If you set visibility to INVISIBLE, the view is still there, you cann't just see it. If you set it to GONE view should be gone :) Maybe I'm wrongPrevision
yes, you are totally right, my mystake, I copied it from a code where i needed it to be invisible but still have the space there. In your case it should be GONE.Nonparous
the strangest thing is, that it is the same behaviour for both visibilities :(Prevision
and what about what Carsten proposed? that would also be done in the getView of the adapater. That for sure wont have a problem.Nonparous
this adapter created another guy. I was trying to understand his logic and then gave up. And I don't really like it. The easiest way would be to hide item, but as I wrote, that doesn't work properly for me. So, i guess, it's easier to rewrite / modificate existing adapter using some code of himPrevision
@MurVotema I believe the problem is that Android pre-measures your views and assumes that view sizes won't change. Therefore the View with visibility GONE is actually gone, but the surrounding container still consumes the same space.Mildamilde
@Mildamilde that is wrong. The problem is that ListView draws the view regardless of its visibility. The solution is overriding ListView and changing this behaviour. But which function in ListView to change?Infralapsarian
G
2

I have a CursorAdapter that can't be modified with backing array, because checking whether item should be shown or not was after getting result from database. I've implemented solution in bindView(View v, Context context, Cursor c) in similar method as was described in other posts. I think that the best way is overriding bindView() method rather then getView(int position, View convertView, ViewGroup parent) because you should carry about null-ckecking for convertView in getView().
The second thing: I've tried to hide View v in bindView(View v, Context context, Cursor c) and it doesn't worked. After investigation I have figured out that I have to hide each element in view (including layouts that contain your texts, images and etc.)

Gammadion answered 17/1, 2013 at 11:51 Comment(0)
U
1

A hack would be to set the height of the list item you want hidden to 0.

But, as seretur says, the correct approach is to remove the item and use notifyDataSetChanged(). Why is this solution not appropriate in your case?

Undeniable answered 18/2, 2011 at 13:45 Comment(3)
because DataSet won't be changed. It stays the same. I'll try to set height to 0Prevision
I'm trying to set height to 0, but I'm getting a ClassCastException :((( it's not my dayPrevision
This is a bad solution. When all we want to do is hide an item. For example, hidden files in a directory. And show them if requested.Infralapsarian
M
1

Simple way to resolve this problem for me, just parse and check your list in activity before calling method "setAdapter", for example:

for(Object a : list){
//here must be some condition for getting all items without hidden
newList += a;
}
setAdapter(newList);
Mikamikado answered 4/5, 2016 at 9:32 Comment(0)
T
0

listview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp" >

<ImageView
    android:id="@+id/imgView"
    android:layout_width="40dp"
    android:layout_height="20dp"/>

<TextView
    android:id="@+id/totalTime"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:textSize="14dp"
    android:text="16 分鐘"
    android:layout_weight="1"
    android:paddingLeft="10dp" />
<TextView
    android:id="@+id/currentTime"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:textSize="14dp"
    android:text="11:46"
    android:layout_weight="1"
    android:gravity="right"
    android:paddingLeft="10dp" />
<ImageView
    android:id="@+id/img"
    android:layout_width="40dp"
    android:layout_height="20dp"
    />
</LinearLayout>

MainActivity.java

lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            view.findViewById(R.id.img).setVisibility(View.GONE);

        }
    });

it's effective for me.

Tuberculin answered 13/6, 2018 at 18:52 Comment(0)
C
0

For me a simple solution was to create my own adapter with a custom row layout, containing a wrapper of all content (e.g. LinearLayout), and set this wrapper visibility to View.GONE in the adapter's getView() method, when I did not want that item shown. No need to modify the data set or maintain two lists. When creating the ListView, I also set it to not automatically show dividers:

    android:divider="@null"
    android:dividerHeight="0dp"

and draw my own divider (which I also set to GONE when I don't want that item). Otherwise you'd see multiple dividers or a thick gray line where multiple items were hidden.

Crampton answered 23/5, 2019 at 2:28 Comment(2)
Best answer. The only issue is creating your own divider.Infralapsarian
my friend I don't know who voted you down but this is thee solution. upvote!Eris
A
0

I use Jetpasck Compose so I don't use XML layouts.

Anyway, I found a solution for the case that the hidden Items follow a pattern or RegEx.

Within the LazyColumn Scope I added a filter to the List then in the selection code I do a lookup in the unfiltered List

This a partial code using this option:

LazyColumn(modifier = Modifier.fillMaxWidth(), state = listState) {
                    if (notSetLabel != null) {
                        item {
                            LargeDropdownMenuItem(
                                text = notSetLabel,
                                selected = false,
                                enabled = false,
                                onClick = { },
                            )

                        }
                    }
                    //Adding the filter. I my case a contains function was good enough
                    itemsIndexed(items.filter { x -> x.toString().contains(term, true)
                    }) { index, item ->
                        val selectedItem = index == selectedIndex
                        drawItem(
                            item,
                            selectedItem,
                            true
                        ) {
                            //onItemSelected(index, item) --- Original code
                            //Change the selection to a lookup for original index in the unfiltered List
                            onItemSelected(items.indexOf(item), item)
                            expanded = false
                        }

                        if (index < items.lastIndex) {
                            Divider(modifier = Modifier.padding(horizontal = 8.dp))
                        }
                    }

                }

That is it every thing stays the same.

Anticline answered 16/12, 2023 at 17:15 Comment(0)
R
-1

Here my solution:

public class MyAdapter extends ArrayAdapter<MyEntry> {

    public MyAdapter(Context context, int resId) {
        super(context, resId);
    }

    private List<MyEntry> visibleEntries() {

        List<MyEntry> result = new ArrayList<MyEntry>();

        int i = 0;
        try {
            while (true) {
                if (getItem(i).isVisible())
                    result.add(getItem(i));
                i++;
            }

        } catch(Exception e) {

        }

        return result;

    }

    @Override
    public int getCount() {
        return visibleEntries().size();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        LinearLayout layout = 
            convertView == null ? 
                (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.entry, parent, false) :
                (LinearLayout) convertView;

        MyEntry entry = visibleEntries().get(position);

        ...

        return layout;
    }
}
Robinette answered 9/3, 2017 at 14:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.