Mapbox Android SDK - 可拖动的标记

9

如何使标记在Mapbox Android SDK中可拖动?这是否可能?

如果不行,还有哪些其他免费开放的地图引擎支持此功能?

谢谢!

6个回答

6
我不得不为我的一个项目实现可拖动标记。我找到的唯一解决方案是扩展现有的Marker类,通过检查拖动事件并相应地更新标记位置来实现。
package com.example.map;

import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.mapbox.mapboxsdk.api.ILatLng;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.overlay.Marker;
import com.mapbox.mapboxsdk.views.MapView;
import com.mapbox.mapboxsdk.views.util.Projection;

public class DraggableMarker extends Marker {

    private static final String TAG = "map.DraggableMarker";

    private boolean mIsDragged;
    private static final RectF mTempRect = new RectF();
    private static final PointF mTempPoint = new PointF();
    private float mDx, mDy;

    public DraggableMarker(String title, String description, LatLng latLng) {
        super(title, description, latLng);
        mIsDragged = false;
    }

    public DraggableMarker(MapView mv, String aTitle, String aDescription, LatLng aLatLng) {
        super(mv, aTitle, aDescription, aLatLng);
        mIsDragged = false;
    }

    public boolean drag(View v, MotionEvent event) {
        final int action = event.getActionMasked();
        if(action == MotionEvent.ACTION_DOWN) {
            Projection pj = ((MapView)v).getProjection();
            RectF bound = getDrawingBounds(pj, mTempRect);
            if(bound.contains(event.getX(), event.getY())) {
                mIsDragged = true;
                PointF p = getPositionOnScreen(pj, mTempPoint);
                mDx = p.x - event.getX();
                mDy = p.y - event.getY();
            }
        }
        if(mIsDragged) {
            if((action == MotionEvent.ACTION_CANCEL) ||
                    (action == MotionEvent.ACTION_UP)) {
                mIsDragged = false;
            } else {
                Projection pj = ((MapView)v).getProjection();
                ILatLng pos = pj.fromPixels(event.getX() + mDx, event.getY() + mDy);
                setPoint(new LatLng(pos.getLatitude(), pos.getLongitude()));
            }
        }

        return mIsDragged;
    }
}

稍后,您需要在MapView上添加触摸事件侦听器,并检查事件是否影响到您的标记(或标记集合中的其中一个)。
mMarker = new DraggableMarker(mMapView, "", "", aCenter);
mMarker.setIcon(new Icon(getActivity().getApplicationContext(), Icon.Size.SMALL, "marker-stroked", "FF0000"));
mMapView.addMarker(mMarker);

mMapView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return mMarker.drag(v, event);
    }
});

1

1
我最近在使用Mapbox Android SDK,遇到了使用标记选择位置的情况。因此,我创建了一个带有标记(使用图像视图)位于中心的屏幕。每次相机空闲监听器时,您可以获取屏幕中心的位置。
    /**
     * Method to create the location picker icon at the center of screen 
     */
    private void createLocationPickerMarker() {
        ivLocationPicker = new ImageView(this); //Here image view is dynamically created
        ivLocationPicker.setImageResource(R.drawable.ic_location_picker); //reference to the drawable image
        // Statically Set drop pin in center of screen
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 
        ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER); //image parameters are sent and set at the center
        float density = getResources().getDisplayMetrics().density; //get screen density 
        params.bottomMargin = (int) (12 * density); //inorder to place bottom tip at the center bottom margin added - here 12 is multiplied with the screen density and added as bottom margin as this will be mostly at the centre (based on average value of all density location_picker image height)
        ivLocationPicker.setLayoutParams(params); //parameters are set to the image
        mapView.addView(ivLocationPicker); //image is added to the map
    }

    /**
     * Method to pick location from marker
     */
    private LatLng getLatLngFromMarker() {
        return mapboxMap.getProjection().fromScreenLocation(new PointF(ivLocationPicker.getLeft() + (ivLocationPicker.getWidth() / 2), ivLocationPicker.getBottom()));
    }

    @Override
    public void onMapReady(MapboxMap mapboxMap) {
        pickedLatLng = getLatLngFromMarker(); // used to pick lat lng while coming to screen
        mapboxMap.addOnCameraIdleListener(() -> { //listener for on camera idle change
            pickedLatLng = getLatLngFromMarker();
        });
    }

0
创建一个新的活动并将以下代码粘贴到您的java文件中:

SymbolListenerActivity.java

import android.os.Bundle;
import android.widget.Toast;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.plugins.annotation.OnSymbolClickListener;
import com.mapbox.mapboxsdk.plugins.annotation.OnSymbolDragListener;
import com.mapbox.mapboxsdk.plugins.annotation.OnSymbolLongClickListener;
import com.mapbox.mapboxsdk.plugins.annotation.Symbol;
import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager;
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

/**
 * Change symbol icon by pressing on icon
 */
public class SymbolListenerActivity extends AppCompatActivity implements
        OnMapReadyCallback {

    private MapView mapView;
    private static final String MAKI_ICON_CAFE = "cafe-15";
    private static final String MAKI_ICON_HARBOR = "harbor-15";
    private static final String MAKI_ICON_AIRPORT = "airport-15";
    private SymbolManager symbolManager;
    private Symbol symbol;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Mapbox access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Mapbox.getInstance(this, getString(R.string.mapbox_access_token));//your mapbox access token 

        // This contains the MapView in XML and needs to be called after the access token is configured.
        setContentView(R.layout.activity_annotation_plugin_symbol_listener);

        mapView = findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this);
    }

    @Override
    public void onMapReady(@NonNull final MapboxMap mapboxMap) {
        mapboxMap.setStyle(Style.DARK, new Style.OnStyleLoaded() {
            @Override
            public void onStyleLoaded(@NonNull Style style) {

                // Set up a SymbolManager instance
                symbolManager = new SymbolManager(mapView, mapboxMap, style);

                symbolManager.setIconAllowOverlap(true);
                symbolManager.setTextAllowOverlap(true);

                // Add symbol at specified lat/lon
                symbol = symbolManager.create(new SymbolOptions()
                        .withLatLng(new LatLng(60.169091, 24.939876))
                        .withIconImage(MAKI_ICON_HARBOR)
                        .withIconSize(2.0f)
                        .withTextAnchor("Person First")
                        .withTextSize(23f)
                        .withDraggable(true));

                // Add click listener and change the symbol to a cafe icon on click
                symbolManager.addClickListener(new OnSymbolClickListener() {
                    @Override
                    public void onAnnotationClick(Symbol symbol) {

                        Toast.makeText(SymbolListenerActivity.this,
                                getString(R.string.clicked_symbol_toast), Toast.LENGTH_SHORT).show();

                        symbol.setIconImage(MAKI_ICON_CAFE);
                        symbolManager.update(symbol);
                    }
                });

                // Add long click listener and change the symbol to an airport icon on long click
                symbolManager.addLongClickListener((new OnSymbolLongClickListener() {
                    @Override
                    public void onAnnotationLongClick(Symbol symbol) {
                        Toast.makeText(SymbolListenerActivity.this,
                                getString(R.string.long_clicked_symbol_toast), Toast.LENGTH_SHORT).show();
                        symbol.setIconImage(MAKI_ICON_AIRPORT);
                        symbolManager.update(symbol);
                    }
                }));

                symbolManager.addDragListener(new OnSymbolDragListener() {
                    @Override
                    // Left empty on purpose
                    public void onAnnotationDragStarted(Symbol annotation) {
                    }

                    @Override
                    // Left empty on purpose
                    public void onAnnotationDrag(Symbol symbol) {
                    }

                    @Override
                    // Left empty on purpose
                    public void onAnnotationDragFinished(Symbol annotation) {
                    }
                });
                Toast.makeText(SymbolListenerActivity.this,
                        getString(R.string.symbol_listener_instruction_toast), Toast.LENGTH_SHORT).show();
            }
        });

    }

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

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

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

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

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

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

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

活动的XML是:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:mapbox="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        mapbox:mapbox_cameraTargetLat="60.169091"
        mapbox:mapbox_cameraTargetLng="24.939876"
        mapbox:mapbox_cameraZoom="12" />

</FrameLayout>

权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

我用于此的依赖项

implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.6.1'
implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.42.6'
implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation:0.42.6'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-places-v9:0.12.0'

在项目级别的'build.gradle'文件中。
allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://mapbox.bintray.com/mapbox' }

        maven {
            url 'https://api.mapbox.com/downloads/v2/releases/maven'
            authentication {
                basic(BasicAuthentication)
            }
            credentials {
                // Do not change the username below.
                // This should always be `mapbox` (not your username).
                username = 'mapbox'
                password = "YourMapBoxAccessToken
            }
        }
    }
}

如果这对您有用,请标记答案为有用。谢谢。 - Ammar
如果您有任何疑问,请随时询问。感谢! - Ammar

0

基本上我使用了 setOnTouchListener() 来获取 mapview 中的所有触摸位置。然后我将它们转换为坐标。之后,我选择了标记并最终使用 setPosition() 设置了它。

YourMapView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            final int action = event.getActionMasked();
            if(action == MotionEvent.ACTION_DOWN) {
                LatLng new_position = YourmapboxMap.getProjection().fromScreenLocation(new PointF(event.getX(),event.getY()));
                YourmapboxMap.getMarkers().get(0).setPosition(new_position);
            }
            return false;
        }
    });

-4
你有没有尝试过Leaflet呢?它支持Android,IOS以及所有标准移动平台。据我所知,甚至Mapbox SDK也是基于Leaflet构建的。
这里有一个链接供您参考:http://leafletjs.com/examples/mobile.html

2
Mapbox Android SDK 是本地的,不是基于 Web 或 Leaflet 的。 - incanus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接