مستندات جامع اندروید
نسخه 1.0.3
هدف از این بخش از پروژه، مسیریابی بین دو نقطه از نقشه ، به وسیله سرویس مسیریابی نشان است. با استفاده از این سرویس ، بهترین مسیر جهت رفتن از نقطه مبدا به نقطه مقصد محاسبه می شود.
برای آشنایی سریع با وب سرویس مسیریابی نشان ، میتوانید از لینک زیر استفاده کنید
در این پروژه برای ارسال درخواست مسیریابی و دریافت نتیجه از SDK سرویس های نشان استفاده شده است که در ادامه با نحوه استفاده از آن آشنا می شویم.
دسترسی زیر را برای استفاده از اینترنت به فایل مانیفست برنامه اضافه کنید:
<uses-permission android:name="android.permission.INTERNET"/>
activity_routing.xml
:
در این صفحه علاوه بر المان نقشه ، دوToggleButton
برای فعالسازی نمایش مسیر دریافت شده از سرور بر روی نقشه وجود دارد و یک Guideline
که جهت کمک به چیدمان این 2 کلید استفاده شده است. به این شکل که Guideline
در وسط صفحه قرار گرفته و ToggleButton
ها یکی در سمت چپ و یکی در سمت راست آن ها قرار می گیرند.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="org.neshan.sample.starter.activity.Routing">
<org.neshan.mapsdk.MapView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/map"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<ToggleButton
android:id="@+id/overviewToggleButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/toggle_button_bg"
android:checked="false"
android:drawablePadding="8dp"
android:drawableTint="@color/toggle_button_text_color"
android:elevation="8dp"
android:onClick="findRoute"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/toggle_button_text_color"
android:textOff="رسم Overview"
android:textOn="رسم Overview"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent"
tools:targetApi="m" />
<ToggleButton
android:id="@+id/stepByStepToggleButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/toggle_button_bg"
android:checked="false"
android:drawablePadding="8dp"
android:drawableTint="@color/toggle_button_text_color"
android:elevation="8dp"
android:onClick="findRoute"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/toggle_button_text_color"
android:textOff="رسم StepByStep"
android:textOn="رسم StepByStep"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline"
tools:targetApi="m" />
</androidx.constraintlayout.widget.ConstraintLayout>
RoutingActivity.kt
:
متد initLayoutRefrences
برای دادن مقادیر اولیه به تمامی المانهای مربوط به رابط کاربری نوشته شدهاست.
همچنین Listener مربوط به نقشه نیز در همین متد قرار دارد .
با افزودن setOnMapLongClickListener
به نقشه پس از هر بار کلیک طولانی بر روی نقشه بررسی می شود که اگر تعداد نشانه های روی نقشه کمتر از 2 عدد است، یک نشانه جدید به نقشه اضافه شود و اگر با افزودن نشانه جدید، تعداد نشانه ها 2 عدد شد، یعنی مبدا و مقصد تعیین شده و متد neshanRoutingApi
جهت دریافت مسیر صدا زده می شود تا مسیریابی انجام شود.
// Initializing layout references (views, map and map events)
private fun initLayoutReferences() {
// Initializing views
initViews()
// Initializing mapView element
initMap()
// when long clicked on map, a marker is added in clicked location
map.setOnMapLongClickListener {
if (markers.size < 2) {
markers.add(addMarker(it));
if (markers.size == 2) {
runOnUiThread {
overviewToggleButton.isChecked = true
neshanRoutingApi();
}
}
} else {
runOnUiThread {
Toast.makeText(this@RoutingActivity,"مسیریابی بین دو نقطه انجام میشود!",Toast.LENGTH_SHORT).show()
}
}
}
}
در متد initViews
ارتباط بین View
های فایل activity_routing.xml
و اشیای مربوطه در جاوا برقرار شده است. همچین یک OnCheckedChangeListener
تعریف و به هر دو ToggleButton
اضافه می شود تا با فعال شدن هر کدام، کلید دیگر غیرفعال شود و در صورت نیاز، مسیر ترسیم شده بر روی نقشه که در متغیر onMapPolyline
ذخیره شده، از روی نقشه حذف شود.
// We use findViewByID for every element in our layout file here
private fun initViews() {
map = findViewById(R.id.mapview)
// CheckChangeListener for Toggle buttons
val changeChecker = CompoundButton.OnCheckedChangeListener { toggleButton, isChecked -> // if any toggle button checked:
if (isChecked) {
// if overview toggle button checked other toggle button is uncheck
if (toggleButton === overviewToggleButton) {
stepByStepToggleButton.isChecked = false
overview = true
}
if (toggleButton === stepByStepToggleButton) {
overviewToggleButton.isChecked = false
overview = false
}
}
if (!isChecked && onMapPolyline != null) {
map.removePolyline(onMapPolyline)
}
}
// each toggle button has a checkChangeListener for uncheck other toggle button
overviewToggleButton = findViewById(R.id.overviewToggleButton)
overviewToggleButton.setOnCheckedChangeListener(changeChecker)
stepByStepToggleButton = findViewById(R.id.stepByStepToggleButton)
stepByStepToggleButton.setOnCheckedChangeListener(changeChecker)
}
قبل از بررسی متد neshanRoutingApi
، یادآوری می کنیم که برای استفاده از سرویس های نشان به API_KEY
مرتبط با سرویس مورد نظر خود نیاز دارید که میتوانید به صورت رایگان از پنل توسعه دهندگان نشان بدست آروید .
این متد بعد از تعیین دو نقطه مبدا و مقصد صدا زده می شود. ابتدا با کمک متد Builder از کلاس NeshanDirection
مقادیر مورد نیاز مبدا، مقصد، کلید دسترسی و سایر پارامترهای ممکن ست شده و با صدا زدن متد build یک شی از نوع NeshanDirection
ساخته شده می شود. اکنون با صدا زدن متد call این شیء ساخته شده میتوانیم درخواست مسیریابی را به سرور ارسال نماییم. ورودی این متد یک Callback است که با override کردن تابع onResponse
و onFailure
میتوان از نتیجه وب سرویس در آن مطلع شد.
سپس با استفاده از متد Builder
از کلاس NeshanSearch
، پارامترهای ورودی وب سرویس مقدار دهی می شود و شی از نوع NeshanSearch
با صدا زدن متد build
ساخته می شود. حال با صدا زدن متد call
این شی می توانیم وب سرویس را صدا زده و نتیجه آن را در Callback
که به عنوان ورودی متد call
تعریف کرده ایم دریافت کنیم.
در صورت موفقیت آمیز بودن دریافت پاسخ از سرور، onResponse
فراخوانی می شود و بدنه ( body
) پارامتر response
که از نوع NeshanDirectionResult
است شامل اطلاعات مسیرهای پیشنهادی خواهد بود. ( توضیحات اجزای پاسخ وب سرویس مسیریابی را می توانید در اینجا ببینید ) مسیرها در پاسخی که از سرور دریافت می شوند به شکل encode
شده هستند که می توان با استفاده از متد decode
از کلاس PolylineEncoding
آن ها را به آرایه ای از نقاط تبدیل و در رسم روی نقشه استفاده کرد.
// request routing method from Neshan Server
private fun neshanRoutingApi() {
NeshanDirection.Builder(
"service.VNlPhrWb3wYRzEYmstQh3GrAXyhyaN55AqUSRR3V",
markers[0].latLng,
markers[1].latLng
)
.build().call(object : Callback<NeshanDirectionResult?> {
override fun onResponse(
call: Call<NeshanDirectionResult?>,
response: Response<NeshanDirectionResult?>
) {
// two type of routing
if (response.body() != null && response.body()!!.routes != null && !response.body()!!.routes.isEmpty()
) {
val route: Route = response.body()!!.routes[0]
routeOverviewPolylinePoints = java.util.ArrayList(
PolylineEncoding.decode(
route.overviewPolyline.encodedPolyline
)
)
decodedStepByStepPath = java.util.ArrayList()
// decoding each segment of steps and putting to an array
for (step in route.legs[0].directionSteps) {
decodedStepByStepPath!!.addAll(PolylineEncoding.decode(step.encodedPolyline))
}
onMapPolyline = Polyline(routeOverviewPolylinePoints, getLineStyle())
//draw polyline between route points
map.addPolyline(onMapPolyline)
// focusing camera on first point of drawn line
mapSetPosition(overview)
} else {
Toast.makeText(this@RoutingActivity, "مسیری یافت نشد", Toast.LENGTH_LONG)
.show()
}
}
override fun onFailure(call: Call<NeshanDirectionResult?>, t: Throwable) {}
})
}