Как скрыть элемент в списке в Android
Я знаю, этот вопрос задавали раньше, но я не видел для него рабочего ответа.
Есть ли способ скрыть некоторые элементы в ListView
без изменения исходных данных?
Я попытался установить видимость представления элемента для удаления, он больше не будет отображаться, но место, зарезервированное для этого элемента, все еще существует.
Я также установил:
android:dividerHeight="0dp"
android:divider="#FFFFFF"
Без успеха.
Ответы
Ответ 1
Вы можете написать собственный ListAdapter
или подкласс один из существующих.
В вашем ListAdapter
вы просто отфильтровываете элементы, которые вы не хотите отображать, возвращая измененные значения для getCount()
, getItem()
и getItemId()
, если это необходимо.
Ответ 2
Я попробовал несколько решений, включая setVisibitlity(View.GONE)
, и раздул представление по умолчанию null
, но все они имеют общую проблему и что разделители между скрытыми элементами сложены и делают плохо видимое серое пространство в больших списках.
Если ваш ListView
поддерживается CursorAdapter
, лучшим решением является обернуть его CursorWrapper
.
Итак, мое решение (основанное на @RomanUsachev answer здесь) таково:
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
, вы контролируете отображение или скрытие скрытых элементов. И преимущество заключается в том, что вы не видите, что нежелательные разделители сложены.: -)
Ответ 3
если вы хотите скрыть элемент следующим образом:
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));
}
Ответ 4
В некоторых случаях у вас есть простое решение:
Мне нужно скрыть представление в представлении списка, потому что элементы для заполнения представления недопустимы, поэтому я не хочу видеть представление:
В моем адаптере списка:
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 ...
}
Ответ 5
У меня есть CursorAdapter
, который нельзя изменить с помощью массива поддержки, поскольку проверка того, должен ли элемент отображаться или нет, был после получения результата из базы данных. Я реализовал решение в bindView(View v, Context context, Cursor c)
аналогичным способом, как описано в других сообщениях. Я думаю,, что лучший способ - переопределить метод bindView()
, а не getView(int position, View convertView, ViewGroup parent)
, потому что вы должны переносить нуль-ckecking для convertView в getView().
Вторая вещь. Я попытался скрыть View v
в bindView(View v, Context context, Cursor c)
, и это не сработало. После исследования я выяснил, что мне нужно скрыть каждый элемент в представлении (включая макеты, содержащие ваши тексты, изображения и т.д.).
Ответ 6
Хлопок должен был бы установить высоту элемента списка, который вы хотите скрыть до 0.
Но, как говорит серетур, правильный подход заключается в том, чтобы удалить элемент и использовать notifyDataSetChanged()
. Почему это решение не подходит в вашем случае?
Ответ 7
Если вы создаете свой собственный adapter
, вы можете сделать это в методе public View getView(int position, View convertView, ViewGroup parent)
. Это может быть полезно, если вы планируете показывать невидимые предметы в какой-то момент. Например:
if(item.getYourRequirement == undesiredVlue)
convertView.setVisibility(View.GONE);
else
convertView.setVisibility(View.VISIBLE);
Я надеюсь, что это поможет
Ответ 8
Простой способ решить эту проблему для меня, просто проанализировать и проверить ваш список в активности перед вызовом метода setAdapter, например:
for(Object a : list){
//here must be some condition for getting all items without hidden
newList += a;
}
setAdapter(newList);
Ответ 9
Здесь мое решение:
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;
}
}
Ответ 10
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);
}
});
это эффективно для меня.
Ответ 11
Для меня простым решением было создать свой собственный адаптер с пользовательским макетом строки, содержащий оболочку всего содержимого (например, LinearLayout), и установить видимость этой оболочки в View.GONE в методе getView() адаптера, когда я не хотел этот предмет показан. Нет необходимости изменять набор данных или поддерживать два списка. При создании ListView я также установил, чтобы он не показывал автоматически разделители:
android:divider="@null"
android:dividerHeight="0dp"
и нарисуйте свой собственный делитель (который я также установил на УТРА, когда я не хочу этот элемент). В противном случае вы увидите несколько разделителей или толстую серую линию, где скрыты несколько элементов.