2011-11-29 11 views
16

Sto utilizzando un ExpandableListView in un navigatore sinistro per uno schermo del tablet.ExpandableListView - mantiene il child selezionato in uno stato "premuto"

Quando un utente preme un figlio di un gruppo nel mio elenco espandibile, vorrei mantenere il bambino nello stato premuto in modo che l'utente sappia per quale bambino viene mostrato il contenuto della mano destra.

Per un ListView, ho potuto ottenere questo effetto con questa linea nel codice:

getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); 

e poi applicare questo selettore:

<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
<item android:state_activated="true" android:drawable="@drawable/menu_item_pressed" /> 
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/menu_item_normal" /> 
<item android:state_pressed="true" android:drawable="@drawable/menu_item_pressed" /> 
<item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/menu_item_pressed" /> 

Il "state_activiated" è true sulla voce listView dopo aver premuto la voce listView.

Speravo che questa stessa tecnica funzionasse per il mio expandableListView ma non è così. Ho usato:

getExpandableListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); 

e quindi utilizzato lo stesso selettore sopra, ma non funziona. Ho anche provato altri stati come state_selected e state_checked e anche quelli non funzionano.

Il selettore in alto applica correttamente lo stato premuto e non premuto. Sembra comunque con ExpandableListView, lo stato non è "attivato" dopo aver premuto un bambino.

Qualsiasi aiuto è apprezzato. Grazie!

+0

La mia risposta è stata utile? C'è una ragione per cui non l'hai contrassegnata come la risposta accettata? – hotshot309

+0

Sì, è stato molto utile grazie (mi dispiace ho dimenticato di votare). Continuerò a contrassegnarlo come la risposta accettata nel caso in cui qualcuno condivida un modo di crescere a casa per ottenere l'effetto che sto cercando. Grazie ancora! – Scott

risposta

8

Ho risolto questo problema senza utilizzare un selettore nella visualizzazione figlio. L'adattatore per espandibileListView prende i dati per ogni gruppo e bambino. Ho aggiunto un valore booleano "selezionato" per i dati figlio. Il frammento si occupa di impostare correttamente il valore booleano selezionato di ciascun bambino su true o false. Quindi, nel metodo "getChildView" dell'adattatore, se il booleano "selezionato" del bambino è vero, ho impostato la risorsa di sfondo della vista sul mio sfondo "premuto". Altrimenti l'ho impostato sullo sfondo "non premuto". Facendo questo oltre ad usare un selettore per la visualizzazione testo sulla vista figlio per cambiare il colore del testo quando premuto, si ottiene l'effetto desiderato.

Qui di seguito sono le cose che mio Frammento fa a mantenere booleano "selezionato" del bambino:

  • conservati i dati di gruppo/bambino come una variabile di classe nel vostro frammento
  • onChildClick - un ciclo tra i dati del bambino, impostazione di tutti "selezionato" = falso. Quindi imposta il figlio del gruppo/figlio corrente su true. Aggiorna la finestra espandibileListView chiamando setEmptyView, reimposta i listener e imposta la scheda di elenco.
  • onGroupClick - loop dei dati figlio, impostazione di tutti "selezionato" = falso. Quindi imposta il figlio del gruppo/figlio corrente su true.
  • ovunque nel frammento in cui viene visualizzato l'elenco: compilare i dati dell'adattatore, impostare l'adattatore di elenco, chiamare notifyDataSetChanged sull'adattatore. Aggiorna la finestra espandibileListView chiamando setEmptyView, reimposta i listener e imposta la scheda di elenco.

In aggiunta a quanto sopra, mantengo correnteGroupPosition e currentChildPosition come variabili di classe. L'utilizzo di setRetainInstance = true consente a questo di funzionare correttamente su cose come lo schermo ruota.

+0

Credo che chiamare 'notifyDataSetChanged' sia abbastanza ... Perché è necessario aggiornare la vista chiamando' setEmptyView'? –

5

Non credo ci sia alcun modo integrato per fare ciò che si sta tentando con la funzionalità integrata di Android. Per eseguire alcuni test, ho iniziato con il codice degli esempi dell'API Android (C:\<android-sdk-directory>\samples\android-15\ApiDemos\src\com\example\android\apis\view\ExpandableList3.java).

Ho utilizzato ExpandableListView.CHOICE_MODE_SINGLE. La modalità scelta si applica a qualsiasi cosa sia selezionata o selezionata View. (Il codice sorgente ExpandableListView dice che determina il numero di elementi possono essere selezionato in una sola volta, ma in ListView, sembra applicarsi a ciò che è controllato.) Si noti inoltre che il dottore dice che non si può ottenere un risultato valido da getCheckedItemPosition per a ListView in modalità scelta singola. Questo sembra essere vero.

I miei test hanno mostrato che ExpandableListActivity non rende automaticamente selezionato o selezionato l'elemento selezionato/toccato, né attribuisce automaticamente il focus al figlio cliccato. Solo lo ExpandableListActivity stesso e il gruppo che detiene l'elemento figlio sono focalizzati auotmaticamente dopo aver fatto clic su uno dei figli.

Ecco un po 'del mio codice (in gran parte preso dagli esempi Android):

@Override 
public void onCreate(Bundle savedInstanceState) { 

    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    final String NAME = "NAME"; 
    final String IS_EVEN = "IS_EVEN"; 

    List<Map<String, String>> groupData = new ArrayList<Map<String, String>>(); 
    List<List<Map<String, String>>> childData = new ArrayList<List<Map<String, String>>>(); 

    for (int i = 0; i < 12; i++) { 

     Map<String, String> curGroupMap = new HashMap<String, String>(); 
     groupData.add(curGroupMap); 

     curGroupMap.put(NAME, "Group " + i); 
     curGroupMap.put(IS_EVEN, (i % 2 == 0) ? "This group is even" : "This group is odd"); 

     List<Map<String, String>> children = new ArrayList<Map<String, String>>(); 

     for (int j = 0; j < 12; j++) { 

      Map<String, String> curChildMap = new HashMap<String, String>(); 
      children.add(curChildMap); 

      curChildMap.put(NAME, "Child " + j); 
      curChildMap.put(IS_EVEN, (j % 2 == 0) ? "This child is even" : "This child is odd"); 
     } 

     childData.add(children); 
    } 

    SimpleExpandableListAdapter adapter = new SimpleExpandableListAdapter(

     this, 
     groupData, 
     android.R.layout.simple_expandable_list_item_1, 
     new String[] {NAME, IS_EVEN}, 
     new int[] {android.R.id.text1, android.R.id.text2}, 
     childData, 
     R.layout.child, 
     new String[] {NAME, IS_EVEN}, 
     new int[] {android.R.id.text1, android.R.id.text2} 
     ); 

    ExpandableListView expListView = (ExpandableListView)findViewById(android.R.id.list); 

    setListAdapter(adapter); 
    expListView.setChoiceMode(ExpandableListView.CHOICE_MODE_SINGLE); 
    expListView.setOnChildClickListener(new OnChildClickListener() { 

     @Override 
     public boolean onChildClick(ExpandableListView expListView, View viewClicked, int groupPosition, int childPosition, long childRowId) { 

      viewClicked.setSelected(true); // the only way to force a selection of the clicked item  

Nota l'ultima riga, che costringe il bambino cliccato per avere uno stato selezionato. È quindi possibile utilizzare un XML selector con un elenco di stati Drawable per evidenziare l'elemento selezionato quando è selezionato. (Questo non funziona completamente - più su quello sotto.)

All'interno onChildClick nel mio ExpandableListActivity, ho fatto alcuni test e trovato il seguente:

groupPosition; // index of group within the ExpandableListViewActivity   
childRowId; // for my particular ExpandableListView, this was the index of the child in the group 
childPosition; // for my particularExpandableListView, this was also the index of the child in the group 

expListView.getCheckedItemPosition(); // null - from ListView class 
expListView.getSelectedView(); // null - from AbsListView class 
expListView.getSelectedItemId(); // a long, based on the packed position - from AdapterView class 
expListView.getSelectedId(); // -1 - from ExpandableListView class, calls getSelectedPosition() 
expListView.getSelectedPosition(); // -1 - from ExpandableListView class, calls getSelectedItemPosition() 
expListView.getSelectedItemPosition(); // -1 - from AdapterView class 
expListView.getFocusedChild(); // null - from ViewGroup class 
expListView.getItemIdAtPosition(1); // a long (View ID) - from AdapterView class 
viewClicked.isFocused()); // false 
viewClicked.isPressed()); // false 
viewClicked.isSelected()); // true only if we explicitly set it to true 
expListView.isFocused()); // true 
expListView.isPressed()); // false 
expListView.isSelected()); // false 
expListView.getCheckedItemPosition())); // -1 - from ListView 
expListView.getSelectedId())); // -1 - from ExpandableListView 

/* expListView.getChildAt(childPosition) is not helpful, because it looks at the rendered 
    LinearLayout with a bunch of TextViews inside it to get the index and find the child, 
    and you won't generally know where to look for the child you want */ 

Ho provato anche la creazione di una nuova OnItemClickListener e attuare il suo metodo onItemClick. Anche se la documentazione suggerisce che onItemClickListener funzionerà in qualche modo su ExpandableListView s, non l'ho mai visto chiamare.

La prima cosa che ho provato a cambiare il colore di sfondo sull'elemento figlio scelto e tenerlo evidenziato non ha funzionato bene. Ho impostato uno Drawable per android:state_window_focused="true" all'interno del file XML selector. Ha funzionato in qualche modo, ma ha presentato diversi problemi.

La seconda cosa che ho provato è stata selezionare esplicitamente l'elemento premuto in onChildClick chiamando viewClicked.setSelected(true);. I problemi qui erano minori:

1) Se si scorreva fino a quando l'elemento evidenziato era fuori visualizzazione e si ritornava ad esso, l'evidenziazione era scomparso, 2) Lo stesso accadrebbe se avessi ruotato il telefono, e 3) Lo stato l'elemento evidenziato non veniva più selezionato non appena non era più visibile sul display.

L'Android ExpandableListView apparentemente non era pensato per funzionare come un menu di navigazione a sinistra in stile fisarmonica, anche se questo sembra un uso abbastanza ovvio. Se vuoi usarlo in questo modo, penso che dovrai fare una buona dose di personalizzazione e scavare nel modo in cui vengono disegnati gli View s.

Buona fortuna.

+2

Quanti ingegneri del team di sviluppo Android impiegano per cambiare una lampadina? Nessuno, perché hanno deciso di non supportare il cambiamento della lampadina, quindi devi modificarlo da solo. –

33

Effettuare le seguenti operazioni su ExpandableListView:

Fase 1. Impostare la modalità scelta per singola (può essere fatto in XML come android: choiceMode = "singleChoice")

Fase 2. utilizzare un selettore di xml come sfondo (Android: listSelector = "@ drawable/selector_list_item")

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android" 
    android:exitFadeDuration="@android:integer/config_mediumAnimTime"> 

    <item android:drawable="@android:color/holo_blue_bright" android:state_pressed="true"/> 
    <item android:drawable="@android:color/holo_blue_light" android:state_selected="true"/> 
    <item android:drawable="@android:color/holo_blue_dark" android:state_activated="true"/> 

</selector> 

Punto 3. Chiama espandibileListView.setItemChecked (index, true) nel callback onChildClick().

indice è un indice 0 sulla base della voce bambino calcolato come segue

Gruppo 1 [index = 0]

  • Bambino item 1
  • bambino 2
  • Articolo figlio 3 [indice = 3]

Gruppo 2 [index = 4]

  • bambini item 1
  • bambino 2
  • elemento bambino 3 [index = 7]

Gruppo 3 [index = 8]

  • articolo figlio 1
  • voce bambino 2
  • voce bambino 3 [index = 11]

Se abbiamo capilista così, si sarebbe anche tenere conto di valori di indice.

Ecco un esempio di lavoro:

@Override 
public boolean onChildClick(ExpandableListView parent, View v, 
     int groupPosition, int childPosition, long id) { 
    ... 

    int index = parent.getFlatListPosition(ExpandableListView.getPackedPositionForChild(groupPosition, childPosition)); 
    parent.setItemChecked(index, true); 


    return true; 
} 
+0

grazie per la spiegazione dettagliata. Ha funzionato come un fascino per me – Akshay

+1

Sembra avere alcuni risultati strani per me se comprimi il gruppo con l'elemento selezionato e ne apri un altro –

+0

grazie per la spiegazione. Funziona per me :) –

3

Declare a livello globale:

View view_Group; 

yourExpandableListView.setChoiceMode(ExpandableListView.CHOICE_MODE_SINGLE); 
yourExpandableListView.setOnChildClickListener(new OnChildClickListener() { 

    @Override 
    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { 
    v.setSelected(true); 
    if (view_Group != null) { 
     view_Group.setBackgroundColor(Default Row Color Here); 
    } 
    view_Group = v; 
    view_Group.setBackgroundColor(Color.parseColor("#676767"));  
} 
+0

questa soluzione non funziona ed esegue il comportamento scorretto per me ... –

0

Questa soluzione funziona per me (espandibile visualizzazione elenco utilizzato all'interno di vista di navigazione):

Creare modello di menu personalizzato , ad esempio

public class DrawerExpandedMenuModel { 

String iconName = ""; 
int id = -1; 
int iconImg = -1; 
boolean isSelected = false; 
. 
. 
. 

Override onChildClick di ExpandableListView

 expandableList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { 
     @Override 
     public boolean onChildClick(ExpandableListView expandableListView, View view, int groupPosition, int childPosition, long l) { 
      for (int k = 0; k < listDataHeader.size(); k++){ 
       listDataHeader.get(k).isSelected(false); 
       if (listDataChild.containsKey(listDataHeader.get(k))) { 
        List<DrawerExpandedMenuModel> childList = listDataChild.get(listDataHeader.get(k)); 
        if (childList != null) { 
         for (int j = 0; j < childList.size(); j++) { 
          if (k == groupPosition && j == childPosition) { 
           childList.get(j).isSelected(true); 
          } else { 
           childList.get(j).isSelected(false); 
          } 
         } 
        } 
       } 
      } 
      mMenuAdapter.notifyDataSetChanged(); 

Override onGroupClick di ExpandableListView

 expandableList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() { 
     @Override 
     public boolean onGroupClick(ExpandableListView expandableListView, View view, int groupPosition, long l) { 
      for (int k = 0; k < listDataHeader.size(); k++){ 
       if (k == groupPosition) { 
        listDataHeader.get(k).isSelected(true); 
       } else { 
        listDataHeader.get(k).isSelected(false); 
       } 
       if (listDataChild.containsKey(listDataHeader.get(k))) { 
        List<DrawerExpandedMenuModel> childList = listDataChild.get(listDataHeader.get(k)); 
        if (childList != null) { 
         for (int j = 0; j < childList.size(); j++) { 
          childList.get(j).isSelected(false); 
         } 
        } 
       } 
      } 
      mMenuAdapter.notifyDataSetChanged(); 

Override getGroupView e getChildView

public class DrawerExpandableListAdapter extends BaseExpandableListAdapter { 
. 
. 
. 
    @Override 
public View getGroupView(final int groupPosition, final boolean isExpanded, View convertView, final ViewGroup parent) { 
    if (headerTitle.isSelected() == true) convertView.setBackgroundColor(mContext.getResources().getColor(R.color.gray)); 
    else convertView.setBackgroundColor(mContext.getResources().getColor(R.color.transparent)); 
. 
. 
. 
@Override 
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { 
    if (childObj.isSelected() == true) convertView.setBackgroundColor(mContext.getResources().getColor(R.color.gray)); 
    else convertView.setBackgroundColor(mContext.getResources().getColor(R.color.transparent)); 

Questo è tutto!

Problemi correlati