Tagebuch: Geburtstage app (Android) (von Cyborg)


#1

Tag,
ich kann mir Geburtstage nicht merken, und brauch dafür eine App.
Die App soll erstmal Folgendes können:

  • ExpandableListView mit Gruppen:
    – Unmittelbar
    – > 1 Woche
    – 2 bis 4 Wochen
    – 5 bis 12 Wochen
    – > 3 Monate
  • Notification at specific time:
    – wahrscheinlich AlarmManager
  • Name und Tag eingeben:
    – zum Beispiel dd.MM.yyyy oder dd.MM.
    – oder DatePickerDialog

Jetzt bin ich gleich zu einem Modellierungsproblem gestoßen: Bei manchen weiß ich das Geburtsjahr und bei manchen nicht. Wie modelliert man das “geschickt”? Danke für Hilfe.

Edit: Die App ist ziemlich leer, und sieht erstmal so aus:

Unbenannt


#2

Also nochmal just(e) das Problem: Es können ja nicht alle im Jahr 1970 geboren sein, wenn ich das Geburtsjahr nicht weiß…
Ich möchte aber lediglich timeInMillis habn bzw. speichern. :frowning:


#3

Ist doch kein Problem. Wenn du das Jahr nicht weißt, dann werte es nicht aus. Grob ausgedrückt würde deine Model-Klasse dann so aussehen:

public class Person {
     private String name;
     private Date birthdate;
     private boolean yearKnown;

     // .... ganz viel weiterer Code
}

Bei denen wo yearKnown = false ist, zeigst du halt im GUI nur Tag+Monat an.


#4

Apropo kannst du die Geburtstage auch einfach in deine Kontaktliste eintragen. Die informiert dich dann auch.
Letzteres - als Notifications - ist nicht so einfach umzusetzen auf Android.


#5

@Tomate_Salat Daran hab ich gedacht. Dazu bin ich auch gekommen, aber dennoch wird’s dann schwer mit “ganz viel weiterer Code…”.

Vielleicht 'ne eigene Metrik sinnvoll.

@TMII … bekomme aber keine Notifications - es sei denn, ich trage das zu einer Uhrzeit im Kalender ein. :frowning:

Wird schon :smiley:


#6

Nein, doch nicht:

class Person {
    String name, tag, jahr = null;
    static Person fromEntry(Map.Entry entry) {
        Person p = new Person();
        p.name = (String) entry.getKey();
        String[] s = ((String) entry.getValue()).split(" ");
        p.tag = s[0];
        if (s.length > 1) {
            p.jahr = s[1];
        }
        return p;
    }
    Map.Entry toEntry() {
        if (jahr == null) {
            return new AbstractMap.SimpleEntry(name, tag);
        }
        return new AbstractMap.SimpleEntry(name, tag + " " + jahr);
    }
    @Override
    public String toString() {
        return name + " " + tag + " " + jahr;
    }
}

Das Problem sind Schaltjahre, an die ich nicht gedacht hab…

Habt ihr bitte noch einen Tipp?


#7

Du solltest besser erklären, was deine Eingabe und deine aktuelle Ausgabe ist und welche du stattdessen erwartet hättest.

Ganz ehrlich. Keine Ahnung woher du deinen Quellcode hast, aber habe noch nie gesehen, dass man ein gutes Objekt wo man alle Informationen hat in ein Map.Entry haut, was die gesamte Kohärenz des Objekts komplett zerstört.

Wenn du zweimal einen “Martin” hast, dann funktioniert doch dein Ansatz wieder nicht.


#8

Danke für Deine Antwort. Ich hab dann Martin A. und Martin B. …

Ich möchte ein Map.Entry habn, weil ich das in einer ganz einfachen Properties-Textdatei speichern möchte, denn dann ist sie auch manuell editierbar, und keine Gedanken machen, über Import und Export.

Von mir, und vor einer Stunde ca. geschrieben. :smiley:


#9

Über Properties?! Jetzt bin ich baff.

Nimm doch XML in der simpelsten Form
<person name="martin" birthday="04.02.1942" />
Ist relativ einfach zu implementieren.


#10

Ich meinte aber sowas: https://de.wikipedia.org/wiki/Java-Properties-Datei#Struktur . Das ist relativ einfach im Vergleich zu XML finde ich.


#11

Ich weiß was Properties sind, aber wie ließt du Personen aus der Properties aus?
Hast du eine separate Liste mit den Namen der Personen die man aus der Properties Liste auslesen kann?
Und wenn ja warum schreibst du in diese Liste dann nicht gleich die Eigenschaften mit dazu?

XML ist so unheimlich einfach über SAX. Insbesondere wenn du den Aufbau wie ich oben gestaltest.


#12

Danke für Deine Antwort, TMII.
Prinzipiell kann ich mir über das Format (xml dom sax stax… oder doch etwas anderes) noch Gedanken machen.

Edit: Da der Code mehr oder weniger (quick-and-)dirty, doch kein Code.


#15

Vielen dank :wink:


#16

Neue Frage:

//... CoordinatorLayout ...
//...
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_input_add" />
//...

bw_plus_1

Wie bekommt man das Pluszeichen LightGray, DarkGray und Schwarz?
Das Grün passt überhaupt nicht.


#17

Erhalte von Gradle gerade eine komische Fehlermeldung:

    // Remote binary dependency
    // https://mvnrepository.com/artifact/com.jcraft/jsch
    compile group: 'com.jcraft', name: 'jsch', version: '0.1.54'

bw_plus_2

Was mach’ ich falsch?


#18

Da dürfte wohl alles zusammengefasst stehen. api und implementation sind feingranularer als compile. Damit kann man einstellen ob die Abhängigkeit auch nach aussen hin sichtbar ist oder nicht.


#19

D h, wenn ich die verbergen möchte und es eincompiliert werden soll dann kann ich das einstellen?


#20

So steht es dort.


#22

Und hier kann etwas nicht richtig sein; es ist doch nicht ‘best ui practices’, immer findViewById… usw. aufzurufen - oder doch:
( Abgesehen von dem ‘direkten’ Parse… usw. )

    private void add....() {
        String cat = ((EditText) findViewById(R.id.et_cat)).getText().toString();
        String a = ((EditText) findViewById(R.id.et_a)).getText().toString().toLowerCase();
        String b = ((EditText) findViewById(R.id.et_b)).getText().toString().toLowerCase();
        int richtig = Integer.parseInt(((EditText) findViewById(R.id.et_richtig)).getText().toString());
        int falsch = Integer.parseInt(((EditText) findViewById(R.id.et_falsch)).getText().toString());
        long last = Long.parseLong(((EditText) findViewById(R.id.et_last)).getText().toString());
        Vokabel vokabel = new Vokabel(a, b, richtig, falsch, last);
        if (Cats.gruppen.containsKey(cat)) {
            Cats.gruppen.get(cat).add(vokabel);
        } else {
            Cats.gruppen.put(cat, new ArrayList<Vokabel>());
            Cats.gruppen.get(cat).add(vokabel);
        }
        Toast.makeText(getApplicationContext(), "Hinzugefügt: " + a + " " + b, Toast.LENGTH_SHORT).show();
    }

Wie geht das ‘professionell’?


#23

Entweder du nimmst kotlin und lässt das einfach machen oder du hälst die Referenzen in Feldern der Klasse.

Btw wäre hier auch wieder (konstruktiv gemeinte) Kritik angebracht zu dem anderen Code:

  • Sinnvolle Variablennamen und Ids (z.B.: et_a ist absolut nichtssagend)
  • Kein Denglisch
  • Zu offene Sichtbarkeiten. Cats.gruppen.containsKey. Das schaut richtig gruselig aus. Zum einen scheint Cats einfach irgendeine statische Klasse zu sein die gleich mehrere Designfehler aufweist:
    – Der Name. Es klingt nach Katzen! Und ich wette, die haben damit absolut nix zu tun.
    – gruppen sieht mir nach einer statischen Liste aus. Innerhalb einer Model-klasse. Warum?
    – Es finden keine wirklich kontrollierten Zugriffe auf “gruppen” statt. Da kann jeder mit der Liste treiben was er möchte. Cats.gruppen = null; wäre z.B. ohne weiteres möglich.
    – Und auch hier wieder Denglisch
    – Außerdem sollte es schon sehr gute Gründe geben, warum andere Klassen direkt auf Felder von fremden Klassen zugreifen können.