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;
}
}
精彩评论