Beam Calc II

Ich habe die Library überarbeitet. Es werden nun auch veränderliche Linien- Lasten unterstützt. Dreiecks Lasten und Trapez- Lasten können eingegeben und berechnet werden.

Beam Calc II, der Name ist etwas irreführend. Es gibt nämlich keinen ersten Teil. Die Namensgebung kommt daher, weil ich die erste Version über Code Review habe beurteilen lassen. Das verbesserte Ergebnis ist Beam Calc II.

Die Library kann man sich hier anschauen/ herunterladen: GitHub CodingByChanche.

Beispiel Rechnung mit detailliertem Ergebnis.

Die Library benutze ich zur Entwicklung einer Android App. Die kann man sich hier im im Blog anschauen: Projekte, Einfeldträger.

Wer möchte kann mich im Google Play Store besuchen, dort kann man sich die aktuelle Version dieser App direkt herunterladen. Einfeldträger (BeamCalc) bei Google Play.

Einfeldträger (BeamCalc) App.

Android Live Data

Livedata Ist Bestandteil der Android Architecture Components. Ermöglicht es einer Activity oder einem Fragment auf Änderungen in einem View Model zu reagieren.

Das ViewModel ist eine prima Sache, wenn man vorübergehend Nutzerdaten einer Activity oder eines Fragmentes einfach verwalten möchte.

LiveData kann mit einem ViewModel kombiniert werden, wenn man möchte, dass die aktive Activity oder das aktive Fragment unmittelbar auf Änderungen der Daten im ViewModel reagiert. Man erspart sich damit beispielsweise die implementation einer eigenen Ablauflogik (savedInstanceState). LiveData ist vergleichbar mit dem Observer Pattern. der Unterschied ist aber der, dass LiveData den Android LifeCycle kennt und nur aktive Komponenten über Updates informiert.

Ich stelle hier eine kleine App vor, die das ganze demonstriert.

Demo App

Vorbereitung

Im build.grade für die app müssen die aktuellen Bibliotheken eingetragen werden:

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'<br>implementation 
"androidx.lifecycle:lifecycle-viewmodel:2.2.0"

Layout

In der layout- Dateie ist nicht besonderes zu beachten. Ich habe die hier nur der Vollständigkeit halber mit aufgenommen.

<?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">

    <TextView
        android:id="@+id/liveDataView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="LIVE_DATA"
        app:layout_constraintHorizontal_bias="0.533"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/updateLiveDataView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Update"
        app:layout_constraintTop_toBottomOf="@+id/liveDataView"
        tools:layout_editor_absoluteX="39dp"></Button>

</androidx.constraintlayout.widget.ConstraintLayout>

Das ViewModel

package com.example.livedatatest;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {

    private MutableLiveData<String> name;


    /**
     * Note: There is no 'setName' method within this viewModel....
     * LiveData Objects are updated by invoking  the observers 'setValue' method
     *
     * setValue should be used from the mainThread. postValue can be used
     * from any other thread.
     *
     * @return Either an empty string or the updated liveData which was
     * passed by the 'set' Method invoked by the active Activity or Fragment
     */
    public MutableLiveData<String> getName(){
        if (name==null)
            name=new MutableLiveData<String>();
        return name;
    }
}

Das ViewModel soll ein einfaches String– Objekt für seine verbundene Activity speichern. Das Entscheidende hier ist, dass man keine getter- Methode für das Objekt findet. Weshalb das so ist, wird deutlich, wenn man sich die Activity anschaut.

Die Activity

package com.example.livedatatest;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private  MainViewModel mainViewModel;
    private Button updateLiveDataView;
    private TextView liveDataView;

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

        // UI
        updateLiveDataView=findViewById(R.id.updateLiveDataView);
        liveDataView=findViewById(R.id.liveDataView);

        // View Model
        mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);

        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                liveDataView.setText(newName);
            }
        };

        // Observe Live Data
        //
        // This invokes onChanged method of the nameObserver if the liveData object
        // in the viewModel holds data. If not, onChanged is not invoked...
        mainViewModel.getName().observe(this, nameObserver);


        // Update the view which displays the current liveData objects
        // contents....
        // Replaces the current value inside the liveData Object with this...
        updateLiveDataView.setOnClickListener(v -> {

            // There is no dedicated 'setter' method for the liveData.
            // Live Data Objects are updated by invoking the 'setValue'
            // method of he Observer.
            // When not inside the main thread, use the 'postValue'
            // method instead.
            Long time=System.currentTimeMillis();
            mainViewModel.getName().setValue("Hi+"+time);
        });
    }
}

Es wird ein Observer implementiert, der die Methode onChanged enthält. Die Methode getName aus dem ViewModel wird über Methode observe mit dem ViewModel verbunden.

Wenn man nun das LiveData Objekt aktualisiert, dann wird jedesmal die onChanged– Methode des Observers aufgerufen. Die Akltuallisierung geschieht nun nicht über eine eigene setter- Methode im ViewModel, sondern über die ‚setValue‚ Methode des Observers.

mainViewModel.getName().setValue("Hi+"+time); 

Wenn man das LiveData- Objekt nicht vom main- thread aus aktuallisiert, sondern, beispielsweise aus einem anderen thread heraus, dann sollte man stattdessen die ‚postValue‚ Methode benutzen.

Noch etwas grob, nur so zur Notiz…..

Android Architecture Components

Ein Einstieg: What are Android Architecture Components?

Android Developers: Guide to App Architecture

Data Binding
Kommt noch.

Live Data
Ermöglicht es einer Activity oder einem Fragment auf Änderungen von Objekten in einem View Model zu reagieren.

Paging
Dient dazu Listen mit kleinen Datenblöcken aus einer lokalen oder externen Datenquelle zu befüllen. Mehr dazu unter Android Developers: Codelab, Android Paging.


View Model
Ist nebene dem SavedInstanceState eine weitere Methode um Nutzer- Daten, innerhalb des Lebenszyklus einer Activity oder eines Fragmentes, im Speicher zu halten (zum Beispiel dann, wenn sich die Bildschirmorientierung ändert oder die Activity/ das Fragemnt gewechselt wird). Der Vorteil gegenüber onSaveInstanceState() ist, dass sich damit komplexere Daten, zum Beispiel aus einer Datenbank oder Bilddateien vorübergehend sichern lassen. Die beiden erstgenannten Methoden eigene sich für einfache Daten Sammlungen, die sich leicht in Schlüssen- Daten Paare aufteilen lassen.

Lifecycles (LifeCycle, LifeCycleObserver, LifeCycleOwner)
Eine Activity oder ein Fragment reagiert auf Änderungen im Life- Cycle , indem die entsprechenden Call- Back Methoden aufgerufen werden (onCreate(), onStart(),onResume() etc…).

Dabei kann es aber unter Anderem passieren das, während der Code der in onStart() ausgeführt wird – nehmen wir an es findet der Verbindungsaufbau zu einem Bluetooth Gerät statt – onStop() vom System aufgerufen wird. wenn man nun dort die Bluetooth– Verbindung über diese Änderung informieren möchte, dann wird es zu einem Fehler kommen weil es ja noch keine Verbindung gibt (am ehestens bekommt man eine null pointer exception wenn man beispielsweise mit dem connectedThread– Objekt kommunizieren möchte).

Das oben beschriebene Fehlverhalten kann vermieden werden, indem man jene Komponenten einer Activity oder eines Fragmentes (also zum Beispiel das oben erwähnte bluetoothConnectedThread– Objekt – selbst auf die Änderungen im Life- Cycle reagieren läßt. Dabei ist die Activity oder das Fragment immer der Life- Cycle Owner. Die jeweilige Komponente ist der sogenannte Observer der über die Änderungen entsprechend informiert wird und darauf reagieren kann.

Neben dem Vorteil der Fehlervermeidung wird der Code lesbarer. Wenn man mehrere Komponenten hat die Life- Cycle abhängig sind, dann hat man in jeder Komponente den kompletten Code stehen und nicht teilweise in der jeweiligen Call- Back Methode der Activity oder des Fragemntes.

Navigation
Komm noch.

Room
Datenbank. Mehr dazu unter: Android Developers.

WorkManager
Kommt noch.

(Git)Clone- Krieger

In letzter Zeit habe ich einige meiner auf GitHub abgelegten Android Projekte auf meinen Desktop- Rechner zuhause importiert (git clone…..). Leider hat das nicht geklappt! Die häufigste Fehlerquelle war, dass Android Studio keine gültige Konfiguration finden konnte.

‚app‘ war durchgestrichen

Die Erste Spur, der Erste Versuch

Leider hat das Bearbeiten der Konfigurationen auch nichts gebracht, weil es nicht möglich war das Module zu benennen.

Nun, wenn man im Netz sucht, dann wird immer wieder auf ein Verfahren hingewiesen, nähmlich:

In der Datei settings.gradle include ‚:app‘ löschen, mit gradle synchronisieren, include ‚:app‘ wieder einfügen und wieder synchronisieren. Hat nicht geklappt!

include ':locationLibrary'
include ':app'
rootProject.name = "BluetoothThermometer"

Oben, die Datei settings.gradle für das Projekt.

Das eigentliche Problem

Nun, ich habe unter anderem den Hinweis im Netz gefunden, dass man, grundsätzlich, beim hochladen eines Android– Projektes das Verzeichnis .idea und alle *.iml Dateien ignorieren sollte. Dort legt Android Studio Informationen zum Projekt ab die nur die IDE benötigt. Das geschieht quasi neben der Konfiguration der Gradle– Projektstruktur. Das Verzeichnis und die Dateien werden bei jedem Build des Projektes von Android Studio neu erzeugt. Also:

Das Verzeichniss .idea und alle *.iml Dateien löschen.

Dann das Projekt mit den Gradle– Files synchronisieren File=> Sync Project with Gradle Files.

Danach wurde mir in der Fußzeile des Fensters von Android Studio angezeigt:

Module ‚app‘ Platform ‚android-28‘ not found

Das habe ich dann schon als Erfolg gewertet, alter Fehler weg, neuer Fehler da, WOW!

Android Studio hat sich also beschwert, dass es das SDK (Software Developement Kit) für den API- Level – der für das Projekt festgelegt wurde – nicht findet.

Die Lösung

File=> Settings => Android SDK und dort die jeweils fehlende SDK abhaken, danach installieren und fertig.

Fehlendes SDK installieren. Danach sollte das Projekt hergestellt sein und dem Kompilieren steht nix mehr im Weg.

Das war es

Ca. 2 Stunden vom Samstag weg (Problemlösung und schreiben dieses Artikels). Hoffe für den der das da Draussen findet spart’s die Zeit.

Scrooge

„Geiz“ ist – vielleicht – eine Tugend, aber sicher eine App. Ein älteres Projekt das ich gerade wiederbelebt habe. Das UI- Design entspricht nicht meinem aktuellsten Kenntnisstand, aber, die App habe ich täglich im Einsatz und als nützlich empfunden.

Das Grundprinzip ist das Folgende: Man setzt sich eine Grenze für seine Täglichen Ausgaben. Diese Challenge kann man nun so lange laufen lassen, wie man es möchte. Die App zeigt einem nun laufend das verbleibende Tagesbudget, die aktuelle, maximale Soll- Summe und die Ist- Summe an. So behält man den Überblick.

Alle ausgaben werden archiviert und können nach Abschluss der jeweils aktuellen Challenge zur Information abgerufen werden.

View Model

Ist nebene dem SavedInstanceState eine weitere Methode um Nutzer- Daten, innerhalb des Lebenszyklus einer Activity oder eines Fragmentes, im Speicher zu halten (zum Beispiel dann, wenn sich die Bildschirmorientierung ändert oder die Activity/ das Fragemnt gewechselt wird). Der Vorteil gegenüber onSaveInstanceState() ist, dass sich damit komplexere Daten, zum Beispiel aus einer Datenbank oder Bilddateien vorübergehend sichern lassen. Die beiden erstgenannten Methoden eigene sich für einfache Daten Sammlungen, die sich leicht in Schlüssen- Daten Paare aufteilen lassen. In Kombination mit LiveData ist es außerdem möglich, die jeweilige Activity oder das Fragment unmittelbar über Änderungen der Daten im View Model zu informieren.

Eine Demo- App die zeigt, wie man mit dem ViewModel umgeht, findet man in diesem Artikel: Android LiveData.

Die Offiziele Dokumentation findet sich dort: Link (Android Developers)

Beim experimentieren mit dem ViewModel ist mir aufgefallen, dass jedes ViewModel an die jeweilige Activity gebunden ist, es kann nicht zwischen mehreren Activity’s geteilt werden um beispielsweise Daten auszutauschen. Wenn man mit Fragmenten arbeitet, dann geht das.

Shared Preferences

Ist eine Methode um einfache Nutzerdaten über den Lebenszyklus einer App hinaus zu speichern. Einfach meint in diesem Zusammenhang das es sich um Daten aus View- Controllern handelt, beispielsweise aus textViews, die sich einfach in Schlüssel- Werte Paare aufteilen lassen. Das funktioniert ganz ähnlich wie mit Bundle- Objekten, die der onCreate– Methode einer Activity übergeben werden.

Mobiler Einsatzplan

Meine Firma veröffentlicht in regelmässigen Abständen die Einsatzpläne per E- Mail. Es handelt sich um eine Textdatei, die automatische von der Planungssoftware ausgegeben wird. Das ist unpraktisch. Nun hat ein Kollege den Wunsch geäussert, dass es ganz prima wäre, wenn es eine app gäbe, die diesen Einsatzplan automatisch in den Kalender seines Handys überträgt. Herausforderung angenommen!

Den Quellcode der app finded man dort: Git- Hub, Make Job Schedule

Den Quellcode des Parser’s, eine Java- Library, die für das Interpretieren der Einsatzplan Textdatei verantwortlich ist, findet man dort: Make Calendar

Die Maus zittert

Neulich habe ich Linux Mint auf meinem Desktop- PC installiert. Lief und läuft super, bis, ja bis der Mauszeiger anfing unkontrolliert zu zittern…

Ouvertüre

Meine erste Recherche im Internet blieb relativ erfolglos. Nun, ich fand zwar die Beschreibung des Problems, mehr als einmal, ich war also nicht alleine, aber, die Antworten lassen sich in etwa so kategorisieren: Kategorie eins, Ich habe zwar die Frage nicht gelesen, aber, ich antworte trotzdem. Kategorie zwei: Ich habe zwar keine Ahnung, aber, du kannst es ja mal hier und da versuchen. Aber, das Beste ist es, wenn du die Maus einfach mal reinigst. Ich weise aber ausdrücklich darauf hin, die Fragen waren prima formuliert, es lag an den vermeintlichen Experten…. Na ja, es kostet halt nix, da darf man sich nicht beschweren.

Genug geredet, die Lösung

Für mich hat das ganze so funktioniert. Erstens, Hardwareprobleme ausschliessen. Dazu habe ich die Maus aus gestöpselt. Ohne Erfolg. Zweitens habe ich den USB- Port im Rechner von Motherboard getrennt, ebenfalls kein Erfolg. Den Mauszeiger hat das nicht beruhigt. Meine Vermutung nun: Ein Treiber Konflikt? Mein Problem: Wo kann ich die installierten Treiber finden und den eventuell verdächtigen ausschalten?

Treiber unter Linux Mint finden, konfigurieren und, wenn nix hilft, deaktivieren

Treiber der EIngabegeräte anzeigen lassen. Das Tool xinput in der Shell:

kuepele@kuepele-HP-Compaq-8200-Elite-CMT-PC:~$ xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ Logitech Wireless Mouse id=8 [slave pointer (2)]
⎜ ↳ ImPS/2 Generic Wheel Mouse id=10 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Power Button id=7 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=9 [slave keyboard (3)]
↳ HP WMI hotkeys id=11 [slave keyboard (3)]

Nehmen wir nun an, die Logitech Wireless Mouse id=8 [slave pointer (2)] ist der Verdächtige, dann merken wir uns die ID und deaktivieren den Treiber so:

xinput disable 8

Prima Sache, in meinem Fall hat das tatsächlich die Lösung gebracht. Ausserdem habe ich ein neues, nützliches Kommando gelernt, mit dem man weit mehr machen kann, als überflüssige Treiber auszuschalten.

Credits und ein dickes Dankeschön

Hier kann man alles noch einmal etwas detaillierter nachlesen: Linux Mint Forums