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. 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. 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();
	}
});
            
        

NavigationDrawer anpassen

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ält die Informationen eines DrawerItems für den NavigationDrawer
 */
class DrawerItem{
    String name, location;
    int icon_id;
    Fragment fragment;

    /**
     * Konstruktor fü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ück
     * @return
     */
    public String getName(){
        return name;
    }

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

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

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

    /**
     * Gibt zurü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ächste Zeile musst du hinzufü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 übergebenen Strings eine Reihe von Räumen und fü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üpunkt hinzufügen
        drawerItemList.add(new DrawerItem("Ü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üpunkt hinzufügen
        drawerItemList.add(new DrawerItem("Einstellungen", Icons.getDrawerIcon("settings"), "settings", new SettingsFragment()));
    }
    catch(Exception e){
        fehlermeldung("Fehler beim Laden der Rä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. 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. 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 übergebenen Key's den passenden Icon zurü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.