Je sais, cette question a déjà été posée, mais je n'ai pas trouvé de réponse satisfaisante à cette question.
Est-il possible de cacher certains éléments dans une ListView
sans modifier les données source?
J'ai essayé de régler la visibilité de la vue d'élément sur disparu, elle ne sera plus affichée, mais la place réservée à cet élément est toujours là.
J'ai aussi mis:
Android:dividerHeight="0dp"
Android:divider="#FFFFFF"
Sans succès.
Vous pouvez écrire votre propre variable ListAdapter
ou une des sous-classes existantes.
Dans votre ListAdapter
, vous filtreriez simplement les éléments que vous ne voulez pas afficher en renvoyant les valeurs modifiées pour getCount()
, getItem()
et getItemId()
selon le cas.
J'ai essayé plusieurs solutions, y compris setVisibitlity(View.GONE)
et en gonflant une vue null
par défaut, mais toutes ont un problème commun et les séparateurs entre les éléments masqués sont empilés et créent un mauvais espace gris visible dans les grandes listes.
Si votre ListView
est soutenu par un CursorAdapter
, la meilleure solution consiste à l'envelopper avec un CursorWrapper
.
Donc ma solution (basée sur la réponse @RomanUsachev ici ) est la suivante:
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;
}
}
lorsque votre Cursor
est prête, passez à FilterCursorWrapper
avec l'index de colonne souhaité.
FilterCursorWrapper filterCursorWrapper = new FilterCursorWrapper(cursor, true,DATA_COLUMN_INDEX);
dataAdapter.changeCursor(filterCursorWrapper);
et si vous filtrez et triez, n'oubliez pas d'utiliser FilterCursorWrapper
partout:
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);
}
});
et pour rafraîchir la liste, cela suffit pour interroger avec un filtre vide:
dataAdapter.getFilter().filter("");
et vous avez terminé, en changeant simplement la logique de la méthode isHidden
, vous contrôlez l’affichage ou le masquage des éléments masqués. Et l'avantage est que vous ne voyez pas les séparateurs indésirables empilés. :-)
si vous voulez masquer l'élément comme ceci:
convertView.setLayoutParams(new AbsListView.LayoutParams(-1,1));
convertView.setVisibility(View.GONE);
ne peut pas être AbsListView.LayoutParams (-1,0);
si convertview est réutilisé, vous devriez ajouter ceci ci-dessous pour le régler en hauteur:
if(convertView.getVisibility() == View.GONE) {
convertView.setVisibility(View.VISIBLE);
convertView.setLayoutParams(new AbsListView.LayoutParams(-1,-2));
}
Dans certains cas, vous avez une solution simple:
Je dois masquer une vue dans une vue de liste car les éléments à remplir pour afficher la vue sont invalides
Dans mon adaptateur de liste:
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 ...
}
J'ai un CursorAdapter
qui ne peut pas être modifié avec un tableau de sauvegarde, car vérifier si l'élément doit être affiché ou non est obtenu après l'obtention du résultat de la base de données. J'ai implémenté la solution dans bindView(View v, Context context, Cursor c)
selon une méthode similaire à celle décrite dans d'autres publications. Je pense que le meilleur moyen est de surcharger la méthode bindView()
plutôt que getView(int position, View convertView, ViewGroup parent)
car vous devriez effectuer environ null-ckecking pour convertView dans getView ().
La deuxième chose : J'ai essayé de cacher View v
dans bindView(View v, Context context, Cursor c)
et cela n'a pas fonctionné. Après enquête, j'ai compris que je devais masquer chaque élément en vue (y compris les mises en page contenant vos textes, images, etc.)
Un moyen simple de résoudre ce problème pour moi, il suffit d'analyser et de vérifier votre liste d'activité avant d'appeler la méthode "setAdapter" , par exemple
for(Object a : list){
//here must be some condition for getting all items without hidden
newList += a;
}
setAdapter(newList);
Si vous créez votre propre adapter
, vous pouvez le faire avec la méthode public View getView(int position, View convertView, ViewGroup parent)
. Cela peut être utile si vous envisagez d'afficher les éléments invisibles à un moment donné. Par exemple:
if(item.getYourRequirement == undesiredVlue)
convertView.setVisibility(View.GONE);
else
convertView.setVisibility(View.VISIBLE);
J'espère que ça aide
Un hack serait de définir la hauteur de l'élément de la liste que vous voulez masquer à 0.
Mais, comme le dit seretur, la bonne approche consiste à retirer l’article et à utiliser notifyDataSetChanged()
. Pourquoi cette solution n'est-elle pas appropriée dans votre cas?
Voici ma 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;
}
}
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);
}
});
c'est efficace pour moi.