1

Je développe une application Android qui a une liste de plus de 80 objets "God", chacun ayant un attribut de chaîne "ClassName" ("Warrior", "Hunter", "Assassin", etc.).ListView Adapter avec ToggleButton Filters

J'essaye d'implémenter des ToggleButtons qui filtreront la liste par ClassName. Plusieurs filtres devraient être autorisés à la fois (exemple: En cochant les boutons "Warrior" et "Hunter", ToggleButtons devrait afficher tous les objets avec ClassName "Warrior" OU "Hunter").

Jusqu'ici, j'ai seulement pu filtrer une liste basée sur une contrainte ClassName. Toute aide pour y parvenir serait grandement appréciée!

Voici mon fragment principal:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View myView = inflater.inflate(R.layout.home,null); 

     final ArrayList<God> gods = getGods(); 

     lv = (ListView) myView.findViewById(R.id.home_list); 
     sv = (SearchView) myView.findViewById(R.id.home_search); 
     sv.setQueryHint("Search Gods..."); 
     mage = (ToggleButton) myView.findViewById(R.id.mage); 
     assassin = (Button) myView.findViewById(R.id.assassin); 

     adapter = new GodAdapter(getActivity(), gods); 
     lv.setAdapter(adapter); 

     mage.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
      @Override 
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
       if(isChecked){ 
        //Include all mages 
       } 
       else { 
        //Remove all mages 

       } 
      } 
     }); 

assassin.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
       @Override 
       public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
        if(isChecked){ 
         //Include all assassins 
        } 
        else { 
         //Remove all assassins 

        } 
       } 
      }); 

(...other buttons...) 



     sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { 
      public boolean onQueryTextSubmit(String query) { 
       return false; 
      } 
      public boolean onQueryTextChange(String query) { 
       adapter.getFilter().filter(query); 
       return false; 
      } 
     }); 

     lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
      public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { 
       String name = adapter.getItem(position).getName(); 

       for(int i = 0; i < gods.size(); i++) { 
        if(gods.get(i).getName().equals(name)) 
        { 
         Fragment myFragment = new TabFragment(); 
         myFragment.setArguments(createGodBundle(gods.get(i))); 
         replaceFragment(myFragment); 
         hideSoftKeyboard(getActivity()); 
         break; 
        } 
       } 
      } 
     }); 

     return myView; 
    } 

Et voici mon GodAdapter:

public class GodAdapter extends BaseAdapter implements Filterable { 

    Context c; 
    ArrayList<God> gods; 
    ArrayList<God> filterList; 
    CustomFilter filter; 

    public GodAdapter(Context c, ArrayList<God> gods) { 
     this.c = c; 
     this.gods = gods; 
     this.filterList = gods; 
    } 

    public int getCount() { 
     return gods.size(); 
    } 

    public God getItem(int position) { 
     return gods.get(position); 
    } 

    public long getItemId(int position) { 
     return gods.indexOf(getItem(position)); 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 


     LayoutInflater inflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

     if(convertView == null) 
      convertView = inflater.inflate(R.layout.god_selection, null); 

     TextView name = (TextView) convertView.findViewById(R.id.god_name); 
     ImageView image = (ImageView) convertView.findViewById(R.id.god_image); 
     ImageView pantheon =(ImageView) convertView.findViewById(R.id.pantheon); 
     ImageView type =(ImageView) convertView.findViewById(R.id.type); 

     name.setText(gods.get(position).getName()); 
     image.setImageResource(gods.get(position).getImage()); 
     pantheon.setImageResource(gods.get(position).getPantheonIcon()); 
     type.setImageResource(gods.get(position).getClassIcon()); 

     return convertView; 
    } 

    public Filter getFilter() { 
     if(filter == null) 
      filter = new CustomFilter(); 
     return filter; 
    } 


    //Inner class for filtering 
    class CustomFilter extends Filter { 

     protected FilterResults performFiltering(CharSequence constraint) { 

      FilterResults results = new FilterResults(); 

      if(constraint != null && constraint.length() > 0) 
      { 
       constraint = constraint.toString().toUpperCase(); 
       ArrayList<God> filters = new ArrayList<God>(); 

       for(int i = 0; i < filterList.size(); i++) 
       { 
        if(filterList.get(i).getName().toUpperCase().contains(constraint)) 
        { 
         God god = new God(filterList.get(i).getName(), filterList.get(i).getTitle(), filterList.get(i).getNameString(), filterList.get(i).getImage(), 
           filterList.get(i).getPantheon(), filterList.get(i).getPantheonIcon(), filterList.get(i).getClassName(), filterList.get(i).getClassIcon(), filterList.get(i).getType(), 
           filterList.get(i).getHealth(), filterList.get(i).getMana(), filterList.get(i).getDamage(), filterList.get(i).getProtPhys(), filterList.get(i).getProtMag(), filterList.get(i).getSpeed(), 
           filterList.get(i).getHp5(), filterList.get(i).getMp5(), filterList.get(i).getAttackSpeed()); 
         filters.add(god); 
        } 
       } 
       results.count = filters.size(); 
       results.values = filters; 
      } else { 
       results.count = filterList.size(); 
       results.values = filterList; 
      } 
      return results; 
     } 

     protected void publishResults(CharSequence constraint, FilterResults results) { 
      gods = (ArrayList<God>) results.values; 
      notifyDataSetChanged(); 
     } 
    } 

} 

Répondre

1

Je résolu cette question en gardant une ArrayList de tous les filtres dans le Adapter.

Cette ArrayList serait alors votre contrainte et vous pouvez filtrer les éléments en vérifiant s'ils contiennent la classe God.


Exemple:

public class GodAdapter extends BaseAdapter implements Filterable { 

    ... 
    ArrayList<God> gods; 
    ArrayList<God> filteredGods; 
    private ArrayList<String> classFilters; 

    ... 

    public boolean toggleFilter(String class) { 
     if (classFilters.contains(class)) { 
      classFilters.remove(class); 
      return false; 
     } else { 
      classFilters.add(class); 
      return true; 
     } 
    } 

    ... 

    class CustomFilter extends Filter { 

     protected FilterResults performFiltering(CharSequence constraint) { 

      FilterResults results = new FilterResults(); 

      if(classFilters.size() > 0){ 
       ArrayList<God> filteredList = new ArrayList<God>(); 

       for(int i = 0; i < filterList.size(); i++){ 
        God god = filterList.get(i); 
        if(classFilters.contains(god.getName().toUpperCase())){ 
         filteredList.add(god); 
        } 
       } 
       results.count = filteredList.size(); 
       results.values = filteredList; 
      } else { 
       results.count = filterList.size(); 
       results.values = filterList; 
      } 
      return results; 
     } 

     protected void publishResults(CharSequence constraint, FilterResults results) { 
      // IMPORTANT publish the results to a separate list 
      filteredGods = (ArrayList<God>) results.values; 
      notifyDataSetChanged(); 
     } 
} 

Je recommande l'utilisation d'un enum pour décrire God des classes au lieu de String s. Expliquant pourquoi serait trop loin de cette question, but you can read about it here.

+0

Cela a résolu mon problème! Merci pour votre aide –

1

Ce que je l'ai fait pour un situatios similaire est la suivante:

J'ai un tableau avec toutes les données. Mais pour remplir la liste, je génère un second tableau en filtrant le premier.

Pour le filtre, j'utilise une combinaison de touches non exclusives.

public static fianl STATUS_WARRIOR = 1; //binary: 00000001 
public static fianl STATUS_HUNTER = 2; //binary: 00000010 
public static fianl STATUS_ASSASIN = 4; //binary: 00000100 

Pour chaque instance d'Arme, son statut sera l'un de ces trois.

Votre filtre creteria basé sur les bascules peut être n'importe quelle combinaison de ces trois valeurs.

Exemples: Guerrier et Assasin: 00000101 Hunter et Assasin: 00000110

(Vous avez juste besoin '|' (ou) les valeurs actives)

donc votre bascule définiront un nombre compris entre 0 et 7

Pour le filtrage, il suffit de vérifier l'état de l'arme '&' (et) la creteria du filtre. Si ce n'est pas 0, vous incluez le tableau que vous passerez dans la liste. Clause de non-responsabilité: Je n'ai pas exécuté le code de sorte qu'il puisse contenir quelques fautes de frappe. Si oui, faites le moi savoir et je vais vérifier. Mais vous pouvez toujours avoir l'idée.

+0

J'adore les opérations binaires. Je voudrais étendre votre réponse en utilisant un vrai 'Filter' dans un' BaseAdapter', comme dans la question. En guise de contrainte, on pourrait passer l'entier du filtre sous la forme d'une chaîne en utilisant 'Integer.toBinaryString (int i);' et ensuite l'analyser dans le filtre en utilisant 'Integer.parseInt (contrainte, 2);'. – Barthy

+0

@Barthy, je suis d'accord avec vous sur l'utilisation du filtre, mais vous pouvez éviter les conversions en binaire et gérer tout en radix 10. – Juan

+0

mais vous auriez toujours besoin d'analyser et de 'String' /' CharSequence', non? – Barthy