开发者

How to hide an item in a listview in Android

开发者 https://www.devze.com 2023-02-11 16:31 出处:网络
I 开发者_如何学Goknow, this question was asked before, but I haven\'t seen a working answer for it.

I 开发者_如何学Goknow, 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.


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.


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


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. :-)


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 ...


}


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


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


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?


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


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.


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.


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

精彩评论

暂无评论...
验证码 换一张
取 消