{"id":2937,"date":"2023-10-31T20:32:15","date_gmt":"2023-10-31T19:32:15","guid":{"rendered":"https:\/\/www.art-events.de\/weblog\/?p=2937"},"modified":"2023-11-24T00:18:32","modified_gmt":"2023-11-23T23:18:32","slug":"ae-android-kochbuch-notizen-app-dateien-speichern","status":"publish","type":"post","link":"https:\/\/www.art-events.de\/weblog\/ae-android-kochbuch-notizen-app-dateien-speichern\/","title":{"rendered":"AE Android Kochbuch: Notizen App. Dateien speichern"},"content":{"rendered":"<p>Im Teil3 vom AE Android Kochbuch schreiben wir eine kleine Notizen App und speichern diese als Datei.<\/p>\n<p>Unser AE Android Kochbuch f\u00fcr Praktiker. Hier zeige ich euch wie ihr schnell und einfach Android programmieren lernt. Tipps aus der Praxis. Vorweg noch mal der Hinweis: Dieses ist zwar ein Grundkurs, aber ihr solltet schon etwas Grundlagenwissen haben. Zum Beispiel wissen, was eine Variable ist, und dass man Zahlen in Integer und Texte in String Variablen speichert. Wenn euch solche Basics noch fremd sind \u2013 ich empfehle die ersten Kapitel vom Buch Java ist auch nur eine Insel oder andere Literatur, die euch in solche grundlegenden Dinge einf\u00fchrt.<\/p>\n<hr \/>\n<p>Hinweis: F\u00fcr diesen Teil gibt es einen Nachfolger:<\/p>\n<p><a href=\"https:\/\/www.art-events.de\/weblog\/ae-android-kochbuch-notizen-app-reloaded\/\">AE Android Kochbuch Teil 3A: Notizen App Reloaded<\/a><\/p>\n<hr \/>\n<h1><a id=\"post-2937-__RefHeading___Toc231_3005313255\"><\/a>Die Idee der Notizbuch App<\/h1>\n<p>Wir wollen mit dem Android Studio eine kleine App schreiben mit der man Notizen schreiben und speichern kann. Was mich an der Android Notizen im System st\u00f6rt: ich muss mittels Google Konto eingelegt sein, um sie zu verwenden. Das soll bei unserer App nicht sein. Einfach sch\u00f6n und schlank: App starten \u2013 Text schreiben \u2013 speichern. Und vielleicht noch das eine oder andere Feature mehr.<\/p>\n<p>Basis unserer App sind Sachen, die wir bereits in Teil1 und Teil2 besprochen haben. Hier haben wir Grundlagen gelegt, um um mit unserer Android App Texte anzuzeigen, Eingaben zu t\u00e4tigen und Buttons zu klicken. Weiteres folgt dann im Lauf dieses Teiles, u.a. basteln wir nun eigene Methoden, um Daten zu speichern.<\/p>\n<p><a href=\"https:\/\/www.art-events.de\/weblog\/android-programmieren-fuer-starter-tipps-und-tricks\/\">Die anderen Teile im AE Android Kochbuch gibt es hier.<\/a><\/p>\n<h1><a id=\"post-2937-__RefHeading___Toc2226_395087288\"><\/a>Neue App anlegen: Writer<\/h1>\n<p>Wir starten das Studio, w\u00e4hlen neues Projekt und legen ein neues Projekt Writer oder AE Writer an. Den Titel k\u00f6nnen ihr euch selbst \u00fcberlegen. Wie das mit dem Projekt anlegen funktioniert, wurde in <a href=\"https:\/\/www.art-events.de\/weblog\/ae-android-kochbuch-getting-started-u-device-manager\/\">Teil1 Getting Started<\/a>\u00a0erkl\u00e4rt!<\/p>\n<h1><a id=\"post-2937-__RefHeading___Toc250_148005660\"><\/a>Unser Layout<\/h1>\n<p>Zuerst einmal brauchen wir in unserer Layout Datei drei Elemente:<\/p>\n<p>1) Eine TextView als \u00dcberschrift<br \/>\n2) Ein EditText Element, das sich \u00fcber mehrere Zeilen erstreckt und eine Eingabe erlaubt<br \/>\n3) Einen Button, der unsere Eingabe l\u00f6scht.<\/p>\n<pre><em>&lt;?<\/em>xml version=\"1.0\" encoding=\"utf-8\"<em>?&gt;\r\n<\/em> &lt;LinearLayout xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\" \r\nxmlns:app=\"http:\/\/schemas.android.com\/apk\/res-auto\" \r\nxmlns:tools=\"http:\/\/schemas.android.com\/tools\" \r\nandroid:layout_width=\"match_parent\" \r\nandroid:layout_height=\"match_parent\" \r\nandroid:orientation=\"vertical\" \r\ntools:context=\".MainActivity\"&gt; \r\n\r\n&lt;TextView \r\nandroid:layout_width=\"wrap_content\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:text=\"Texteingabe\" \/&gt; \r\n\r\n&lt;EditText \r\nandroid:id=\"@+id\/editTextText\" \r\nandroid:layout_width=\"match_parent\" \r\nandroid:layout_height=\"300dp\" \r\nandroid:background=\"@drawable\/drawback\" \r\nandroid:gravity=\"top\" \r\nandroid:scrollHorizontally=\"false\" \r\nandroid:scrollbars=\"vertical\" \r\nandroid:inputType=\"textMultiLine\" \r\nandroid:text=\"\" \/&gt; \r\n\r\n&lt;Button \r\nandroid:id=\"@+id\/buttonClear\" \r\nandroid:layout_width=\"120dp\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:text=\"Clear\" \/&gt; \r\n\r\n&lt;\/LinearLayout&gt;<\/pre>\n<p>Das Ganze sieht in der Displayvorschau dann so aus:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"420\" height=\"397\" class=\"wp-image-2938\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-1.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-1.png 420w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-1-300x284.png 300w\" sizes=\"auto, (max-width: 420px) 100vw, 420px\" \/><\/p>\n<h1><a id=\"post-2937-__RefHeading___Toc252_148005660\"><\/a>Der Java Programm Code<\/h1>\n<p>Mit den Erkenntnissen aus den ersten beiden Teilen wisst ihr schon, was im Java Code passieren muss. Wir brauchen eine globale private static Variable, wenn wir den Eingabetext aus EditText in verschiedenen Methoden der Klasse auswerten oder ver\u00e4ndern wollen. Also ganz oben am Anfangsbereich unserer Klasse eintragen:<\/p>\n<pre>private static EditText <em>etEingabe1<\/em>;<\/pre>\n<p>In der Methode onCreate m\u00fcssen wir die EditText Variable initialisieren, damit wir sp\u00e4ter die Eingaben lesen oder ver\u00e4ndern k\u00f6nnen.<\/p>\n<pre><em> etEingabe1 <\/em>= findViewById(R.id.<em>editTextText<\/em>);<\/pre>\n<p>Dann wollen wir noch einen onClick Listener f\u00fcr den Button Clear registrieren.<\/p>\n<pre>findViewById(R.id.<em>buttonClear<\/em>).setOnClickListener(this);<\/pre>\n<p>Wie immer, wenn wir erstmalig einen onClick Listener zuf\u00fcgen, werden vom Studio eine Reihe von Sachen erledigt, die mehr oder minder automatisch ablaufen. Wenn Ihr dazu mehr wissen wollt \u2013 siehe unseren Teil2 dieses Kochbuchs!<\/p>\n<p>Mit der Methode onClick haben wir dann den Ort erhalten, in dem wir festlegen, was passieren soll, wenn der Benutzer den Button Clear bet\u00e4tigt. In unserem Fall wollen wir EditText l\u00f6schen und anschlie\u00dfend den Cursor darauf setzen, damit eine neue Eingabe get\u00e4tigt werden kann.<\/p>\n<pre>@Override \r\npublic void onClick(View v) { \r\n<em>   \/\/Unser Eingabe Listener\r\n<\/em>   switch (v.getId()) { \r\n      case R.id.<em>buttonClear<\/em>: \r\n<em>      \/\/Button CLEAR\r\n      etEingabe1<\/em>.setText(\"\"); \r\n<em>      etEingabe1<\/em>.requestFocus(); \r\n      break; \r\n   } \r\n}<\/pre>\n<p>Umfassend sieht der Java Code dann wie folgt aus:<\/p>\n<pre>package de.terminalsystems.aewriter; \r\n\r\nimport ... \r\n\r\n<em>\/\/----------------------------------------------------------------------\r\n<\/em> public class MainActivity extends AppCompatActivity implements View.OnClickListener { \r\n\r\nprivate static EditText <em>etEingabe1<\/em>; \r\n<em>\r\n\/\/----------------------------------------------------------------------\r\n<\/em>@Override \r\nprotected void onCreate(Bundle savedInstanceState) { \r\n   super.onCreate(savedInstanceState); \r\n   setContentView(R.layout.<em>activity_main<\/em>); \r\n\r\n<em>   etEingabe1 <\/em>= findViewById(R.id.<em>editTextText<\/em>); \r\n\r\n   findViewById(R.id.<em>buttonClear<\/em>).setOnClickListener(this); \r\n\r\n<em>   \/\/am Start: Cursor in das Textfeld setzen\r\n   etEingabe1<\/em>.requestFocus(); \r\n}\r\n\r\n\r\n<em>\/\/----------------------------------------------------------------------\r\n<\/em> @Override \r\npublic void onClick(View v) { \r\n<em>   \/\/Unser Eingabe Listener\r\n\r\n<\/em>   switch (v.getId()) { \r\n      case R.id.<em>buttonClear<\/em>: \r\n<em>      \/\/Button CLEAR\r\n      etEingabe1<\/em>.setText(\"\"); \r\n<em>      etEingabe1<\/em>.requestFocus(); \r\n      break; \r\n   } \r\n} \/\/end class<\/pre>\n<h1><a id=\"post-2937-__RefHeading___Toc254_148005660\"><\/a>Erfolgserlebnis: Anwendung im Device Manager ausf\u00fchren<\/h1>\n<p>Wenn wir die Anwendung jetzt kurz im Device Manager auf einem simulierten Smartphone ausprobieren, macht es schon einen halbwegs fertigen Eindruck! Alle Elemente erscheinen, die Zusatztastatur f\u00fcr Texteingabe wird eingeblendet und wir k\u00f6nnen einen Text tippen und der Clear Button l\u00f6scht den auch.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"437\" height=\"780\" class=\"wp-image-2939\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-2.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-2.png 437w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-2-168x300.png 168w\" sizes=\"auto, (max-width: 437px) 100vw, 437px\" \/><\/p>\n<h1><a id=\"post-2937-__RefHeading___Toc256_148005660\"><\/a>Eingabe speichern<\/h1>\n<p>Wer schon Erfahrungen mit anderen Programmiersprachen hat, kennt die Abl\u00e4ufe beim Daten speichern. Datei mittels Writer \u00f6ffnen. Daten hinein schreiben. Datei \/ Writer schlie\u00dfen. Fertig! Schon ist eine Datei erstellt, die man an anderer Stelle auswerten kann, z.B. an einen Computer \u00fcbertragen, ausdrucken oder sonst wie bearbeiten. Nicht anders l\u00e4uft es auch bei Android.<\/p>\n<p>Doch zuerst bauen wir den Button Speichern mit der ID buttonSpeichern in die Layout Datei. Hier integrieren wir auch erstmals ein horizontales Layout in unser vertikales hinein, damit die Buttons nebeneinander stehen.<\/p>\n<pre>&lt;TextView \r\nandroid:layout_width=\"wrap_content\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:text=\"Texteingabe\" \/&gt; \r\n\r\n&lt;EditText \r\nandroid:id=\"@+id\/editTextText\" \r\nandroid:layout_width=\"match_parent\" \r\nandroid:layout_height=\"300dp\" \r\nandroid:background=\"@drawable\/drawback\" \r\nandroid:gravity=\"top\" \r\nandroid:scrollHorizontally=\"false\" \r\nandroid:scrollbars=\"vertical\" \r\nandroid:inputType=\"textMultiLine\" \r\nandroid:text=\"\" \/&gt;\r\n\r\nVer\u00e4ndert wird ab hier:\r\n\r\n\r\n&lt;LinearLayout \r\nandroid:layout_width=\"match_parent\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:orientation=\"horizontal\"&gt; \r\n\r\n&lt;Button \r\nandroid:id=\"@+id\/buttonClear\" \r\nandroid:layout_width=\"120dp\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:text=\"Clear\" \/&gt; \r\n\r\n&lt;Button \r\nandroid:id=\"@+id\/buttonSpeichern\" \r\nandroid:layout_width=\"120dp\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:layout_marginLeft=\"10dp\" \r\nandroid:text=\"Speichern\" \/&gt; \r\n\r\n&lt;\/LinearLayout&gt;<\/pre>\n<p>Damit zwischen den Buttons ein kleiner Abstand ist, haben wir beim buttonSpeichern erstmals einen Rand nach links eingef\u00fchrt. So wird der neue Button etwas vom bereits bestehenden Button abgesetzt. Die Buttons kleben nicht so aneinander.<\/p>\n<p><strong>In onCreate m\u00fcssen wir den button Speichern f\u00fcr den onClick Listener registrieren. <\/strong><\/p>\n<pre>findViewById(R.id.<em>buttonSpeichern<\/em>).setOnClickListener(this);<\/pre>\n<p><strong>In der onClick Methode f\u00fcgen wir schon mal die Behandlung f\u00fcr den neuen Button ein.<\/strong><\/p>\n<p>Den Code reichen wir sp\u00e4ter nach.<\/p>\n<pre>case R.id.<em>buttonSpeichern<\/em>: \r\n<em>   \/\/hier kommt das Speichern rein!\r\n<\/em>   break;<\/pre>\n<p>Kommen wir zum Programmcode f\u00fcr das Speichern! Hier basteln wir uns eine eigene, neue Methode zum Speichern der Daten. Wenn wir sp\u00e4ter mal anders speichern wollen, k\u00f6nnen wir diese Methode dann schnell austauschen.<\/p>\n<p><strong>Methode zum Speichern der Daten deklarieren<\/strong><\/p>\n<p>Irgendwo in eurer Klasse aber au\u00dferhalb der bestehenden Methoden k\u00f6nnt ihr jetzt eure erste eigene Methode einbauen:<\/p>\n<pre>private boolean doEingabeSpeichern (String myData) { \r\n<em>   \/\/Hier speichern wir Daten<\/em>\r\n<em>   \/\/Return: true wenn erfolgreich, false wenn Fehler\r\n<\/em>   return false; \r\n}<\/pre>\n<p>Unsere erste eigene Methode bekommt eine Angabe private zu Lebensdauer. Da wir sie nur in dieser Klasse verwenden ist sie private. Sie soll uns einen Boolean Wert zur\u00fcckliefern ob das Speichern geklappt hat (true) oder ob es Fehler gegeben hat (false). Die Daten zum Speichern \u00fcbergeben wir als String Wert beim Aufruf der Methode.<\/p>\n<p>Auch wenn die Methode noch nichts macht, damit die Syntaxpr\u00fcfung die neue Methode akzeptiert, tragen wir prophylaktisch schon mal return false ein. W\u00fcrden wir diese Zeile weglassen gibt es Mecker, weil eine als boolean definierte Methode IMMER einen Returnwert true oder false zur\u00fcckliefern muss.<\/p>\n<p>Eigentlich k\u00f6nnten wir sie jetzt mit Leben f\u00fcllen. Doch wie es so spielt \u2013 manche Methoden erfordert mehr Informationen \u2013 und so springe ich gleich zum vollst\u00e4ndigen Abbild der Speichern Methode. Erkl\u00e4rung folgt im Anschluss.<\/p>\n<h1><a id=\"post-2937-__RefHeading___Toc510_3947055182\"><\/a>In eine Datei speichern<\/h1>\n<p>Wir f\u00fcllen nun die Methode mit Leben, die das speichern von Daten \u00fcbernimmt. Die Daten werden beim Aufruf der Methode als Parameter \u00fcbergeben. Genauso wie der Dateiname. Dazu kommt noch ein Context mit dem Android intern unterschiedliche Projekte identifiziert.<\/p>\n<pre>private boolean doEingabeSpeichern (Context context, String myFileName, String myData) { \r\n<em>   \/\/Hier speichern wir Daten\r\n   \/\/Return: true wenn erfolgreich, false wenn Fehler\r\n\r\n<\/em>   File myFile = new File (context.getFilesDir(), myFileName); \r\n\r\n<em>   \/\/Entscheidg ob Datwei existiert. Fuer write Kopfdaten\r\n<\/em>   boolean flagnewfile = (myFile.exists()) ? false : true; \r\n\r\n   FileOutputStream outputStream = null; \r\n   OutputStreamWriter ofw;<\/pre>\n<p>Wir verwenden f\u00fcr unseren Speichervorgang den File-Konstruktor File, der uns eine Datei im von Android benutzten Dateisystem anlegt. Dann deklarieren wir uns einen Output Stream und einen Streamwriter mit dem das Speichern im File erledigt werden soll.<\/p>\n<p>Davor gibt es noch ein kleines Schmankerl: mit dem boolean Flag flagnewfile merken wir uns, ob die Datei schon existiert oder erstmalig erzeugt wird. Dieses Flag k\u00f6nnen wir sp\u00e4ter verwenden wenn wir zum Beispiel am Anfang der Datei Kopfdaten speichern m\u00f6chten. Wenn euch der Sinn und Unsinn von Flags noch nicht ganz klar ist \u2013 wir haben einen kleinen Exkurs: AE Android Kochbuch. Flags. Spa\u00df mit Flaggen.<\/p>\n<p><a href=\"https:\/\/www.art-events.de\/weblog\/ae-android-kochbuch-exkurs-flags-die-sachen-mit-den-flaggen\/\">https:\/\/www.art-events.de\/weblog\/ae-android-kochbuch-exkurs-flags-die-sachen-mit-den-flaggen\/<\/a><\/p>\n<h2><a id=\"post-2937-__RefHeading___Toc514_3947055182\"><\/a>Speichern mit try \/ catch<\/h2>\n<p>Unsere Speicherfunktion setzt sich fort:<\/p>\n<pre>try { \r\n   myFile.createNewFile(); <em>\/\/ if file already exists will do nothing\r\n<\/em>   Log.<em>d <\/em>(\"Hier gespeichert:\", myFile.toString()); \r\n   outputStream = new FileOutputStream(myFile, true); \r\n   ofw = new OutputStreamWriter(outputStream, StandardCharsets.<em>ISO_8859_1<\/em>); \r\n \r\n   if (flagnewfile) { \r\n<em>      \/\/Falls Datei bisher nicht existierte koennten wir hier Kopfzeile schreiben!\r\n<\/em>      Log.<em>d <\/em>(\"EingabeSpeichern\", \"Kopf anlegen\"); \r\n      flagnewfile = false; \r\n   } \r\n\r\n<em>   \/\/wir speichern daten und erweitern Zeile um CRLF\r\n<\/em>   ofw.write(myData + \"\\r\\n\"); \r\n<em>   \/\/dann speichern wir noch trenner zwischen den texten\r\n<\/em>   ofw.write(\"------------------------------\\r\\n\"); \r\n   Log.<em>d <\/em>(\"Daten schreiben\", myData); \r\n   ofw.close(); \r\n\r\n   } catch (Exception e) { \r\n   String x2 = e.getMessage(); \r\n   Log.<em>e<\/em>(\"Error0149\", x2); \r\n   e.printStackTrace(); \r\n   return false; \r\n}\r\n\r\nreturn true;<\/pre>\n<p>&nbsp;<\/p>\n<p>Im zweiten Teil der Methode folgt der eigentliche Speichervorgang. Diesen kapseln wir in einer try \u2013 catch Schleife, f\u00fcr den Fall, dass Android Fehler meldet. Bei einer solchen Fehlermeldung wird die Verarbeitung im try Zweig unterbrochen, wenn das Programm eigentlich abst\u00fcrzen w\u00fcrde &#8211; und das Programm setzt im catch Zweig fort. Im catch Exception Zweig lassen wir uns die Fehlermeldung von Android als Debug Meldung ausgeben und beenden die Speichermethode dann mit false, so dass wir im Hauptprogramm auch eine Meldung an den Benutzer geben k\u00f6nnen, dass sein Speicherversuch nicht geklappt hat.<\/p>\n<p>Im Hauptzweig von try erzeugen wir unsere Datei, falls sie noch nicht existiert und aktiveren unseren Streamwriter. Dabei setzen wir auch gleich ein Charakter Set, um z.B. Umlaute zu verarbeiten, die der Benutzer eingegeben hat. Die verschiedenen ISO Charakter Sets bieten euch unterschiedliche M\u00f6glichkeiten zum Speichern regionaler Zeichen.<\/p>\n<p>Dann werten wir auch das boolean Flag flagnewfile aus. Sollte die Datei bisher nicht existieren, k\u00f6nnen wir hier eine Kopfzeile speichern. Ob das hier Sinn oder Unsinn macht, sei dahingestellt. Aber manchmal kommt es halt vor, dass man am Anfang einer Datei Kopfzeilen speichern will, z.b. wenn ihr XLS CSV Dateien f\u00fcr Excel aufbauen m\u00f6chtet.<\/p>\n<p>Die nachfolgenden Zeilen mit ofw.write sind es schlussendlich, die unsere Daten als String in eine Datei speichern. Jeder String wird dabei noch um einen Text Zeilenvorschlag CRLF erweitert und nach dem Speichern des Datenstrings f\u00fcgen wir noch eine Trennzeile mit vielen Strichen an.<\/p>\n<p>Die gesamte Speicherroutine in voller Pracht stellt sich dann so da:<\/p>\n<pre><em>\/\/----------------------------------------------------------------------\r\n<\/em> private boolean doEingabeSpeichern (Context context, String myFileName, String myData) { \r\n<em>   \/\/Hier speichern wir Daten\r\n   \/\/Return: true wenn erfolgreich, false wenn Fehler\r\n\r\n<\/em>   File myFile = new File (context.getFilesDir(), myFileName); \r\n\r\n<em>   \/\/Entscheidg ob Datwei existiert. Fuer write Kopfdaten\r\n<\/em>   boolean flagnewfile = (myFile.exists()) ? false : true; \r\n\r\n   FileOutputStream outputStream = null; \r\n   OutputStreamWriter ofw; \r\n   try { \r\n      myFile.createNewFile(); <em>\/\/ if file already exists will do nothing\r\n<\/em>      Log.<em>d <\/em>(\"Hier gespeichert:\", myFile.toString()); \r\n      outputStream = new FileOutputStream(myFile, true); \r\n      ofw = new OutputStreamWriter(outputStream, StandardCharsets.<em>ISO_8859_1<\/em>); \r\n\r\n      if (flagnewfile) { \r\n<em>         \/\/Falls Datei bisher nicht existierte koennten wir hier Kopfzeile schreiben!\r\n<\/em>         Log.<em>d <\/em>(\"EingabeSpeichern\", \"Kopf anlegen\"); \r\n         flagnewfile = false; \r\n      } \r\n\r\n<em>      \/\/wir speichern daten und erweitern Zeile um CRLF\r\n<\/em>      ofw.write(myData + \"\\r\\n\"); \r\n<em>      \/\/dann speichern wir noch trenner zwischen den texten\r\n<\/em>      ofw.write(\"------------------------------\\r\\n\"); \r\n      Log.<em>d <\/em>(\"Daten schreiben\", myData); \r\n      ofw.close(); \r\n\r\n   } catch (Exception e) { \r\n      String x2 = e.getMessage(); \r\n      Log.<em>e<\/em>(\"Error0149\", x2); \r\n      e.printStackTrace(); \r\n      return false; \r\n   } \r\n   return true; \r\n}<\/pre>\n<p><strong>KeyListener onClick erweitern<\/strong><\/p>\n<p>Bleibt nur noch eine Sache: wir m\u00fcssen den Aufruf der Speichermethode im Key Listener unterbringen.<\/p>\n<pre>case R.id.<em>buttonSpeichern<\/em>: \r\n<em>   \/\/hier kommt das Speichern rein!\r\n<\/em>   if (!doEingabeSpeichern(this, \"WriterData.txt\", <em>etEingabe1<\/em>.getText().toString())) { \r\n<em>      \/\/Speichern hat nicht geklappt!! Wir koennten hier was tun\r\n<\/em>      ; \r\n   }\r\n   break;<\/pre>\n<p>Das erledigen wir mit obigen Zeilen. Das Ergebnis vom speichern werten wir aus \u2013 wir tun jedoch noch nichts. Hier m\u00fcsste im Ernstfall dann irgend etwas eingebaut werden, was passieren soll, wenn ein Speicherfehler eingebaut wird. Zum Beispiel eine Meldung an den Benutzer.<\/p>\n<p>Bei den Zeilen bitte darauf achten:<\/p>\n<pre>if (!doEingabeSpeichern \u2026..)<\/pre>\n<p>ist identisch mit der Formulierung<\/p>\n<pre>if (doEingabeSpeichern \u2026. == false)<\/pre>\n<p>Das Ausrufezeichen am Anfang der Klammer signalisiert, wenn der Ausdruck in der Klammer false ist, dann soll was geschehen. Die untere Schreibweise ist nur die Langschreibweise \u2013 aber manchmal \u00fcbersichtlicher. Ihr k\u00f6nnt verwenden, was euch Spa\u00df macht!<\/p>\n<p>Alternativ k\u00f6nnte es auch so aussehen &#8211; mit kleiner Erfolgsmeldung f\u00fcr den Benutzer:<\/p>\n<pre>case R.id.buttonSpeichern: \r\n   \/\/hier kommt das Speichern rein! \r\n   if (doEingabeSpeichern(this, \"WriterData.txt\", etEingabe1.getText().toString())) { \r\n      \/\/Speicher OK. Meldung anzeigen und weiter\r\n<em>      Toast.makeText(this, \"Datei gespeichert!\", Toast.LENGTH_LONG).show();<\/em>\r\n<em>      etEingabe1<\/em>.setText(\"\"); <em> \r\n      etEingabe1<\/em>.requestFocus(); \r\n   }\r\nbreak;<\/pre>\n<p>&nbsp;<\/p>\n<p><strong>Fertig!<\/strong><\/p>\n<p>Wenn ihr die App jetzt ausf\u00fchrt, k\u00f6nnt ihr den Button Speichern dr\u00fccken und die Eingaben werden gespeichert.<\/p>\n<h2><a id=\"post-2937-__RefHeading___Toc516_3947055182\"><\/a>Der Speicherort<\/h2>\n<p>Wir dr\u00fccken den Speichern Button, aber es passiert nichts. Speichert unser Programm wirklich? Im wahren Leben einer App k\u00f6nnen wir jetzt den Returnwert als Ergebnis auswerten und dem Benutzer eine OK oder Fehlermeldung anzeigen. Doch das entbindet uns als Programmierer nicht von der Pflicht zu pr\u00fcfen, ob das mit dem Speichern auch richtig aussieht!<\/p>\n<p>Um uns die Speicherdatei anzusehen, m\u00fcssen wir zuerst herausfinden, wo Android diese Datei hin speichert. Mit der Log Anzeige<\/p>\n<pre>Log.<em>d <\/em>(\"Hier gespeichert:\", myFile.toString());<\/pre>\n<p>wird uns der Inhalt der myFile Variable im Logcat ausgegeben. Unsere Log Ausgaben im Device Emulator finden wir unter RUN (Android Studio Chipmunk) oder im Logcat (Android Studio Giraffe).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"184\" class=\"wp-image-2940\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-3.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-3.png 650w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-3-300x85.png 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>Unter \u201eHier gespeichert\u201c finden wir den Eintrag:<\/p>\n<p>\/data\/user\/0\/de.terminalsystems.aewriter\/files\/WriterData.txt<\/p>\n<p>und kennen nun das Verzeichnis, das Android f\u00fcr unsere Datei gew\u00e4hlt hat. Hier erschlie\u00dft sich auch, was mit Context gemeint war, den wir an bestimmten Stellen im Code eingegeben mussten. Android verwaltet f\u00fcr jede App eigene Speicherbereiche und nimmt Context dazu, diese zu identifizieren.<\/p>\n<p><strong>Device Explorer im Device Emulator starten<\/strong><\/p>\n<p>Wollen wir uns die Datei ansehen: Im Device Emulator f\u00fcr das aktive Ger\u00e4t das Speichersystem \u00f6ffnen:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"630\" height=\"74\" class=\"wp-image-2941\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-4.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-4.png 630w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-4-300x35.png 300w\" sizes=\"auto, (max-width: 630px) 100vw, 630px\" \/><\/p>\n<p>Es \u00f6ffnet sich der Device Explorer und ihr k\u00f6nnt im Dateisystem euer Simulation bl\u00e4ttern. Navigiert zum betroffenen Verzeichnis und schaut euch die Datei an:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"618\" height=\"203\" class=\"wp-image-2942\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-5.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-5.png 618w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-5-300x99.png 300w\" sizes=\"auto, (max-width: 618px) 100vw, 618px\" \/><\/p>\n<p>Doppelklick auf unsere gespeicherte Datei zeigt den Inhalt und alle unsere bisher gespeicherten Texte:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"493\" height=\"313\" class=\"wp-image-2943\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-6.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-6.png 493w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-6-300x190.png 300w\" sizes=\"auto, (max-width: 493px) 100vw, 493px\" \/><\/p>\n<h1><a id=\"post-2937-__RefHeading___Toc1741_395087288\"><\/a>Speichern an \u00f6ffentlichen Orten &#8211; Intents<\/h1>\n<p>Ich bef\u00fcrchte, das wird jetzt etwas langweilig \u2013 ist aber ungemein wichtig! Ich h\u00f6rte bereits einige professionelle Android Programmierer st\u00f6hnen: Das \u00dcbel begann mit Android Version 11.<\/p>\n<p>Bis Android10 konnten wir in in unseren Anwendungen Dateinamen und Speicherorte mehr oder minder frei w\u00e4hlen und Dateien lesen und schreiben, wo und wie wir wollten. Wir konnten auch bereits per Upload \u00fcbertragene Dateien einfach auslesen und verarbeiten, ohne dass der Benutzer, sich um Dateinamen und Speicherorte k\u00fcmmern musste.<\/p>\n<p>Ab Android11 hat Google ein Sicherheitskonzept eingef\u00fchrt, das es untersagt, dass eine von einer Anwendung erstellte Datei automatisch programmgesteuert von einer anderen Anwendung verwendet werden kann. Konkret: eine Anwendung soll automatisch nur noch in dem ihr zugewiesenen Bereich speichern und lesen. Wenn ihr Dateien irgendwo anders hin speichern oder auslesen wollt, soll der Benutzer das freigeben und Dateiname und Ort ausw\u00e4hlen bzw. best\u00e4tigen.<\/p>\n<p>\u00dcber Sinn und Unsinn dieses Sicherheitskonzeptes will ich nicht urteilen. Es bereitet jedoch professionellen Enterprise Programmierern in Business Anwendungen etwas Kopfzerbrechen und verlangt vom Benutzer zus\u00e4tzliche Schritte, die seinen Tagesablauf nicht einfacher gestalten.<\/p>\n<p>In der Praxis hatten wir oftmals Anwendungen bei denen im lfd Betrieb Dateien ausgetauscht wurden, ohne dass der Benutzer informiert wurde. Stellt Euch vor Ihr sollt Pakete ausliefern und bei jeder Auslieferung den Barcode scannen und dann den Name desjenigen eingeben, der das Paket in Empfang nimmt.\u00a0 Da kann es schnell vorkommen, dass noch w\u00e4hrend der Tour ein Download erfolgt, der die erledigten Pakete von Android Device auf einen Computer zieht &#8211; und \/ oder ein Upload, der neue Paketadressen bereit stellt.\u00a0 Bis Android10 konnte ein solcher Transfer einfach im Hintergrund erfolgen, ohne dass der Benutzer sich darum k\u00fcmmern musste. Er hatte einfach immer aktuelle Daten. Ab Android11 muss er neue Daten jedoch erst einmal manuell einlesen und seiner Anwendung bekannt machen, damit er sie nutzen kann. Kurz: Es ist nun bei Dateien ein Benutzereingriff notwendig und das ist bei professionellen Anwendungen zur Datenerfassung nicht immer von Vorteil.<\/p>\n<p>F\u00fcr uns Programmierer hei\u00dft das: wir brauchen eine Logik bei der der Anwender etwa ausw\u00e4hlt und wir m\u00fcssen auf seine Auswahl reagieren. Das Speichern und Lesen von Daten geschieht dabei nicht mehr \u00fcber programmgesteuerte Java Funktionen auf File Ebene, sondern \u00fcber sogenannte INTENTS.<\/p>\n<p>Generell sind Intents erst mal eine gute Sache. Sie geben euch Zugriff auf die Ressourcen des Smartphones, z.B. Camera, GPS, Sensoren oder halt das Dateisystem. Jedes Intent muss aber ab Android11 immer vom Benutzer aktiviert und freigegeben werden.<\/p>\n<p>In unserem kleinen Beispiel: wenn ihr mit dem Button Speichern in eine Datei speichert, werden alle Eingaben immer direkt an die vorhandene Datei angef\u00fcgt und die Datei wird irgendwo im internen Android Dateisystem hinterlegt. Wo genau haben vor im vorherigen Kapitel gekl\u00e4rt!<\/p>\n<p>Jetzt wollen wir die M\u00f6glichkeit schaffen, diese interne Datei zu exportieren, so dass der Benutzer sie f\u00fcr andere Anwendungen oder f\u00fcr den Download mittels USB Kabel freigeben kann. Der Export soll \u00fcber ein Intent erfolgen, bei dem der Benutzer Dateiname und Ort w\u00e4hlen kann.<\/p>\n<p>Zuerst erweitern wir unsere App in der Layout Datei um einen Button buttonExport. Wir f\u00fcgen den Button als dritten Button im Horizontalen Layout ein.<\/p>\n<pre>&lt;LinearLayout \r\nandroid:layout_width=\"match_parent\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:orientation=\"horizontal\"&gt; \r\n\r\n&lt;Button \r\nandroid:id=\"@+id\/buttonClear\" \r\nandroid:layout_width=\"120dp\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:text=\"Clear\" \/&gt; \r\n\r\n&lt;Button \r\nandroid:id=\"@+id\/buttonSpeichern\" \r\nandroid:layout_width=\"120dp\" \r\nandroid:layout_height=\"wrap_content\" \r\nandroid:layout_marginLeft=\"10dp\" \r\nandroid:text=\"Speichern\" \/&gt; \r\n\r\n&lt;Button \r\nandroid:id=\"@+id\/buttonExport\" \r\nandroid:layout_width=\"120dp\" \r\nandroid:layout_height=\"50dp\" \r\nandroid:layout_marginLeft=\"10dp\" \r\nandroid:text=\"Export\" \/&gt; \r\n\r\n&lt;\/LinearLayout&gt;<\/pre>\n<p><strong>In onCreate registrieren wir den Listener f\u00fcr den neuen Button:<\/strong><\/p>\n<pre>findViewById(R.id.<em>buttonExport<\/em>).setOnClickListener(this);<\/pre>\n<h2><a id=\"post-2937-__RefHeading___Toc1743_395087288\"><\/a>Nutzung des Intent einbauen<\/h2>\n<p><strong>Unser onClick Listener bekommt den Code, was zu tun ist, wenn der Button bet\u00e4tigt wird. <\/strong><\/p>\n<p>Hier arbeiten wir erstmals mit einem Intent, dessen Code wir vorher selbst vergeben. Das Intent ist Erzeugen eines Dokumentes = Action_Create_Document. Der Dateiname \/ Titel des Dokumentes ist uns egal. Den kann der Benutzer eingeben. Das Intent zum Speichern von Daten liefert uns keinen File Konstruktor, sondern eine URI. Wir brauchen also jetzt eine Speicherfunktion, die Daten in eine Datei speichert, die mit einer URI definiert wurde. <a href=\"https:\/\/de.wikipedia.org\/wiki\/Uniform_Resource_Identifier\" target=\"_blank\" rel=\"noopener\">Infos zur Uri<\/a><\/p>\n<pre>case R.id.<em>buttonExport<\/em>: \r\n   Intent intent = new Intent((Intent.<em>ACTION_CREATE_DOCUMENT<\/em>)); \r\n   intent.addCategory(Intent.<em>CATEGORY_DEFAULT<\/em>); \r\n   intent.setType(\"text\/plain\"); \r\n   intent.putExtra(Intent.<em>EXTRA_TITLE<\/em>, \"\"); \r\n   startActivityForResult(intent, <em>REQUESTCODE_SAVE<\/em>); \r\n   break;<\/pre>\n<p><strong>Neue Klassenvariable<\/strong><\/p>\n<p>Neu eingef\u00fchrt haben wir eine Variable REQUESTCODE_SAVE. Dieses bezeichnet einen ID Wert, den vor vorher global als private static f\u00fcr die ganze Klasse definiert haben. Wir k\u00f6nnen jedem Intent einen willk\u00fcrlichen Requestcode zuweisen, die wir dann sp\u00e4ter auswerten und so erkennen, welches Intent der Benutzer aktiviert hat. Ich habe mal drei m\u00f6gliche Requestcodes aufgenommen, die wir h\u00e4ufig verwenden:<\/p>\n<pre><em>\/\/----------------------------------------------------------------------\r\n<\/em> public class MainActivity extends AppCompatActivity implements View.OnClickListener { \r\n\r\nprivate static EditText <em>etEingabe1<\/em>; \r\n\r\nprivate static final int <em>REQUESTCODE_SAVE <\/em>= 123; <em>\/\/Requestcode f. Export \/ Save\r\n<\/em>private static final int <em>REQUESTCODE_LOAD <\/em>= 125; <em>\/\/Requestcode f. Import \/ Laden\r\n<\/em>private static final int <em>REQUESTCODE_CAMERA <\/em>= 160; <em>\/\/Requestcode f. Camera \/ Scanner<\/em><\/pre>\n<p><strong>Android mit missionarischem Eifer<\/strong><\/p>\n<p>An dieser Stelle werdet ihr mit einer Google Besonderheit vertraut gemacht: Google ist ein gro\u00dfes Unternehmen und besch\u00e4ftigt wohl viele Programmierer, die mehr oder weniger eifrig am Android Code und Verbesserungen arbeiten.<\/p>\n<p>Leider sehen die ihre Arbeit manchmal auch mit etwas erzieherischen Charakter. Bew\u00e4hrte Methoden werden manchmal als veraltet dargestellt und durch neue ersetzt, deren Nutzung sich in vielen F\u00e4llen aber als komplizierter erweist und \/ oder den Code un\u00fcbersichtlicher gestalten.<\/p>\n<p>Gl\u00fccklicherweise funktionieren oftmals die alten Methoden in vielen F\u00e4llen noch \u2013 sie werden jedoch vom Android Studio als veraltet markiert.<\/p>\n<p>In meinem Kochbuch geht es mir nicht darum den sch\u00f6nsten Java Code zu erzeugen. In der Programmierung ist es wie im richtigen Leben: viele Wege f\u00fchren nach Rom. Der Praktiker will schnelle, funktionale Ergebnisse sehen, deshalb ignoriere ich einen Hinweis auf veraltet an dieser Stelle und benutze die mir bekannte Methoden weiterhin.<\/p>\n<p>In diesem Fall ist die Methode startAcitivityForResult betroffen, die von meinem Studio angemeckert \u2013 aber trotz Anmeckerung ausgef\u00fchrt wird. Wenn Ihr wissen wollt, wir ihr die ersetzt: benutzt Google! Oder ich schreibe irgendwann einmal ein Kapitel in dem das erkl\u00e4rt wird.<\/p>\n<h2><a id=\"post-2937-__RefHeading___Toc1745_395087288\"><\/a>Was hat der Benutzer gemacht? Ein Intent abfragen<\/h2>\n<p>Anschlie\u00dfend bauen wir uns mit onActivityResult eine Methode, die uns informiert, ob \/ welche Intents aktiviert wurden. Sollte das Intent Daten speichern basteln sein, wird eine Speicherroutine aufgerufen, die unsere interne Daten in eine Datei mit Namen und Speicherort nach Angaben des Benutzers exportiert.<\/p>\n<pre><em>\/\/----------------------------------------------------------------------\r\n<\/em> public void onActivityResult(int requestCode, int resultCode, Intent resultData) { \r\n<em>   \/\/Intent auswerten\r\n\r\n<\/em>   super.onActivityResult(requestCode, resultCode, resultData); \r\n \r\n<em>   \/\/RequestCode Save \/ Export\r\n<\/em>   if (requestCode == <em>REQUESTCODE_SAVE <\/em>&amp;&amp; resultCode == Activity.<em>RESULT_OK<\/em>) { \r\n      Uri uri = null; \r\n      if (resultData != null) { \r\n         uri = resultData.getData(); \r\n         doExportDatei(uri, \"WriterData.txt\"); \r\n      } \r\n   } \r\n<em>   \/\/Andere RequestCodes hier einbauen\r\n<\/em> }<\/pre>\n<h2><a id=\"post-2937-__RefHeading___Toc1747_395087288\"><\/a>Speichern mit Intents \u2013 die URI<\/h2>\n<p>Und die Speicherroutine<\/p>\n<pre><em>\/\/----------------------------------------------------------------------\r\n<\/em> private void doExportDatei (Uri myZielUri, String myQuelldatei) {\r\n\r\n   try {\r\n\r\n      OutputStream stream = getContentResolver().openOutputStream(myZielUri);\r\n      PrintWriter writer = new PrintWriter(stream);\r\n\r\n      File myFileQuelle = new File (this.getFilesDir(), myQuelldatei);\r\n      if (!myFileQuelle.exists()) {\r\n<em>         \/\/Datei nicht vorhanden!\r\n         Toast.makeText(this, \"Datei nicht vorhanden!\", Toast.LENGTH_LONG).show();\r\n         return;\r\n<\/em>      }\r\n<em>      \/\/Read text from file\r\n<\/em>      StringBuilder text = new StringBuilder();\r\n\r\n      try {\r\n         FileInputStream fileIn = new FileInputStream(myFileQuelle);\r\n         InputStreamReader InputRead = new InputStreamReader(fileIn, \"ISO_8859_1\");\r\n         BufferedReader br = new BufferedReader(InputRead);\r\n\r\n         String line;\r\n\r\n         while ((line = br.readLine()) != null) {\r\n            text.append(line);\r\n            text.append('\\n');\r\n         }\r\n         br.close();\r\n      }\r\n      catch (IOException e) {\r\n<em>         \/\/You'll need to add proper error handling here\r\n<\/em>         String x2 = e.getMessage();\r\n         Log.<em>e <\/em>(\"Error_Show050\", x2);\r\n         Toast.<em>makeText<\/em>(this, x2, Toast.<em>LENGTH_LONG<\/em>).show();\r\n         e.printStackTrace();\r\n      }\r\n\r\n      writer.write(text.toString());\r\n      writer.flush();\r\n      stream.close();\r\n\r\n      \/\/Quelle nach Erfolg loeschen\r\n      myFileQuelle.delete ();\r\n\r\n      \/\/Meldung an Benutzer\r\n<em>      Toast.makeText(this, \"Datei exportiert!\", Toast.LENGTH_LONG).show();<\/em>\r\n\r\n   } catch (IOException e) {\r\n      Log.<em>e<\/em>(getLocalClassName(), \"caught IOException\", e);\r\n   }\r\n}<\/pre>\n<h2><a id=\"post-2937-__RefHeading___Toc1749_395087288\"><\/a>Finaler Text im Device Manager<\/h2>\n<p>Fertig. Das war es. Jetzt via Device Manager die Anwendung aufrufen, Button Export und Datei mit Namen MeineNotizen.txt ins Download Verzeichnis speichern.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"244\" height=\"500\" class=\"wp-image-2944\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-7.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-7.png 244w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-7-146x300.png 146w\" sizes=\"auto, (max-width: 244px) 100vw, 244px\" \/><\/p>\n<p>Um unser Werk anzusehen: wir aktivieren unter Android in den Datei Explorer, wechseln in das Verzeichnis Download und starten durch Doppellkick den Viewer f\u00fcr diese Datei.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"244\" height=\"500\" class=\"wp-image-2945\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-8.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-8.png 244w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-8-146x300.png 146w\" sizes=\"auto, (max-width: 244px) 100vw, 244px\" \/> <img loading=\"lazy\" decoding=\"async\" width=\"244\" height=\"500\" class=\"wp-image-2946\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-9.png\" srcset=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-9.png 244w, https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2023\/10\/word-image-2937-9-146x300.png 146w\" sizes=\"auto, (max-width: 244px) 100vw, 244px\" \/><\/p>\n<p>Jetzt haben wir unsere Daten unter Android zur freien Verf\u00fcgung. Wir k\u00f6nnen sie per USB Kabeltransfer, Bluetooth an einen Computer laden, in die Cloud transferieren oder was auch immer damit veranstalten.<\/p>\n<h2>Video zum Text<\/h2>\n<p><iframe loading=\"lazy\" title=\"AE Android Kochbuch f\u00fcr Beginner. Video 03. Eine kleine Notizen App programmieren\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/Ge3HTFqwNTk?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<hr \/>\n<p>Hinweis: Dieser Text ist ein Preview. Die \u00fcberarbeitete Fassung gibt es bei uns als PDF!<\/p>\n<hr \/>\n<p><a href=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2021\/06\/20080607hjwx-204.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2104\" src=\"https:\/\/www.art-events.de\/weblog\/wp-content\/uploads\/2021\/06\/20080607hjwx-204.jpg\" alt=\"\" width=\"204\" height=\"153\" \/><\/a><\/p>\n<p>Text und Entwurf. (c)\u00a0<a href=\"https:\/\/www.terminal-systems.de\/\" target=\"_blank\" rel=\"noopener\">AE SYSTEME Testcenter<\/a>, Hans-J. Walter<br \/>\nHans-J. Walter ist Programmierer f\u00fcr Windows DOT.NET \/ C# und Android und als eingetragener, unabh\u00e4ngiger Journalist verantwortlich f\u00fcr Fachberichte und Schulungstexte \u00fcber Technik u. Entwicklung.\u00a0<a href=\"mailto:hjw@terminal-systems.de\">hjw@terminal-systems.de<\/a><\/p>\n<p><em>F\u00fcr diese und alle nachfolgenden Seiten gilt ebenso der obligatorische Hinweis: Alle Angaben ohne Gew\u00e4hr. Bilder und Codes zeigen Beispiele. Diese Beschreibung bezieht sich auf unsere Installation und stellt keine Bewertung der verwendeten Techniken da. Fehler und Irrt\u00fcmer vorbehalten!<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im Teil3 vom AE Android Kochbuch schreiben wir eine kleine Notizen App und speichern diese als Datei. Unser AE Android Kochbuch f\u00fcr Praktiker. Hier zeige ich euch wie ihr schnell und einfach Android programmieren lernt. Tipps aus der Praxis. Vorweg noch mal der Hinweis: Dieses ist zwar ein Grundkurs, aber ihr solltet schon etwas Grundlagenwissen [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33,34],"tags":[],"class_list":["post-2937","post","type-post","status-publish","format-standard","hentry","category-android","category-programmierung","entry"],"_links":{"self":[{"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/posts\/2937","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/comments?post=2937"}],"version-history":[{"count":0,"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/posts\/2937\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/media?parent=2937"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/categories?post=2937"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.art-events.de\/weblog\/wp-json\/wp\/v2\/tags?post=2937"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}