Pernah terpikir untuk membuat tampilan table seperti pada gambar diatas pada Android Studio? Jika iya maka kalian dapat mencoba tutorial berikut mengenai cara membuat tampilan tabel pada Android Studio.
Pada tutorial ini widget yang digunakan adalah ListView. Untuk tutorial penggunaan pada RecyclerView, insya Allah admin akan membahasnya pada lain kesempatan.
Sedikit membahas tentang perbedaan antara ListView VS RecyclerView, ListView sudah lama diperkenalkan sejak Android SDK API 1. Penggunaannya juga relatif mudah dan untuk menampung data juga lumayan lancar. Sayangnya, untuk menampung data dalam jumlah besar, widget ini menjadi kurang smooth ketika melakukan scrolling data secara vertikal.
Semenjak Android Lollipop diluncurkan dan memperkenalkan widget RecyclerView, masalah diatas telah teratasi dengan baik jika menggunakan RecyclerView. Untuk perbedaan mendasar lebih lanjut antara ListView VS RecyclerView dapat dibaca pada tutorial Perbedaan RecyclerView dan ListView.
Paragraf diatas hanya sedikit gambaran mengenai ListView pada Android Studio. Kembali ke topik utama tentang cara membuat tampilan tabel pada Android Studio. Tabel biasanya digunakan untuk membuat laporan data agar lebih enak dibaca baik untuk tujuan formal maupun nonformal.
Seperti halnya laporan keuangan, akan lebih enak dibaca jika menggunakan media tabel sebagai sarana visual untuk mempresentasikan data yang ingin disampaikan kepada para pembaca. Ya, itu hanyalah sedikit alasan yang dibuat untuk mendukung tutorial ini agar lebih relevan dalam penggunaannya.
[ads id="ads1"]
Langkah-Langkah Membuat Table Layout
Hal pertama yang dilakukan adalah kita memerlukan desain tabel terlebih dahulu. Dengan menggunakan LinearLayout, kita akan mengakomodasi desain yang diinginkan. Contoh berikut yang admin gunakan sebagai layout untuk membuat tampilan tabel.Simpanlah dengan nama
layout_table_three.xml
:<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:id="@+id/listLayout" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:background="@android:color/white" android:layout_height="wrap_content"> <TextView android:id="@+id/txtNo" android:layout_width="50dp" android:layout_height="wrap_content" android:gravity="center" android:padding="5dp" android:text="No" android:ellipsize="end" android:singleLine="true" android:background="@drawable/border_right_bottom" android:textColor="#000" /> <TextView android:id="@+id/txtDescription" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="left" android:padding="5dp" android:text="Description" android:ellipsize="end" android:singleLine="true" android:background="@drawable/border_right_bottom" android:textColor="#000"/> <TextView android:id="@+id/txtAmount" android:layout_width="100dp" android:layout_height="wrap_content" android:gravity="right" android:padding="5dp" android:text="Amount" android:ellipsize="end" android:singleLine="true" android:background="@drawable/border_right_bottom" android:textColor="#000" /> </LinearLayout>
Lalu untuk drawable border_right_bottom seperti berikut ini. Drawable ini digunakan untuk membentuk border (garis) kanan dan bawah pada cell tabel yang akan digunakan.
Simpanlah dengan nama
border_right_bottom.xml
:<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android = "http://schemas.android.com/apk/res/android"> <item android:left = "-2dp" android:top = "-2dp"> <shape android:shape = "rectangle" > <stroke android:width = "1dp" android:color = "#ababb2" /> <solid android:color="@android:color/transparent"/> </shape> </item> </layer-list >
Membuat Class Table
Selanjutnya membuat class/object untuk data yang akan ditampilkan pada layout. Pada layout diatas terdiri dari 3 cell yaitu No., Description, dan Amount. Pembuatan class ini tujuannya agar lebih mudah dalam menambahkan data ke dalam adapter yang nantinya akan digunakan sebagai penampung data listview.Oh iya admin lupa menyampaikan kalau dalam tutorial ini bahasa yang digunakan adalah Kotlin. Salah satu alasannya adalah karena Google sudah memutuskan Kotlin menjadi bahasa resmi pada pemograman sistem operasi Android selanjutnya.
Simpan class berikut dengan nama
TableClass.kt
import android.content.Context class TableClass(context: Context?) { val mContext : Context? = context; var no: Int? = 1; var description: String? = null; var amount: Double? = 0.0; var headerNo : String? = null; var headerDesc : String? = null; var headerAmount : String? = null; fun addHeader(headerNo : String, headerDesc: String, headerAmount : String) : TableClass{ val data = TableClass(mContext); data.headerNo = headerNo; data.headerDesc = headerDesc; data.headerAmount = headerAmount; return data; } fun addData(no : Int, description: String, amount : Double) : TableClass{ val data = TableClass(mContext); data.no = no; data.description = description; data.amount = amount; return data; } }
Pada class diatas terdapat dua fungsi/method yaitu
addHeader
dan addData
.addHeader berfungsi untuk menambahkan row header table.
addData berfungsi untuk menambahkan row data pada tabel.
Membuat Adapter TableAdapter
Lalu selanjutnya adalah membuat adapter yang berfungsi untuk menampung data, lalu kemudian di-set kedalam ListView sebagai data final.Simpan class adapter berikut dengan nama
TableAdapter.kt
import android.content.Context import android.graphics.Color import android.graphics.Typeface import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.LinearLayout import android.widget.TextView import com.nuslab.tablelayout.R import com.nuslab.tablelayout.model.TableClass class TableAdapter(context: Context, data: ArrayList<TableClass>) : ArrayAdapter<TableClass>(context, -1, data) { private val mContext: Context; private val dataSet: ArrayList<TableClass>; private val headerColor = Color.parseColor("#0062cc"); init { mContext = context; dataSet = data; } class ViewHolder(row : View?) { var layout : LinearLayout? = null; var txtNo : TextView? = null; var txtDescription: TextView? = null; var txtAmount: TextView? = null; init { layout = row?.findViewById(R.id.listLayout) as LinearLayout; txtNo = row?.findViewById(R.id.txtNo) as TextView; txtDescription = row?.findViewById(R.id.txtDescription) as TextView; txtAmount = row?.findViewById(R.id.txtAmount) as TextView; } } override fun getViewTypeCount(): Int { return super.getCount() } override fun getItemViewType(position: Int): Int { return position; } private var lastPosition = -1 override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val model: TableClass? = getItem(position); val viewHolder : ViewHolder; val view : View; if (convertView == null) { val inflater = mContext?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater; view = inflater.inflate(R.layout.layout_table_three, parent, false); viewHolder = ViewHolder(view); view.tag = viewHolder; }else{ view = convertView; viewHolder = view.tag as ViewHolder; viewHolder.txtNo?.text = ""; viewHolder.txtAmount?.text = ""; viewHolder.txtDescription?.text = ""; } lastPosition = position if(model?.headerNo !=null && model?.headerDesc !=null && model?.headerAmount !=null){ viewHolder.txtNo?.text = model?.headerNo.toString(); viewHolder.txtDescription?.text = model?.headerDesc.toString(); viewHolder.txtAmount?.text = model?.headerAmount.toString(); viewHolder.layout?.setBackgroundColor(headerColor); viewHolder.txtNo?.setTypeface(null, Typeface.BOLD); viewHolder.txtDescription?.setTypeface(null, Typeface.BOLD); viewHolder.txtAmount?.setTypeface(null, Typeface.BOLD); viewHolder.txtNo?.setTextColor(Color.WHITE); viewHolder.txtDescription?.setTextColor(Color.WHITE); viewHolder.txtAmount?.setTextColor(Color.WHITE); }else{ viewHolder.txtNo?.text = model?.no.toString(); viewHolder.txtDescription?.text = model?.description; viewHolder.txtAmount?.text = (String.format("%,.0f",model?.amount)); viewHolder.layout?.setBackgroundColor(Color.WHITE); viewHolder.txtNo?.setTypeface(null, Typeface.NORMAL); viewHolder.txtDescription?.setTypeface(null, Typeface.NORMAL); viewHolder.txtAmount?.setTypeface(null, Typeface.NORMAL); viewHolder.txtNo?.setTextColor(Color.BLACK); viewHolder.txtDescription?.setTextColor(Color.BLACK); viewHolder.txtAmount?.setTextColor(Color.BLACK); } return view as View; } }
[ads id="ads2"]
Penggunaan pada MainActivity
Untuk penggunaannya pada activity adalah dengan membuat variabel ArrayList<TableClass> terlebih dahulu. Dan kemudian kita dapat menambahkan data ke dalam ArrayList. Lalu akhirnya data tersebut di-set kedalam ListView.activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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="match_parent" tools:context=".MainActivity" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical|center_horizontal|center" android:textAlignment="center" android:textColor="@android:color/black" android:layout_marginTop="20dp" android:textStyle="bold" android:padding="5dp" android:text="DAFTAR HARGA PERTAMINA"/> <ListView android:id="@+id/listPertamina" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical|center_horizontal|center" android:textAlignment="center" android:textColor="@android:color/black" android:layout_marginTop="20dp" android:padding="5dp" android:textStyle="bold" android:text="DAFTAR HARGA SHELL"/> <ListView android:id="@+id/listShell" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
MainActivity.kt
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import com.nuslab.tablelayout.adapter.TableAdapter import com.nuslab.tablelayout.model.TableClass import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); val pertamina = TableClass(this); val data = ArrayList<TableClass>(); data.clear(); data.add(pertamina.addHeader("No", "Description", "Price")); data.add(pertamina.addData(1, "Pertamax Turbo", 9850.0)); data.add(pertamina.addData(2, "Pertamax", 9000.0)); data.add(pertamina.addData(3, "Pertalite", 7650.0)); data.add(pertamina.addData(4, "Premium", 6450.0)); data.add(pertamina.addData(5, "Pertamina Dex", 10200.0)); data.add(pertamina.addData(6, "Dexlite", 9500.0)); data.add(pertamina.addData(7, "Bio Solar", 9400.0)); TableAdapter(this, data).also{ adapter -> listPertamina.adapter = adapter } val shell = TableClass(this); val data2 = ArrayList<TableClass>(); data2.clear(); data2.add(shell.addHeader("No", "Description", "Price")); data2.add(shell.addData(1, "Shell Super", 9125.0)); data2.add(shell.addData(2, "Shell V-Power", 9650.0)); data2.add(shell.addData(3, "Shell Diesel", 9850.0)); data2.add(shell.addData(4, "Shell Regular", 9075.0)); TableAdapter(this, data).also{ adapter -> listShell.adapter = adapter } } }
Hasil akhirnya akan menjadi seperti berikut ini (klik gambar untuk memperbesar):
Pada tahap ini kita sudah selesai dengan cara menampilkan data dalam bentuk tabel pada Android. Untuk tahap lebih lanjut mengenai pencarian data atau filter data yang ditampilkan. Tujuan dari filter data ini salah satunya adalah untuk mencari lebih spesifik data yang ingin ditampilkan pada ListView.
Menambahkan Filter untuk Pencarian Data
Langkah selanjutnya jika ingin menambahkan filter query untuk pencarian pada ListView, maka perlu ada penambahan method getFilter() pada TableAdapter.Untuk langkah ini ada modifikasi pada beberapa method/fungsi yang digunakan. Karena filter query minimal setidaknya memiliki 2 (dua) ArrayList yang digunakan. Satu ArrayList digunakan untuk menampung data asli/original, dan satunya lagi juga digunakan untuk menampung data asli dan data yang telah terfilter sebagai hasil pencarian.
Untuk kebutuhan tersebut, maka ditambahkan variabel baru ArrayList yang bernama dataSetFilter sebagai variabel penampung data utama dan penampung data yang telah terfilter. Untuk selengkapnya dapat dilihat pada baris yang ditandai berikut ini :
import android.content.Context import android.graphics.Color import android.graphics.Typeface import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.* import com.nuslab.tablelayout.R import com.nuslab.tablelayout.model.TableClass class TableAdapter(context: Context, data: ArrayList<TableClass>) : ArrayAdapter<TableClass>(context, -1, data), Filterable { private val mContext: Context; private var dataSet: ArrayList<TableClass>; private var dataSetFilter: ArrayList<TableClass>; private val headerColor = Color.parseColor("#0062cc"); init { this.mContext = context; this.dataSet = data!!; this.dataSetFilter = data!!; } class ViewHolder(row : View?) { var layout : LinearLayout? = null; var txtNo : TextView? = null; var txtDescription: TextView? = null; var txtAmount: TextView? = null; init { layout = row?.findViewById(R.id.listLayout) as LinearLayout; txtNo = row?.findViewById(R.id.txtNo) as TextView; txtDescription = row?.findViewById(R.id.txtDescription) as TextView; txtAmount = row?.findViewById(R.id.txtAmount) as TextView; } } override fun getViewTypeCount(): Int { return this.dataSetFilter.size; } override fun getItemViewType(position: Int): Int { return position; } override fun getCount(): Int { return this.dataSetFilter.size; } override fun getItem(position: Int): TableClass? { return dataSetFilter.get(position); } private var lastPosition = -1 override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val model: TableClass? = getItem(position); val viewHolder : ViewHolder; val view : View; if (convertView == null) { val inflater = mContext?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater; view = inflater.inflate(R.layout.layout_table_three, parent, false); viewHolder = ViewHolder(view); view.tag = viewHolder; }else{ view = convertView; viewHolder = view.tag as ViewHolder; viewHolder.txtNo?.text = ""; viewHolder.txtAmount?.text = ""; viewHolder.txtDescription?.text = ""; } lastPosition = position if(model?.headerNo !=null && model?.headerDesc !=null && model?.headerAmount !=null){ viewHolder.txtNo?.text = model?.headerNo.toString(); viewHolder.txtDescription?.text = model?.headerDesc.toString(); viewHolder.txtAmount?.text = model?.headerAmount.toString(); viewHolder.layout?.setBackgroundColor(headerColor); viewHolder.txtNo?.setTypeface(null, Typeface.BOLD); viewHolder.txtDescription?.setTypeface(null, Typeface.BOLD); viewHolder.txtAmount?.setTypeface(null, Typeface.BOLD); viewHolder.txtNo?.setTextColor(Color.WHITE); viewHolder.txtDescription?.setTextColor(Color.WHITE); viewHolder.txtAmount?.setTextColor(Color.WHITE); }else{ viewHolder.txtNo?.text = model?.no.toString(); viewHolder.txtDescription?.text = model?.description; viewHolder.txtAmount?.text = (String.format("%,.0f",model?.amount)); viewHolder.layout?.setBackgroundColor(Color.WHITE); viewHolder.txtNo?.setTypeface(null, Typeface.NORMAL); viewHolder.txtDescription?.setTypeface(null, Typeface.NORMAL); viewHolder.txtAmount?.setTypeface(null, Typeface.NORMAL); viewHolder.txtNo?.setTextColor(Color.BLACK); viewHolder.txtDescription?.setTextColor(Color.BLACK); viewHolder.txtAmount?.setTextColor(Color.BLACK); } return view as View; } override fun getFilter() : Filter { return object : Filter(){ override fun publishResults(constraint: CharSequence?, results: FilterResults) { dataSetFilter = results?.values!! as ArrayList<TableClass>; notifyDataSetChanged(); } override fun performFiltering(constraint: CharSequence?): FilterResults { val charString = constraint.toString(); if (charString.isEmpty()) { dataSetFilter = dataSet; } else { val filteredList: ArrayList<TableClass> = ArrayList(); for (row in dataSetFilter) { if (row?.description.toString()!!.toLowerCase()?.contains(charString.toLowerCase())!! || row?.amount.toString()!!.toLowerCase()?.contains(charString.toLowerCase())!!) { filteredList.add(row) } } dataSetFilter = filteredList } val filterResults = FilterResults(); filterResults.values = dataSetFilter; filterResults.count = dataSetFilter.size; return filterResults; } }; return filter; } }
Jika ingin menggunakan mode pencarian pada listview atau fasilitas yang lebih lengkap, maka ganti class TableAdapter yang sebelumnya dengan menggunakan class TableAdapter yang diatas.
Lalu kita lanjutkan ke tahap berikutnya yaitu mengenai cara membuat tombol di toolbar pada Android.
Membuat Tombol Pencarian pada Toolbar di Android
Untuk membuat tombol pencarian pada toolbar di Android seperti diatas, kita memerlukan file-file pendukung seperti menu, searchable, dan sedikit modifikasi pada Android Manifest.
Yang pertama buatlah sebuah XML baru pada folder res/menu. Beri nama dan simpan dengan masing-masing nama berikut:
menu_search.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_search" android:icon="@android:drawable/ic_menu_search" android:title="Search" app:showAsAction="always|collapseActionView" app:actionViewClass="androidx.appcompat.widget.SearchView" /> </menu>
searchable.xml
<?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android="http://schemas.android.com/apk/res/android" android:hint="Search" android:label="@string/app_name" />
Hasil akhirnya jadi seperti berikut :
Lalu bukalah file AndroidManifest.xml dan tambahkan kode yang ditandai berikut ini kedalam activity yang ingin diberikan pencarian/filter. Dalam tutorial ini admin membuat sebuah Activity baru yaitu FilterActivity yang digunakan dalam contoh tutorial ini agar lebih mudah dipahami.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.nuslab.tablelayout"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".FilterActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> </intent-filter> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Lalu langkah selanjutnya adalah membuat menu pencarian agar dapat tampil dan bisa digunakan. Pada activity yang digunakan, tambahkan method onCreateOptionsMenu dan onOptionsItemSelected jika belum ada.
override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.menu_search, menu); val searchManager : SearchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager; val searchView : SearchView = menu?.findItem(R.id.action_search)?.actionView as SearchView; searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName)); searchView.maxWidth = Int.MAX_VALUE; searchView.setOnQueryTextListener( object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { mAdapter.filter.filter(query); return false; } override fun onQueryTextChange(newText: String?): Boolean { mAdapter.filter.filter(newText); return false; } }); return super.onCreateOptionsMenu(menu) } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when(item?.itemId){ android.R.id.home ->{ super.onBackPressed(); return true; } } return super.onOptionsItemSelected(item) }
Sehingga hasil akhir dari Activity diatas menjadi :
import android.app.SearchManager import android.content.Context import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.appcompat.widget.SearchView import com.nuslab.tablelayout.adapter.TableAdapter import com.nuslab.tablelayout.model.TableClass import kotlinx.android.synthetic.main.activity_filter.* class FilterActivity : AppCompatActivity() { lateinit var mAdapter : TableAdapter; override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_filter); supportActionBar?.setDisplayHomeAsUpEnabled(true); val pertamina = TableClass(this); val data = ArrayList<TableClass>(); data.clear(); data.add(pertamina.addHeader("No", "Description", "Price")); data.add(pertamina.addData(1, "Pertamax Turbo", 9850.0)); data.add(pertamina.addData(2, "Pertamax", 9000.0)); data.add(pertamina.addData(3, "Pertalite", 7650.0)); data.add(pertamina.addData(4, "Premium", 6450.0)); data.add(pertamina.addData(5, "Pertamina Dex", 10200.0)); data.add(pertamina.addData(6, "Dexlite", 9500.0)); data.add(pertamina.addData(7, "Bio Solar", 9400.0)); mAdapter = TableAdapter(this, data).also{ adapter -> listviewFilter.adapter = adapter } } override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.menu_search, menu); val searchManager : SearchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager; val searchView : SearchView = menu?.findItem(R.id.action_search)?.actionView as SearchView; searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName)); searchView.maxWidth = Int.MAX_VALUE; searchView.setOnQueryTextListener( object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { mAdapter.filter.filter(query); return false; } override fun onQueryTextChange(newText: String?): Boolean { mAdapter.filter.filter(newText); return false; } }); return super.onCreateOptionsMenu(menu) } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when(item?.itemId){ android.R.id.home ->{ super.onBackPressed(); return true; } } return super.onOptionsItemSelected(item) } }
Sampai pada langkah ini kita telah selesai membuat listview dengan tampilan tabel dan juga pencarian data pada listview. Terima kasih sudah membaca dan untuk yang membutuhkan source code sebagai panduan atau sekedar pembelajaran lebih lanjut, dapat kalian download di https://drive.google.com/open?id=1-Cljr95XB5j2120-GDEpdYJ4q0fj1Uwh
Semoga bermanfaat.
Posting Komentar