การทำระบบ Login ด้วย SharedPreferences
บทความสอนเขียนแอพ Android บทความนี้ ขอนำเสนอ การทำระบบ Login ด้วย SharedPreferences ซึ่งบทความนี้เป็นบทความ 1 ใน 4 บทความในซีรีย์ การสร้างระบบ Login บน Android ที่ประกอบไปด้วย
- การทำระบบ Login ด้วย SharedPreferences
- การทำระบบ Login ด้วย SQLite
- การทำระบบ Login ผ่าน Web Service ด้วย Parse.com
- การทำระบบ Login ด้วย Facebook ร่วมกับ Parse.com
ก่อนอื่นเลย สำหรับบทความนี้คือ การ Login และเก็บข้อมูลด้วย SharedPreferences ฉะนั้นหากใครยังไม่รู้จักว่า SharedPreferences คืออะไร ให้อ่านบทความนี้ประกอบครับ เซฟข้อมูลด้วย SharedPreferences
Overview
มาดูภาพรวมของระบบนี้กันก่อนนะครับ ระบบก็ไม่มีอะไรซับซ้อนมาก ดูภาพด้านล่างประกอบได้เลย
จะมีด้วยกันอยู่ 3 หน้า คือ หน้าล็อคอิน หน้าสมัครสมาชิก และหน้าหลัก เมื่อล็อคอินได้เรียบร้อยแล้ว
สำหรับภาพรวมของระบบ จะใช้อันนี้กับบทความอื่นด้วยนะครับ การทำงานเหมือนกันหมด ต่างกันแค่วิธีการเก็บข้อมูลเท่านั้นเอง
เริ่มต้นสร้างโปรเจ็ค
ให้ทำการสร้างโปรเจ็คขึ้นมาเลยครับ ในบทความผมใช้ Android Studio หากใครใช้ Eclipse ก็ปรับตัวเรื่องหน้าตาอาจจะไม่เหมือนในบทความเอานะครับ แต่ยังไงพวกโค๊ดและ xml ก็ต้องมีเหมือนกันอยู่แล้ว ฉะนั้น IDE มันคงไม่ใช่ปัญหาซักเท่าไหร่
ใครสร้างไม่เป็นแนะนำให้อ่านนี้ก่อนละกัน หรือเสิชหาในเนตครับ มีเพียบ อย่าบอกนะว่าหาไม่เจอ :D
- สร้างโปรเจ็คด้วย Eclipse/ADT Bundle Credit: Thaicreate.com
- สร้างโปรเจ็คด้วย Android Studio
- สร้างโปรเจ็คบน Android Studio สำหรับเวอร์ชัน 0.6.1 ขึ้นไป
มาเริ่มกันเลยดีกว่า
สร้างหน้า Login
ทำการสร้างหน้าล็อคอินขึ้นมาก่อนอันดับแรก เริ่มต้นสร้างไฟล์เลเอาท์ก่อน ตั้งชื่อว่า activity_login.xml
เซฟไว้ใน res/layout/
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#ff3eaff9"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="280dp"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center"
android:layout_centerInParent="true"
android:background="#ffffffff">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_title"
android:textSize="24sp"
android:textColor="#ff3eaff9"
android:layout_marginBottom="16dp"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:id="@+id/username"
android:hint="@string/username_title"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:id="@+id/password"
android:inputType="textPassword"
android:hint="@string/password_title"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button_login"
android:background="#ff3eaff9"
android:textColor="#ffffff"
android:text="@string/button_login"
android:textSize="18sp"
android:layout_marginTop="16dp"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/register"
android:text="@string/register"
android:textColor="#ffffff"
android:layout_centerInParent="true"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="16dp"/>
</RelativeLayout>
หน้าตาเลเอาท์เราจะได้ประมาณนี้
ส่วนไฟล์ res/values/strings.xml
ที่ใช้ในเลเอาท์นี้ ก็มีประมาณนี้
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">DevahoyLogin</string>
<string name="login_title">Devahoy</string>
<string name="username_title">Username</string>
<string name="password_title">Password</string>
<string name="button_login">Login</string>
<string name="register">New User? Register here</string>
<string name="login_error_message">Username or Password is incorrect.</string>
</resources>
ต่อมาสร้างคลาส LoginActivity.java
ขึ้นมา จะได้โค๊ดแบบนี้
package com.devahoy.sample.login;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class LoginActivity extends ActionBarActivity {
private Button mLogin;
private EditText mUsername;
private EditText mPassword;
private TextView mRegister;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mContext = this;
mLogin = (Button) findViewById(R.id.button_login);
mUsername = (EditText) findViewById(R.id.username);
mPassword = (EditText) findViewById(R.id.password);
mRegister = (TextView) findViewById(R.id.register);
mLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
checkLogin();
}
});
mRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mContext, RegisterActivity.class);
startActivity(intent);
}
});
}
private void checkLogin() {
}
}
จากโค๊ดด้านบน ไม่มีอะไรมาก ทำการเชื่อม View ในคลาสกับ View ที่สร้างไว้ใน xml จากนั้นปุ่ม Button สำหรับ Login ก็ทำการ setListener ให้มัน จากนั้นก็เรียกเมธอด checkLogin()
ซึ่งเมธอดนี้เดี๋ยวจะเขียนทีหลังนะครับ ตอนนี้ประกาศเป็น no body ไปก่อน
ส่วน TextView สำหรับ Register ก็ setListener เช่นกัน ส่วนนี้เมื่อกดก็จะไปเปิด Activity ใหม่ ซึ่งเป็นหน้า RegisterActivity
เราจะสร้างคลาสนี้ในสเตปถัดไป ตอนนี้ปล่อย error ไว้ก่อน
สร้าง UserManager
่ต่อมาในส่วนของ Logic สำหรับ Login เอาไว้เช็ค username และ password รวมถึงเซฟลง SharedPreferences เราจะทำในส่วนของคลาสนี้ ฉะนั้นไฟล์ในคลาสนี้จะได้ประมาณนี้ ทำการสร้างคลาส UserManager.java
ขึ้นมาแบบนี้
package com.devahoy.sample.login;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
public class UserManager {
/**
* KEY_PREFS ไว้สำหรับเป็น key ของ SharedPreferences
*/
private final String KEY_PREFS = "prefs_user";
/**
* ชื่อ key ที่ไว้เซฟ username ใน SharedPreferences
*/
private final String KEY_USERNAME = "username";
/**
* ชื่อ key ที่ไว้เซฟ password ใน SharedPreferences.
*/
private final String KEY_PASSWORD = "password";
private SharedPreferences mPrefs;
private SharedPreferences.Editor mEditor;
/**
* รับค่า Context เพื่อเอาไว้ใช้สำหรับ getSharedPreferences
* @param context
*/
public UserManager(Context context) {
mPrefs = context.getSharedPreferences(KEY_PREFS, Context.MODE_PRIVATE);
mEditor = mPrefs.edit();
}
/**
* ทำการเช็ค Username กับ Password ใน SharedPreferences<br />
* โดยเงื่อนไข EditText ของ Username และ password ต้องไม่เป็นค่าว่าง <br />
* และค่าที่ได้ ต้องตรงกับใน SharedPreferences
* @param username - username จาก EditText ที่ใส่
* @param password - password จาก EditText ที่ใส่
* @return หากใส่ข้อมูล ให้ส่งค่ากลับเป็น true, ถ้าใส่ผิดก็ส่ง false
*/
public boolean checkLoginValidate(String username, String password) {
String realUsername = mPrefs.getString(KEY_USERNAME, "");
String realPassword = mPrefs.getString(KEY_PASSWORD, "");
if ( (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) &&
username.equals(realUsername) &&
password.equals(realPassword)) {
return true;
}
return false;
}
/**
* เมธอดสำหรับลงทะเบียนสมาชิกใหม่ โดยส่งค่า username และ password มา<br />
* จากนั้นจะเซฟลง SharedPreferences
* @param username - username จาก EditText ที่ใส่
* @param password - password จาก EditText ที่ใส่
* @return ส่งค่ากลับไปเป็น false หากลงทะเบียนไม่สำเร็จ <br />
* เป็น true หากลงทะเบียนสำเร็จ
*/
public boolean registerUser(String username, String password) {
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
return false;
}
mEditor.putString(KEY_USERNAME, username);
mEditor.putString(KEY_PASSWORD, password);
return mEditor.commit();
}
}
ในส่วน UserManager ผมได้ทำการคอมเม้น อธิบายรายละเอียดไว้แล้ว ฉะนั้นก็ข้ามกลับไปที่ไฟล์ LoginActivity
แล้วก็มาเขียนโค๊ดตรงเมธอด checkLogin()
ที่ได้สร้างไว้ก่อนหน้านี้กันครับ
ที่ไฟล์ LoginActivity.java
ทำการสร้าง new instance ของ UserManager ขึ้นมา จากนั้น ก็เปลี่ยนเมธอด checkLogin()
เป็นแบบนี้
public class LoginActivity extends ActionBarActivity {
private UserManager mManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
mManager = new UserManager(this);
// ...
}
private void checkLogin() {
String username = mUsername.getText().toString().trim().toLowerCase();
String password = mPassword.getText().toString().trim();
boolean isSuccess = mManager.checkLoginValidate(username, password);
if (isSuccess) {
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
} else {
String message = getString(R.string.login_error_message);
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
}
}
ด้านบน ที่เมธอด `checkLogin()` เป็นการรับค่าจาก EditText ที่กรอก มาเช็คว่าค่าตรงกับที่เซฟใน SharedPreference หรือไม่ ถ้าตรง ก็ลอคอินสำเร็จ ไปเปิดคลาส `MainActivity.java` (ยังไม่ได้สร้างคลาสนี้) หากพาสเวิร์ดไม่ตรง ก็ขึ้นโชว์ error.
## สร้างหน้า Register
ต่อมาสร้างหน้า Register สำหรับหน้านี้เอาไว้ลงทะเบียน username และ password กรณีที่ยังไม่ได้บันทึกข้อมูลไว้ใน SharedPreference เริ่มแรก ก็สร้างเลเอาท์เลย ชื่อว่า `activity_register.xml` เซฟไว้ใน `res/layout/`
```xml
จะได้หน้าเลเอาท์พื้นๆ แบบนี้
อัพเดทไฟล์ res/values/strings.xml
<string name="password_confirm_title">Confirm Password</string>
<string name="register_error_message">Register failed, Try again!</string>
ต่อมาสร้างคลาส RegisterActivity.java
และเพิ่มโค๊ดด้านล่างนี้ลงไป
package com.devahoy.sample.login;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class RegisterActivity extends ActionBarActivity {
private EditText mUsername;
private EditText mPassword;
private EditText mConfirmPassword;
private Button mRegister;
private Context mContext;
private UserManager mManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
mManager = new UserManager(this);
mContext = this;
mUsername = (EditText) findViewById(R.id.username);
mPassword = (EditText) findViewById(R.id.password);
mConfirmPassword = (EditText) findViewById(R.id.confirm_password);
mRegister = (Button) findViewById(R.id.button_register);
mRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String username = mUsername.getText().toString().trim().toLowerCase();
String password = mPassword.getText().toString();
String confirmPassword = mConfirmPassword.getText().toString();
if (password.equals(confirmPassword)) {
boolean isSuccess = mManager.registerUser(username, password);
if (isSuccess) {
String message = getString(R.string.register_success);
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
finish();
} else {
String message = getString(R.string.register_error_message);
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
} else {
String message = getString(R.string.register_password_error);
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
}
});
}
}
จากโค๊ดด้านบน เมื่อกดปุ่ม Register ก็จะทำการเช็ค password 2 ช่อง ว่าตรงกันหรือไม่ ถ้าตรง ก็จะทำการบันทึกข้อมูลลง SharedPreferences หากไม่สามารถบันทึกข้อมูลได้ ก็จะโชว์ error
ไม่ได้มีการเช็ค validate อะไรทั้งสิ้นนะครับ เช็คแค่ไม่ใช่ค่าว่างเฉยๆ จะใส่พาสเวิร์ดกี่ตัวก็ได้
อัพเดทไฟล์ strings.xml
ใหม่ โดยเพิ่ม String เป็นแบบนี้
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">DevahoyLogin</string>
<string name="login_title">Devahoy</string>
<string name="username_title">Username</string>
<string name="password_title">Password</string>
<string name="password_confirm_title">Confirm Password</string>
<string name="button_login">Login</string>
<string name="button_register">Register</string>
<string name="register">New User? Register here</string>
<string name="login_error_message">Username or Password is incorrect.</string>
<string name="register_error_message">Register failed, Try again!</string>
<string name="register_password_error">Password does not match</string>
<string name="register_success">Register Successful</string>
<string name="welcome_message">Welcome to Devahoy</string>
</resources>
สร้างหน้า Main
ต่อมาหน้า Main เอาไว้เวลาทำการล็อคอินถูกต้อง ก็จะเข้ามาหน้านี้ แสดงแค่โลโก้และข้อความเท่านั้นครับ เลเอาท์และไฟล์ MainActivity.java
ก็ธรรมดา แบบนี้
ไฟล์ activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:src="@drawable/ic_launcher"
android:id="@+id/logo"
android:layout_gravity="center"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_message"
android:id="@+id/welcome"
android:textSize="32sp"
android:layout_gravity="center"/>
</LinearLayout>
หน้าตาก็บ้านๆ แบบนี้ ไ่ม่มีอะไรมาก แค่โชว์โลโก้เว็บกับข้อความซะหน่อย
ไฟล์ MainActivity.java
package com.devahoy.sample.login;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
สุดท้ายไฟล์ AndroidManifest.xml
อย่าลืมทำการประกาศ Activity ทั้งหมดที่เราใช้ด้วย จะได้ดังนี้
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.devahoy.sample.login" >
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".LoginActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RegisterActivity"
android:label="@string/app_name"/>
<activity android:name=".MainActivity"
android:label="@string/app_name" />
</application>
</manifest>
สรุป
สำหรับวิธี Login ด้วยการเก็บข้อมูลแบบ SharedPreferences แบบตัวอย่างข้างต้นนั้นมีข้อดีคือ ความสะดวก รวดเร็ว แต่มีข้อเสียคือ เก็บ password เป็น plain text ใน xml ไฟล์ และข้อเสียอีกข้อคือไม่สามารถ ค้นหา username และ password ได้ ทำได้เพียงแค่หาข้อมูลโดยระบุ key เท่านั้น และที่สำคัญ สามารถลงทะเบียนได้แค่ไอดีเดียว เพราะเมื่อมีการลงทะเบียน user ใหม่ มันก็จะไปเซฟทับกับ key ใน SharedPreferences เดิม :D
สำหรับตัวโปรเจ็ค เอาไปลอง import ได้ครับ (เป็นโปรเจ็ค gradle นะครับ)
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit