ListView with ability to hide rows

I do not know why! but someone on stackoverflow was asking about setting the height of a row of a ListView to zero.

Setting the height to zero will not work indeed! Many suggest to use View.Gone for the visibility of the row. However this is a ListView and therefore, setting a Visibility of Gone to one of the views will actually keep its space occupied while making its content vanish.

Anyway, here is an implementation of ArrayAdapter that has two public functions hide(int) and unHide(int) allowing the user to hide/unhide the row at the position.

The approach taken here is based on keeping the views used by the ListView intact. This means we do not want memory to be overflowed and at the same time we want to be able to hide some entries.

My way depends on, as usual, an array of objects. Without loss of generality, lets consider that this Adapter takes an array of String type and keeps it locally. To be able to hide, we need an array of Boolean type of the same size. These two arrays are items and hidden respectively. We initialize the hidden array to false in the constructor.

public class SherifHidingAdapter extends ArrayAdapter<String> {
 String[] items = null;
 boolean[] hidden = null;

 public SherifHidingAdapter(Context mContext, String[] objects) {
  super(mContext, R.layout.some_layout, objects);
  items = objects;
  hidden = new boolean[objects.length];
  for (int i = 0; i < objects.length; i++)
   hidden[i] = false;
 }
}

Now ArrayAdapter needs to override the function getCount(). This function usually returns the number of objects (rows) in the ListView. Typically, that would be the size of array items. In our case however, this size is not the real size of the ListView. We should subtract the number of hidden items from the size of items. And thereby:

@Override
public int getCount() {
 return (items.length - getHiddenCount());
}
private int getHiddenCount() {
 int count = 0;
 for(int i=0;i<items.length;i++)
  if(hidden[i])
   count++;
 return count;
}

Our public interface consists of two functions: one function hide() used to hide a certain row and another function unHide() used to unHide() a row. These function simply set the chosen index of the hidden array to true or false accordingly.

public void hide(int position) {
 hidden[getRealPosition(position)] = true;
 notifyDataSetChanged();
 notifyDataSetInvalidated();
}
public void unHide(int position) {
 hidden[getRealPosition(position)] = false;
 notifyDataSetChanged();
 notifyDataSetInvalidated();
}

Now the real work is done in the getView() function. The idea is very simple. Hopefully you can get it easily. We have a data source which is our array items in this case. We will create our Views as normally. However, we need some kind of translation between the position supplied by getView() or, more generally, by the ListView and the position of data. For example, if index 0 is hidden. The ListView will call getView with position = 0 as expected. The only difference will be that the data supplied will not be from index 0 of the data but from index 1. This translation is supplied by the function getRealPosition as follows:


private int getRealPosition(int position) {
 int hElements = getHiddenCountUpTo(position);
 int diff = 0;
 for(int i=0;i<hElements;i++) {
  diff++;
  if(hidden[position+diff])
   i--;
 }
 return (position + diff);
}
private int getHiddenCountUpTo(int location) {
 int count = 0;
 for(int i=0;i<=location;i++) {
  if(hidden[i])
   count++;
 }
 return count;
}

The last step is to translate the position supplied by getView to the new position using getRealPosition.

@Override
public View getView(int index, View convertView, ViewGroup arg2) {
 //this is the secret step:
 int position = getRealPosition(index);
 if(convertView == null) {
  LayoutInflater vi = (LayoutInflater) getContext().getSystemService(
    Context.LAYOUT_INFLATER_SERVICE);
  convertView = vi.inflate(R.layout.some_layout, null);
 }

 // bla bla bla
 // initialise the view
 // bla bla bla
 return (convertView);
}

This is all what you need to get a ListView that has the capability of hiding and unhiding its rows.

Enjoy


Click here for Sample Project + Source Code (download)

11 comments:

  1. This doesn't appear to function properly at all.

    ReplyDelete
    Replies
    1. Please check the sample project. It does work actually.

      Delete
  2. Hi,

    great article.

    ReplyDelete
  3. Hi Sherif,
    want to add a new item to adapter with the add method, but get unsupportedoperationexception.
    do u have an example of howto add and remove items?

    regards
    eardgyl

    ReplyDelete
  4. Hi Sherif,
    Very useful example.thank you.

    ReplyDelete
  5. Hi Sharif, i have one issue. it worked for hiding rows but i hav implemented sorting on users selection. when am doing sorting hidden row again showing, when listview refreshed after sorting.

    ReplyDelete
    Replies
    1. You have to implement your own data filtering because the hidden array will be useless after filtering your data. A simple way would be to reinitialize the array hidden with values that reflect the real value from the original hidden array.

      Delete
  6. too bad it throws an indexoutofbounds exception in getRealPosition

    ReplyDelete
  7. Got it fixed now. I just removed the call to getRealPosition in the hide and unhide functions. That was needed if the position parameter is absolute position in itemlist.

    ReplyDelete