Tutorial Membuat Aplikasi Informasi Gempa dengan Android Studio

    Tutorial Membuat Aplikasi Informasi Gempa dengan Android Studio
    Membuat Aplikasi Informasi Gempa
    السَّلاَمُ عَلَيْكُمْ وَرَحْمَةُ اللهِ وَبَرَكَاتُه sobat 48😁. Bagaimana kabarnya? Semoga selalu dalam keadaan sehat ya. Menjelang akhir tahun 2019 ini saya akan membagikan sebuah artikel lagi yang membahas tentang Tutorial Membuat Aplikasi Informasi Gempa dengan Android Studio. Aplikasi Pencarian Aplikasi Informasi Gempa ini dibuat menggunakan Google Maps API dan Data BMKG. Bahasa yang digunakan untuk membuat aplikasi ini sepenuhnya pakai Kotlin. Buat kalian yang sedang skripsi, mungkin artikel ini bisa dijadikan referensi😊

    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_