Dalam modul ini kita akan belajar tentang komponen-komponen dasar yang digunakan untuk membuat aplikasi android yang sederhana. Beberapa komponen di antaranya adalah :
- Activity
Merupakan satu komponen yang berhubungan dengan pengguna. Activity menangani window (tampilan) mana yang akan di tampilkan ke dalam interface (antarmuka). Activity memiliki daur hidup (life cycle) tersendiri yang dimulai dari onCreate hingga onDestroy. - Intent
Komunikasi antar komponen di dalam sebuah aplikasi merupakan hal yang sangat sering dilakukan. Inilah peran dari suatu intent. Beberapa fungsi dari intent adalah dapat digunakan untuk menjalankan sebuah activity, mengirimkan pesan ke broadcast receiver, dan dapat juga digunakan untuk berkomunikasi dengan service yang sedang berjalan. - Views and ViewGroup
Pada dasarnya semua elemen antar pengguna di aplikasi Android dibangun menggunakan dua buah komponen inti, yaitu view dan viewgroup. Sebuah view adalah obyek yang menggambar komponen tampilan ke layar yang mana pengguna dapat melihat dan berinteraksi langsung.
Contoh komponen turunan dari view seperti :- TextView, komponen yang berguna untuk menampilkan teks ke layar.
- Button, komponen yang membuat pengguna dapat berinteraksi dengan cara ditekan untuk melakukan sesuatu.
- ImageView, Komponen untuk menampilkan gambar.
- ListView, komponen untuk menampilkan informasi dalam bentuk list.
- GridView, komponen untuk menampilkan informasi dalam bentuk grid.
- RadioButton, komponen yang memungkinkan pengguna dapat memilih satu pilihan dari berbagai pilihan yang disediakan.
- Checkbox, komponen yang memungkinkan pengguna dapat memilih lebih dari satu dari pilihan yang ada.
- Style and ThemePrinsip dasar dalam merancang antarmuka aplikasi Android harus mematuhi kaidah yang ditetapkan oleh Design Guideline. Guideline ini dibuat oleh tim Android di Google. Beberapa prinsipnya adalah:
- Menampilkan informasi yang hanya dibutuhkan.
- Jika aplikasi meminta izin pengguna untuk melakukan sebuah aksi, maka pengembang harus menyediakan mekanisme untuk membatalkan izin tersebut.
- Lakukan interupsi jika diperlukan.
- Menggunakan teks secara singkat. Gunakan gambar untuk menjelaskan informasi secara lebih deskriptif.
- Jaga data pengguna.
- Permudah pengguna untuk melakukan sesuatu yang penting secara cepat.
- Jika terlihat sama, maka perilaku haruslah sama.
- Bantu pengguna untuk membuat keputusan tapi tetap biarkan pengguna menentukan keputusannya.
- RecyclerViewRecyclerView adalah sebuah komponen tampilan (widget) yang lebih canggih ketimbang pendahulunya listview. Ia bersifat lebih fleksibel. RecyclerView memiliki kemampuan untuk menampilkan data secara efisien dalam jumlah yang besar. Terlebih jika Anda memiliki koleksi data dengan elemen yang mampu berubah-ubah sewaktu dijalankan (runtime).Gambar di atas menerangkan beberapa komponen yang harus Anda ketahui sebelum menggunakan recyclerview.
- RecyclerView dan LayoutManager: Komponen antarmuka yang bertugas untuk menampilkan data set yang dimiliki di dalamnya. Layoutmanager akan mengatur posisi tampilan data baik itu secara list (vertikal), grid (baris dan kolom) atau staggered grid (grid yang memiliki susunan tak seragam / tak beraturan)
- Adapter: Komponen yang akan mengatur bagaimana menampilkan data set ke dalam RecyclerView. Di sinilah terjadi proses pengisian tampilan (ViewInflate) dari file layout xml untuk tiap elemen dari data yang sebelumnya terpasang (bind) ke dalam RecyclerView.
- Dataset: Kumpulan data yang dimiliki dan ingin ditampilkan. Bisa berupa array, list maupun obyek map.
- Item Animator: Ini yang spesial. Kita bisa pasang animasi untuk tiap item di dalamnya. Contoh animasi yang umum seperti penambahan (add) dan penghapusan (removal) item. Kita akan mempelajari hal ini pada materi terpisah
Teori
- Activity merupakan sebuah komponen di Android yang berfungsi untuk menampilkan user interface ke layar handset Android pengguna. Ini seperti pada saat Anda melihat daftar percakapan pada aplikasi chat atau daftar email pada aplikasi Gmail di ponsel Android Anda.
- Umumnya dalam sebuah aplikasi terdapat lebih dari satu activity yang saling terhubung dengan tugas yang berbeda-beda.
- Activity merupakan salah satu komponen penting Android yang memiliki daur hidup (life cycle) dalam sebuah stack pada virtual sandbox yang disiapkan oleh Dalvik Virtual Machine (DVM) atau Android Runtime (ART) yang bersifat last in first out.
- Pada implementasinya, activity selalu memiliki satu layout user interface dalam bentuk berkas xml.
- Suatu aplikasi Android bisa memiliki lebih dari satu activity dan harus terdaftar di berkas AndroidManifest.xml sebagai sub aplikasi.
- Sebuah class Java dinyatakan sebuah activity jika mewarisi (extends) superclass Activity atau turunannya seperti AppCompatActivity dan FragmentActivity.
Untuk lebih mendalami activity, kami menyarankan Anda untuk membaca referensi berikut :
Activity Lifecycle
Developer yang baik harus mengetahui secara detail tentang life cycle sebuah activity. Terutama untuk melakukan aksi yang tepat, saat terjadi perubahan state activity. Callback methods yang ada dapat digunakan untuk melakukan beragam proses terkait state dari activity. Misalnya melakukan semua inisialisasi komponen di onCreate(), melakukan disconnect terhadap koneksi ke server pada onStop() atau onDestroy() dan lain sebagainya.
Pemahaman yang baik tentang daur hidup activity akan membuat implementasi rancangan aplikasi Anda menjadi lebih baik. Hal ini juga akan meminimalisir terjadinya error/bug/force close yang tidak diinginkan.
Last In, First Out (LIFO)
Gambar 1 | Gambar 2 | Gambar 3 |
---|---|---|
Aktif: Activity 1 onCreate() → onStart() → onResume() | Aktif: Activity 2 Stack append: Activity 2 [ onResume() ] | Activity 1 onStop() → onRestart() → onStart() → onResume() |
Aksi: Klik Button1 (Pindah) | Aksi: Klik Hardware Back Button | Aktif: Activity 1 |
Stack append: Activity 1 [ onStop() ] | Activity 2 [ finish() ] Stack pop: Activity 2 [ onDestroy() ] |
- Gambar 1
Jika Anda memiliki sebuah aplikasi yang terdiri dari 2 activity, maka activity pertama akan dijalankan setelah pengguna meluncurkan aplikasi melalui ikon aplikasi di layar device. Activity yang ada saat ini berada pada posisi activity running setelah melalui beberapa state onCreate (created) → onStart (started) → onResume (resumed) dan masuk ke dalam sebuah stack activity.
Bila pada activity pertama Anda menekan sebuah tombol untuk menjalankan activity kedua, maka posisi state dari activity pertama berada pada posisi stop. Saat itu, callback onStop() pada activity pertama akan dipanggil.
Ini terjadi karena activity pertama sudah tidak berada pada layar foreground / tidak lagi ditampilkan. Semua informasi terakhir pada activity pertama akan disimpan secara otomatis.
Sementara itu, activity kedua masuk ke dalam stack dan menjadi activity terakhir yang masuk. - Gambar 2
Activity kedua sudah muncul di layar sekarang. Ketika Anda menekan tombol back pada physical button menu utama atau menjalankan metode finish(), maka activity kedua Anda akan dikeluarkan dari stack.
Pada kondisi di atas, state activity kedua akan berada pada destroy. Oleh karenanya, metode onDestroy() akan dipanggil.
Kejadian keluar dan masuk stack pada proses di atas menandakan sebuah model Last In, First Out. Activity kedua menjadi yang terakhir masuk stack (Last In) dan yang paling pertama keluar dari stack (First Out). - Gambar 3Activity pertama akan dimunculkan kembali di layar setelah melalui beberapa state dengan rangkaian callback method yang terpanggil, onStop → onRestart → onStart → onResume.
Detailnya dapat Anda baca di sini :
Saving Activity State
Ketika sebuah activity mengalami pause kemudian resume, maka state dari sebuah activity tersebut dapat terjaga. Sebabnya, obyek activity masih tersimpan di memory sehingga dapat dikembalikan state-nya.
Dengan menjaga state dari activity, maka ketika activity tersebut ditampilkan, kondisinya akan tetap sama dengan kondisi sebelumnya.
Akan tetapi ketika sistem menghancurkan activity untuk keperluan memori misalnya karena memori habis, maka obyek activity dihancurkan. Alhasil, ketika activity ingin ditampilkan kembali diperlukan proses recreate activity yang dihancurkan tadi.
Kejadian di atas adalah hal yang lumrah terjadi. Oleh karena itu, perubahan yang terjadi pada activity perlu disimpan terlebih dahulu sebelum ia dihancurkan. Di sinilah metode onSaveInstanceState() digunakan.
Dalam onSaveInstanceState terdapat bundle yang dapat digunakan untuk menyimpan informasi. Informasi dapat disimpan dengan memanfaatkan fungsi seperti putString() dan putInt().
Ketika activity di-restart, bundle akan diberikan kepada metode onCreate dan onRestoreInstanceState. Bundle tersebut akan dimanfaatkan untuk mengembalikan kembali perubahan yang telah terjadi sebelumnya.
Proses penghancuran activity dapat juga terjadi ketika terdapat perubahan konfigurasi seperti perubahan orientasi layar (portrait-landscape), keyboard availability, dan perubahan bahasa. Penghancuran ini akan menjalankan callback method onDestroy dan kemudian menjalankan onCreate. Penghancuran ini dimaksudkan agar activity dapat menyesuaikan diri dengan konfigurasi baru yang muncul pada kejadian-kejadian sebelumnya.
Hal yang perlu diingat ketika menggunakan onSaveInstanceState adalah untuk tidak menyimpan data yang besar pada bundle. Contohnya, hindari penyimpanan data bitmap pada bundle. Bila data pada bundle berukuran besar, proses serialisasi dan deserialisasi akan memakan banyak memori.
Contoh Project Sederhana
Codelab ini bertujuan untuk mengimplementasikan komponen activity pada aplikasi pertama yang Anda bangun. Harapannya aktifitas ini dapat memberi gambaran yang jelas tentang cara kerja activity.
Codelab pertama adalah dengan membuat aplikasi yang dapat menghitung volume balok. Seperti ini tampilannya.
Logika Dasar
Melakukan input ke dalam obyek EditText → melakukan validasi input → melakukan perhitungan volume balok ketika tombol hitung diklik.
Codelab Membuat Proyek Baru
- Buat proyek baru dengan klik File → New → New Project pada Android Studio Anda atau Anda bisa memilih Start a new Android Studio project di bagian dashboard.
- Pada bagian ini kita akan memilih tipe activity awal dari template yang telah disediakan. Saat ini Android Studio sudah menyediakan berbagai macam template activity dari yang paling sederhana hingga yang paling kompleks seperti:
Add No Activity : Tidak ada activity yang ditambahkan
Basic Activity : Activity dengan template komponen material design seperti FloatingActionButton
Bottom Navigation Activity : Activity dengan tampilan side bar menu di bagian bawah
Empty Activity : Activity dalam bentuk yang paling dasar
Fragment + ViewModel : Activity dengan menerapkan architecture component
Fullscreen Activity : Activity fullscreen tanpa status bar
Google AdMob Ads Activity : Activity dengan konfigurasi default iklan Admob
Google Maps Activity : Activity dengan menyediakan konfigurasi dasar Google Maps
Login Activity : Activity untuk halaman login
Master / Detail Flow : Activity yang diperuntukan untuk alur aplikasi master detail pada peranti tablet
Navigation Drawer Activity : Activity dengan tampilan side bar menu
Scrolling Activity : Activity dengan kemampuan scroll konten didalamnya secara vertikal
Settings Activity : Activity yang diperuntukkan untuk konfigurasi aplikasi
Tabbed Activity : Activity yang diperuntukkan untuk menampilkan lebih dari satu tampilan, dapat digeser ke kanan dan ke kiri (swipe) dan dengan menggunakan komponen ViewPager
Selain itu, Anda juga bisa memilih target device mana yang akan dibuat seperti Phone and Tablet, Wear OS, TV, Android Auto atau Android Things.
Saat ini kita pilih tipe Empty Activity, klik Next untuk melanjutkan. - Selanjutnya masukkan nama aplikasi dan nama package aplikasi Anda. Sebaiknya jangan sama dengan apa yang ada di contoh, karena ini berfungsi sebagai id dari aplikasi yang Anda buat. Kemudian Anda bisa menentukan lokasi proyek yang akan Anda buat. Setelah itu pilih tipe gawai/peranti (device) untuk aplikasi beserta target minimum SDK yang akan digunakan. Pilihan target Android SDK akan mempengaruhi banyaknya peranti yang dapat menggunakan aplikasi. Di sini kita memilih nilai minimum SDK kita pasang ke Level 21 (Lollipop). Klik Finish untuk melanjutkan.
Catatan: Untuk Android Studio versi 3.4.1 ke bawah, penggunaan AndroidX belum menjadi default. Jadi, silakan beri tanda centang pada "Use AndroidX artifacts" di setiap kali membuat project. Informasi lebih detail mengenai AndroidX bisa Anda lihat di sini, dan untuk melihat perubahan antara sebelum dan sesudah AndroidX, atau cara migrasi menggunakan AndroidX bisa lihat di sini. Perlu Anda ketahui juga, ketika Anda sudah menggunakan AndroidX maka Anda tidak bisa menambahkan library lama atau sebelum AndroidX.
Kelas ini sudah mendukung bahasa Kotlin. Untuk menggunakan bahasa Kotlin ubahlah languages dari Java menjadi Kotlin, maka secara otomatis Android Studio akan menyesuaikan dengan bahasa Kotlin. - Tampilan layar Anda akan seperti contoh di bawah ini:
- Di sebelah kanan Anda adalah workspace di mana Activity Anda berada dan bernama MainActivity dengan layout-nya activity_main.xml. Di sebelah kiri Anda terdapat struktur proyek, di mana nanti kita akan banyak menambahkan berbagai komponen baru, asset dan library. Untuk lebih mengenal Android Studio lebih dalam silakan baca materi ini https://developer.android.com/studio/intro/index.html.
Selanjutnya kita akan mulai melakukan pengkodean aplikasi atau lebih enaknya disebut ngoding.
Berikut flow umumnya:
- Ngoding Layout untuk user interface aplikasi.
- Ngoding Activity untuk menambahkan logika aplikasi.
Untuk mengoptimalkan proses pengetikan, Anda dapat memanfaatkan code completion dengan menekan ctrl + space. Android Studio juga akan mengimpor package dari komponen yang digunakan.
Dilarang Keras untuk copy - paste! Ngoding pelan-pelan akan membuat Anda lebih jago di masa depan.
Selamat ngoding!
Codelab Layouting
Menambahkan Code Sederhana pada Layout Activity
- Silakan pilih tab berkas activity_main.xml pada workspace Anda(res/layout/activity_main.xml).
Pastikan project window pada pilihan Android, seperti di bawah ini.
Maka akan ada tampilan seperti ini, kemudian pilih tab Code di sebelah pojok kanan atas.
Dan tambahkan baris-baris berikut:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout 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"
- android:padding="16dp"
- android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Panjang" />
- <EditText
- android:id="@+id/edt_length"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:lines="1" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Lebar" />
- <EditText
- android:id="@+id/edt_width"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:lines="1" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Tinggi" />
- <EditText
- android:id="@+id/edt_height"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:lines="1" />
- <Button
- android:id="@+id/btn_calculate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Hitung" />
- <TextView
- android:id="@+id/tv_result"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:text="Hasil"
- android:textSize="24sp"
- android:textStyle="bold" />
- </LinearLayout>
- Perlu diperhatikan root layout (tag layout terluar) yang dipakai di sini adalah LinearLayout. Jika kita menggunakan Android Studio versi 3 maka secara default root yang dipakai adalah ConstraintLayout. Agar sesuai dengan latihan ini, kita tinggal menggantinya menjadi LinearLayout.
Untuk Layout akan dibahas nanti pada modul yang berbeda. - Kemudian akan muncul warning pada atribut android:text pada layout tersebut.
Ini karena kita melakukan hardcoding pada nilai string-nya. Mari kita hilangkan code warning tersebut dengan menekan Alt+Enter (option + return pada Mac) atau menekan lampu kuning yang muncul pada attribut android:text.
Akan muncul dialog seperti ini, pilih extract string resource. - Kemudian akan muncul dialog seperti di bawah ini. Sesuaikan dengan nama yang ada.
- Fungsi extract string resource akan secara otomatis menambahkan nilai dari android:text ke dalam berkas res → values → strings.xml.
Lakukan hal yang sama pada View lainnya hingga tidak ada warning lagi. Jika kita buka berkas strings.xml, maka isinya akan menjadi seperti ini:- <resources>
- <string name="app_name">BarVolume</string>
- <string name="width">Lebar</string>
- <string name="height">Tinggi</string>
- <string name="calculate">Hitung</string>
- <string name="result">Hasil</string>
- <string name="length">Panjang</string>
- </resources>
- Maka kode di dalam activity_main.xml akan menjadi seperti ini:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout 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"
- android:padding="16dp"
- android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/length" />
- <EditText
- android:id="@+id/edt_length"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:lines="1" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/width" />
- <EditText
- android:id="@+id/edt_width"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:lines="1" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/height" />
- <EditText
- android:id="@+id/edt_height"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:lines="1" />
- <Button
- android:id="@+id/btn_calculate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/calculate" />
- <TextView
- android:id="@+id/tv_result"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:text="@string/result"
- android:textSize="24sp"
- android:textStyle="bold" />
- </LinearLayout>
- Jika Anda perhatikan, hasil layout sementara akan menjadi seperti ini:
Codelab Kode Logika
Menambahkan Kode Logika Sederhana pada MainActivity.
- Selanjutnya setelah selesai, lanjutkan dengan membuka berkas MainActivity dan lanjutkan ngoding baris-baris di bawah ini.
- Tambahkan beberapa variabel yang akan digunakan untuk menampung View.
- private EditText edtWidth;
- private EditText edtHeight;
- private EditText edtLength;
- private Button btnCalculate;
- private TextView tvResult;
- private lateinit var edtWidth: EditText
- private lateinit var edtHeight: EditText
- private lateinit var edtLength: EditText
- private lateinit var btnCalculate: Button
- private lateinit var tvResult: TextView
Kemudian inisiasi variabel yang telah kita buat dengan menambahkan kode berikut di dalam metode onCreate.
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- edtWidth = findViewById(R.id.edt_width);
- edtHeight = findViewById(R.id.edt_height);
- edtLength = findViewById(R.id.edt_length);
- btnCalculate = findViewById(R.id.btn_calculate);
- tvResult = findViewById(R.id.tv_result);
- }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- edtWidth = findViewById(R.id.edt_width)
- edtHeight = findViewById(R.id.edt_height)
- edtLength = findViewById(R.id.edt_length)
- btnCalculate = findViewById(R.id.btn_calculate)
- tvResult = findViewById(R.id.tv_result)
- }
Kemudian tambahkan implementasi interface onClickListener pada kelas MainActivity.
- public class MainActivity extends AppCompatActivity implements View.OnClickListener {
- ...
- }
- class MainActivity : AppCompatActivity(), View.OnClickListener {
- ...
- }
Jika terdapat baris merah seperti ini:
Jangan khawatir! Silakan klik di atas baris merah tersebut dan klik pada ikon bola lampu. Atau dengan tekan tombol Alt+Enter (option+return pada Mac) atau menekan lampu merah yang muncul lalu pilih implement methods.
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- ...
- btnCalculate.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.btn_calculate) {
- String inputLength = edtLength.getText().toString().trim();
- String inputWidth = edtWidth.getText().toString().trim();
- String inputHeight = edtHeight.getText().toString().trim();
- boolean isEmptyFields = false;
- boolean isInvalidDouble = false;
- if (TextUtils.isEmpty(inputLength)) {
- isEmptyFields = true;
- edtLength.setError("Field ini tidak boleh kosong");
- }
- if (TextUtils.isEmpty(inputWidth)) {
- isEmptyFields = true;
- edtWidth.setError("Field ini tidak boleh kosong");
- }
- if (TextUtils.isEmpty(inputHeight)) {
- isEmptyFields = true;
- edtHeight.setError("Field ini tidak boleh kosong");
- }
- Double length = toDouble(inputLength);
- Double width = toDouble(inputWidth);
- Double height = toDouble(inputHeight);
- if (length == null) {
- isInvalidDouble = true;
- edtLength.setError("Field ini harus berupa nomer yang valid");
- }
- if (width == null) {
- isInvalidDouble = true;
- edtWidth.setError("Field ini harus berupa nomer yang valid");
- }
- if (height == null) {
- isInvalidDouble = true;
- edtHeight.setError("Field ini harus berupa nomer yang valid");
- }
- if (!isEmptyFields && !isInvalidDouble) {
- double volume = length * width * height;
- tvResult.setText(String.valueOf(volume));
- }
- }
- }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- ...
- btnCalculate.setOnClickListener(this)
- }
- override fun onClick(v: View) {
- if (v.id == R.id.btn_calculate) {
- val inputLength = edtLength.text.toString().trim()
- val inputWidth = edtWidth.text.toString().trim()
- val inputHeight = edtHeight.text.toString().trim()
- var isEmptyFields = false
- var isInvalidDouble = false
- if (inputLength.isEmpty()) {
- isEmptyFields = true
- edtLength.error = "Field ini tidak boleh kosong"
- }
- if (inputWidth.isEmpty()) {
- isEmptyFields = true
- edtWidth.error = "Field ini tidak boleh kosong"
- }
- if (inputHeight.isEmpty()) {
- isEmptyFields = true
- edtHeight.error = "Field ini tidak boleh kosong"
- }
- val length = toDouble(inputLength)
- val width = toDouble(inputWidth)
- val height = toDouble(inputHeight)
- if (length == null) {
- isInvalidDouble = true
- edtLength.error = "Field ini harus berupa nomer yang valid"
- }
- if (width == null) {
- isInvalidDouble = true
- edtWidth.error = "Field ini harus berupa nomer yang valid"
- }
- if (height == null) {
- isInvalidDouble = true
- edtHeight.error = "Field ini harus berupa nomer yang valid"
- }
- if (!isEmptyFields && !isInvalidDouble) {
- val volume = length as Double * width as Double * height as Double
- tvResult.text = volume.toString()
- }
- }
- }
Terakhir tambahkan kode untuk validasi nilai double.
- private Double toDouble(String str) {
- try {
- return Double.valueOf(str);
- } catch (NumberFormatException e) {
- return null;
- }
- }
- private fun toDouble(str: String): Double? {
- return try {
- str.toDouble()
- } catch (e: NumberFormatException) {
- null
- }
- }
Akhirnya kelas MainActivity akan memiliki kode seperti berikut ini.
- public class MainActivity extends AppCompatActivity implements View.OnClickListener {
- private EditText edtWidth, edtHeight, edtLength;
- private Button btnCalculate;
- private TextView tvResult;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- edtWidth = findViewById(R.id.edt_width);
- edtHeight = findViewById(R.id.edt_height);
- edtLength = findViewById(R.id.edt_length);
- btnCalculate = findViewById(R.id.btn_calculate);
- tvResult = findViewById(R.id.tv_result);
- btnCalculate.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.btn_calculate) {
- String inputLength = edtLength.getText().toString().trim();
- String inputWidth = edtWidth.getText().toString().trim();
- String inputHeight = edtHeight.getText().toString().trim();
- boolean isEmptyFields = false;
- boolean isInvalidDouble = false;
- if (TextUtils.isEmpty(inputLength)) {
- isEmptyFields = true;
- edtLength.setError("Field ini tidak boleh kosong");
- }
- if (TextUtils.isEmpty(inputWidth)) {
- isEmptyFields = true;
- edtWidth.setError("Field ini tidak boleh kosong");
- }
- if (TextUtils.isEmpty(inputHeight)) {
- isEmptyFields = true;
- edtHeight.setError("Field ini tidak boleh kosong");
- }
- Double length = toDouble(inputLength);
- Double width = toDouble(inputWidth);
- Double height = toDouble(inputHeight);
- if (length == null) {
- isInvalidDouble = true;
- edtLength.setError("Field ini harus berupa nomer yang valid");
- }
- if (width == null) {
- isInvalidDouble = true;
- edtWidth.setError("Field ini harus berupa nomer yang valid");
- }
- if (height == null) {
- isInvalidDouble = true;
- edtHeight.setError("Field ini harus berupa nomer yang valid");
- }
- if (!isEmptyFields && !isInvalidDouble) {
- double volume = length * width * height;
- tvResult.setText(String.valueOf(volume));
- }
- }
- }
- private Double toDouble(String str) {
- try {
- return Double.valueOf(str);
- } catch (NumberFormatException e) {
- return null;
- }
- }
- }
- class MainActivity : AppCompatActivity(), View.OnClickListener {
- private lateinit var edtWidth: EditText
- private lateinit var edtHeight: EditText
- private lateinit var edtLength: EditText
- private lateinit var btnCalculate: Button
- private lateinit var tvResult: TextView
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- edtWidth = findViewById(R.id.edt_width)
- edtHeight = findViewById(R.id.edt_height)
- edtLength = findViewById(R.id.edt_length)
- btnCalculate = findViewById(R.id.btn_calculate)
- tvResult = findViewById(R.id.tv_result)
- btnCalculate.setOnClickListener(this)
- }
- override fun onClick(v: View) {
- if (v.id == R.id.btn_calculate) {
- val inputLength = edtLength.text.toString().trim()
- val inputWidth = edtWidth.text.toString().trim()
- val inputHeight = edtHeight.text.toString().trim()
- var isEmptyFields = false
- var isInvalidDouble = false
- if (inputLength.isEmpty()) {
- isEmptyFields = true
- edtLength.error = "Field ini tidak boleh kosong"
- }
- if (inputWidth.isEmpty()) {
- isEmptyFields = true
- edtWidth.error = "Field ini tidak boleh kosong"
- }
- if (inputHeight.isEmpty()) {
- isEmptyFields = true
- edtHeight.error = "Field ini tidak boleh kosong"
- }
- val length = toDouble(inputLength)
- val width = toDouble(inputWidth)
- val height = toDouble(inputHeight)
- if (length == null) {
- isInvalidDouble = true
- edtLength.error = "Field ini harus berupa nomer yang valid"
- }
- if (width == null) {
- isInvalidDouble = true
- edtWidth.error = "Field ini harus berupa nomer yang valid"
- }
- if (height == null) {
- isInvalidDouble = true
- edtHeight.error = "Field ini harus berupa nomer yang valid"
- }
- if (!isEmptyFields && !isInvalidDouble) {
- val volume = length as Double * width as Double * height as Double
- tvResult.text = volume.toString()
- }
- }
- }
- private fun toDouble(str: String): Double? {
- return try {
- str.toDouble()
- } catch (e: NumberFormatException) {
- null
- }
- }
- }
- Setelah selesai, silakan jalankan aplikasi dengan memilih menu Run → Run ‘app’ dari menu bar. Kemudian ada pilihan seperti ini. Itu tandanya adb (Android Debugger) pada peranti yang Anda punya telah terhubung dengan Android Studio. Jika Anda tidak memiliki peranti, maka Anda dapat menggunakan emulator. Ikuti materinya di modul sebelumnya https://www.dicoding.com/academies/14/tutorials/1132 atau di sini https://developer.android.com/studio/run/managing-avds.html. Kami merekomendasikan Anda menggunakan peranti Android sewaktu mengembangkan aplikasi. Selain karena beban memori pada peranti Anda akan jadi lebih rendah, pendekatan ini juga akan memungkinkan Anda untuk merasakan bagaimana aplikasi berjalan di device sebenarnya. Pilih OK untuk menjalankan dan tunggu hingga proses building dan instalasi APK selesai. Jika sudah, seharusnya hasilnya akan seperti ini. Silakan masukkan nilai panjang, lebar dan tinggi kemudian tekan tombol Hitung dan hasilnya akan ditampilkan di obyek textview tvHasil.
- Apakah kita sudah selesai? Belum! Masih ada yang kurang. Ketika nilai volume sudah dihitung dan kemudian terjadi pergantian orientasi (portrait-landscape) pada peranti, maka hasil perhitungan tadi akan hilang. Untuk mengatasinya, tambahkan metode onSaveInstanceState() pada MainActivity dan sesuaikan seperti berikut:
- private static final String STATE_RESULT = "state_result";
- ...
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putString(STATE_RESULT, tvResult.getText().toString());
- }
- companion object {
- private const val STATE_RESULT = "state_result"
- }
- ...
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- outState.putString(STATE_RESULT, tvResult.text.toString())
- }
Kemudian tambahkan juga beberapa baris berikut pada baris terakhir metode onCreate.
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- ...
- if (savedInstanceState != null) {
- String result = savedInstanceState.getString(STATE_RESULT);
- tvResult.setText(result);
- }
- }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- ...
- if (savedInstanceState != null) {
- val result = savedInstanceState.getString(STATE_RESULT) as String
- tvResult.text = result
- }
- }
- Silakan jalankan kembali aplikasinya. Ulangi proses perhitungan seperti sebelumnya. Kemudian ganti orientasi peranti Anda. Jika sudah benar maka hasil perhitungan tidak akan hilang.
Bedah Kode
Pembahasan tentang layout xml
Layout merupakan user interface dari suatu activity. Layout dituliskan dalam format xml (extensible markup language).
- xml version="1.0" encoding="utf-8"?>
Baris ini mengidentifikasi bahwa berkas ini berformat xml.
- xmlns:android="http://schemas.android.com/apk/res/android"
Kode di atas menandakan namespace yang digunakan dalam keseluruhan berkas xml ini.Macam Views
Di sini kita menggunakan beberapa komponen user interface yang disebut view. Di antaranya:TextView : Komponen view untuk menampilkan teks ke layarEditText : Komponen view untuk memberikan input teksButton : Komponen view untuk melakukan sebuah aksi klikLinearLayout : Komponen view bertipe viewgroup yang menjadi parent dari semua sub komponen view (sub view) di dalamnya. Komponen ini bersifat sebagai kontainer untuk komponen lain dengan orientasi secara vertikal atau horizontal.Cara membaca :
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/calculate"
- android:layout_marginBottom="16dp"/>
Komponen di atas adalah sebuah TextView. Perhatikan gambar di bawah ini. Warna ungu menandakan namespace yang digunakan; warna biru adalah atribut dari komponen dan warna hijau adalah nilai dari atribut. Penjelasannya seperti di bawah ini:match_parent: Ini berarti bahwa ukuran dimensi sebuah View disesuaikan dengan ukuran layar secara horizontal jika pada layout_width dan vertikal jika pada layout_height.wrap_content : Ini berarti bahwa ukuran dimensi sebuah View disesuaikan dengan ukuran konten di dalamnya baik secara horizontal pada layout_width dan vertikal jika pada layout_height.@string/calculate: value calculate berasal dari berkas strings.xml yang bisa Anda lihat dengan cara menekan dan tahan tombol Ctrl (atau command) + arahkan kursor ke atasnya dan kemudian klik sekali. .Penggunaan centralize resource value akan memudahkan Anda sewaktu mengembangkan aplikasi Android. Cara tersebut digunakan agar Anda tidak menulis nilai yang sama berulang-ulang.Apa itu ‘@+id/’ ?
Salah satu contoh penerapan penggunaan @+id/ sebagai berikut:
- <Button
- android:id="@+id/btn_calculate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/calculate"/>
Penjelasannya sebagai berikut:
- android:id="@+id/btn_calculate"
Jika kita memberikan id pada sebuah view maka kita telah memberikan identifier untuk view tersebut. Pemberian id ini dimaksudkan agar kita bisa melakukan manipulasi/pengendalian pada level logic di komponen seperti activity atau fragment.Id di atas akan diciptakan di berkas R dan disimpan dalam bentuk hexa bertipe data integer public static final int btn_calculate=0x7f0b0057.Acuan untuk menyusun tampilan pada relativelayout akan dibahas pada modul selanjutnya.Pembahasan tentang Logika Kode
Kode logika dituliskan ke dalam kelas Java atau Koltin. Di sinilah semua aktifitas dari suatu aplikasi berjalan.Activity
- public class MainActivity extends AppCompatActivity
- class MainActivity : AppCompatActivity()
Menandakan bahwa kelas Java / Kotlin di atas merupakan sebuah activity karena inherit ke superclass bernama AppCompatActivity.OnClickListener
- implements View.OnClickListener
- , View.OnClickListener
Ini adalah listener yang kita implementasikan untuk memantau kejadian klik pada komponen tombol (button).Views
- private EditText edtWidth;
- private EditText edtHeight;
- private EditText edtLength;
- private Button btnCalculate;
- private TextView tvResult;
- private lateinit var edtWidth: EditText
- private lateinit var edtHeight: EditText
- private lateinit var edtLength: EditText
- private lateinit var btnCalculate: Button
- private lateinit var tvResult: TextView
Kode di atas mendeklarasikan semua komponen view yang akan dimanipulasi. Kita deklarasikan secara global agar bisa dikenal di keseluruhan bagian kelas.OnCreate
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- edtWidth = findViewById(R.id.edt_width);
- edtHeight = findViewById(R.id.edt_height);
- edtLength = findViewById(R.id.edt_length);
- btnCalculate = findViewById(R.id.btn_calculate);
- tvResult = findViewById(R.id.tv_result);
- btnCalculate.setOnClickListener(this);
- }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- edtWidth = findViewById(R.id.edt_width)
- edtHeight = findViewById(R.id.edt_height)
- edtLength = findViewById(R.id.edt_length)
- btnCalculate = findViewById(R.id.btn_calculate)
- tvResult = findViewById(R.id.tv_result)
- btnCalculate.setOnClickListener(this)
- }
Metode onCreate() merupakan metode utama pada activity. Di sinilah kita dapat mengatur layout xml.Semua proses inisialisasi komponen yang digunakan akan dijalankan di sini. Pada metode ini kita casting semua komponen view yang kita telah deklarasikan sebelumnya, agar dapat kita manipulasi.SetContentView
- setContentView(R.layout.activity_main);
- setContentView(R.layout.activity_main)
Maksud baris di atas adalah kelas MainActivity akan menampilkan tampilan yang berasal dari layout activity_main.xml.Casting View
- edtWidth = findViewById(R.id.edt_width);
- edtWidth = findViewById(R.id.edt_width)
Maksud dari baris di atas adalah obyek EditTextedtWidth disesuaikan (cast) dengan komponen EditText ber-ID edt_width di layout activity_main.xml melalui metode findViewById().SetOnClickListener
- btnCalculate.setOnClickListener(this);
- btnCalculate.setOnClickListener(this)
Kita memasang event click listener untuk obyek btnCalculate sehingga sebuah aksi dapat dijalankan ketika obyek tersebut diklik.Keyword this merujuk pada obyek Activity saat ini yang telah mengimplementasikan listener OnClickListener sebelumnya.Sehingga ketika btnCalculate diklik, maka fungsi onClick akan dipanggil dan melukakan proses yang ada di dalamnya.Mengambil value dari EditText
- String inputLength = edtLength.getText().toString().trim();
- String inputWidth = edtWidth.getText().toString().trim();
- String inputHeight = edtHeight.getText().toString().trim();
- val inputLength = edtLength.text.toString().trim()
- val inputWidth = edtWidth.text.toString().trim()
- val inputHeight = edtHeight.text.toString().trim()
Sintaks .text.toString() di atas berfungsi untuk mengambil isi dari sebuah EditText kemudian menyimpannya dalam sebuah variabel. Tambahan .trim() berfungsi untuk menghiraukan spasi jika ada, sehingga nilai yang didapat hanya berupa angka.Cek inputan yang kosong
- boolean isEmptyFields = false;
- boolean isInvalidDouble = false;
- if (TextUtils.isEmpty(inputLength)) {
- isEmptyFields = true;
- edtLength.setError("Field ini tidak boleh kosong");
- }
- if (TextUtils.isEmpty(inputWidth)) {
- isEmptyFields = true;
- edtWidth.setError("Field ini tidak boleh kosong");
- }
- if (TextUtils.isEmpty(inputHeight)) {
- isEmptyFields = true;
- edtHeight.setError("Field ini tidak boleh kosong");
- }
- var isEmptyFields = false
- var isInvalidDouble = false
- when {
- inputLength.isEmpty() -> {
- isEmptyFields = true
- edtLength.error = "Field ini tidak boleh kosong"
- }
- inputWidth.isEmpty() -> {
- isEmptyFields = true
- edtWidth.error = "Field ini tidak boleh kosong"
- }
- inputHeight.isEmpty() -> {
- isEmptyFields = true
- edtHeight.error = "Field ini tidak boleh kosong"
- }
- }
Sintaks .isEmpty() berfungsi untuk mengecek apakah inputan dari Editext itu masih kosong.Jika iya, maka kita akan menampilkan pesan error dengan menggunakan .setError("Field ini tidak boleh kosong") dan mengganti variabel Boolean isEmptyField menjadi true supaya bisa lanjut ke proses selanjutnya.Validasi input yang bukan angka
- Double length = toDouble(inputLength);
- Double width = toDouble(inputWidth);
- Double height = toDouble(inputHeight);
- if (length == null) {
- isInvalidDouble = true;
- edtLength.setError("Field ini harus berupa nomer yang valid");
- }
- if (width == null) {
- isInvalidDouble = true;
- edtWidth.setError("Field ini harus berupa nomer yang valid");
- }
- if (height == null) {
- isInvalidDouble = true;
- edtHeight.setError("Field ini harus berupa nomer yang valid");
- }
- ...
- private Double toDouble(String str) {
- try {
- return Double.valueOf(str);
- } catch (NumberFormatException e) {
- return null;
- }
- }
- val length = toDouble(inputLength)
- val width = toDouble(inputWidth)
- val height = toDouble(inputHeight)
- when {
- length == null -> {
- isInvalidDouble = true
- edtLength.error = "Field ini harus berupa nomer yang valid"
- }
- width == null -> {
- isInvalidDouble = true
- edtWidth.error = "Field ini harus berupa nomer yang valid"
- }
- height == null -> {
- isInvalidDouble = true
- edtHeight.error = "Field ini harus berupa nomer yang valid"
- }
- }
- ...
- private fun toDouble(str: String): Double? {
- return try {
- str.toDouble()
- } catch (e: NumberFormatException) {
- null
- }
- }
toDouble() adalah sebuah fungsi yang kita buat sendiri di luar onCreate untuk merubah data inputan yang sebelumnya berupa String menjadi Double, hal ini karena secara default input di EditText tipe datanya berupa String, padahal kita perlu dalam bentuk Double supaya bisa dilakukan proses perhitungan.Fungsi ini juga sekaligus mengecek apakah inputan yang dimasukkan berupa angka atau bukan, jika bukan maka ditaampilkan pesan error.Menampilkan data ke EditText
- if (!isEmptyFields && !isInvalidDouble) {
- double volume = length * width * height;
- tvResult.setText(String.valueOf(volume));
- }
- if (!isEmptyFields && !isInvalidDouble) {
- val volume = length as Double * width as Double * height as Double
- tvResult.text = volume.toString()
- }
Sintaks !isEmptyFields memiliki arti "jika semua inputan tidak kosong", dan !isInvalidDouble memiliki arti "jika semua inputan berisi angka".Jika kedua kondisi tersebut terpenuhi, maka langkah selanjutnya yaitu melakukan proses perhitungan dan kemudian kita tampilkan pada TextView tvResult dengan menggunakan .setText().Di sini dapat kita lihat bahwa kita perlu merubah datanya yang sebelumnya Double menjadi String, karena untuk menampilkan data dengan setText() harus berupa String.Pembahasan saveInstanceState
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putString(STATE_RESULT, tvResult.getText().toString());
- }
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- outState.putString(STATE_RESULT, tvResult.text.toString())
- }
Perhatikan metode onSaveInstanceState.Di dalam metode tersebut, hasil perhitungan yang ditampilkan pada tvResult dimasukkan pada bundle kemudian disimpan isinya.Untuk menyimpan data disini menggunakan konsep Key-Value, dengan STATE_RESULT sebagai key dan isi dari tvResult sebagai value. Fungsi onSaveInstanceState akan dipanggil secara otomatis sebelum sebuah Activity hancur.Di sini kita perlu menambahkan onSaveInstanceState karena ketika orientasi berubah, Activity tersebut akan di-destroy dan memanggil fungsi onCreate lagi, sehingga kita perlu menyimpan nilai hasil perhitungan tersebut supaya data tetap terjaga dan tidak hilang ketika orientasi berubah.
- if (savedInstanceState != null){
- String hasil = savedInstanceState.getString(STATE_RESULT);
- tvResult.setText(hasil);
- }
- if (savedInstanceState != null) {
- val result = savedInstanceState.getString(STATE_RESULT) as String
- tvResult.text = result
- }
Pada onCreate inilah kita menggunakan nilai bundle yang telah kita simpan sebelumnya pada onSaveInstanceState. Nilai tersebut kita dapatkan dengan menggunakan Key yang sama dengan saat menyimpan, yaitu STATE_RESULT. Kemudian kita isikan kembali pada tvResult.Selamat! Anda telah berhasil membuat dan menjalankan latihan Android pertama di peranti atau emulator. Silakan lanjut ke codelab berikutnya.Anda dapat mengunduh source code materi ini di tautan berikut: