
Smarthome App #2: Login-Bildschirm
Im letzten Teil des Tutorials habe ich dir erklärt, wie du in Android Studio ein Projekt erstellen und die ersten Aufgaben erledigen kannst. Im heutigen Tutorial wird der Login-Bildschirm der App designed und implementiert. Dazu startest du, falls nicht bereits geschehen, Android Studio und öffnest das Projekt über „File -> Open…“ bzw. „File -> Open Recent“.
Dieses Tutorial gibt es auch als Video!
Den Login-Bildschirm designen
Als erstes wird das Layout des Login-Bildschirms definiert. Dazu wird die Datei mobile -> res -> layout -> activity_login.xml geöffnet und der Code mit folgendem Code ersetzt (wenn die Datei geöffnet ist, kannst du links unten zwischen Design- und Textansicht umschalten):
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="de.smarthome_blogger.smarthome.LoginActivity" android:background="@color/layoutBackground" android:id="@+id/frame"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_margin="15dp" app:cardElevation="@dimen/cardViewElevation" app:cardCornerRadius="@dimen/cardViewCornerRadius" app:cardUseCompatPadding="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="5dp"> <ImageView android:layout_width="match_parent" android:layout_height="120dp" android:id="@+id/app_logo" android:layout_margin="10dp" android:src="@mipmap/ic_launcher"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPersonName" android:hint="Nutzername" android:layout_marginBottom="5dp" android:id="@+id/username"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:hint="Passwort" android:layout_marginBottom="5dp" android:id="@+id/password"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textUri" android:hint="Server-IP" android:layout_marginBottom="5dp" android:id="@+id/server_ip"/> <CheckBox android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Login-Daten speichern" android:id="@+id/save_login" android:gravity="center_horizontal|center_vertical" android:layout_marginBottom="5dp" android:checked="false"/> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Widget.AppCompat.Button.Colored" android:background="@color/colorPrimary" android:textColor="@color/textColorLight" android:text="Login" android:layout_alignParentRight="true" android:id="@+id/login_button"/> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminate="true" android:layout_centerInParent="true" android:visibility="gone" android:id="@+id/loading_animation"/> </RelativeLayout> </LinearLayout> </android.support.v7.widget.CardView> </RelativeLayout>
Du kannst dir ansehen, wie das Design momentan aussieht, indem du links unten den Design-Tab anklickst. Das Design sollte nun in etwa so aussehen, wie im folgenden Bild:
LoginActivity implementieren
Nachdem das Design des Login-Bildschirms definiert worden ist, wird als nächstes die Funktionalität implementiert. Öffne dazu die Datei mobile -> java -> de.smarthome_blogger.smarthome -> LoginActivity.java und füge über der Methode „protected void onCreate(Bundle savedInstanceState)“ folgende Variablen ein:
private EditText usernameField, passwordField, serverIpField; private Button loginButton; private CheckBox saveLogin; private View loadingAnimation;
Diese Variablen sind die Textfelder, Buttons und die Checkbox des Login-Bildschirms.
In der Methode „protected void onCreate()“ fügst du anschließend unter der Zeile „setContentView(R.layout.activity_login);“ den folgenden Code ein:
//Actionbar verstecken getSupportActionBar().hide(); //Views deklarieren usernameField = (EditText) findViewById(R.id.username); passwordField = (EditText) findViewById(R.id.password); serverIpField = (EditText) findViewById(R.id.server_ip); loadingAnimation = findViewById(R.id.loading_animation); loginButton = (Button) findViewById(R.id.login_button); saveLogin = (CheckBox) findViewById(R.id.save_login); //Dem Button eine Aktion zuweisen loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String username = usernameField.getText().toString(); String password = passwordField.getText().toString(); String serverIp = serverIpField.getText().toString(); //Eingaben auf Vollständigkeit prüfen if(username.equals("")){ fehlermeldung("Bitte gib deinen Nutzernamen ein"); } else if(password.equals("")){ fehlermeldung("Bitte gib dein Passwort ein"); } else if(serverIp.equals("")){ fehlermeldung("Bitte gib die Adresse des Servers ein"); } else{ SaveData.setSaveLoginData(getApplicationContext(), saveLogin.isChecked()); login(username, password, serverIp); } } }); //Haken je nach gespeicherten Daten setzen oder nicht saveLogin.setChecked(SaveData.getSaveLoginData(getApplicationContext())); //Bei jeder Änderung neu speichren, ob Login-Daten gespeichert werden sollen saveLogin.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { SaveData.setSaveLoginData(getApplicationContext(), isChecked); } }); //Wenn Nutzerdaten vorhanden: automatisch einloggen if(SaveData.getSaveLoginData(getApplicationContext())){ String username = SaveData.getUsername(getApplicationContext()); String password = SaveData.getPassword(getApplicationContext()); String serverIp = SaveData.getServerIp(getApplicationContext()); if(username != null && password != null && serverIp != null){ //Textfelder mit gespeicherten Daten füllen usernameField.setText(username); passwordField.setText(password); serverIpField.setText(serverIp); saveLogin.setChecked(true); login(username, password, serverIp); } }
Erklärung des Codes
Als erstes wird die sogenannte Actionbar (die Titelleiste der App) im Login-Bildschirm ausgeblendet. die UI-Komponenten initialisiert und ihnen die entsprechenden Funktionen (OnClickListener, OnCheckedChangeListener) zugewiesen. Als letztes wird mit einer if-Abfrage geprüft, ob Nutzerdaten vorhanden sind und ob beim letzten Login angegeben wurde, dass sich die App automatisch einloggen soll. Ist dies der Fall, versucht die App, sich mit den vorhandenen Nutzerdaten einzuloggen.
Als nächstes fügst du einfach den folgenden Code unter der Methode „onCreate()“ ein:
/** * Versucht den Nutzer einzuloggen * @param username * @param password * @param serverIp */ public void login(final String username, final String password, final String serverIp){ loadingAnimation.setVisibility(View.VISIBLE); Map<String, String> requestData = new HashMap<>(); requestData.put("action", "getrooms"); requestData.put("username", username); requestData.put("password", password); HTTPRequest.sendRequest(getApplicationContext(), requestData, serverIp, new HTTPRequest.HTTPRequestCallback() { @Override public void onRequestResult(String result) { loadingAnimation.setVisibility(View.GONE); if(result.equals("wrongdata")){ fehlermeldung("Anmeldung nicht möglich! Bitte überprüfe deine Eingaben"); } else if(result.equals("unknownuser")){ fehlermeldung("Dieser Nutzer existiert nicht"); } else{ //gegebenenfalls Login-Daten speichern if(saveLogin.isChecked()){ SaveData.setLoginData(getApplicationContext(), username, password); SaveData.setServerIp(getApplicationContext(), serverIp); } Intent intent = new Intent(LoginActivity.this, MainActivity.class); intent.putExtra(MainActivity.EXTRA_ROOMS, result); startActivity(intent); } } @Override public void onError(String msg) { loadingAnimation.setVisibility(View.GONE); fehlermeldung(msg); } }); } /** * Zeigt die übergebene Nachricht an * @param msg */ public void fehlermeldung(String msg){ Snackbar.make(findViewById(R.id.frame), msg, Snackbar.LENGTH_SHORT).show(); }
Erklärung des Codes
Die Methode „login()“ versucht, sich mit den angegebenen Nutzerdaten auf dem Server einzuloggen. Bei Erfolg wird zur Main-Actvity (sozusagen das Hauptmenü der App) gewechselt und die zurückgegebenen Daten des Servers (in diesem Fall eine Liste der vorhandenen Räume, damit die Menüpunkte im Hauptmenü erstellt werden können) an die Main-Activity übergeben. Bei Scheitern der Verbindung wird eine Fehlermeldung ausgegeben.
Die Methode „fehlermeldung()“ gibt lediglich den übergebenen Text aus.
SaveData anlegen & vorläufig implementieren
Nun muss noch die Klasse SaveData angelegt werden. Dazu klickst du mit der rechten Maustaste auf den Ordner „de.smarthome_blogger.smarthome“ oder eine darin enthaltene Klasse und wählst „New -> Java class“. Dort gibst du nun als Namen „SaveData“ ein. Alles andere lässt du unverändert und klickst „Ok“.
Jetzt öffnet sich die Klasse und du kannst sie bearbeiten. Vorerst werden nur Platzhalter-Methoden definiert, die beispielsweise vordefinierte Nutzerdaten zurückgeben. Diese Nutzerdaten solltest du noch entsprechend anpassen, indem du den Rückgabewert der Methode „getServerIp“ auf die IP-Adresse deines Raspberry und den Nutzernamen und das Passwort in den Methoden „getUsername“ und „getPassword“ änderst.
Den folgenden Code fügst du nun in die Klasse SaveData ein (unter der Zeile „public class SaveData{„):
public static String getUsername(Context context){ return "Testnutzer"; } public static String getPassword(Context context){ return "password"; } public static String getServerIp(Context context){ return "192.168.178.xyz"; } public static void setLoginData(Context context, String username, String password){} public static void setServerIp(Context context, String serverIp){} public static void setSaveLoginData(Context context, boolean saveLogin){} public static boolean getSaveLoginData(Context context){ return true; }
Der Login-Bildschirm ist vorerst fertig, das Design wird jedoch eventuell noch etwas überarbeitet. Im nächsten Teil der Tutorials kann dann mit dem Hauptmenü der APP begonnen werden.
Falls du Fragen oder Probleme hast, kannst du mir gerne einen Kommentar hinterlassen.