Android, Soft Keyboard

Wenn eine App/ Activity startet und das Layout ein oder mehrere Text- Eingabefelder hat, dann wird sofort das Soft Keyboard erscheinen. Um das zu verhindern, geht man wie hier beschrieben vor

Im Layout der Main Activity fügt man diese Zeilen ein:

android:focusable="true"
android:focusableInTouchMode="true"

Layout per Code erzeugen

Layouts sind in aller Regel in XML– Dateien vordefiniert. Nun kann es vorkommen, dass man ein Layout dynamisch zur Laufzeit neu erzeugen und anzeigen möchte oder, man möchte ein bestehendes Layout aus einer XML– Datei zur Laufzeit anzeigen lassen.

Ein Layout zur Laufzeit neu erzeugen und in ein bestehendes Layout einfügen.

Es gibt keine XML- Datei dazu. Alle View’s werden programmatisch erzeugt. Der folgende source code zeigt wie das geht. Es werden zwei Eltern Layouts erzeugt. Für jedes Eltern Layout wird ein Kind erzeugt und jeweils hinzugefügt.

package berthold.layouttest;
/**
 * Shows some advanced techniques of layout creation.
 */

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

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

        // Get an existing layout which will be inflated by main layout and we want to add
        // our views to.
        LinearLayout layoutToAddOurLayoutTo=findViewById(R.id.linear_layout_to_add_our_layout_to);

        /////////////////////////
        // PARENT 1
        // Creates a new linear layout.
        // When initiating this object not in ManActivity use: Context context of MainActivity
        LinearLayout parent_1 = new LinearLayout(this);
        parent_1.setBackgroundColor(Color.RED);
        parent_1.setOrientation(LinearLayout.VERTICAL);
        LinearLayout.LayoutParams paramsForParent = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        parent_1.setLayoutParams(paramsForParent);

        // CHILD OF PARENT 1
        // Creates TextViews we want to add to our parent- layout
        TextView childOfParent_1=new TextView(this);
        childOfParent_1.setText("Child of Parent 1");
        childOfParent_1.setBackgroundColor(Color.GREEN);
        LinearLayout.LayoutParams paramsForChildOfPranet_1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        childOfParent_1.setLayoutParams(paramsForChildOfPranet_1);

        // Add child to parent 1
        parent_1.addView(childOfParent_1);
        layoutToAddOurLayoutTo.addView(parent_1);


        /////////////////////////
        // PARENT 2
        // This is added to MainActivity's existing layout, below PARENT 1
        LinearLayout parent_2 = new LinearLayout(this);
        parent_2.setBackgroundColor(Color.BLUE);
        parent_2.setOrientation(LinearLayout.VERTICAL);
        LinearLayout.LayoutParams paramsForParent_2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        parent_2.setLayoutParams(paramsForParent_2);

        // CHILD OF PARENT 2
        // Creates TextViews we want to add to our parent- layout
        TextView childOfParent_2=new TextView(this);
        childOfParent_2.setText("Child of Parent 2");
        childOfParent_2.setBackgroundColor(Color.GREEN);
        LinearLayout.LayoutParams paramsForChildOfPrant_2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        childOfParent_2.setLayoutParams(paramsForChildOfPrant_2);

        // Add child to parent 2
        parent_2.addView(childOfParent_2);
        layoutToAddOurLayoutTo.addView(parent_2);



    }
}

Beide Eltern werden in das vordefinierte activity_main.xml – Layout an der markierten Stelle eingefügt:

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

    <LinearLayout
        android:id="@+id/linear_layout_to_add_our_layout_to"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Wichtig ist es hier, dass für alle programmatisch erzeugten View’s die Layout Parameter über die Methode setLayoutParams über einer Instanz der Klasse LinearLayout.LayoutParams entsprechend gesetzt werden.

Das jeweilige Elternteil kennt das View dann, weil es dem ja mittels der Methode add diesem direkt hinzugefügt wurde. Deswegen können die Argumente, die mit setLayoutParams vorher gesetzt wurden auch wie erwartet angewandt werden und so, wie in diesem Code- Beispiel gezeigt, dann auch die Breite mit MATCH_PARENT richtig anwenden.

Ein bestehendes Layout zur Laufzeit zur Anzeige bringen

Wenn man ein Layout in einer XML- Datei bereits vordefiniert hat, dann kann man daraus mit der Methode inflate einer Instanz der Klasse LayoutInflater ein View zeichnen und dann, wie oben gezeigt, in ein bestehendes Layout einfügen und damit zur Anzeige bringen.

Das oben gezeigte Beispiel wurde wie folgt geändert: Im zweiten Eltern- Layout wird das Kind aus einer XML– Datei erzeugt und dann das bestehende Layout main_activity.xml unmittelbar eingefügt und angezeigt:

 // CHILD OF PARENT 2
        // Get child's layout from XML, inflate it and insert it into Parent 2
        int idOfChildOfParent_2=R.layout.child_of_parent_2;

        // If you do this not in MainActivity, then use: LayoutInflater.from(context)
        // Where context is MainActivity's context.
        LayoutInflater inflaterChildOfParent2=getLayoutInflater();
        inflaterChildOfParent2.inflate(idOfChildOfParent_2,parent_2,true);

Die letzte Zeile im Code fügt das Layout unmittelbar in sein Elternteil ein und zeichnet es. Dafür verantwortlich ist das Argument true. Wenn man hier false übergibt, dann teilt man dem Inflater mit, dass man später selbst dafür sorgt.

Alternativ hätte man das auch so schreiben können:

View viewOfChildOfParent_2= inflaterChildOfParent2.inflate(idOfChildOfParent_2,null);
parent_2.addView(viewOfChildOfParent_2);

Das Problem dabei ist dass beim zeichnen des Layouts dem Inflater kein Elternteil übergeben wurde und alle gesetzte Parameter die sich darauf beziehen folglich nicht richtig angewandt werden können.

Das hier gezeigte Beispiel könnte nan nun leicht erweitern und zum Beispiel dazu nutzen um eine Liste oder eine Tabelle dynamisch zu erzeugen. Dabei wird man aber auf weitere Herausforderungen stoßen. Zum Beispiel, wenn man eine Tabelle zeichnen möchte: Jedes Elternteil stellt eine Zeile der Tabelle dar. Jede Zelle ist ein Kind der jeweiligen Zeile. Die Breite der Zellen nun hängt ab vom Inhalt und kann variieren. Das ist immer dann der Fall wenn keine keine feste Größe für die Breite einstellt wurde, sondern, zum Beispiel, das Argument WRAP_ CONTENT in der XML- Datei gesetzt ist. Das hat zur Folge das die Zellen in der Tabelle nicht direkt über- oder untereinander stehen.

Offene Frage, zum weiter forschen: Wie ist es nun möglich die Breiter der Kinder einen Elternteils jeweils an jene Breite der Kinder eines anderen Elternteils anzupassen?

Das mit „C2“ bezeichnete Kind von Parent 2 (Parent 2 ist blau markiert) soll genauso breit dargestellt werden wie das als „Child of Parent 1“ bezeichnete TextView das ein Kind von Parent 1 ist (Parent 1 ist rot markiert).