前回の記事でOsmdroidの機能を使い、自位置を地図に反映させました。 今回は緯度経度による地図の検索と画面のタップによる緯度経度の表示機能を追加したいと思います。
緯度経度の表示
Androdid端末の画面(地図)をタップしたときに、その地点の緯度経度を吹き出しで表示させます。
オーバーレイとしてMapEventsOverlay
を追加し、タップされた位置をMapEventsReceiver
で受け取ります。MapEventsReceiver
は短押し(タップ)と長押しの判別が可能ですが、今回は短押しのみの実装となります。
val mapEventReceiver = object : MapEventsReceiver{ override fun singleTapConfirmedHelper(p: GeoPoint?): Boolean { val toast = Toast.makeText( baseContext, "${getString(R.string.latitude)}: ${p?.latitude}\n${getString(R.string.longitude)}: ${p?.longitude}", Toast.LENGTH_LONG) toast.setGravity(BOTTOM, 0, 200) toast.show() return false } override fun longPressHelper(p: GeoPoint?): Boolean { return false } } mapView.overlays.add(MapEventsOverlay(mapEventReceiver))
売り上げランキング: 2,864
緯度経度による地図の検索
緯度経度の表示はオーバーレイを追加するのみで実装は簡単でした。
緯度経度からの検索は
- 検索ボタンの押下
- 緯度経度を記入する画面の表示
- 検索する緯度経度を取得
- 記入された緯度経度を地図のセンターとして表示
といくつか段階をふむ必要があります。
もちろん地図画面上に緯度経度を記入するテキストボックスを常時表示しておくことも可能ですが、地図が見づらくなってしまうので記入するボックスは別画面としたほうがよさそうです。
検索ボタンの設置
前回記事のセンターボタン同じイメージビューの設置ではなく、オプションメニューを設置します。オプションメニューに「戻る」、「検索」のメニューをいれ、うち「検索」は常時表示させるようにします。
メニューレイアウト
resディレクトリにmenuディレクトリを作成し、xmlファイルを作成します(res/layout/menu/option.xml)。
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_findByLL" android:icon="@android:drawable/ic_menu_search" android:title="@string/findByLL" app:showAsAction="always" /> <item android:id="@+id/menu_back" android:title="@string/menuBack" /> </menu>l
オプションメニューを表示する関数(MainActivity.kt)
オプションメニューを作成するonCreateOptionsMenu
とオプション選択結果を受け取るonOptionsItemSelected
を追加します。
検索オプションが選択された場合は緯度経度記入用ダイアログを開きます。
override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.option, menu) return super.onCreateOptionsMenu(menu) } override fun onOptionsItemSelected(item: MenuItem): Boolean { return when(item.itemId) { R.id.menu_findByLL -> { FindByLLDialogFragment().show(supportFragmentManager, "find") return true } R.id.menu_back -> { return true } else -> super.onOptionsItemSelected(item) } }
緯度経度記入ダイアログレイアウト
res/layoutディレクトリにxmlファイルを作成します(res/layout/dialog_findll.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" android:layout_width="300dp" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/header" android:layout_width="match_parent" android:layout_height="60dp" android:background="#FFC107" android:gravity="center" android:text="@string/findByLL" android:textSize="24sp" /> <EditText android:id="@+id/latitude" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:ems="10" android:hint="@string/latitude" android:importantForAutofill="no" android:inputType="numberDecimal" android:singleLine="true" android:textSize="24sp" /> <EditText android:id="@+id/longitude" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:ems="10" android:hint="@string/longitude" android:importantForAutofill="no" android:inputType="numberDecimal" android:singleLine="true" android:textSize="24sp" /> </LinearLayout>
ダイアログ表示クラス
緯度経度を記入するダイアログを表示するクラスです。"Ok"が押下された場合はリスナーで受け取ります。
import android.app.AlertDialog import android.app.Dialog import android.content.Context import android.os.Bundle import androidx.fragment.app.DialogFragment import java.lang.IllegalStateException import kotlin.ClassCastException class FindByLLDialogFragment: DialogFragment() { private lateinit var listener: NoticeDialogListener interface NoticeDialogListener { fun onDialogPositiveClick(dialog: DialogFragment) // fun onDialogNegativeClick(dialog: DialogFragment) } override fun onAttach(context: Context) { super.onAttach(context) try { listener = context as NoticeDialogListener } catch (e: ClassCastException) { throw ClassCastException("$context must implement NoticeDialogListener") } } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return activity?.let { val builder = AlertDialog.Builder(it) val inflater = requireActivity().layoutInflater builder.setView(inflater.inflate(R.layout.dialog_findbyll, null)) .setPositiveButton("Ok", ({_, _ -> listener.onDialogPositiveClick(this) })) .setNegativeButton("Cancel", ({dialog, _ -> dialog?.cancel() })) builder.create() } ?: throw IllegalStateException("Activity cannot be null") } }
ダイアログのリスナー(MainActivity.kt)
ダイアログ結果を受け取るリスナーです。
単純に記載位置をセンターにして、値チェックの実装は省いています。またMyLocationNewOverlay
により自位置がセンターになってしまうのを止めるため、同機能をDisableにしています。
override fun onDialogPositiveClick(dialog: DialogFragment) { val lat = dialog.dialog?.findViewById<EditText>(R.id.latitude)?.text.toString().toDouble() val lon = dialog.dialog?.findViewById<EditText>(R.id.longitude)?.text.toString().toDouble() locationOverlay.disableFollowLocation() mapView.controller.animateTo(GeoPoint(lat, lon)) }