Hallo allerseits! Ich heiĂe Anatoly Varivonchik. Ich arbeite seit ĂŒber einem Jahr bei Badoo und habe insgesamt mehr als fĂŒnf Jahre Erfahrung in der Android-Entwicklung.
In meiner Praxis mĂŒssen meine Kollegen und ich hĂ€ufig Ideen so schnell und einfach wie möglich testen. Wir möchten nicht viel Aufwand fĂŒr die Implementierung aufwenden, da wir wissen, dass wir den Code wegwerfen werden, wenn das Experiment nicht erfolgreich ist.
In diesem Artikel werde ich anhand realer Beispiele zeigen, wie wir in solchen Situationen handeln und welche Prinzipien uns helfen, eine Entscheidung zugunsten einer bestimmten Lösung des Problems zu treffen. Die Analyse von Beispielen soll helfen, unser Denkmuster zu verstehen: Wie kann man manchmal Abstriche machen und die Entwicklung beschleunigen?

Der Plan ist folgender:
- Prinzipien des Entwicklungsansatzes in Badoo.
- Fallstudien.
- Design-System.
- Wann sind die beschriebenen GrundsÀtze anzuwenden?
Dieser Artikel ist eine Textversion meines Berichts auf AppsConf. Das Video kann hier angesehen
werden .
Prinzipien des Entwicklungsansatzes
Hunderte Millionen Menschen verwenden Badoo, sodass wir keine neuen Funktionen einfĂŒhren können, wenn wir nicht sicher sind, ob es den Benutzern gefĂ€llt und sich als nĂŒtzlich erweisen.
Unser Entwicklungsansatz wird von mehreren Faktoren beeinflusst.
Verwenden von A / B-Tests
Wir haben heute Dutzende von A / B-Tests auf mobilen Plattformen aktiv, wÀhrend mehrere hundert abgeschlossen sind. Wenn Sie die Badoo-Anwendung auf zwei verschiedenen GerÀten verwenden, gibt es dementsprechend mit hoher Wahrscheinlichkeit einige Unterschiede zwischen ihnen, die möglicherweise auf den ersten Blick nicht wahrnehmbar sind.
Warum brauchen wir A / B-Tests? Es ist wichtig zu verstehen, dass das, was Produktmanager fĂŒr notwendig halten und was uns offensichtlich erscheint, in der RealitĂ€t nicht immer nĂŒtzlich ist. Manchmal mĂŒssen wir Code löschen, den wir vor ein oder zwei Monaten geschrieben haben. Manchmal ist es sinnvoll, die Idee einer neuen Funktion zu testen, um zu verstehen, ob sie geeignet ist oder nicht. Und wenn den Benutzern die FunktionalitĂ€t gefallen hat, können wir bereits Zeit in ihre Entwicklung investieren.
Entwicklungskosten reduzieren
NatĂŒrlich möchten wir, dass alles schnell funktioniert und schön ist. Dies ist jedoch nicht immer in kurzer Zeit möglich. Manchmal dauert es viele Tage. Um diese Probleme zu vermeiden, versuchen wir, den Produktmanagern zu helfen, indem wir die Kosten der Aufgaben vorab bewerten und angeben, was fĂŒr uns schwierig und was einfach ist.
Regel fĂŒr die meisten Benutzer
Stellen Sie sich vor, Sie haben eine Funktion, die unter allen Szenarien auf allen GerÀten perfekt funktioniert, aber gleichzeitig gibt es eine Gruppe von Benutzern mit chinesischen GerÀten, bei denen sie nicht genau wie erwartet funktioniert. In diesem Fall lohnt es sich möglicherweise nicht, das Problem so schnell wie möglich zu beheben, da Sie höchstwahrscheinlich wichtigere Aufgaben haben.
Wie beschleunigen wir die Entwicklung?
Schauen wir uns einige Beispiele an, die veranschaulichen, wie diese Prinzipien funktionieren. Hier werden reale FĂ€lle vorgestellt, auf die wir in unserer Arbeit gestoĂen sind, sowie ĂŒberlegte Lösungsoptionen.
ZunĂ€chst schlage ich vor, dass Sie sich ĂŒberlegen, wie Sie diesen Fall lösen können. Und dann werde ich jede der Optionen mit einer ErklĂ€rung betrachten, warum sie in unseren Fall kam / nicht passte.
Beispiel 1. Die SchaltflÀche zur Fortschrittsakkumulation
Wir mĂŒssen dem Benutzer den Prozess des Sammelns von Krediten mit dem Fortschritt der Batterie mit abgerundeten Ecken von 0 bis 1 zeigen.

Was sind die Lösungsmöglichkeiten?

Option A. Wir benötigen dieses Symbol nicht. Wir mĂŒssen die Designer bitten, die FunktionalitĂ€t zu wiederholen. Lassen Sie dort nur einen Text anzeigen.
Option B. Verwenden Sie Bitmap-Masken. Mit der richtigen Mischung bekommen wir genau das, was wir brauchen.

Option C: Nehmen Sie einfach ein paar Symbole, codieren Sie sie auf dem Client fest und zeigen Sie eines davon an.

In unserem Fall sind wir zu den Lösungen B und C gekommen. Wir werden dies genauer diskutieren.

Warum nicht
Option A ? Wir können dieses spezielle Problem lösen, es ist nicht kompliziert. Wir verwenden das gleiche Design in iOS und im mobilen Web. Dementsprechend gibt es keinen Grund, dies abzulehnen und zu sagen, dass wir dies nicht tun, und wir mĂŒssen uns ein anderes Design einfallen lassen.

Bitmap-Masken (
Option B ) sind eine ideale Lösung fĂŒr dieses Problem. Wir können leicht ein abgerundetes Rechteck zeichnen. Wir können leicht ein regulĂ€res Rechteck ĂŒber den Prozentsatz der FĂŒllung zeichnen, den wir benötigen. Es bleibt, sie zu mischen und die richtigen Einstellungen vorzunehmen. Danach verschwinden beide Ecken links.

Im Code sieht es ungefĂ€hr so ââaus:
data class GoalInProgress(val progress: Float) private val unchargedPaint = Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.MULTIPLY) } private fun mixChargedAndUncharged(canvas: Canvas) { drawFullyCharged(canvas) drawUnchargedPart(canvas) }
Ich habe den gröĂten Teil des Codes gelöscht. Weitere Informationen zu Bitmap-Masken finden Sie im Artikel:
https://habr.com/en/company/badoo/blog/310618/ . Sie lernen auch, wie Sie Masken mischen, welche Effekte erzielt werden und wie sie in Bezug auf die Leistung funktionieren.
Diese Lösung erfĂŒllt zu 100% unsere Anforderungen, dh es ist möglich, Fortschritte von 0 bis 1 anzuzeigen.
Das einzig Negative: Wenn Sie dies noch nie zuvor getan haben, mĂŒssen Sie Zeit damit verbringen, Bitmap-Masken herauszufinden. AuĂerdem mĂŒssen Sie noch mit ihnen spielen, RandfĂ€lle anzeigen und testen. Ich denke, dass es insgesamt ungefĂ€hr vier Stunden dauern wird.
Option C. Wir nehmen nur einige feste Arten von Symbolen und zeigen je nach Fortschritt eines davon an. Wenn der Fortschritt des Benutzers beispielsweise weniger als 0,5 betrĂ€gt, wird ein leeres Symbol angezeigt. Offensichtlich erfĂŒllt diese Lösung nicht 100% der Anforderungen. FĂŒr die Implementierung mĂŒssen Sie jedoch nur fĂŒnf Codezeilen schreiben und drei Symbole vom Designer erhalten.
fun getBackground(goal: GoalInProgress) = when (goal.progress) { in 0.0..0.5 -> R.drawable.ic_not_filled in 0.5..0.99 -> R.drawable.ic_half_filled else -> R.drawable.ic_full_filled }
DarĂŒber hinaus ist diese Lösung unter Bedingungen mit starkem Zeitmangel optimal (wie bei der Freigabe der Livestream-FunktionalitĂ€t). Es dauert nicht lange - Sie rollen es einfach aus und ersetzen es in der nĂ€chsten Version durch die richtige schöne Lösung. Eigentlich wie zu unserer Zeit.
Beispiel 2. Eine Zeile zur Eingabe einer Telefonnummer
Das nÀchste Beispiel ist die Eingabe einer Telefonnummer. Besonderheiten:
- Das LÀnderprÀfix befindet sich links.
- Das PrÀfix kann nicht gelöscht werden.
- es gibt einen Einzug;
- Das PrÀfix kann nicht angeklickt werden.

Lassen Sie uns darĂŒber nachdenken, wie dies implementiert werden kann.

Option A: Schreiben Sie einen benutzerdefinierten TextWatcher, der die gewĂŒnschte Logik implementiert. Es wird dieses PrĂ€fix halten, ein Leerzeichen halten und die Cursorposition steuern.
Option B: Teilen Sie diese Komponente in zwei unabhÀngige Felder auf. Aus Sicht der BenutzeroberflÀche handelt es sich um dieselbe Komponente.
Option C: Fragen Sie nach einem anderen Design, um es uns einfacher zu machen.

Wir haben uns fĂŒr die Implementierung von Option B entschieden.

Nach einem anderen Design zu fragen (Option C) ist das erste, was wir versucht haben. Die Produkte bestanden jedoch auf der ursprĂŒnglichen Idee. Und wenn das Unternehmen auf einer Art von FunktionalitĂ€t besteht, besteht unsere Aufgabe darin, diese zu implementieren.
Benutzerdefinierter TextWatcher (Option A) scheint nur auf den ersten Blick eine einfache Lösung zu sein, aber tatsĂ€chlich mĂŒssen viele RandfĂ€lle behandelt werden. Zum Beispiel benötigen Sie:
- Klicken Sie irgendwie auf das PrÀfix, um die Cursorposition zu Àndern.
- zusĂ€tzlich einrĂŒcken;
- Löschen verbieten, damit der Benutzer weder das Leerzeichen noch das PrÀfix des Landes löschen kann.
All dies zu tun ist natĂŒrlich möglich, aber ziemlich schwierig. Es scheint, dass es eine einfachere Option gibt.
Und er wurde wirklich gefunden:
<merge xmlns:android="http://schemas.android.com/apk/res/android" tools:parentTag="android.widget.LinearLayout"> <TextView android:id="@+id/country_code" /> <EditText android:id="@+id/phone_number" /> </merge>
Wir haben diese Komponente einfach in zwei Teile unterteilt: TextView und EditText. Programmatisch setzen wir in TextView den Hintergrund so, dass genau das Design erhalten wird, das die Produkte erwarten.
Die einzige Ăberlegung wert ist, dass in Android standardmĂ€Ăig die Breite der unteren Zeile zunimmt, wenn EditText im Fokus steht. Wir können jedoch problemlos eine Ănderung des Fokus abonnieren und den Hintergrund am PrĂ€fix Ă€ndern. Nichts kompliziertes:
phoneNumber.setOnFocusChangeListener { _, hasFocus -> countryCode.setBackgroundResource(background(hasFocus)) } private fun background(hasFocus: Boolean) = when (hasFocus) { true -> R.drawable.phone_input_active false -> R.drawable.phone_input_inactive }
Diese Lösung hat mehrere Vorteile:
- Keine Notwendigkeit, Klicks auf das PrÀfix zu verarbeiten;
- Sie mĂŒssen nicht mit der Cursorposition arbeiten - sie befindet sich immer in einem separaten Feld.
- Bei einer solchen Implementierung treten viel weniger RandfÀlle und Probleme auf.
Beispiel 3. Problem mit der automatischen VervollstÀndigung
Wie Sie in der Animation links sehen können, funktioniert die automatische VervollstĂ€ndigung nicht wie gewĂŒnscht. Wir möchten, dass alles wie die Animation auf der rechten Seite aussieht.


Lassen Sie uns darĂŒber nachdenken, was wir dagegen tun können.

Option A: Es scheint, dass dies ein seltener Fall ist, den niemand behebt. Warum machen wir nicht dasselbe?
Option B: Benutzerdefinierter TextWatcher macht es viel besser und löst alle unsere Probleme.
Option C: Entfernen Sie die Begrenzung der Anzahl der Zeichen (wie in der Animation gezeigt, haben wir eine bestimmte Anzahl von Zeichen in dieser Komponente). Wir senden die gesamte Telefonnummer mit dem PrĂ€fix an den Server und lassen den Server dann entscheiden, ob die Nummer gĂŒltig ist oder nicht.
Option D: Nimm N Zeichen vom Ende.
Wir haben uns fĂŒr Option D entschieden.

Option A. Ich habe in mehreren groĂen Anwendungen gesucht. Niemand scheint es zu reparieren.
Dennoch werden in Zukunft immer mehr Felder mit einem Autophilen gefĂŒllt. Je frĂŒher Sie dieses Problem lösen, desto loyaler werden Ihre Benutzer und desto angenehmer wird es sein, die Anwendung selbst zu verwenden. Zum Beispiel freue ich mich sehr, wenn ich mit zwei Klicks durch den gesamten Bildschirm gehe.
Option B. Es ist wirklich einfacher, benutzerdefinierten TextWatcher zu implementieren, da nicht so viele Edge-Skripte vorhanden sind wie im vorherigen Beispiel. Sie können den eingefĂŒgten Text leicht abfangen. Es gibt nur ein kleines Problem: In einigen LĂ€ndern gibt es lokale Aliase. Zum Beispiel bedeuten +44 und 0 dasselbe.
Benutzerdefinierter TextWatcher kann hier nicht helfen. In diesem Fall mĂŒssen Sie zusĂ€tzliche Logik schreiben und den Server auffordern, alle möglichen lokalen Aliase fĂŒr dieses Land zurĂŒckzugeben. Um dieses Problem zu lösen, mĂŒssen Sie Ănderungen am Kommunikationsprotokoll mit dem Server vornehmen und diese FunktionalitĂ€t dann auf dem Server implementieren. Dies dauert lĂ€nger als etwas auf dem Client zu tun. Es scheint, dass es eine einfachere Lösung gibt (und wir werden darauf zurĂŒckkommen).
Option C. Wir entfernen die Begrenzung der Anzahl der Zeichen - und dann ĂŒberprĂŒft der Server. Dies ist eine groĂartige Option. Es ist in Ordnung, dass das PrĂ€fix zweimal angezeigt wird. Wenn der Benutzer mit dem nĂ€chsten Schritt fortfĂ€hrt und die Telefonnummer gĂŒltig bestimmt ist, gibt es im Prinzip keine Probleme.
Aber es gibt immer noch einen Haken. Stellen Sie sich vor, der Benutzer verwendet nicht die automatische VervollstĂ€ndigung, sondern gibt einfach seine Telefonnummer ein. In diesem Fall ist es fĂŒr ihn viel schwieriger, eine Ziffer versehentlich zu duplizieren, wenn die Anzahl der Zeichen begrenzt ist. Am Ende wird er feststellen, dass die letzte Ziffer nicht gedruckt wurde. Aus diesem Grund haben wir uns entschieden, diese Methode nicht zu verwenden.
Option D. Die Verwendung von N Zeichen vom Ende schien uns eine geeignete Lösung zu sein.
class DigitsTrimStartFilter(private val max: Int) : InputFilter { override fun filter(...): CharSequence? { val s = source.subSequence(start, end).filter { it.isDigit() } val keep = max - (dest.length - (dend - dstart)) return when { keep <= 0 -> "" keep >= s.length -> null
Wir haben die maximale LĂ€nge einer Telefonnummer, die eingegeben werden kann. Wir schreiben eine einfache Klasse, sie ist gekapselt und kann an anderen Orten wiederverwendet werden. Wenn ein anderer Entwickler den Code sieht, wird er auĂerdem schnell herausfinden, was was ist. Es gibt aber noch zwei andere Probleme.
Erstens gibt es LĂ€nder mit unterschiedlichen Telefonnummern. In solchen FĂ€llen zeigt unsere Lösung eine zusĂ€tzliche Ziffer aus dem PrĂ€fix an. Zweitens kann die gleiche Situation auftreten, wenn ein Benutzer ein PrĂ€fix fĂŒr ein anderes Land mit einer automatischen Datei einfĂŒgt. Der zweite Fall erscheint uns selten, da der Server die Telefonnummer zunĂ€chst abhĂ€ngig vom Land zurĂŒckgibt, in dem sich der Benutzer befindet. Wenn wir jedoch verstehen, dass dies ein Problem ist, mĂŒssen wir das Protokoll auf dem Server so Ă€ndern, dass es eine Liste aller Zahlen gleichzeitig zurĂŒckgibt, und zusĂ€tzliche Logik schreiben (jetzt halten wir dies nicht fĂŒr notwendig).
Beispiel 4. Datumseingabekomponente
Designer und Produkte möchten eine Maske zur Eingabe eines Datums wie folgt sehen:

Lassen Sie uns darĂŒber nachdenken, wie dies implementiert werden kann.

Option A: Mach es einfach. Die Aufgabe sieht einfach aus, ist leicht zu lösen, es sollten keine Probleme auftreten.
Option B: Verwenden Sie die Maskenbibliothek. Sie passt zu uns in dieser Situation.
Option C: Deaktivieren Sie die Steuerung der Cursorposition. Dadurch vereinfachen wir die Anforderungen ein wenig und es wird uns leichter fallen, diese FunktionalitÀt zu implementieren.
Option D: Verwenden Sie die Standard-Datumseingabekomponente fĂŒr Android, die wir alle gesehen haben.
Wir kamen zu Option C.

Option A. Die Aufgabe scheint einfach zu sein. Sicherlich sind wir nicht die Ersten, die diese FunktionalitÀt implementieren. Warum nicht nachsehen, ob es im Internet eine geeignete Lösung gibt?

Wir nehmen diese Lösung, fĂŒgen sie dem Code hinzu und fĂŒhren sie aus. Wir beginnen zu testen:
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { if (edited) { edited = false return } var working = getEditText() working = manageDateDivider(working, 2, start, before) working = manageDateDivider(working, 5, start, before) edited = true input.setText(working) input.setSelection(input.text.length) }
Auf den ersten Blick scheint es uns mehr oder weniger zu passen. Es stimmt, es gibt Probleme mit der Tatsache, dass die Cursorposition nach jeder Ănderung zum Ende springt. Dann fangen wir an, genauer zu testen und zu verstehen, dass nicht alles so gut ist und es unerklĂ€rliche Szenarien gibt.

Wir alle mĂŒssen das verfeinern. Ich möchte dies vermeiden, denn dann ist dies die Arbeit der Tester, und unsere Aufgabe besteht wiederum darin, Fehler usw. zu beheben.
Option B. Warum nicht eine fertige Bibliothek fĂŒr
Decoro- Masken oder
Input-Mask-Android verwenden ? Sie haben alle Szenarien getestet, man kann einfach alles wiederverwenden und das Leben genieĂen. Wenn Sie eine Bibliothek fĂŒr Masken in Ihrem Projekt haben oder bereit sind, diese hinzuzufĂŒgen, ist dies eine groĂartige Lösung.
Wir hatten keine Bibliothek. Und es fĂŒr eine kleine Komponente, die nirgendwo anders verwendet wird, in das Projekt zu ziehen, schien ĂŒberflĂŒssig.
Option D. Verwenden Sie die Standard-Datumseingabekomponente.

Dies scheint die klĂŒgste Lösung zu sein. Mit ihm ist alles in Ordnung, bis auf einen kleinen Fehler. Wenn Sie diese Komponente öffnen, haben Sie bereits einen vordefinierten Wert und ein gĂŒltiges Datum. Wenn Sie ein gĂŒltiges Datum festlegen, um mit dem nĂ€chsten Schritt fortzufahren, z. B. dem 1. Januar 1980, erhalten Sie Millionen von Benutzern, die an diesem Tag geboren wurden. Andernfalls werden viele identische Fehler angezeigt: Der Benutzer kann sich nicht registrieren, weil er zu alt oder zu jung ist.
Aus diesem Grund haben wir einmal den Standarddialog zur Eingabe von Daten auf dem Registrierungsformular in Badoo aufgegeben. Die Anzahl der Fehler bezĂŒglich eines ungĂŒltigen Datums wurde dreimal reduziert.

Und noch ein kleines Minus. Es scheint, dass nur fortgeschrittene Benutzer vom ersten in den zweiten Status wechseln können:

Wenn nicht nur fortgeschrittene Benutzer Ihre Anwendung verwenden, sortieren sie Monat fĂŒr Monat nach dem gewĂŒnschten Datum.
Daher haben wir entschieden, dass Option A nicht so schlecht ist. Sie mĂŒssen es nur verfeinern und ein wenig vereinfachen:
class DateEditText : AppCompatEditText(context, attrs, defStyleAttr) { private var canChange: Boolean = false private var actualText: StringBuilder = StringBuilder() override fun onSelectionChanged(selStart: Int, selEnd: Int) { super.onSelectionChanged(selStart, selEnd) if (!canChange) return canChange = false setSelection(actualText.length) canChange = true } }
Die Nachteile von Option A traten auf, als der Benutzer die Cursorposition Ànderte. Und wir dachten: "Warum die Möglichkeit geben, diesen Cursor zu bewegen?" und einfach verboten, dies zu tun.

Also haben wir alle Probleme gelöst. Produkte haben eine Implementierung, die zu ihnen passt. Und wenn sie in Zukunft entscheiden, dass Sie noch die Möglichkeit geben mĂŒssen, Zeichen aus der Mitte zu entfernen, werden wir es tun.
Beispiel 5. Tools auf dem Video-Streaming-Bildschirm
Beim Starten des Video-Streamings wollten die Produkte QuickInfos anzeigen, mit denen Benutzer die Verwendung der Funktionen lernen können.
Zum Zeitpunkt der Feature-Implementierung hatten wir sechs Arten von Tooltips. Gleichzeitig sollte nicht mehr als eine auf dem Bildschirm angezeigt werden. Tooltips kamen dynamisch zu zufÀlligen Zeiten vom Server an. Einige mussten wiederholt werden. Wenn der Tooltip angezeigt wurde, der Benutzer jedoch nicht darauf geklickt hat, sollte er nach N Minuten erneut angezeigt werden.

All dies schien ziemlich schwierig umzusetzen. Wir haben das Produkt um ein paar Dinge gebeten.
FĂŒgen Sie zunĂ€chst einen Klassifizierer hinzu, der QuickInfos priorisiert. In jedem Fall treten Situationen auf, in denen sowohl der eine als auch der andere Tooltip gleichzeitig angezeigt werden sollen und einer von ihnen ausgewĂ€hlt werden muss. Dementsprechend brauchen wir PrioritĂ€ten. Zweitens haben wir um eine kleine Vereinfachung gebeten: einen Timer nur fĂŒr den Tooltip mit der höchsten PrioritĂ€t zu verwalten.
Bisher waren Tooltip-Wiederholungszeitgeber unabhÀngig:

Wir haben darum gebeten, den Timer nur fĂŒr den Tooltip mit der höchsten PrioritĂ€t zu unterstĂŒtzen:

Dementsprechend funktionierte der Timer fĂŒr uns nur fĂŒr Tooltip 1. Sobald Tooltip 1 angezeigt wurde, wurde er entfernt und der nĂ€chste Prozess gestartet.

So haben wir die Anforderungen vereinfacht: Es wurde fĂŒr uns viel einfacher, die Funktion zu implementieren, und fĂŒr Tester war es einfacher, sie zu testen. Am Ende haben wir festgestellt, dass diese Entscheidung fĂŒr jeden geeignet ist.
Beispiel 6. Fotos neu anordnen
Dieser Entwurf kam zu uns:

Wir kamen zu dem Schluss, dass die Implementierung ziemlich schwierig ist, wir mĂŒssen drei Tage fĂŒr die Entwicklung aufwenden und dachten: âWarum sollten wir das tun, wenn wir nicht wissen, ob der Benutzer es benötigt?â Wir haben vorgeschlagen, mit einer vereinfachten Version zu beginnen und zu bewerten, wie stark diese Funktion gefragt ist.

Es stellte sich heraus, dass Benutzer an dieser FunktionalitĂ€t interessiert sind. Danach haben wir das Rendering auf den Zustand des ursprĂŒnglichen Designs verbessert.
Gesamt:
- Wir haben uns und das Unternehmen vor dem Risiko geschĂŒtzt, zu viel Arbeitszeit fĂŒr eine Funktion aufzuwenden, die möglicherweise unbrauchbar ist.
- Dadurch wurden die Produktanforderungen vollstÀndig umgesetzt.
Beispiel 7. PIN-Eingabekomponente
Wir entwickeln nicht nur die Badoo-Anwendung, sondern auch andere Anwendungen mit einem völlig anderen Design. In allen drei Anwendungen verwenden wir dieselbe Komponente zur Eingabe des PIN-Codes:

Aus UX-Sicht sollte sich eine Komponente gleich verhalten. Trotzdem in unterschiedlichen Anwendungen unterschiedliche Schriftarten, EinrĂŒckungen, sogar unterschiedliche HintergrĂŒnde. Ich möchte dies nicht in jede Anwendung kopieren, sondern wiederverwenden. Das Design-System kann uns dabei helfen.
Ein Entwurfssystem besteht aus einer Reihe von UX-Regeln fĂŒr das Verhalten bestimmter Komponenten. Zum Beispiel haben wir klar angegeben, dass jede SchaltflĂ€che bestimmte ZustĂ€nde haben muss und dass sie sich auf eine bestimmte Weise verhalten muss.



Weitere
Informationen zum Designsystem finden Sie im Bericht von
Rudy Artyom .
In der Zwischenzeit zurĂŒck zur PIN-Eingabekomponente. Was möchten wir?
- Korrektes Tastaturverhalten;
- die Möglichkeit, die BenutzeroberflÀche vollstÀndig so anzupassen, dass sie in verschiedenen Anwendungen unterschiedlich aussieht;
- Empfangen Sie einen Standarddatenstrom von dieser Komponente, wie von einem regulÀren EditText.

Was waren unsere Lösungsmöglichkeiten?

Option A: Verwenden Sie vier separate EditText, wobei jedes PIN-Element ein separater EditText ist.
Option B: Verwenden Sie einen EditText, fĂŒgen Sie etwas KreativitĂ€t hinzu - und holen Sie sich das, was Sie brauchen.
Wir haben Option B gewÀhlt.

Option A. Es gibt Probleme mit vier separaten EditTexts. Android fĂŒgt allseitig zusĂ€tzliche Polster hinzu, die wir richtig handhaben mĂŒssen. AuĂerdem mĂŒssen Sie ein langes ZurĂŒcktippen implementieren, damit der Benutzer den gesamten PIN-Code löschen kann. Wir mĂŒssen manuell mit Fokus arbeiten und das Löschen von Zeichen behandeln. Es scheint ziemlich kompliziert.
Aus diesem Grund haben wir uns entschlossen, ein wenig zu schummeln und einen unsichtbaren EditText der GröĂe 0 x 0 erstellt, der die Datenquelle sein wird:
private fun createActualInput(lengthCount: Int) = EditText(context) .apply { inputType = InputType.TYPE_CLASS_NUMBER isClickable = false maxHeight = 0 maxWidth = 0 alpha = 0F addOrUpdateFilter(InputFilter.LengthFilter(lengthCount)) } private fun createPinItems(count: Int) { actualText = createActualInput(count) actualText.textChanges() .subscribe { updatePins(it.toString()) pinChangesRelay.accept(it) } overlay.clicks().subscribe { focus() } }
Jede Ziffer des PIN-Codes wird programmgesteuert hinzugefĂŒgt. Aus diesem Grund können wir jede Art von BenutzeroberflĂ€che zeichnen, EinrĂŒckungen einfĂŒgen usw. Nachdem der Benutzer auf die Komponente geklickt hat, setzen wir den Fokus in unseren EditText. Somit erhalten wir eine korrekt funktionierende Tastatur.
AuĂerdem abonnieren wir, um den Text des unsichtbaren EditText zu Ă€ndern und auf der BenutzeroberflĂ€che anzuzeigen. Danach ist es fĂŒr uns einfach, den Datenstrom von dieser Komponente abzurufen. TatsĂ€chlich haben wir den Standard-Android-EditText wiederverwendet und die erforderliche Logik nur geringfĂŒgig hinzugefĂŒgt.
Zusammenfassung
Diese GrundsÀtze sind nicht immer anwendbar. Ich gebe Ihnen die Bedingungen, unter denen sie gut funktionieren.
- Der Entwickler hat die Möglichkeit, die FunktionalitÀt zu beeinflussen . Andernfalls muss er nur die Aufgabe erledigen.
- Der Entwickler arbeitet fĂŒr ein Produktunternehmen , in dem Funktionen aktiv geteilt und schnell freigegeben werden und Hypothesen zu diesen Funktionen schnell ĂŒberprĂŒft werden . Unter solchen Bedingungen manifestieren sich diese Prinzipien in vollem Umfang, da wir von Anfang an nicht zu 100% sicher sein können, welche Updates den Benutzern gefallen und welche nicht.
- Der Entwickler hat die Möglichkeit, Aufgaben zu zerlegen . Diese Prinzipien sind eine logische Lösung in einer Situation, in der Produktmanager und Entwickler ĂŒber eine wechselseitige Kommunikation verfĂŒgen, die es beiden Parteien ermöglicht, herauszufinden, was ĂŒberarbeitet werden kann und sollte.
- Outsourcing . In seltenen FĂ€llen kann der Kunde an einem Angebot interessiert sein, um beispielsweise die Zeit fĂŒr die AusfĂŒhrung einer Aufgabe zu verkĂŒrzen, indem ein Teil der FunktionalitĂ€t vereinfacht wird.
Wie verwende ich diese Prinzipien? Leider ist es aus dem Zusammenhang heraus schwierig, Empfehlungen abzugeben. Ich kann Ihnen jedoch raten, die folgenden Dinge zu beachten.
Möglicherweise haben Sie Probleme mit UI / UX, wie in den meisten Beispielen, oder Sie haben Probleme mit der GeschĂ€ftslogik, wie im Tooltip-Beispiel. Sie mĂŒssen versuchen, Ihre Aufgabe in mehrere kleine Unteraufgaben zu zerlegen und diese dann auszuwerten.
Danach können Sie genau herausfinden, wo die Probleme liegen werden. Als nÀchstes besprechen Sie mit Kollegen, wie Sie sie lösen können. Vielleicht kann etwas vereinfacht werden. Oder Sie kennen einfach keine einfache Lösung, die Ihre Kollegen bereits kennen. In der nÀchsten Phase koordinieren Sie mit den Produkten eine alternative Lösung. Wenn sie zufrieden sind, setzen Sie Ihr Angebot um.
Ich möchte hinzufĂŒgen, dass alle Menschen manchmal Fehler machen.
Vielleicht haben die Produkte eine Aufgabe gestellt, die ihr eigentliches Problem nicht löst. Vielleicht haben Ihnen die Designer ein Design fĂŒr iOS geschickt. Möglicherweise ist das Kommunikationsprotokoll zwischen dem Server und dem Client fĂŒr den Client absolut unpraktisch. Wir mĂŒssen ĂŒber all diese Dinge sprechen, wir mĂŒssen sie diskutieren und Feedback geben. So steigern Sie Ihren Wert als Entwickler und Ihren Nutzen fĂŒr das Unternehmen. Das heiĂt, es ist Win-Win fĂŒr beide Parteien.Links um mich zu kontaktieren:PS , . , , . â , . ? .