2012-03-30 6 views
3

Situation:Android onlistclick Listenansicht Zeilenhöhe wechseln muliple was zu Veränderungen

Meine Anwendung enthält eine ToDo-Liste Stil Listview (jede Zeile hat eine Checkbox). Die Listenansichtszeilen sind strukturiert mit Textansichten ausgelegt vertikal. Der oberste Text ist der Titel und unten ist die Beschreibung, aber die Beschreibung ist versteckt (View.GONE). Mit der Methode ListActivities onListItemClick kann die Höhe und Sichtbarkeit der gedrückten Zeile festgelegt werden.

@Override 
protected void onListItemClick(ListView list, View view, int position, long id) { 
    super.onListItemClick(list, view, position, id); 

    view.getLayoutParams().height = 200; 
    view.requestLayout(); 

    TextView desc = (TextView) view.findViewById(R.id.description); 
    desc.setVisibility(View.VISIBLE); 
} 

Hinweis: Code zur elementarsten gestrippt

Der obige Code funktioniert gut, außer dass es sich ausdehnt, sowohl die gedrückt Reihe sowie der 10. Zeile oben oder unten (nächste entladen Ansicht?). Die Reihenerweiterung ändert sich auch, wenn die Liste verschoben wird.

Hintergrund:

Die Listenansicht Daten aus einer SQLite-Datenbank durch einen verwalteten Cursor und stellen durch einen benutzerdefinierten Adapter Cursor abgerufen werden. Der verwaltete Cursor ist nach dem Kontrollkästchen-Wert sortiert.

private void updateList() { 
    todoCursor = managedQuery(TaskContentProvider.CONTENT_URI, projection, null, null, TaskSQLDatabase.COL_DONE + " ASC"); 

    startManagingCursor(todoCursor); 

    adapter = new TaskListAdapter(this, todoCursor); 
    taskList.setAdapter(adapter); 
} 

Der CursorAdapter besteht aus den grundlegenden newView() und bindView().

Frage:

ich einige Systeme erfordern, das erweitert werden verfolgen, welche Reihen hält. Ich habe versucht, die Cursor-IDs in Arrays zu speichern und dann den Adapter einzuchecken, wenn die Zeile erweitert werden sollte, aber ich kann nicht funktionieren. Irgendwelche Vorschläge würden sehr geschätzt!

Antwort

2

Die ListView wird Ansichten recyceln, wie Sie es nach oben und unten scrollen, wenn es ein neues Kind View zu zeigen benötigt, wird es zuerst sehen, ob es nicht bereits eins (wenn es eines findet es wird es verwenden). Wenn Sie die Kinder eines ListView wie Sie (in der onListItemClick() Methode) ändern und dann die Liste scrollen, wird die ListView schließlich am Ende dieses Kind View wiederverwenden, das Sie geändert haben, und Sie werden mit bestimmten Ansichten in Position enden, die Sie don 't wollen (wenn Sie weiter scrollen ListView Sie sehen zufällige Position ändern wegen der View Recycling).

Eine Möglichkeit, dies zu verhindern, besteht darin, sich an die Positionen zu erinnern, auf die der Benutzer geklickt hat, und im Adapter das Layout dieser bestimmten Zeile zu ändern, jedoch nur für die gewünschte Position. Sie können (in der Klasse ein Feld) jene ids in einem HashMap speichern:

private HashMap<Long, Boolean> status = new HashMap<Long, Boolean>(); 

ich ein HashMap verwendet, aber Sie können andere Behälter verwenden (im Code unten sehen, warum ich ein HashMap wählen).Als nächstes werden in der onListItemClick() Methode werden Sie die geklickt Zeile ändern, sondern speichern auch diese Zeile id so der Adapter kennt (und Maßnahmen zu ergreifen, so dass Sie nicht mit dem falschen recycelten Ansichten am Ende):

@Override 
     protected void onListItemClick(ListView l, View v, int position, long id) { 
      // check and see if this row id isn't already in the status container, 
      // if it is then the row is already set, if it isn't we setup the row and put the id 
      // in the status container so the adapter will know what to do 
      // with this particular row 
      if (status.get(id) == null) { 
       status.put(id, true); 
       v.getLayoutParams().height = 200; 
       v.requestLayout(); 
       TextView d = (TextView) v.findViewById(R.id.description); 
       d.setVisibility(View.VISIBLE); 
      } 
    } 

dann in verwenden der Adapter den Status Behälter mit dem alle ids Setup die Zeilen richtig und verhindern, dass das Recycling zu Chaos mit unseren Reihen:

private class CustomAdapter extends CursorAdapter { 

    private LayoutInflater mInflater; 

    public CustomAdapter(Context context, Cursor c) { 
     super(context, c); 
     mInflater = LayoutInflater.from(context); 
    } 

    @Override 
    public void bindView(View view, Context context, Cursor cursor) { 
     TextView title = (TextView) view.findViewById(R.id.title); 
     title.setText(cursor.getString(cursor.getColumnIndex("name"))); 
     TextView description = (TextView) view 
       .findViewById(R.id.description); 
     description.setText(cursor.getString(cursor 
       .getColumnIndex("description"))); 
     // get the id for this row from the cursor 
     long rowId = cursor.getLong(cursor.getColumnIndex("_id")); 
     // if we don't have this rowId in the status container then we explicitly 
     // hide the TextView and setup the row to the default 
     if (status.get(rowId) == null) { 
      description.setVisibility(View.GONE); 
      // this is required because you could have a recycled row that has its 
      // height set to 200 and the description TextView visible 
      view.setLayoutParams(new AbsListView.LayoutParams(
        AbsListView.LayoutParams.MATCH_PARENT, 
        AbsListView.LayoutParams.MATCH_PARENT)); 
     } else { 
      // if we have the id in the status container then the row was clicked and 
      // we setup the row with the TextView visible and the height we want 
      description.setVisibility(View.VISIBLE); 
      view.getLayoutParams().height = 200; 
      view.requestLayout(); 
      // this part is required because you did show the row in the onListItemClick 
      // but it will be lost if you scroll the list and then come back 
     } 
    } 

    @Override 
    public View newView(Context context, Cursor cursor, ViewGroup parent) { 
     View v = mInflater.inflate(R.layout.adapters_showingviews, null); 
     return v; 
    } 

} 

Wenn Sie die Zeile geklickt wechseln wollen (und etwas sagt mir, dass Sie dies wünschen) , zeigen Sie die TextView auf einen Klick/verstecken Sie die TextView auf einer anderen Zeile geklickt dann einfach eine else clause, um die onListItemClick() Methode hinzufügen und die angeklickt Reihe id aus dem Status Behälter entfernen und die Zeile wieder her:

//... 
else { 
      status.remove(id); 
      v.setLayoutParams(new AbsListView.LayoutParams(
        AbsListView.LayoutParams.MATCH_PARENT, 
        AbsListView.LayoutParams.MATCH_PARENT)); 
      TextView d = (TextView) v.findViewById(R.id.description); 
      d.setVisibility(View.GONE); 
     } 
+0

Ihre Antwort auf meine Frage war genau richtig! Ich hatte zuvor einen ähnlichen Ansatz mit ArrayLists versucht, war aber gescheitert. Ich war mit HasMaps nicht vertraut, aber sie sind großartig. Es war ein wenig unklar, wo man die HashMap stecken sollte, also habe ich es wegen der vielen Lesevorgänge in den Adapter als öffentlichen statischen Speicher gesetzt. Mein Projekttermin ist der 4./5. Du bist ein Lebensretter. Meine tiefste Dankbarkeit. – Swiftwork

+0

@Swiftwork Setzen Sie die 'HashMap' dort hin, wo Sie möchten, stellen Sie sicher, dass sie von Ihrer' Aktivität' und auch vom 'Adapter' aus erreichbar ist. Du könntest es mit einer 'ArrayList' machen, aber es ist viel schwieriger zu manipulieren, anstatt einer' get() 'Methode auf einer' HashMap' musst du die gesamte Liste durchlaufen um zu sehen, ob die ID bereits existiert die Position in der Liste etc ... – Luksprog