Smarthome App #2: Login-Bildschirm


27.09.2016  |  Tutorial, Smarthome App

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:
So sollte der Login-Bildschirm jetzt aussehen.

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&auml;ndigkeit pr&uuml;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 &Auml;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&uuml;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&ouml;glich! Bitte &uuml;berpr&uuml;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 &uuml;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".
So legst du eine neue Klasse an.
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.

Ü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!