Layout per Code erzeugen

Ich brauchte eine Klasse, die mir hilft, Tabellen dynamisch zu erzeuge und in ein bestehendes Layout einzufügen.

Wie erzeugt man ein Layout per Code? Das geht ganz einfach. Zunächst einmal zeige ich wie man ein Layout komplett per Code erzeugt. Dann beschäftige ich mich mit der Frage, wie füge ich ein per Code– erzeugtes Layout an einer vorbestimmten Stelle in ein bestehendes Layout ein?

Ein komplettes Layout per Code erzeugen und anzeigen

public class MainActivity extends AppCompatActivity {

    ViewGroup container;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //setContentView(R.layout.activity_main);
        // Create layout params like you would inside the layout.xml file
        LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.MATCH_PARENT);

        // Now create the new layout
        LinearLayout linearLayout=new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.HORIZONTAL);

        TextView cell=new TextView(this);
        cell.setText("Cell");
        cell.setLayoutParams(params);

        // Add UI- elements
        linearLayout.addView(cell);

        //LinearLayout pl=(LinearLayout) findViewById(R.id.table);
        //pl.addView(linearLayout);

        // Add layout to existing view
        addContentView(linearLayout,params);
    }
}

Ein Layout per Code erzeugen und in ein bestehendes Layout an einer vorher bestimmten Stelle einsetzten

Dafür benötigen wir zuerst ein fertiges Layout, dass wir per xml– definieren und im Ordner res/layout ablegen:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:theme="@style/Theme.AppCompat.NoActionBar">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This ist the main layout....."
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/table"
        android:orientation="horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView">

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Die Stelle an die wir das per Code erzeuge Layout einsetzen möchten ist das LinearLayout, android:id=“@+id/table“.

Der Java– Code der MainActivity schaut so aus:

public class MainActivity extends AppCompatActivity {

    ViewGroup container;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Create layout params like you would inside the layout.xml file
        LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.MATCH_PARENT);

        // Now create the new layout
        LinearLayout linearLayout=new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.HORIZONTAL);

        TextView cell=new TextView(this);
        cell.setText("Cell");
        cell.setLayoutParams(params);

        // Add UI- elements
        linearLayout.addView(cell);

        LinearLayout pl=(LinearLayout) findViewById(R.id.table);
        pl.addView(linearLayout);

        // Add layout to existing view
        //addContentView(linearLayout,params);
    }
}

Das war es dann auch schon. Anhand der auskommentierten Stellen im Java– Code kann man die Unterschiede nachvollziehen.


IlligalStateException

Unmitelbar nachdem eine Activity beendet und die nächste gestartet wurde, wurde ein Fragment geöffnet. Das Resultat:

IlligalStateException

Die Lösung des Problems wird dort, auf StackOverflow ausführlich besprochen (Letzter Aufruf 28.5.2018).

Für mich hat das die Lösung gebracht:

Der Folgende Code sollte erst dann aufgerufen werden, wenn OnResume() aufgerufen wurde:

FragmentManager fm = getSupportFragmentManager();
FragmentTextInputDialog yn = FragmentTextInputDialog.newInstance("ss");
yn.show(fm, "fragment_text_input_dialog");

 

OnSaveInstanceState();

Die Methode OnSaveInstanceState() speichert Nutzerdaten vorübergehend, über den Lebenszyklus einer Activity.

Standardmäßig speichert diese Methode den Inhalt von EditTextFeldern oder etwa die Scroll- Position von List View Widget’sUI– Elemente werden beim Durchlaufen der Methode onRestoreInstanceState() nur dann vom System wieder hergestellt, wenn diesen Elementen mit dem Attribut android:id eine gültige Identifikation zugewiesen wurde.

Die Methode onSaveInstanceState() wird nur dann aufgerufen, wenn eine Activity  vom System selbst zerstört wird. Das ist nicht der Fall wenn der Benutzer den Back- Button drückt oder die Methode finish() der Übergeordneten Activity– Klasse aufgerufen wird.

Eine Typische Situation für eine von System veranlasste „Zerstörung“ einer Activity ist zum Beispiel das wechseln der Bildschirm Ausrichtung.

Beim Durchlaufen der Methode OnCreate() werden die gespeicherten Inhalte über die Methode onRestoreInstanceState() vom System wieder hergestellt.

Wenn man Nutzerdaten über den Lebens Zyklus der Activity – und die Betonung liegt hier auf Activity, nicht auf App! – hinaus retten möchte, dann muss man die Methode onSaveInstanceState() der Activity– Klasse überschreiben. Die zu speichernden Inhalte werden in einem Objekt namens Bundle als Key/ Value Pairs (Schlüssel/ Wert Paare) abgelegt. Beim Durchlauf von onCreate() kann man sich die gespeicherten Inhalte dort wieder abrufen.

Der Methode onRestoreInstanceState() wird das Selbe Bundle- Objekt übergeben wie der onCreate()Methode. Dort wo es sinnvoll ist kann diese Methode überschrieben werden. Das beschriebene Bundle– Objekt eignet sich laut Google vor allem zur Behandlung von sogenannten lightweight, transistent user data (einfache, vorübergehend zu speichernde Nutzerdaten) die nicht über den Lebenszyklus einer App hinweggerettet werden müssen. Also zum Beispiel Werte aus Eingaben in EditText- Felder.

 

 

 

 

Focus lost..

Die Folgende Fehlermeldung hatte ich erhalten:

W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=202.125, y[0]=1358.5625, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=5879866, downTime=5875801, deviceId=0, source=0x1002 }

Das Resultat: In der auslösenden Activity wurde der der Klick auf den dort eingebaute Floating Action Button (fab) nicht mehr erkannt, satt dessen, Fehler wie oben beschrieben.

Der Grund:


setContentView(R.layout.activity_new_score);

wurde zweimal aufgerufen. Einmal in on Create() und das zweite Mal in onResume(). Nachdem ich den Befehl in onResume() entfernt hatte, war der Fehler verschwunden und das anklicken des fab wieder erkannt.

Android+ Big Data

Anbei ein paar Notizen zum Umgang mit großen Dateien.

Ich wollte wissen, wie man eine App schreibt, die eine Datenbank mitbringt, ohne das sich der Benutzer eine Datei selber herunterladen muss. Mein Ansatz war eine Datenbank die eine Sternenkarte enthält. Benutzt habe ich dazu die H2- Engine. Größe der Datenbank ca. 134MB.

Erster Versuch: Die Datenbank in den Assests Folder ablegen und von da, beim Start der App, in das Dateisystem kopieren. Das hat keinen Sinn gemacht, es funktioniert zwar, aber verdammt langsam!

Zweiter Versuch: Die Datenbank als Datei in das Filesystem geschrieben (Data/Data….) und beim Start der App geladen. Dauert zwar immer noch relativ lange, aber, es passiert in einer einigermaßen vertretbarer Zeitspanne. Da ist aber noch Optimierungsbedarf! Denn, auch wenn das Gerät gedreht wird, muss wieder gewartet werden.

H2 Datenbank

Mein Einstieg in die Datenbank Programmierung mit Java. Momentan wird gelernt. Der Text der jetzt folgt, gibt meine Erfahrungen im Lernprozess stichwortartig wieder. Ich möchte schließlich nix vergessen….

Abstrakt

Früher hat man sich eine Datenbank selbst gebastelt. Heute gilbt es fertige Lösungen. Das macht Sinn, mann muss ja schließlich nicht immer wieder das Rad neu erfinden. Eine dieser Lösungen bietet die Hypersonic 2 – Database Engine (H2) . Dieser Artikel beschreibt meine ersten Schritte – learnin by doing – mit dieser Lösung.

Voraussetzungen

Ich arbeite mit:

  • Mac OS X 10.7.5 bzw Android Studio 2.0 (JRE 1.6.0.65)
  • java version 1.8.0_66
  • Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
  • h2- Engine Version 1.4.191 (2016-01-21), Beta
  • Version: Mars.1 Release (4.5.1). Build id: 20150924-1200

Alle externen Links wurden zuletzt aufgerufen am: 03.05.2016

Installation

H2 ist eine leichtgewichtige Engine in Java/ für Java. Man bekommt Sie Dort. Nach dem Download liegt der Ordner “h2” im gewählten Verzeichnis.

Eine ausführliche Einführung wie man H2 für eigenen Projekte nutzen kann gibt es bei javabeginners. Dort steht vieles, aber nicht wie man die Engine installiert und in den CLASSPATH (OS X) oder den BUILDPATH (Eclipse unter OS X) einbindet. Ich habe das so gemacht:
Falls man mit Eclipse arbeite:
Die jar- Datei der H2- Engine muss in den Build- Path eingetragen werden.
Ich habe die Engine geladen und in einem mir genehmen Verzeichnis abgelegt[1]. Als nächstes ein neues Projekt in Eclipse angelegt und Project -> Properties -> Java Build Path aufgerufen, AddJar’s gewählt und den Pfad in ich den Ordner “h2” abgelegt habe eingetragen.

Das reicht dann schon um loszulegen und einen ersten Beispiel Code zu testen.

Bildschirmfoto 2016-04-19 um 12.36.16

[1] Es ist egal welches Verzeichnis man wählt, möchte man seine Java- Umgebung aber sauber halten, dann geht man am besten wie unter Falls man von der Kommandozeile aus arbeitet beschrieben vor.

Falls man von der Kommandozeile aus arbeitet:
Zusätzliche Klassen sucht java in /Library/Java/Extensions bzw. im Heim- Verzeichnis des angemeldeten Nutzers unter ~/Library/Java/Extensions. Klassen (egal in welchem „Aggregatzustand“ *.java/ *.class oder *.jar) die jedem Java- Programm das man schreibt zur verfügung stehen sollen, kopiert man dorthin.
Findet java die *.class Datei nicht, sucht es nach dem Quellcode – also der *.java Datei – und kompiliert die. Selbstverständlich werden auch *.jar Archive durchsucht.

Ich habe also die Datei h2-1.4.191.jar in das oben erwähnte Verzeichnis kopiert.
Falls man mit Android Studio arbeitet:

  • Maven
    File-> Project Structure -> App -> Dependencies wählen:

Screen Shot 2016-06-29 at 1.27.48 PM

Dort dann auf „+“ klicken und 1 Library depenency wählen:

Screen Shot 2016-06-29 at 1.30.43 PM

Im Suchfeld gibt man dann „h2“ ein und die Library wird eingefügt. Man kann nun damit arbeiten.

Anmerkung: Im Hintergrundfenster kann man sehen, dass die Library schon eingefügt worden ist.

  • Lokales *.jar
    Einfach im Projektordner in libs/ die *.jar einfügen. Das war es.

Die Maven– Variante hat nicht immer funktioniert. In meinem SimpleLitaratureDB hat der Compiler beim erstellen des Gradel einen Fehler gemeldet. Variante 2, lokales *.jar hat anstandslos geklappt.

Die Datenbank (programmatisch) benutzen
Ein erster Test
Um zu testen ob der Treiber richtig installiert wurde habe ich dann Beispiel- Code von javabeginners übernommen und leicht abgeändert. Alles weiter ist im Code- Beispiel kommentiert:

  • Beispiel Code einfügen => H2_Test.java

Wenn der Code läuft und in der Shell „OK“ ausgegeben wird, dann ist alles richtig installiert und man hat eine leere Datenbank zum experementieren.
Struktur einer relationaler Datenbank
Ich setze voraus dass das Prinzip einer relationen – Datenbank bekannt ist. Die Beschreibung würde den Rahmen dieses Artikels sprengen. Aber, in aller Kürze: Man stellt sich solche Datenbanken einfach als eine Sammlung von Tabellenbättern vor, bei dem jede Zelle mit einer Zelle eines anderen Tabellenbalttes in Beziehung stehen kann. Daher der Namens Zusatz relational.
SQL- Abfrage
H2 lässt sich komplett mit SQL– Statements steuern. Grundsätzlich geht das so:

  • Conection object instanzieren (=Verbindung mit der Datenbank herstellen)
Class.forName("org.h2.Driver");

Connection conn = DriverManager.

// Open DB. If not present, create one
// Parameters: Driver name (jdbc:h2 + Pathname + username + pwd)
getConnection("jdbc:h2:~/Documents/Java/0_Eclipse_Projects/H2_Test/FirstDB", "", "");
System.out.println("OK");
  • Über die Klasse PreparedStatement mittels SQL die Datenbank füllen, abfragen oder verändern.Instanzen der Datenbank- Klasse (in diesem Beispiel conn) übergibt man mittels der Methode prepareStatement einen String mit einem beliebigen SQL- Statement. das Rückgabe Objekt (im Beispiel selectPreparedStatement) enthält eine vor- kompilierten Version dieses Strings. Über die Methode executeQuery() die man auf dieses Objekt anwendet erhält man das Ergebnis der Anfrage :
    PreparedStatement selectPreparedStatement = null;
    selectPreparedStatement = conn.prepareStatement(sqlString);
    ResultSet rs = selectPreparedStatement.executeQuery();
    
  • das Ergebnis lässt sich mittels der Methode next() abfragen:

    while (rs.next()) {…}
    Methoden von ResultSet:
    z.B. rs.getInt(„Spalte“);

    Wichtig zu Wissen! Das Ergebnis von ResultSet stellt man sich am besten als Tabelle vor. Die Anzahl der Spalten hängt von der Art der Abfrage ab und muss nicht mit der Anzahl der Spalten der abgefragten Tabelle übereinstimmen. Die Anzahl der Zeilen kann beliebig sein und entspricht der Anzahl der passenden Ergebnisse der Abfrage.

    Wenn ResultSet keine Ergebnisse enthält, dann kann man das herausfinden indem man die Methode isBeforeFirst() aufruft. Normalerweise wird der Cursor in der Ergebnistabelle vor der ersten Zeile platziert. Dann ist isBeforeFirst() war (true). Wenn nicht, wenn es also kein Ergebnis gab, dann kann der Cursor auch nicht vor einer solchen Zeile stehen. Die Methode liefert dann ein nicht wahr= false.

    ResultSet rs=newResultSet();
    if(!rs.isBeforeFirst()) System.out.println(„Kein Ergebniss gefunden“);

Nützliche Links

H2 API
Klassen und Methoden der H2- Engine im Überblick.

RazorSQL
Ein Tool zum erstellen, manipulieren und betrachten von JDBC- basierenden Datenbanken (dazu zählt natürlich auch H2).

SQL- Tutorial
SQL (Structured Query Language) sollte man ansatzweise beherrschen, damit man mit der H2– API auch umgehen kann. Hier findet man einen ersten Anlaufpunkt für weitere Recherchen.

Begriffe

API
Application Programing Interface

JDBC
Java Datebase Conectivity. Ist eine API zur Datenbank Anbindung. Das Gegenstück in der Windows- Welt ist ODBC. JDBC kann einen Treiber verwenden um damit zu kommunizieren.