Smarthome App #3: Hauptmenü


11.10.2016  |  Tutorial, Smarthome App

In den letzten Tutorials der Smarthome App wurde das Projekt angelegt und der Login-Bildschirm implementiert. Im heutigen Tutorial zeige ich dir, wie du das Hauptmenü erstellst, das sich nach dem Einloggen öffnet. Dieses Menü enthält vorerst die angelegten Räume im Smarthome-System, einen Punkt, der eine Übersicht öffnet, in der alle aktuellen Sensorwerte aus allen Räumen angezeigt werden und den Punkt "Einstellungen", unter dem die Server-Informationen abgerufen werden können und Nutzer, Szenen und mehr verwaltet werden kann. Los geht's!

Dieses Tutorial gibt es jetzt auch als Video!

Layout-Dateien bearbeiten

Als erstes wird der Rand um den Inhalt der MainActivity entfernt.
Die Datei content_main.xml liegt im Pfad mobile -> res -> layout.
Dazu wird die Datei mobile -> res -> layout -> content_main.xml geöffnet und folgende vier Zeilen entfernt:
android:paddingBottom="16dp"
android:paddingLeft="64dp"
android:paddingRight="64dp"
android:paddingTop="16dp"
Anschließend wird der sogenannte "FloatingActionButton" aus dem Hauptfenster entfernt, indem du die Datei mobile -> res -> layout -> app_bar_main.xml öffnest und den entsprechenden Code-Block löschst:
<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="16dp"
	android:src="@android:drawable/ic_dialog_email" />
Danach muss auch noch die Referenz auf den eben entfernten FloatingActionButton gelöscht werden.
Die MainActivity liegt unter mobile -> java -> de.smarthome_blogger.smarthome.
Dazu öffnest du die Datei mobile -> java -> de.smarthome_blogger.smarthome -> MainActivity und entfernst in der Methode "public void onCreate()" den folgenden Code-Block, der sich etwa in der Mitte der Methode befindet:
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new OnClickListener(){
	@Override
	void onClick(View view){
		Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
			.setAction("Action", null).show();
	}
});
Da die Menu-Elemente des Hauptmenüs nicht statisch sind, sondern die einzelnen Räume bei jedem Start vom Server geladen werden und das Menü mit diesen Daten erstellt wird, muss die standardmäßig erstellte Menü-Ressourcendatei angepasst werden. Öffne dazu die Datei mobile -> res -> menu -> activity_main_drawer.xml und entferne alle Elemente innerhalb des menu-Tags, sodass lediglich folgender Text übrig bleibt:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

</menu>

Die Klasse "DrawerItem" in MainActivity erstellen

Um mit den Menüpunkten besser arbeiten zu können, und die vom Server geladenen Räume besser verwalten zu können, wird in der MainActivity eine neue Klasse namens "DrawerItem" angelegt. Dazu fügst du einfach am unteren Ende (aber innerhalb des Klassen-Blocks, also über der letzten geschweiften Klammer) der Datei MainActivity.java den folgenden Code ein:
/**
 * Enth&auml;lt die Informationen eines DrawerItems f&uuml;r den NavigationDrawer
 */
class DrawerItem{
    String name, location;
    int icon_id;
    Fragment fragment;

    /**
     * Konstruktor f&uuml;r ein DrawerItem
     * @param menuName
     * @param menuIcon
     * @param location_name
     * @param frag
     */
    public DrawerItem(String menuName, int menuIcon, String location_name, Fragment frag){
        this.name = menuName;
        this.icon_id = menuIcon;
        this.location = location_name;
        this.fragment = frag;
    }

    /**
     * Gibt den Namen des Items zur&uuml;ck
     * @return
     */
    public String getName(){
        return name;
    }

    /**
     * Gibt die Location eines Items zur&uuml;ck
     * @return
     */
    public String getLocation(){
        return location;
    }

    /**
     * Gibt die Icon-ID des Items zur&uuml;ck
     * @return
     */
    public int getIcon(){
        return icon_id;
    }

    /**
     * Gibt das Fragment des Items zur&uuml;ck
     * @return
     */
    public Fragment getFragment(){
        return fragment;
    }

    /**
     * Gibt zur&uuml;ck, ob Fragment eine Instanz von RoomFragment ist
     * @return
     */
    public boolean isRoom(){
        return fragment instanceof RoomFragment;
    }
}

Intent von LoginActivity abfangen

Der Login-Bildschirm aus dem letzten Teil schickt bei erfolgreicher Anmeldung einen sogenannten Intent an die MainActivity. Ein Intent kann verschiedene Daten enthalten, wie in unserem Beispiel einen JSON-String, der alle Räume des Smarthome-Systems enthält. Um mit den Daten aus dem Intent arbeiten zu können, muss dieser zuerst abgefangen werden. Dann können die Daten beispielsweise gespeichert oder verarbeitet werden. Als erstes wird eine Variable deklariert, in der der JSON-String abgespeichert werden soll. Dazu fügst du in der MainActivity über der Methode "public void onCreate()" die folgende Zeile ein:
String roomData;
Anschließend fragst du den Intent ab und speicherst den Wert in der gerade deklarierten Variable ab, indem du folgenden Code-Block am unteren Ende der Methode "public void onCreate()" einfügst:
//Intent abfangen
Bundle extras = getIntent().getExtras();
if(extras != null && extras.containsKey(MainActivity.EXTRA_ROOMS)){
    roomData = extras.getString(MainActivity.EXTRA_ROOMS);
    createRooms(roomData);
}
else{
    //loadRooms();
}

Daten aus Intent verarbeiten

Bevor die Daten den Intents verarbeitet werden, deklarierst du über der Methode "onCreate()" ein paar neue Variablen:
//NavigationDrawer
Menu drawerMenu;
ArrayList<DrawerItem> drawerItemList = new ArrayList<>();
Anschließend initialisierst du in der Methode "onCreate()" die Variable DrawerMenu, indem du die mittlere Zeile in die Methode "onCreate()" einfügst:
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);

//Die n&auml;chste Zeile musst du hinzuf&uuml;gen
drawerMenu = navigationView.getMenu();

navigationView.setNavigationItemSelectedListener(this);
Danach erstellst du in der Klasse MainActivity eine neue Methode namens "createRooms", die aus dem übergebenen String im JSON-Format dem NavigationDrawer die einzelnen Menüpunkte zuweist:
/**
 * Erstellt anhand des &uuml;bergebenen Strings eine Reihe von R&auml;umen und f&uuml;gt sie dem NavigationDrawer hinzu
 * @param roomData
 */
public void createRooms(String roomData){
    try{
        JSONObject jsonObj = null;
        try{
            jsonObj = new JSONObject(roomData);
        }
        catch(JSONException e){
            e.printStackTrace();
        }

        JSONArray rooms = jsonObj.getJSONArray("rooms");

        //Statichen Men&uuml;punkt hinzuf&uuml;gen
        drawerItemList.add(new DrawerItem("&Uuml;bersicht", Icons.getDrawerIcon("overview"), "overview", new OverviewFragment()));

        for(int i = 0; i < rooms.length(); i++){
            JSONObject o = rooms.getJSONObject(i);

            drawerItemList.add(new DrawerItem(o.getString("name"), Icons.getDrawerIcon(o.getString("location")), o.getString("location"), new RoomFragment()));
        }

        //Statischen Men&uuml;punkt hinzuf&uuml;gen
        drawerItemList.add(new DrawerItem("Einstellungen", Icons.getDrawerIcon("settings"), "settings", new SettingsFragment()));
    }
    catch(Exception e){
        fehlermeldung("Fehler beim Laden der R&auml;ume");
    }

    for(int i = 0; i < drawerItemList.size(); i++){
        drawerMenu.add(drawerItemList.get(i).getName());
        drawerMenu.getItem(i).setIcon(drawerItemList.get(i).getIcon());
    }
}

Fehlermeldung in Hauptmenü anzeigen

Damit Fehlermeldungen im Hauptmenü angezeigt werden, legst du nun die Methode "fehlermeldung()" an, die übergebene Meldungen anzeigt:
public void fehlermeldung(String msg){
    Snackbar.make(findViewById(R.id.frame), msg, Snackbar.LENGTH_SHORT).show();
}

Fragments erstellen

Als nächstes müssen noch Fragments angelegt werden, in denen später der Inhalt der einzelnen Menüpunkte angezeigt wird.
Erstelle das Fragment mit einem Rechtsklick auf das Java-Paket oder eine Java-Datei.
Klicke dazu mit rechts auf eine Java-Datei und wähle "New -> Fragment -> Fragment (Blank)". In dem Fenster, dass sich nun öffnet, gibst du dem Fragment den Namen "RoomFragment", entfernst die Haken bei den Punkten "Include fragment factory methods?" und "Include interface callbacks?" und klickst auf "Finish".
Gib einen Namen ein und entferne die Haken bei den genannten Punkten.
Das ganze machst du nun noch einmal, jedoch mit dem Namen "OverviewFragment" und ein weiteres mal mit dem Namen "SettingsFragment". Die Fragments wurden jetzt angelegt, damit beim Auswählen eines Menüpunktes das richtige Fenster angezeigt wird. Genau implementiert werden die Fragments erst in einem der nächsten Tutorials.

Die Klasse "Icons" erstellen

Damit die einzelnen Menüpunkte, Geräte, Sensoren und mehr immer mit dem richtigen Icon angezeigt werden, wird nun die Klasse "Icons" angelegt, deren Methoden anhand eines übergebenen Strings den passenden Icon zurückliefern. Jetzt geben die Methoden jedoch nur einen Platzhalter-Icon zurück. Im weiteren Verlauf der Tutorialreihe werden hier aber Icons zur Verfügung gestellt und die Methoden entsprechend angepasst. Um die Klasse anzulegen, klickst du mit rechts auf eine Java-Datei und wählst dort "New -> Java Class". Im nächsten Fenster gibst du nun bei "Name" "Icons" ein, veränderst ansonsten nichts und klickst auf "OK". In dieser Klasse erstellst du nun die Methode "getDrawerIcon", die anhand des übergebenen Strings den passenden Icon für die Menüpunkte im NavigationDrawer zurückgibt:
/**
 * Gibt anhand des &uuml;bergebenen Key's den passenden Icon zur&uuml;ck
 * @param key
 * @return
 */
public static int getDrawerIcon(String key){
    switch(key){
        default:
            return R.mipmap.ic_launcher;
    }
}

Die Methode "onNavigationItemSelected" anpassen

Da ziemlich am Anfang dieses Tutorials die Datei "activity_main_drawer.xml" verändert wurde und dabei alle vorhandenen Menüpunkte entfernt wurden, muss nun noch die Referenz auf die Menüpunkte entfernt werden. Dazu wird in der MainActivity die Methode "onNavigationItemSelected" ein wenig angepasst, indem die folgenden Zeilen aus der Methode entfernt werden:
if (post_id == R.id.nav_camera){
	// Handle the camera action
} else if (post_id == R.id.nav_gallery){

} else if (post_id == R.id.nav_slideshow){

} else if (post_id == R.id.nav_manage){

} else if (post_id == R.id.nav_share){

} else if (post_id == R.id.nav_send){

}
Das Hauptmenü ist nun angelegt und zeigt bereits die angelegten Räume im Navigation-Drawer an. Im nächsten Teil werden Klicks auf Menüpunkte behandelt, der Header des NavigationDrawers bearbeitet und mehr. Falls du Fragen oder Probleme hast, kannst du mir gerne einen Kommentar hinterlassen.

Über den Autor


Sascha Huber

Hallo, ich bin Sascha, der Gründer von Smarthome Blogger.

Mit einer Leidenschaft für Technologie und einem Hintergrund als Software Engineer habe ich 2016 Smarthome Blogger gegründet. Mein Ziel war es schon immer, innovative Lösungen zu entdecken, die unser Leben einfacher und intelligenter gestalten können. In meinem beruflichen Leben arbeite ich täglich mit Software und Technik, aber auch in meiner Freizeit bin ich stets auf der Suche nach neuen technischen Spielereien und Möglichkeiten, mein Zuhause zu automatisieren und zu verbessern.

Auf Smarthome Blogger teile ich mein Wissen, meine Erfahrungen und meine Begeisterung für alles rund um das Thema Smarthome.



Dieser Beitrag hat dir gefallen?

Dann abonniere doch unseren Newsletter!