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