Android: How to hide a ListView Item

2019-01-13 21:22发布

问题:

How can you hide an item in a ListView or at least set its height to zero?

I have tried setting the visibility of the View to GONE but it still maintains the item's space (height).

回答1:

Ressurecting an old question, but I just had this issue where I wanted to hide list items temporarily based upon criteria outside of the list data. What I ended up doing was creating a "null item" layout in xml and returned that based upon my criteria instead of the convert view in getView()...

instead of returning the convertView, I returned my null_item...

return inflater.inflate(R.layout.null_item, null);

null_item.xml:

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

</LinearLayout>


回答2:

if you want to hide the item like this:

convertView.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,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.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT));
    convertView.setVisibility(View.VISIBLE);
}


回答3:

When it comes to ListView, to make it efficient, we use ViewHolder pattern. The way to use ViewHolder Pattern and R.layout.row_null of the following xml

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

is to use with getViewTypeCount() and getItemViewType(int position) as follow.

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        return (hideStatusCheck(position)) ? 1 : 0;
    }

    @Override
    public View getView(int pos, View convertView, ViewGroup parent) {
        View rowView = convertView;

        if (hideStatusCheck(pos)) {
            if (rowView == null || rowView.getTag() != null) {
                LayoutInflater inflater = mActivity.getLayoutInflater();
                rowView = inflater.inflate(R.layout.row_null, parent, false);
            }
        } else {
            if (rowView == null) {
                rowView = inflateNormalView(parent);
            } else if (rowView.getTag() == null) {
                rowView = inflateNormalView(parent);
            } else {
                ViewHolder holderToCheck = (ViewHolder) rowView.getTag();
                Integer storedPos = (Integer) holderToCheck.getTag(POSITION);
                if (storedPos == null || storedPos != pos)
                    rowView = inflateNormalView(parent);
            }
            ViewHolder holder = (ViewHolder) rowView.getTag();
            holder.setTag(POSITION,pos);
                        /*
                        Populate data
                        */
            return rowView;
    }

    private View inflateNormalView(ViewGroup parent) {
                    View rowView;
        LayoutInflater inflater = mActivity.getLayoutInflater();
        rowView = inflater.inflate(R.layout.normal_item, parent, false);
        ViewHolder viewHolder = new ViewHolder();
        assert rowView != null;
                    /* Initiate normal findViewById thing*/
        rowView.setTag(viewHolder);
        return rowView;
    }

We do the checking of the item's View type and if it meets the hide check, it will return 1, otherwise 0. The ListView knows that there will be 2 types of View from getViewTypeCount. Now, the getView will return the approriate View depending on the hideStatusCheck. To make a robust ListView, we want to use the ViewHolder pattern. We don't need to use ViewHolder when it is hidden. We simply inflate the R.layout.row_null and return it. We will use the ViewHolder for the R.layout.normal_item. Here is the tricky part assuming the hiding check is not static. The first check of rowView==null is standard. The second check of rowView.getTag()==null is to see if the View is coming back to normal from hiding. The third check in the last else clause is to check if the ViewHolder retained in the tag is the right ViewHolder. If these conditions are met, we always inflate the view again. Yes, it is true that, the ViewHolder pattern is not used throughout but it uses to certain extends. It is better than nothing.



回答4:

I did some tinkering with a drag and drop list from here. When an item is popped out of the list to be moved around the cell space it occupied has it's height set to 1px (see line 238) so it appears "gone". I couldn't find a way to handle this better as setting height to 0 fails as does visibility GONE.

That said, If you really want to get rid of a row less temporarily, it might be a good idea to change the backing of the Adapter and call notifyDataSetChanged() on it.



回答5:

I have look at source code. And there is only one way to hide item without notifyDataSetChanged(). You must set visibility GONE for all inner views and remove background image and paddings for item's view.

Note: Row with such invisible element will be selectable.

P.S: This is very usefull for ExpandableListView if you want to hide group view it self.



回答6:

add to your ListView object: android:dividerHeight="0px" android:divider="#FFFFFF"

Divider color doesn't matter only setting dividerHeight doesn't work

This does remove the divider though...



回答7:

I think I have a much easier / safer solution: you just have to "embed" your item in a Layout, and change the visibility of this parent layout.

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

    <!-- Embed ListView Item into a "parent" Layout -->
    <LinearLayout
        android:id="@+id/parentLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <!-- This is the normal content of your ListView Item -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="World" />

    </LinearLayout>
</LinearLayout>

Then in your code just do:

public View getView(int position, View view, ViewGroup parent) {
    if (view == null) {
        LayoutInflater li = mActivity.getLayoutInflater();
        view = li.inflate(R.layout.my_listview_item, null);
    }
    LinearLayout parentLayout = (LinearLayout) view.findViewById(R.id.parentLayout);
    if (shouldDisplayItem(position)) {
        parentLayout.setVisibility(View.VISIBLE);
    } else {
        parentLayout.setVisibility(View.GONE);
    }
    return view;
}

This way you always use/reuse the same item, and just hide/show it.