Membuat Aplikasi Informasi Gempa |
Tapi sebelum membuat aplikasi ini, ada baiknya kalian buat terlebih dahulu API KEY pada Google Cloud Platform. Login dengan akun Google kalian lalu buka menu Console, pilih API & Layanan. Setelah itu kalian pilih menu AKTIFKAN API DAN LAYANAN. Kemudian aktifkan API KEY Maps. Oh ya, untuk Rest API saya menggunakan Retrofit. Karena menurut saya Retrofit cukup mudah untuk pemula, termasuk saya sendiri. Yang penting kalian paham😂
Jika kalian ingin SOURCE CODE sample aplikasi ini, silahkan download di GITHUB saya DISINI. Tetapi jika kalian ingin tahu cara mengaplikasikannya, silahkan lanjut baca artikel ini sampai selesai.
Untuk kalian yang ingin mencoba melihat Preview aplikasi ini dengan versi video, berikut saya berikan Videonya:
Jangan lupa subscribe Channel Youtube saya juga ya Azhar Rivaldi, karena disana ada banyak tutorial-tutorial untuk membuat aplikasi lainnya. Oke langsung saja tanpa basa-basi lagi kita langsung ke langkah pertama :
1. Buat project baru di Android Studio dengan cara klik File ⇒ Project Baru. Ketika diminta untuk memilih Default Activity, pilih Empty Activity dan klik next. Untuk minSDK, disini saya set API 21 ya.
2. Tambahkan library Google Play Service dan Retrofit di build.gradle :
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-simplexml:2.1.0'
}
3. Ubah sedikit Android Manifest.xml :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.azhar.gempadetector">
<uses-permission android:name="android.permission.INTERNET" />
<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"
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR API KEY" />
</application>
</manifest>
4. Buat MainActivity.kt dan activity_main.xml untuk menampilkan menu utama aplikasi :
package com.azhar.gempadetector
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import com.azhar.gempadetector.adapter.GempaAdapter
import com.azhar.gempadetector.api.ApiInstance
import com.azhar.gempadetector.model.DataGempa
import kotlinx.android.synthetic.main.activity_main.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity(), View.OnClickListener {
@SuppressLint("CheckResult")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
website.setOnClickListener(this)
val getData = ApiInstance.create().getData()
getData.enqueue(object : Callback {
override fun onFailure(call: Call, t: Throwable) {
onError(t)
}
override fun onResponse(
call: Call,
response: Response
) {
val layoutManager = LinearLayoutManager(this@MainActivity)
val offside = ItemOffsetDecoration(20)
val adapter = GempaAdapter(response.body()!!.gempa!!, itemClick)
list_gempa.apply {
setLayoutManager(layoutManager)
addItemDecoration(offside)
setAdapter(adapter)
}
progress_loader.visibility = View.GONE
}
})
}
override fun onClick(v: View) {
when (v.id) {
R.id.website -> {
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://github.com/AzharRivaldi")
}
startActivity(intent)
}
}
}
private val itemClick = object : ItemClick {
override fun OnItemClickRecycler(gempa: DataGempa.Gempa) {
val snackbar = Snackbar.make(
main_layout,
"Dirasakan (skala MMI): ${gempa.dirasakan}",
Snackbar.LENGTH_INDEFINITE
)
val snackView = snackbar.view
val textMsg = snackView.findViewById(R.id.snackbar_text)
textMsg.maxLines = 20
snackbar.setAction("Tutup") {
snackbar.dismiss()
}
snackbar.show()
}
}
private fun onError(it: Throwable?) {
Log.d("Gagal mendapatkan data!", it!!.message)
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="100dp"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title="@string/app_name">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="20sp" />
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/design_header"
android:contentDescription="@null"
android:scaleType="fitXY" />
</LinearLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_gempa"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<ProgressBar
android:id="@+id/progress_loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/website"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_web" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
5. Buat GempaAdapter.kt dan list_item_gempa.xml untuk menampilkan daftar gempa :
package com.azhar.gempadetector.adapter
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.azhar.gempadetector.ItemClick
import com.azhar.gempadetector.R
import com.azhar.gempadetector.model.DataGempa
import com.google.android.gms.maps.*
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import kotlinx.android.synthetic.main.list_item_gempa.view.*
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
class GempaAdapter(var dataList: ArrayList,
var itemClick: ItemClick
) : RecyclerView.Adapter() {
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): Holder {
val view = LayoutInflater.from(p0.context).inflate(R.layout.list_item_gempa, p0, false)
return Holder(view)
}
override fun getItemCount(): Int {
return dataList.size
}
override fun onBindViewHolder(p0: Holder, p1: Int) {
val dataGempa = dataList[p1]
var patternDate = ""
var patternFormat = ""
when {
dataGempa.tanggal!!.contains("WIB") -> {
patternDate = "dd/MM/yyyy-HH:mm:ss 'WIB'"
patternFormat = "EEE dd MMM yyyy / HH:mm:ss 'WIB'"
}
dataGempa.tanggal!!.contains("WIT") -> {
patternDate = "dd/MM/yyyy-HH:mm:ss 'WIT'"
patternFormat = "EEE dd MMM yyyy / HH:mm:ss 'WIT'"
}
dataGempa.tanggal!!.contains("WITA") -> {
patternDate = "dd/MM/yyyy-HH:mm:ss 'WITA'"
patternFormat = "EEE dd MMM yyyy / HH:mm:ss 'WITA'"
}
}
val parseDateFormat = SimpleDateFormat(patternDate)
val dateFormat = SimpleDateFormat(patternFormat, Locale("id"))
val date = parseDateFormat.parse(dataGempa.tanggal)
p0.itemView.tanggal.text = dateFormat.format(date)
p0.itemView.posisi.text = "Koordinat : ${dataGempa.posisi}"
p0.itemView.magnitude.text = "M ${dataGempa.magnitude}"
p0.itemView.kedalaman.text = "Kedalaman : ${dataGempa.kedalaman}"
p0.itemView.keterangan.text = dataGempa.keterangan
p0.setLocation(dataGempa)
p0.itemView.setOnClickListener {
itemClick.OnItemClickRecycler(dataGempa)
}
}
class Holder(itemView: View) : RecyclerView.ViewHolder(itemView), OnMapReadyCallback {
private var mapView: MapView = itemView.findViewById(R.id.map_item)
var gMap: GoogleMap? = null
lateinit var mapData: DataGempa.Gempa
init {
mapView.onCreate(null)
mapView.getMapAsync(this)
}
override fun onMapReady(p0: GoogleMap?) {
MapsInitializer.initialize(itemView.context)
gMap = p0!!
gMap!!.uiSettings.isMapToolbarEnabled = false
updateLocation()
}
fun setLocation(mapLoc: DataGempa.Gempa) {
mapData = mapLoc
if (gMap != null) {
updateLocation()
}
}
private fun updateLocation() {
gMap!!.clear()
val stringLatlng = mapData.point?.coordinates?.split(",")
val lat = stringLatlng?.get(0)?.toDouble()
val lng = stringLatlng?.get(1)?.toDouble()
val marker = LatLng(lat!!, lng!!)
gMap!!.addMarker(MarkerOptions().position(marker).flat(true))
gMap!!.moveCamera(CameraUpdateFactory.newLatLngZoom(marker, 5f))
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="10dp"
card_view:cardElevation="4dp"
card_view:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.gms.maps.MapView xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/map_item"
android:layout_width="match_parent"
android:layout_height="150dp"
map:liteMode="true"
map:mapType="normal" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorPrimary" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/tanggal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tanggal"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/posisi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Koordinat"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/kedalaman"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Kedalaman"
android:textColor="@android:color/black" />
</LinearLayout>
<TextView
android:id="@+id/magnitude"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:text="Skala"
android:textAlignment="textEnd"
android:textColor="@android:color/holo_red_dark"
android:textSize="25sp" />
</LinearLayout>
<TextView
android:id="@+id/keterangan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Keterangan"
android:textColor="@android:color/black" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
6. Buat Class ApiInstance.kt untuk API-nya :
package com.azhar.gempadetector.api
import com.azhar.gempadetector.model.DataGempa
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.simplexml.SimpleXmlConverterFactory
import retrofit2.http.GET
interface ApiInstance {
@GET("/gempadirasakan.xml")
fun getData(): Call
companion object {
fun create(): ApiInstance {
val retrofit = Retrofit.Builder()
.addConverterFactory(SimpleXmlConverterFactory.create())
.baseUrl("http://data.bmkg.go.id")
.build()
return retrofit.create(ApiInstance::class.java)
}
}
}
7. Buat Class DataGempa.kt dan ItemClick.kt untuk Modelnya :
package com.azhar.gempadetector.model
import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root
class DataGempa {
@Root(name = "point")
class Point {
@field:Element(name = "coordinates")
var coordinates: String? = null
}
@Root(name = "Gempa", strict = false)
class Gempa {
@field:Element(name = "Dirasakan")
var dirasakan: String? = null
@field:Element(name = "point")
var point: Point? = null
@field:Element(name = "Magnitude")
var magnitude: String? = null
@field:Element(name = "Tanggal")
var tanggal: String? = null
@field:Element(name = "Posisi")
var posisi: String? = null
@field:Element(name = "Kedalaman")
var kedalaman: String? = null
@field:Element(name = "Keterangan")
var keterangan: String? = null
@field:Element(name = "_symbol")
var symbol: String? = null
}
@Root
class Infogempa {
@field:ElementList(name = "Gempa", inline = true)
var gempa: ArrayList? = null
}
}
package com.azhar.gempadetector
import com.azhar.gempadetector.model.DataGempa
interface ItemClick {
fun OnItemClickRecycler(gempa: DataGempa.Gempa)
}
8. Terakhir buat Class ItemOffsetDecoration.kt untuk menyesuaikan tampilan List Gempa agar sesuai dengan layar smartphone kalian :
package com.azhar.gempadetector
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
class ItemOffsetDecoration @JvmOverloads constructor(
private val spacing: Int,
private var displayMode: Int = -1
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildViewHolder(view).adapterPosition
val itemCount = state.itemCount
val layoutManager = parent.layoutManager
setSpacingForDirection(outRect, layoutManager, position, itemCount)
}
private fun setSpacingForDirection(
outRect: Rect,
layoutManager: RecyclerView.LayoutManager?,
position: Int,
itemCount: Int
) {
if (displayMode == -1) {
displayMode = resolveDisplayMode(layoutManager)
}
when (displayMode) {
HORIZONTAL -> {
outRect.left = spacing
outRect.right = if (position == itemCount - 1) spacing else 0
outRect.top = spacing
outRect.bottom = spacing
}
VERTICAL -> {
outRect.left = spacing
outRect.right = spacing
outRect.top = spacing
outRect.bottom = if (position == itemCount - 1) spacing else 0
}
GRID -> if (layoutManager is GridLayoutManager) {
val gridLayoutManager = layoutManager as GridLayoutManager?
val cols = gridLayoutManager!!.spanCount
val rows = itemCount / cols
outRect.left = spacing
outRect.right = if (position % cols == cols - 1) spacing else 0
outRect.top = spacing
outRect.bottom = if (position / cols == rows - 1) spacing else 0
}
}
}
private fun resolveDisplayMode(layoutManager: RecyclerView.LayoutManager?): Int {
if (layoutManager is GridLayoutManager) return GRID
return if (layoutManager!!.canScrollHorizontally()) HORIZONTAL else VERTICAL
}
companion object {
val HORIZONTAL = 0
val VERTICAL = 1
val GRID = 2
}
}
9. Selesai dan kalian Run. Jika kalian mengikuti langkah-langkah diatas dengan baik, pasti aplikasi yang kalian buat akan berjalan sebagaimana mestinya. Namun jika mengalami Error, silahkan berikan komentar dan kita diskusikan bersama.
Demikian informasi yang saya bagikan untuk kalian. Jangan lupa bagikan artikel ini ke teman-teman kalian agar ikut membaca Tutorial Membuat Aplikasi Informasi Gempa dengan Android Studio ini. Subscribe juga blog Rivaldi 48 ini agar kalian mendapatkan notifikasi saat Admin update artikel terbaru. Semoga kalian lebih nyaman dan mudah dalam mengakses Blog Rivaldi 48 dimanapun kalian berada. Terima Kasih. Follow Instagram Admin @azhardvls_