تغییر زاویه دوربین (Tilt) – کاتلین

مستندات جامع اندروید

نسخه 1.0.3

هدف این بخش از پروژه تغییر زاویه (یا شیب) دوربین است. تغییر شیب دوربین در ایجاد حالت navigation یا برای مشاهده ساختمان‌های سه‌بعدی کاربرد دارد.

فهرست مطالب این صفحه

activity_tilt_camera.xml:

در فایل لی‌اوت این بخش از پروژه، علاوه بر المان نقشه نشان، یک LinearLayout وجود دارد که در آن یک SeekBar   وجود دارد که برای تنظیم میزان شیب به کار می‌رود. نکته‌ای که در SeekBar وجود دارد این است که میزان مینیمم و ماکزیمم آن به ترتیب ۰ و ۶۰ است در حالی که همان‌طور که گفته شد این میزان برای نقشه نشان برابر با ۳۰ و ۹۰ است، پس در طول برنامه برای استفاده از مقداری که این SeekBar مشخص می‌کند، آن مقدار را با ۳۰ جمع می‌کنیم.

            <?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=".activity.TiltCameraActivity">

    <org.neshan.mapsdk.MapView
        android:id="@+id/mapview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:background="@drawable/toggle_button_bg"
        android:checked="true"
        android:elevation="8dp"
        android:onClick="toggleCameraTilt"
        android:paddingStart="8dp"
        android:paddingEnd="8dp"
        android:textColor="@color/white"
        android:textOff="تغییر زاویه دوربین"
        android:textOn="تغییر زاویه دوربین"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        tools:targetApi="m" />

    <LinearLayout
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="32dp"
        android:background="@drawable/rounded_white_bg"
        android:gravity="center"
        android:orientation="horizontal"
        android:padding="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="۳۰ درجه"
            android:textColor="@color/black" />

        <!--it is not possible to set min range for seek bar (default value = 0)-->
        <!--so we get seek bar range 0-60 (camera tilt range in neshan is 30-90)-->
        <!--and add 30 to every read value of seek bar-->
        <SeekBar
            android:id="@+id/tilt_seek_bar"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="4dp"
            android:layout_marginEnd="4dp"
            android:layout_weight="99"
            android:max="60"
            android:progress="60"
            android:progressDrawable="@drawable/seek_bar_bg"
            android:thumb="@drawable/seek_bar_thumb" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="۹۰ درجه"
            android:textColor="@color/black" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
                
        

TiltCameraActivity.kt:

متد initLayoutRefrences جهت مقداردهی اولیه کردن به تمامی المان‌های مربوط به رابط کاربری نوشته شده‌است. به دلیل این که لازم است تا المان اندرویدی نقشه نشان ابتدا به طور کامل ایجاد شود و سپس با آن تعامل برقرار شود (متدهای مختلف بر روی المان نقشه اجرا شود)، تمامی متدهای مربوط به رابط کاربری باید در متد onStart انجام شوند.

            override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_draw_polygon)
    }

    override fun onStart() {
        super.onStart()
        // everything related to ui is initialized here
        initLayoutReferences()
    }
        

در این‌جا بر روی tiltSeekBar یک OnSeekBarChangeListener ست شده است. هرگاه متد onProgressChanged صدا زده شود، مقدار progress را با عدد ۳۰ جمع کرده (به دلیلی که در بالاتر توضیح داده شد) و این مقدار را به عنوان مقدار فیلد tilt که بر روی map صدا زده می‌شود داده می‌شود.

متد tilt میزان زاویه (یا شیب) نقشه را تنظیم می‌کند.

ممکن است کاربر بدون استفاده از tiltSeekBar و با استفاده از دو انگشت و با gesture های از پیش تعریف شده بر روی نقشه، میزان زاویه را تغییر دهد. برای این که بعد از تغییر در این‌حالت، progress نشان داده‌شده بر روی tiltSeekBar نیز مقدار درستی داشته باشد، یک OnCameraMoveListener برای تشخیص تغییر دوربین به map ست می‌شود و در هنگام حرکت نقشه، میزان زاویه نقشه گرفته شده، ۳۰ واحد از آن کم‌می‌شود، به بالا گرد می‌شود و به فیلد progress – که بر روی tiltSeekBar وجود دارد – داده می‌شود.

در متد onMapMoved، همین متد بر روی super نیز صدا زده می‌شود تا عملیاتی که در SDK برای زمانی که نقشه حرکت می‌کند پیاده‌سازی شده است نیز انجام شود.

برای به‌روز‌رسانی میزان پیشرفت نشان‌داده شده بر روی SeekBar موجود در رابط کاربری، بهتر است نخ جدیدی در رابط کاربری ایجاد کرده و این کار را به جای این‌ که در نخی که نقشه در آن وجود دارد، در نخ جدید انجام دهیم. دلیل این کار این است که عملیات مربوط به نمایش نقشه، عملیات سنگینی است و در صورتی که بخواهیم تغییرات دیگر عناصر رابط کاربری را نیز در این نخ انجام دهیم، ممکن است به‌روزرسانی‌های نقشه یا دیگر المان‌های رابط کاربری به درستی انجام نشود. برای ایجاد یک نخ رابط‌کاربری جدید، متد runOnUiThread را صدا زده، یک شی Runnable جدید ایجاد کرده، تابع run را Override کرده و عملیاتی که برای آپدیت دیگر المان‌های رابط کاربری باید انجام شود را در این متد می‌نویسیم.

            // Initializing layout references (views, map and map events)
    private fun initLayoutReferences() {
        // Initializing views
        initViews()
        // Initializing mapView element
        initMap()

        //tilt camera
        seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {

            override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
                map.setTilt(seekBar.progress + 30f, 0f)
            }

            override fun onStartTrackingTouch(p0: SeekBar?) {

            }

            override fun onStopTrackingTouch(p0: SeekBar?) {
                Log.d("asd", seekBar.progress.toString());
            }
        })

        map.setOnCameraMoveListener {
            runOnUiThread {
                seekBar.progress = Math.round(map.tilt) - 30
            }
        }
    }
        

هنگامی که ToggleButton موجود در رابط کاربری، در حالت انتخاب شده باشد، با استفاده از شی Settings که یکی از فیلدهای المان نقشه است و مقداردهی فیلدهای minTiltAngle و maxTiltAngle بر روی آن، حداقل و حداکثر بازه تنظیم شیب نقشه بین ۳۰ درجه تا ۹۰ درجه (تمام مقدار ممکن) انتخاب می‌شود. در صورتی که ToggleButton انتخاب نشده باشد، حداقل و حداکثر بازه تنظیم برابر با درجه فعلی شیب نقشه قرار می‌گیرد و در این صورت امکان تغییر شیب وجود نخواهد داشت.

            fun toggleCameraTilt(view: View) {
        val toggleButton = view as ToggleButton
        if (toggleButton.isChecked) {
            //set tilt range from 30 to 90 degrees
            map.settings.minTiltAngle = 30f
            map.settings.maxTiltAngle = 90f
        } else {
            //set tilt range to 1 degree (only current tilt degree)
            map.settings.minTiltAngle = map.tilt
            map.settings.maxTiltAngle = map.tilt
        }
    }