如何在Android中隐藏ListView中的项目

29

我知道,这个问题以前已经被问过了,但我没有看到一个可行的答案。

有没有办法在不改变源数据的情况下隐藏ListView中的某些项?

我尝试将项视图的可见性设置为gone,它将不再显示,但是仍然保留了此项目的位置。

我也设置了:

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

没有成功。


1
据我所知,如果不改变数据并调用notifyDataSetChanged(),你无法做到这一点。 - ferostar
我认为Mohsen Afshin的回答是最好的答案,应该被接受。 - AndrewS
我写了一篇关于这个的博客文章,链接在这里:https://vshivam.wordpress.com/2015/01/07/hiding-a-list-item-from-an-android-listview-without-removing-it-from-the-data-source/ - Shivam Verma
11个回答

25

您可以编写自己的 ListAdapter 或子类化现有的适配器之一。

在您的 ListAdapter 中,您只需要通过返回适当的修改值来过滤掉不想要显示的项,如 getCount()getItem()getItemId()


谢谢。我想我会尝试重写现有的适配器,这样对我来说就更容易一些,可以用它做我想做的任何事情。 - Tima
7
修改getCount()、getItem()方法并与notifyDataSetChanged()一起使用是解决方案。谢谢。 - Tima
这对我也起作用了;我只是创建了一个可显示的列表,其中默认包含后备列表中的所有内容。当我想要过滤掉某些东西时,我可以从可显示列表中删除它们,然后在清除过滤器时从后备列表重新填充它。 - mattlary
2
有任何例子吗?在getItem()方法中对于不可见的行应该返回什么?是Null吗? - Piotr
1
如果在getItem()中返回null,我会得到一个NPE。 当我将setVisibility(View.GONE)设置为所需的项时,它也不起作用,它只是在列表中显示一个空白的位置而不是该项。 - Subtle Fox
虽然它要求一些重复的充气软件,但它是一个完美的解决方案,无需额外的充气软件代码。 - Vijay Kumar Kanta

15

如果您想要隐藏该项,像这样:

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

不能使用AbsListView.LayoutParams(-1,0);

如果convertview被重用,你应该添加以下内容来将其高度还原:

if(convertView.getVisibility() == View.GONE) {
            convertView.setVisibility(View.VISIBLE);
            convertView.setLayoutParams(new AbsListView.LayoutParams(-1,-2));
        }

这个技巧可以保持位置或索引,并且非常容易实现。 - Tomero Indonesia
我可以问一下第一行代码中的-1,1是什么意思吗? - Keith Mak

11

我尝试了几种解决方案,包括setVisibitlity(View.GONE)和膨胀一个默认的null视图,但它们都有一个共同的问题,那就是隐藏项目之间的分隔线堆叠在一起,在大型列表中形成一个糟糕的灰色可见空间。

如果您的ListViewCursorAdapter支持,则最好的解决方案是使用CursorWrapper进行包装。

所以我的解决方案(基于@RomanUsachev答案这里)是这样的:

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;
    }
}

当你的 Cursor 准备好后,将它与你想要的列索引一起传入 FilterCursorWrapper

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

dataAdapter.changeCursor(filterCursorWrapper);

如果您进行过滤和排序,请不要忘记在所有地方使用FilterCursorWrapper

    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);
        }
    });

如果要刷新列表,只需要使用空过滤器进行查询即可:

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

只需更改isHidden方法的逻辑,即可控制隐藏项目的显示或隐藏。好处是您不会看到不需要的分隔符堆叠起来。 :-)


5
在某些情况下,您可以很容易地解决问题:
我需要隐藏列表视图中的一个视图,因为用于填充该视图的项目无效,所以我不想看到该视图:
在我的列表适配器中:
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 ...


}

2
如果您创建自己的适配器,可以在 public View getView(int position, View convertView, ViewGroup parent) 方法中完成。这在您计划在某个时候显示不可见项的情况下非常有用。例如:
if(item.getYourRequirement == undesiredVlue)
    convertView.setVisibility(View.GONE);
else
    convertView.setVisibility(View.VISIBLE);

希望这对你有所帮助。

3
没有成功。我认为,不可见比消失更糟糕。如果你将视图的可见性设置为不可见,视图仍然存在,只是你看不到它。如果你将其设置为已消失,视图就应该没有了 :) 也许我错了。 - Tima
是的,你说得完全正确,我的错误,我把它从需要不可见但仍然有空间的代码中复制过来了。在你的情况下,应该使用GONE。 - raukodraug
7
最奇怪的事情是,对于两种可见性,它们表现出相同的行为 :( - Tima
那么卡斯滕提出的呢?那也可以在适配器的getView中完成。这肯定不会有问题。 - raukodraug
5
我认为问题在于Android会预先测量你的视图并假设视图大小不会改变。因此,具有GONE可见性的视图实际上已经消失了,但周围的容器仍然占据着相同的空间。 - sstn
显示剩余2条评论

2

我有一个CursorAdapter,无法通过支持数组进行修改,因为检查是否应该显示项目的操作是在从数据库获取结果后进行的。我已经在bindView(View v, Context context, Cursor c)中实现了解决方案,方法类似于其他帖子中描述的方法。我认为最好的方法是覆盖bindView()方法而不是getView(int position, View convertView, ViewGroup parent),因为你需要在getView()中进行convertView的空值检查。
第二件事:我尝试在bindView(View v, Context context, Cursor c)中隐藏View v,但它没有起作用。经过调查,我发现我必须隐藏视图中的每个元素(包括包含您的文本、图像等的布局)。


1

对于我来说,解决这个问题的简单方法是,在调用“setAdapter”方法之前,在活动中解析并检查列表,例如:

只需在调用“setAdapter”方法之前解析和检查您的列表即可。

for(Object a : list){
//here must be some condition for getting all items without hidden
newList += a;
}
setAdapter(newList);

1

一个hack的方法是将想要隐藏的列表项的高度设置为0。

但是,正如seretur所说,正确的方法是删除该项并使用notifyDataSetChanged()。为什么这种解决方案不适用于您的情况?


2
因为 DataSet 不会被更改。它保持不变。我将尝试将高度设置为0。 - Tima
我试图将高度设置为0,但是我得到了一个ClassCastException :((( 今天运气不好。 - Tima
这是一个糟糕的解决方案。当我们只想隐藏一个项目时。例如,在目录中隐藏文件。如果请求的话则显示它们。 - user9599745

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);

        }
    });

这对我很有效。


0
对我来说,一个简单的解决方案是创建自己的适配器,使用自定义行布局,其中包含所有内容的包装器(例如LinearLayout),并在适配器的getView()方法中将此包装器的可见性设置为View.GONE,当我不想显示该项时。无需修改数据集或维护两个列表。创建ListView时,我还将其设置为不自动显示分隔线:
    android:divider="@null"
    android:dividerHeight="0dp"

并且绘制自己的分隔线(当我不想显示该项时,我还将其设置为GONE)。否则,您将看到多个分隔线或在隐藏多个项的情况下看到一条粗灰色线。


最佳答案。唯一的问题是创建自己的分隔符。 - user9599745
我的朋友,我不知道是谁给你投了反对票,但这就是解决方案。点个赞吧! - user3560827

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接