Shared Preference
Kemampuan Akhir Yang Direncanakan
Setelah melakukan praktikum ini mahasiswa mampu:
- Membuat aplikasi yang menggunakan shared preference untuk menyimpan data yang kecil.
- Mengubah perilaku aplikasi sesuai konfigurasi yang dipilih oleh pengguna.
Praktikum 1
Pada praktikum ini, mahasiswa akan membuat aplikasi yang memanfaatkan SharedPreference untuk menyimpan data (dalam hal praktikum ini session). Aplikasi yang dibuat berupa aplikasi yang menampilkan data catatan (notes). Aplikasi ini menampilkan tampilan login jika user belum terautentikasi. Untuk menyimpan data session autentikasi dapat disimpan pada SharedPreference.
Langkah Praktikum
- Bukalah aplikasi Android Studio yang sudah terinstall pada komputer/laptop anda.
Buatlah sebuah project baru dengan nama "Notes App", untuk Company domain sesuaikan dengan identitas anda!
Pada pilihan
Activity
pilihlahBasic Activity
- Kemudian silahkan ikuti langkah-langkah wizard sampai selesai.
- Sebagai langkah awal, buatlah pemodelan data yang dibutuhkan oleh aplikasi.
- Buatlah sebuah class
User
yang digunakan untuk menyimpan informasi pengguna yang akan masuk ke dalam aplikasi. Keterangan atribut classUser
dapat anda lihat pada tabel berikut.
Atribut | Tipe Data |
---|---|
username |
String |
password |
String |
Tips: Gunakan fitur generate code pada Android Studio untuk menyisipkan Constructor serta Setter dan Getter.
- Buatlah sebuah class
Note
yang digunakan untuk menyimpan informasi data catatan. Detail atribut-atribut dapat anda lihat pada tabel berikut.
Atribut | Tipe Data |
---|---|
title |
String |
date |
Date |
content |
String |
Catatan:
Tipe data Date
di-import dari package java.util.Date
.
Untuk memudahkan pengorganisasian project, tambahkan package
models
. Letakkan classUser
danNote
ke dalam package ini.Pada class
Note
sisipkan kode berikut yang digunakan untuk mengatur tampilan bagaimana tanggal akan ditampilkan.public String getFormattedDate() { // Tampilan tanggal yang dicetak menjadi: 01 Mar 2019 DateFormat formatter = new SimpleDateFormat("dd MMM yyyy"); return formatter.format(date); }
Tambahkan class
Data
yang digunakan untuk menyimpan data-data dummy aplikasi.Tambahkan kode berikut pada class
Data
private static List<User> users; private static List<Note> notes;
Isi data dummy tersebut dengan kode berikut.
static { users = new ArrayList<>(); users.add(new User("adi", "rahasia")); users.add(new User("beni", "rahasia")); users.add(new User("cindy", "rahasia")); notes = new ArrayList<>(); notes.add(new Note("Note 1", new Date(), "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua")); notes.add(new Note("Note 2", new Date(), "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat")); notes.add(new Note("Note 3", new Date(), "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur")); notes.add(new Note("Note 4", new Date(), "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")); }
Anda dapat menggunakan data dummy sesuai keinginan!
Untuk memudahkan operasi-operasi yang terkait dengan
SharedPreference
dibutuhkan classConstant
danSettings
. classConstant
berisi daftar konstanta-konstanta sedangkanSettings
membungkus operasi baca tulis keSharedPreference
.Tambahkan kode berikut pada class
Constant
public class Constant { public static final String LAYOUT_MODE = "layout_mode"; public static final String SESSION = "session"; public static final int LAYOUT_MODE_LIST = 0; public static final int LAYOUT_MODE_GRID = 1; }
Pada class
Settings
tambahkan atributpreferences
dengan tipeSharedPreference
Tambahkan constructor pada class
Settings
public Settings(Context context) { this.preferences = PreferenceManager.getDefaultSharedPreferences(context); }
Tambahkan method terkait dengan operasi pada data session user.
public String getUser() { return preferences.getString(Constant.SESSION, null); } public void setUser(String user) { preferences.edit() .putString(Constant.SESSION, user) .apply(); } public void removeUser() { preferences.edit() .remove(Constant.SESSION) .apply(); }
Tambahkan method terkait dengan operasi pada data layout
public int getLayoutMode() { return preferences.getInt(Constant.LAYOUT_MODE, Constant.LAYOUT_MODE_LIST); } public void setLayoutMode(int layout) { preferences.edit() .putInt(Constant.LAYOUT_MODE, layout) .apply(); }
Buatlah class
Session
yang bertanggung jawab mengurusi manajemen session user.public class Session { private Settings settings; private String user; public Session(Settings settings) { this.settings = settings; user = settings.getUser(); } public User doLogin(String username, String password) { User foundUser = null; for (User user : Data.getUsers()) { if (username.equals(user.getUsername()) && password.equals(user.getPassword())) { foundUser = user; break; } } return foundUser; } public void doLogout() { settings.removeUser(); this.user = null; } public boolean isLogin() { return user != null; } public String getUser() { return user; } public void setUser(String user) { settings.setUser(user); this.user = user; } }
Pada
MainActivity
buang kode yang tidak dibutuhkan.// Hapus bagian ini FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }
Pada
activity_main
modifikasi sesuai kebutuhan. Buang bagian FloatingActionButton. Pada bagianinclude
ganti denganFrameLayout
yang digunakan untuk meletakkanFragment
.<FrameLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
Pada percobaan ini, untuk interaksi UI akan menggunakan
Fragment
. Buatlah packagefragments
yang digunakan untuk mengorganisasiFragment
.Tambahkan
LoginFragment
pada packagefragments
.LoginFragment
menampilkan tampilan login serta operasi autentikasi. Gunakan templateFragment (Blank)
. Sesuaikan konfigurasi dengan gambar berikut.Pada resource layout
fragment_login
, salin kode tampilan berikut.<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="56dp" android:paddingLeft="24dp" android:paddingRight="24dp"> <!-- Username Label --> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginBottom="8dp"> <EditText android:id="@+id/text_username" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Username" /> </android.support.design.widget.TextInputLayout> <!-- Password Label --> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginBottom="8dp"> <EditText android:id="@+id/text_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:hint="Password"/> </android.support.design.widget.TextInputLayout> <android.support.v7.widget.AppCompatButton android:id="@+id/button_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:layout_marginBottom="24dp" android:padding="12dp" android:text="Login"/> <TextView android:id="@+id/link_signup" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="24dp" android:text="No account yet? Create one" android:gravity="center" android:textSize="16sp"/> </LinearLayout> </ScrollView>
Sesuaikan tampilan login dengan kebutuhan anda, yang terpenting id yang digunakan sesuai.
Tambahkan Fragment
NoteFragment
pada packagefragments
Bukalah class
MainActivity
kemudian tambahkan atributsettings
dengan tipe dataSettings
dansession
dengan tipe dataSession
.Inisialisasi atribut
settings
dansession
pada methodonCreate
settings = new Settings(this); session = new Session(settings);
Buatlah sebuah method dengan akses private seperti berikut.
private void addFragment() { Fragment fragment = null; if (session.isLogin()) { fragment = new NoteFragment(); } else { fragment = new LoginFragment(); } getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, fragment) .commit(); }
Panggil method
addFragment
di dalam methodonCreate
@Override protected void onCreate(Bundle savedInstanceState) { ... ... addFragment(); }
Coba jalankan dan amati hasilnya melalui Android Studio
Dalam
NoteFragment
digunakanRecyclerView
. Ikuti langkah-langkah pada percobaan sebelumnya, untuk mengatur project yang menggunakanRecyclerView
. Tambahkan dependency recyclerView dan juga cardview.Tambahkan dua file layout dengan nama
item_note_list
danitem_note_grid
kedua layout ini digunakan untuk menyimpan tampilan pada RecyclerView dengan tampilan list dan grid.Sisipkan kode berikut pada
item_note_list
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:cardElevation="3dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="4dp"
android:weightSum="3"
>
<TextView
android:id="@+id/text_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="20sp"
android:textStyle="bold"
tools:hint="Title"
/>
<TextView
android:id="@+id/text_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textEnd"
android:layout_weight="2"
tools:hint="10 Mar 2019"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
- Sisipkan kode berikut pada
item_note_grid
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:cardElevation="3dp"
app:cardUseCompatPadding="true"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="4dp"
android:orientation="vertical"
>
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
tools:hint="Title"
/>
<TextView
android:id="@+id/text_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:hint="Lorem ipsum sit dollor"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
- Tambahkan class
NoteAdapter
pada packageadapters
. Tambahkan baris seperti berikut pada class
NoteAdapter
public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolder> { }
Letakkan pada baris kode yang bergaris bawah merah, kemudian tekan tombol
Alt+Enter
atauOption+Enter
pada OSX.Pada blok class
ViewHolder
tambahkan keywordabstract
serta deklarasikan abstract method sehingga kodenya menjadi seperti berikut.public abstract class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(@NonNull View itemView) { super(itemView); } protected abstract void onBindViewHolder(Note note); }
Sisipkan inner class
ListViewHolder
sertaGridViewHolder
pada classNoteAdapter
.public class ListViewHolder extends ViewHolder { TextView titleText; TextView dateText; public ListViewHolder(@NonNull View itemView) { super(itemView); titleText = itemView.findViewById(R.id.text_title); dateText = itemView.findViewById(R.id.text_date); } @Override protected void onBindViewHolder(Note note) { titleText.setText(note.getTitle()); dateText.setText(note.getFormattedDate()); } }
public class GridViewHolder extends ViewHolder { TextView titleText; TextView contentText; public GridViewHolder(@NonNull View itemView) { super(itemView); titleText = itemView.findViewById(R.id.text_title); contentText = itemView.findViewById(R.id.text_content); } @Override protected void onBindViewHolder(Note note) { titleText.setText(note.getTitle()); contentText.setText(note.getContent()); } }
Tambahkan atribut-atribut berikut pada class
NoteAdapter
private Context context; private List<Note> notes; private int layout;
Dengan menggunakan fitur generate code, tambahkan constructor seperti berikut.
public NoteAdapter(Context context, List<Note> notes) { this.context = context; this.notes = notes; }
Tambahkan setter untuk atribut
layout
public void setLayout(int layout) { this.layout = layout; }
Override juga method
getItemViewType
yang digunakan untuk menentukan layout mana yang akan digunakan (List atau Grid).@Override public int getItemViewType(int position) { return layout; }
Untuk masing-masing method yang wajib di-override sisipkan kode berikut.
@NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { switch (getItemViewType(i)) { case Constant.LAYOUT_MODE_GRID: View gridView = LayoutInflater.from(context) .inflate(R.layout.item_note_grid, viewGroup, false); return new GridViewHolder(gridView); default: View listView = LayoutInflater.from(context) .inflate(R.layout.item_note_list, viewGroup, false); return new ListViewHolder(listView); } }
@Override public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { Note note = notes.get(i); viewHolder.onBindViewHolder(note); }
@Override public int getItemCount() { return (notes != null) ? notes.size() : 0; }
Modifikasi layout pada
fragment_note
menjadi kode berikut.<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context=".fragments.NoteFragment"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_notes" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
Pada Fragment
NoteFragment
mempunyai menu yang berbeda, tambahkan layout menu dengan namamenu_note
pada folderres/menu
Sisipkan kode berikut sebagai isi layout menu.
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context="com.dhanifudin.todoapp.MainActivity"> <item android:id="@+id/action_show_list" android:orderInCategory="97" android:title="List" app:showAsAction="never" /> <item android:id="@+id/action_show_grid" android:orderInCategory="98" android:title="Grid" app:showAsAction="never" /> <item android:id="@+id/action_logout" android:orderInCategory="99" android:title="Logout" app:showAsAction="never" /> </menu>
Pada class
NoteFragment
tambahkan atribut berikut.private RecyclerView recyclerView; private NoteAdapter adapter;
Override method
onCreate
pada classNoteFragment
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); }
Override method
onCreateOptionsMenu
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_note, menu); super.onCreateOptionsMenu(menu, inflater); }
Buat masing-masing method untuk membagi logika menampilkan secara list atau grid.
private void displayAsList() { RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext()); recyclerView.setLayoutManager(layoutManager); adapter.setLayout(Constant.LAYOUT_MODE_LIST); }
private void displayAsGrid() { RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getContext(), 2); recyclerView.setLayoutManager(layoutManager); adapter.setLayout(Constant.LAYOUT_MODE_GRID); }
Override
onOptionsItemSelected
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_show_list: displayAsList(); return true; case R.id.action_show_grid: displayAsGrid(); return true; case R.id.action_logout: return true; } return super.onOptionsItemSelected(item); }
Pada method
onCreateView
modifikasi kode menjadi berikut.View view = inflater.inflate(R.layout.fragment_note, container, false); recyclerView = view.findViewById(R.id.rv_notes); adapter = new NoteAdapter(getContext(), Data.getNotes()); recyclerView.setAdapter(adapter); displayAsList(); return view;
Aplikasi secara tampilan sudah selesai, tinggal menambahkan logic untuk menangani interaksi dari pengguna.
Pada class
LoginFragment
tambahkan interfaceOnLoginFragmentListener
dengan kode berikut.public interface OnLoginFragmentListener { void onLoginClicked(View view, String username, String password); }
Tambahkan atribut
listener
dengan interfaceOnLoginFragmentListener
private OnLoginFragmentListener listener;
Pada method
onCreateView
modifikasi kode menjadi berikut.View view = inflater.inflate(R.layout.fragment_login, container, false); final EditText usernameText = view.findViewById(R.id.text_username); final EditText passwordText = view.findViewById(R.id.text_password); Button loginButton = view.findViewById(R.id.button_login); return view;
Tambahkan event
setOnClickListener
padaloginButton
sehingga menjadi berikut.loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String username = usernameText.getText().toString(); String password = passwordText.getText().toString(); listener.onLoginClicked(view, username, password); } });
Pada class
MainActivity
implementasikan interfaceLoginFragment.OnLoginFragmentListener
Implement method sehingga menjadi berikut.
@Override public void onLoginClicked(View view, String username, String password) { User user = session.doLogin(username, password); String message = "Authentication failed"; if (user != null) { message = "Welcome " + username; session.setUser(username); } Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show(); addFragment(); }
Pada method
addFragment
set listener padaLoginFragment
private void addFragment() { ... if (session.isLogin()) { fragment = new NoteFragment(); } else { fragment = new LoginFragment(); ((LoginFragment) fragment).setListener(this); } ... }
Jalankan aplikasi dan ujilah fungsionalitas tombol login ketika ditekan, untuk data login silahkan buka class
Data
.Jika aplikasi berjalan dengan semestinya, maka akan tampil Fragment
NoteFragment
setelah login berhasil.Tutup aplikasi dan kemudian bukalah kembali, maka aplikasi akan menampilkan langsung Fragment
NoteFragment
.Pada
NoteFragment
dibutuhkan implementasi fungsionalitas untuk logout. Tambahkan interfaceOnNoteFragmentListener
sebagai berikut.public interface OnNoteFragmentListener { void onLogoutClick(); }
Sisipkan atribut
listener
dengan interfaceOnNoteFragmentListener
private OnNoteFragmentListener listener;
Pada class
MainActivity
implementasikan interfaceNoteFragment.OnNoteFragmentListener
Implement method sehingga menjadi berikut.
@Override public void onLogoutClick() { session.doLogout(); addFragment(); }
Register listener
NoteFragment.OnNoteFragmentListener
denganMainActivity
, dengan menggunakan kode berikut pada classMainActivity
methodaddFragment()
private void addFragment() { ... if (session.isLogin()) { fragment = new NoteFragment(); ((NoteFragment) fragment).setListener(this) } else { ... } ... }
Jalankan aplikasi dan ujilah fitur logout pada aplikasi.
Praktikum 2
Pada praktikum ini, mahasiswa akan menambahkan halaman pengaturan dengan
menggunakan PreferenceFragmentCompat
yang memanfaatkan SharedPreference
untuk menyimpan data. Selain menggunakan PreferenceFragmentCompat
dapat juga
memanfaatkan PreferenceActivity
dan PreferenceFragment
, tetapi pendekatan
ini sudah deprecated.
Langkah-langkah Praktikum
Pada project sebelumnya, tambahkan dependency pada file
build.gradle
... implementation 'com.android.support:preference-v7:28.0.0' ...
Sesuaikan versi dengan target SDK yang terinstall.
Tambahkan Android Resource File pada folder
values
dan beri namaarrays
Pada file tersebut, tambahkan dua buah array yang berisi nilai ukuran Text Size
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="text_size"> <item>Small</item> <item>Medium</item> <item>Large</item> </string-array> <string-array name="text_size_value"> <item>14</item> <item>20</item> <item>24</item> </string-array> </resources>
Tambahkan Android Resource Directory pada folder
res
dan beri nama denganxml
. Buatlah Android Resource File dalam folder tersebut dan beri nama denganpreferences
Sisipkan baris kode berikut.
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <ListPreference android:key="pref_text_size" android:title="Text Size" android:summary="Text Size" android:defaultValue="14sp" android:entries="@array/text_size" android:entryValues="@array/text_size_value" /> </PreferenceScreen>
Kemudian tambahkan class java
SettingsFragment
pada packagefragments
. Sesuaikan nilainya dengan gambar berikut.Override method
onCreatePreferences
, kemudian sisipkan kode berikut.addPreferencesFromResource(R.xml.preferences);
Deklarasikan konstanta baru pada class
Constant
public static final String PREF_TEXT_SIZE = "pref_text_size";
Untuk memudahkan manipulasi, tambahkan method pada class
Settings
untuk mengakses nilai preferences.public float getTextSize() { String textSize = preferences.getString(Constant.PREF_TEXT_SIZE, "20"); return Float.parseFloat(textSize); }
Bukalah class
NoteAdapter
, kemudian deklarasikan atributsettings
dengan tipe dataSettings
Inisialisasi atribut tersebut pada constructor.
this.settings = new Settings(context);
Pada blok constructor ListViewHolder lakukan pengambilan nilai Text Size, kemudian atur text size judul dengan menggunakan kode berikut.
float textSize = settings.getTextSize(); titleText.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
Pada class
MainActivity
buatlah methodcreateSettingsFragment
yang bertanggung jawab untuk membuat FragmentSettingsFragment
. Pada peletakkan fragment, ada sedikit perbedaan. Terdapat tambahan methodaddToBackStack
yang dimaksudkan ketika tombol back ditekan, aplikasi tidak langsung keluar.private void createSettingFragment() { Fragment settingsFragment = new SettingsFragment(); getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, settingsFragment) .addToBackStack(null) .commit(); }
Sebagai langkah terakhir, lakukan pemanggilan
createSettingFragment()
ketika menuSettings
ditekan. Pada blok methodonOptionsItemSelected
tambahkan kode berikut.... if (id == R.id.action_settings) { createSettingFragment(); return true; } ...
Jalankan aplikasi, kemudian masuk ke Settings, coba ubah nilai ukuran Text Size kemudian perhatikan perubahan yang terjadi.
Tugas
Lakukan modifikasi pada aplikasi, sehingga default tampilan ketika
NoteFragment
dipanggil adalah tampilan terakhir yang dipilih (List atau Grid).Tambahkan settings tambahan, sehingga pengguna dapat mengkustomisasi aplikasi sesuai keinginan. Sebagai referensi untuk macam-macam bentuk tampilan android preference, bisa merujuk ke halaman android preference