Speichern von zusammengesetzten Ansichts-Widgets

8

Frage

Wie speichern Sie den Status des View-Widget-Exemplars, wenn Komponenten der einzelnen Widget-Instanzen mithilfe von XML-definierten Widget-Layouts alle die gleiche ID haben?

Beispiel

Nehmen wir zum Beispiel das Widget NumberPicker , das im Widget TimePicker verwendet wird (beachten Sie, dass NumberPicker nicht dem SDK ausgesetzt ist). Dies ist ein einfaches Widget mit drei Komponenten, die von number_picker.xml aufgeblasen werden: eine Inkrement-Schaltfläche, eine Dekrement-Schaltfläche und eine EditText , wo Sie direkt eine Zahl eingeben können. Damit der Code mit diesen Widgets interagieren kann, haben sie alle IDs ( R.id.increment , R.id.decrement bzw. R.id.timepicker_input ).

Nehmen wir an, Sie haben drei NumberPicker s in einem XML-Layout und geben ihnen eindeutige IDs (zB R.id.hour , R.id.minute ). ¹ Dieses Layout wird dann zur Inhaltsansicht einer Aktivität aufgebläht. Wir beschließen, die Ausrichtung der Aktivität zu ändern, so dass Activity.onSaveInstanceState(Bundle) unseren Ansichtszustand für jede Ansicht mit einer ID (das ist das Standardverhalten) hilfreich speichert.

Leider haben die drei NumberPicker s EditText s, die alle dieselbe ID haben - R.id.timepicker_input . Wenn also die Aktivität wiederhergestellt wird, ist diejenige, die am weitesten unten in der Ansichtshierarchie ist, diejenige, deren Status für alle drei zu erhalten scheint. Außerdem wird der Fokus bei der Wiederherstellung auf den ersten NumberPicker gesetzt, unabhängig davon, welcher Fokus beim Speichern gespeichert wurde.

TimePicker löst dieses Problem, indem es den Status selbst separat speichert. Leider wird dadurch die Cursorposition oder die fokussierte Ansicht nicht ohne viel mehr Arbeit erhalten. Ich bin mir nicht sicher, wie es diesen Zustand beibehält, wenn es überhaupt funktioniert (und das schnelle Spielen mit einem Zeiteingabedialog scheint darauf hinzuweisen, dass es irgendwie möglich ist).

Bitte beachten Sie den Beispielcode, um dieses Problem zu veranschaulichen: Ссылка

¹ In der Ansichtshierarchie legt dies die ID von LinearLayout fest, die NumberPicker auf Ihre IDs erweitert.

    
Steve Pomeroy 08.02.2011, 10:57
quelle

5 Antworten

14

Ich stolperte über das gleiche Problem, als ich versuchte, meine eigene zusammengesetzte Ansicht zu erstellen. Aus Sicht des Android-Quellcodes glaube ich, dass die Compound-Ansicht selbst die Verantwortung für das Speichern und Wiederherstellen des Instanzstatus der untergeordneten Elemente übernimmt und verhindert, dass Aufrufe den Instanzstatus speichern und wiederherstellen an die Kindansichten weitergegeben. Dies funktioniert um das Problem, dass die IDs von untergeordneten Ansichten nicht eindeutig sind, wenn Sie mehr als eine Instanz derselben zusammengesetzten Ansicht in einer Aktivität haben.

Das mag kompliziert klingen, aber es ist wirklich ziemlich einfach und die APIs sehen tatsächlich genau dieses Szenario vor. Ich habe hier einen Blogeintrag hier geschrieben, wie das gemacht wird Aber im Wesentlichen müssen Sie in Ihrer zusammengesetzten Ansicht die folgenden vier Methoden implementieren, die onSaveInstanceState () und onRestoreInstanceState () anpassen, um Ihre spezifischen Anforderungen zu erfüllen.

%Vor%

Bezüglich des Problems mit NumberPicker / TimePicker, wie in einem anderen Kommentar erwähnt, scheint es einen Bug mit NumberPicker und TimePicker zu geben. Um es zu beheben, könnten Sie beide überschreiben und die beschriebene Lösung implementieren.

    
Charles Harley 03.03.2012, 18:13
quelle
0

Ich habe genau das gleiche Problem mit einer zusammengesetzten Ansicht mit drei NumberPickern. Ich habe eine Lösung auf einer anderen Website gefunden Dazu gehörte die Neuzuweisung der ID des timepicker_input an eine eindeutige Zufalls-ID Das funktioniert aber ist einmal kompliziert Es wird eine neue ID ausgewählt, die für die Konfiguration beibehalten werden muss Änderungen, so dass extra Code benötigt wird, um das zu tun.

Für meine Bewerbung, und wahrscheinlich in 99% der anderen, viel einfacherer Ansatz (Hack) funktioniert. Ich erkannte, dass der Namensraum für Ids bietet Platz für 65536 eindeutige Bezeichner (0x7f090000 to 0x7f09ffff), aber meine Anwendung verwendet nur 20 oder so, und sie werden monoton steigend von der Anfang. Außerdem das NumberPicker Widget selbst hat einen eindeutigen Namen innerhalb des Baumes (zum Beispiel wie in der ursprüngliche Beitrag, R.id.hour, R.id.minute und R.id.second). Meine Lösung besteht darin, die ID des EditText-Widgets neu zuzuweisen zur Id des NumberPicker Widgets plus einem Offset.

Dies ist eine einzeilige Änderung des NumberPicker-Codes. Einfach füge hinzu:

%Vor%

Nach der folgenden Zeile in NumberPicker.java:

%Vor%

Natürlich kann der Offset abhängig von den Anwendungsanforderungen eingestellt werden.

Ich denke, derselbe Ansatz wird auch für andere zusammengesetzte Widgets funktionieren.

Für das obige Beispiel erlaubt dieser Ansatz den Zustand der einzelne EditText Widgets zum Speichern und Wiederherstellen, und die fokussierte Ansicht.

    
Eric Negaard 08.06.2011 21:50
quelle
0

Ich bin ein wenig zu spät zum Spiel, aber ich wollte meine Eingabe geben.

Sie können android:tag verwenden, um jedes View zu gruppieren. So können Sie jeden Status neu laden.

Von der Website Android Developer :

  

android: tag

     

Geben Sie ein Tag für diese Sicht ein, das eine Zeichenfolge enthält, die später abgerufen werden soll   mit View.getTag () oder gesucht mit View.findViewWithTag ().

    
Cody 16.11.2011 21:17
quelle
0

Ich habe ein zusammengesetztes Widget mit einer Zustandsbeschreibung, die aus einer einzigen Ganzzahl besteht mValue
Überschreiben Sie nun die folgenden zwei Methoden wie folgt:

%Vor%

In dispatchSaveInstanceState() serialisieren Sie Ihren Status. Komplexere Zustände erfordern komplexere Speicherstrukturen.
Stellen Sie sicher, dass setDataPosition() korrekt ist.
Ich speichere nur den Status, wenn dem Widget eine ID zugewiesen ist, die ich als Tag des Pakets verwende

In dispatchRestoreInstanceState() entpacken Sie Ihr Paket.
Ich führe das Entpacken nur aus, wenn dem Widget eine ID zugewiesen ist und der Container ein Paket mit einem Tag enthält, das dieser ID entspricht.
Die Parcelable muss auch die richtige Klasse sein. Das Entpacken wird für komplexere Widgets komplizierter.

Die letzte benötigte Komponente ist eine Parcelable Unterklasse wie die folgende:

%Vor%

Die Verwendung dieses Frameworks ist viel einfacher als das Bearbeiten von Fragmenten und das Verwalten der Konfiguration

    
kwi wi 12.01.2012 23:22
quelle
-3

Ganz einfach: Sie nicht. Geh einfach und deaktiviere den Orientierungsänderungs-Mist in deiner Manifest-Datei. Der View-State-Save-Mechanismus ist von Natur aus fehlerhaft, sie haben das einfach nicht durchdacht.

Wenn Sie Ihren Status beibehalten möchten, können Sie eine ID nicht innerhalb einer einzelnen Aktivität wiederverwenden. Das bedeutet im Wesentlichen, dass Sie ein einzelnes Layout nicht mehr als einmal verwenden können, was komplexere Widgets wie TimePicker grundsätzlich unmöglich macht.

Du kannst Kinder dazu bringen, ihren Status beizubehalten, indem du dispatchSaveInstanceState übergibst und einspionierst, aber ich habe auch keinen Weg gefunden, den Fokus zu behalten, außer das auch selbst zu verwalten.

Ich denke, sie könnten das beheben, indem Sie den Statusbereich festlegen, ohne die API zu unterbrechen, aber halten Sie nicht den Atem an.

    
Timo Ohr 11.03.2011 12:33
quelle