• Optimization
  • Navigation
  • Tracking
  • Maps
  • Places

MapView CameraPosition Method

This example shows CameraPosition Method on MapView

  • CameraPosition Method

    • target

    • zoom

    • bearing

    • tilt

For all code examples, refer to Android Maps SDK Code Examples

activity_camera_position.xml view source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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"
    android:id="@+id/coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ai.nextbillion.maps.core.MapView
        android:id="@id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:nbmap_uiAttributionTintColor="@color/redAccent" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@drawable/ic_input"
        app:backgroundTint="@android:color/white" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

CameraPositionActivity view source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
package ai.nextbillion;

import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import ai.nextbillion.maps.camera.CameraPosition;
import ai.nextbillion.maps.camera.CameraUpdateFactory;
import ai.nextbillion.maps.core.MapView;
import ai.nextbillion.maps.core.NextbillionMap;
import ai.nextbillion.maps.core.OnMapReadyCallback;
import ai.nextbillion.maps.geometry.LatLng;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import static ai.nextbillion.maps.constants.GeometryConstants.MAX_LATITUDE;
import static ai.nextbillion.maps.constants.GeometryConstants.MIN_LATITUDE;

/**
 * Test activity showcasing how to listen to camera change events.
 */
public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback, View.OnClickListener,
        NextbillionMap.OnMapLongClickListener {

    private MapView mapView;
    private NextbillionMap nextbillionMap;
    private FloatingActionButton fab;
    private boolean logCameraChanges;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_position);
        mapView = findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this);
    }

    @Override
    public void onMapReady(@NonNull final NextbillionMap map) {
        nextbillionMap = map;
        map.setStyle(StyleConstants.LIGHT, style -> {
            // add a listener to FAB
            fab = findViewById(R.id.fab);
            fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.palette_mint_100));
            fab.setOnClickListener(this);

            toggleLogCameraChanges();

            // listen to long click events to toggle logging camera changes
            nextbillionMap.addOnMapLongClickListener(this);
        });
    }

    @Override
    public boolean onMapLongClick(@NonNull LatLng point) {
        toggleLogCameraChanges();
        return false;
    }

    @Override
    public void onClick(View view) {
        Context context = view.getContext();
        final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null);
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle(R.string.dialog_camera_position);
        builder.setView(onInflateDialogContent(dialogContent));
        builder.setPositiveButton("Animate", new DialogClickListener(nextbillionMap, dialogContent));
        builder.setNegativeButton("Cancel", null);
        builder.setCancelable(false);
        builder.show();
    }

    private void toggleLogCameraChanges() {
        logCameraChanges = !logCameraChanges;
        if (logCameraChanges) {
            nextbillionMap.addOnCameraIdleListener(idleListener);
            nextbillionMap.addOnCameraMoveCancelListener(moveCanceledListener);
            nextbillionMap.addOnCameraMoveListener(moveListener);
            nextbillionMap.addOnCameraMoveStartedListener(moveStartedListener);
        } else {
            nextbillionMap.removeOnCameraIdleListener(idleListener);
            nextbillionMap.removeOnCameraMoveCancelListener(moveCanceledListener);
            nextbillionMap.removeOnCameraMoveListener(moveListener);
            nextbillionMap.removeOnCameraMoveStartedListener(moveStartedListener);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (nextbillionMap != null) {
            nextbillionMap.removeOnMapLongClickListener(this);
        }
        mapView.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    private View onInflateDialogContent(View view) {
        linkTextView(view, R.id.value_lat, R.id.seekbar_lat, new LatLngChangeListener(), 180 + 38);
        linkTextView(view, R.id.value_lon, R.id.seekbar_lon, new LatLngChangeListener(), 180 - 77);
        linkTextView(view, R.id.value_zoom, R.id.seekbar_zoom, new ValueChangeListener(), 6);
        linkTextView(view, R.id.value_bearing, R.id.seekbar_bearing, new ValueChangeListener(), 90);
        linkTextView(view, R.id.value_tilt, R.id.seekbar_tilt, new ValueChangeListener(), 40);
        return view;
    }

    private void linkTextView(
            View view, @IdRes int textViewRes, @IdRes int seekBarRes, ValueChangeListener listener, int defaultValue) {
        final TextView value = (TextView) view.findViewById(textViewRes);
        SeekBar seekBar = (SeekBar) view.findViewById(seekBarRes);
        listener.setLinkedValueView(value);
        seekBar.setOnSeekBarChangeListener(listener);
        seekBar.setProgress(defaultValue);
    }

    private NextbillionMap.OnCameraIdleListener idleListener = new NextbillionMap.OnCameraIdleListener() {
        @Override
        public void onCameraIdle() {
            fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_green_dark));
        }
    };

    private NextbillionMap.OnCameraMoveListener moveListener = new NextbillionMap.OnCameraMoveListener() {
        @Override
        public void onCameraMove() {
            fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_orange_dark));
        }
    };

    private NextbillionMap.OnCameraMoveCanceledListener moveCanceledListener = () -> {};

    private NextbillionMap.OnCameraMoveStartedListener moveStartedListener = new NextbillionMap.OnCameraMoveStartedListener() {

        private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"};

        @Override
        public void onCameraMoveStarted(int reason) {
            // reason ranges from 1 <-> 3
            fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_red_dark));
        }
    };

    private class ValueChangeListener implements SeekBar.OnSeekBarChangeListener {

        protected TextView textView;

        public void setLinkedValueView(TextView textView) {
            this.textView = textView;
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            textView.setText(String.valueOf(progress));
        }
    }

    private class LatLngChangeListener extends ValueChangeListener {

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            super.onProgressChanged(seekBar, progress - 180, fromUser);
        }
    }

    private static class DialogClickListener implements DialogInterface.OnClickListener {

        private NextbillionMap nextbillionMap;
        private View dialogContent;

        public DialogClickListener(NextbillionMap nextbillionMap, View view) {
            this.nextbillionMap = nextbillionMap;
            this.dialogContent = view;
        }

        @Override
        public void onClick(DialogInterface dialog, int which) {
            double latitude = Double.parseDouble(
                    ((TextView) dialogContent.findViewById(R.id.value_lat)).getText().toString());
            double longitude = Double.parseDouble(
                    ((TextView) dialogContent.findViewById(R.id.value_lon)).getText().toString());
            double zoom = Double.parseDouble(
                    ((TextView) dialogContent.findViewById(R.id.value_zoom)).getText().toString());
            double bearing = Double.parseDouble(
                    ((TextView) dialogContent.findViewById(R.id.value_bearing)).getText().toString());
            double tilt = Double.parseDouble(
                    ((TextView) dialogContent.findViewById(R.id.value_tilt)).getText().toString());

            if (latitude < MIN_LATITUDE || latitude > MAX_LATITUDE) {
                Toast.makeText(dialogContent.getContext(), "latitude value must be set "
                                + " between " + MIN_LATITUDE + " and " + MAX_LATITUDE,
                        Toast.LENGTH_SHORT).show();
                return;
            }

            CameraPosition cameraPosition = new CameraPosition.Builder()
                    .target(new LatLng(latitude, longitude))
                    .zoom(zoom)
                    .bearing(bearing)
                    .tilt(tilt)
                    .build();

            nextbillionMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000,
                    new NextbillionMap.CancelableCallback() {
                        @Override
                        public void onCancel() {
                        }

                        @Override
                        public void onFinish() {
                        }
                    });
        }
    }
}

The code is an activity called CameraPositionActivity that demonstrates how to listen to camera change events in a map view. It uses the Nextbillion Maps SDK for Android.

initMapView:

  • The map view is initialized in the onCreate method by setting the content view to the layout file activity_camera_position.xml. The map view is found using findViewById and the getMapAsync method is called to set the callback for when the map is ready.

Operations on Camera Position:

  • The onMapReady method is implemented to receive the NextbillionMap object when it is ready. The map style is set to StyleConstants.SATELLITE_STREETS, and a listener is added to a floating action button (FAB).

  • The toggleLogCameraChanges method is used to toggle the logging of camera changes. When logging is enabled, listeners for camera events are added to the map. When logging is disabled, the listeners are removed.

  • The onMapLongClick method is implemented to toggle the logging of camera changes when a long click event occurs on the map.

  • The onClick method is implemented to show a dialog when the FAB is clicked. The dialog allows the user to set the camera position parameters (latitude, longitude, zoom, bearing, and tilt) and animates the camera to the new position when the "Animate" button is clicked.

  • The LatLngChangeListener class extends ValueChangeListener and adjusts the latitude value by subtracting 180, as the seek bar range is from 0 to 360 but latitude ranges from -90 to 90.

  • The DialogClickListener class is an implementation of DialogInterface.OnClickListener that retrieves the camera position parameters from the dialog content and animates the camera to the new position using nextbillionMap.animateCamera.

Additionally, the code includes lifecycle methods (onStart, onResume, onPause, onStop, onDestroy, onSaveInstanceState) to manage the state of the map view and release resources properly.

The activity demonstrates how to initialize a map view, handle camera change events, and interact with the camera position using a dialog.