如何使用 Android 地图 API v2创建自定义形状的位图标记

我正在开发一个 Android 应用程序,使用的是 GoogleMapAPI v2。我需要在地图上显示自定义标记的用户位置。

每个标记将显示来自 URL 的用户图片。必须以异步模式从服务器下载映像。有关示例,请参见附件中的屏幕截图。

如何在标记中添加图像和自定义信息?

enter image description here

198353 次浏览

谷歌地图 API v2演示中有一个 MarkerDemoActivity类,您可以在其中看到如何将自定义 Image 设置为 GoogleMap。

// Uses a custom icon.
mSydney = mMap.addMarker(new MarkerOptions()
.position(SYDNEY)
.title("Sydney")
.snippet("Population: 4,627,300")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.arrow)));

因为这只是用图像代替了标记,你可能想用 Canvas来画更复杂更花哨的东西:

Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bmp = Bitmap.createBitmap(80, 80, conf);
Canvas canvas1 = new Canvas(bmp);


// paint defines the text color, stroke width and size
Paint color = new Paint();
color.setTextSize(35);
color.setColor(Color.BLACK);


// modify canvas
canvas1.drawBitmap(BitmapFactory.decodeResource(getResources(),
R.drawable.user_picture_image), 0,0, color);
canvas1.drawText("User Name!", 30, 40, color);


// add marker to Map
mMap.addMarker(new MarkerOptions()
.position(USER_POSITION)
.icon(BitmapDescriptorFactory.fromBitmap(bmp))
// Specifies the anchor to be at a particular point in the marker image.
.anchor(0.5f, 1));

这将画布 canvas1绘制到 GoogleMap mMap上。代码应该(大部分)说明自己,有许多教程在那里如何绘制一个 Canvas。您可以从 AndroidDeveloper 页面查看 画布和绘图工具开始。

现在您还希望从 URL 下载图片。

URL url = new URL(user_image_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bmImg = BitmapFactory.decodeStream(is);

您可以从后台线程(可以使用 AsyncTask齐射RxJava)下载图像。

之后,您可以替换的 BitmapFactory.decodeResource(getResources(), R.drawable.user_picture_image)与您下载的图像 bmImg

另一种更简单的解决方案,我也使用是创建自定义标记布局,并将其转换成一个位图。

View _ custom_ mark.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/custom_marker_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/marker_mask">


<ImageView
android:id="@+id/profile_image"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:contentDescription="@null"
android:src="@drawable/avatar" />
</FrameLayout>

使用下面的代码将此视图转换为位图

 private Bitmap getMarkerBitmapFromView(@DrawableRes int resId) {


View customMarkerView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.view_custom_marker, null);
ImageView markerImageView = (ImageView) customMarkerView.findViewById(R.id.profile_image);
markerImageView.setImageResource(resId);
customMarkerView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
customMarkerView.layout(0, 0, customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight());
customMarkerView.buildDrawingCache();
Bitmap returnedBitmap = Bitmap.createBitmap(customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN);
Drawable drawable = customMarkerView.getBackground();
if (drawable != null)
drawable.draw(canvas);
customMarkerView.draw(canvas);
return returnedBitmap;
}

在 Map ready 回调中添加自定义标记。

@Override
public void onMapReady(GoogleMap googleMap) {
Log.d(TAG, "onMapReady() called with");
mGoogleMap = googleMap;
MapsInitializer.initialize(this);
addCustomMarker();
}
private void addCustomMarker() {
Log.d(TAG, "addCustomMarker()");
if (mGoogleMap == null) {
return;
}


// adding a marker on map with image from  drawable
mGoogleMap.addMarker(new MarkerOptions()
.position(mDummyLatLng)
.icon(BitmapDescriptorFactory.fromBitmap(getMarkerBitmapFromView(R.drawable.avatar))));
}

详情请参阅以下连结

如何使用布局创建自定义标记?

我希望现在分享我的解决方案还不算太晚。在此之前,您可以按照 Android 开发者文档中所述的教程操作。要实现这一点,您需要使用 defaultRenderer集群管理器。

  1. 创建一个实现 ClusterItem的对象

    public class SampleJob implements ClusterItem {
    
    
    private double latitude;
    private double longitude;
    
    
    //Create constructor, getter and setter here
    
    
    @Override
    public LatLng getPosition() {
    return new LatLng(latitude, longitude);
    }
    
  2. Create a default renderer class. This is the class that do all the job (inflating custom marker/cluster with your own style). I am using Universal image loader to do the downloading and caching the image.

    public class JobRenderer extends DefaultClusterRenderer< SampleJob > {
    
    
    private final IconGenerator iconGenerator;
    private final IconGenerator clusterIconGenerator;
    private final ImageView imageView;
    private final ImageView clusterImageView;
    private final int markerWidth;
    private final int markerHeight;
    private final String TAG = "ClusterRenderer";
    private DisplayImageOptions options;
    
    
    
    
    public JobRenderer(Context context, GoogleMap map, ClusterManager<SampleJob> clusterManager) {
    super(context, map, clusterManager);
    
    
    // initialize cluster icon generator
    clusterIconGenerator = new IconGenerator(context.getApplicationContext());
    View clusterView = LayoutInflater.from(context).inflate(R.layout.multi_profile, null);
    clusterIconGenerator.setContentView(clusterView);
    clusterImageView = (ImageView) clusterView.findViewById(R.id.image);
    
    
    // initialize cluster item icon generator
    iconGenerator = new IconGenerator(context.getApplicationContext());
    imageView = new ImageView(context.getApplicationContext());
    markerWidth = (int) context.getResources().getDimension(R.dimen.custom_profile_image);
    markerHeight = (int) context.getResources().getDimension(R.dimen.custom_profile_image);
    imageView.setLayoutParams(new ViewGroup.LayoutParams(markerWidth, markerHeight));
    int padding = (int) context.getResources().getDimension(R.dimen.custom_profile_padding);
    imageView.setPadding(padding, padding, padding, padding);
    iconGenerator.setContentView(imageView);
    
    
    options = new DisplayImageOptions.Builder()
    .showImageOnLoading(R.drawable.circle_icon_logo)
    .showImageForEmptyUri(R.drawable.circle_icon_logo)
    .showImageOnFail(R.drawable.circle_icon_logo)
    .cacheInMemory(false)
    .cacheOnDisk(true)
    .considerExifParams(true)
    .bitmapConfig(Bitmap.Config.RGB_565)
    .build();
    }
    
    
    @Override
    protected void onBeforeClusterItemRendered(SampleJob job, MarkerOptions markerOptions) {
    
    
    
    
    ImageLoader.getInstance().displayImage(job.getJobImageURL(), imageView, options);
    Bitmap icon = iconGenerator.makeIcon(job.getName());
    markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon)).title(job.getName());
    
    
    
    
    }
    
    
    @Override
    protected void onBeforeClusterRendered(Cluster<SampleJob> cluster, MarkerOptions markerOptions) {
    
    
    Iterator<Job> iterator = cluster.getItems().iterator();
    ImageLoader.getInstance().displayImage(iterator.next().getJobImageURL(), clusterImageView, options);
    Bitmap icon = clusterIconGenerator.makeIcon(iterator.next().getName());
    markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
    }
    
    
    @Override
    protected boolean shouldRenderAsCluster(Cluster cluster) {
    return cluster.getSize() > 1;
    }
    
  3. Apply cluster manager in your activity/fragment class.

    public class SampleActivity extends AppCompatActivity implements OnMapReadyCallback {
    
    
    private ClusterManager<SampleJob> mClusterManager;
    private GoogleMap mMap;
    private ArrayList<SampleJob> jobs = new ArrayList<SampleJob>();
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_landing);
    
    
    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
    .findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
    }
    
    
    
    
    @Override
    public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    mMap.getUiSettings().setMapToolbarEnabled(true);
    mClusterManager = new ClusterManager<SampleJob>(this, mMap);
    mClusterManager.setRenderer(new JobRenderer(this, mMap, mClusterManager));
    mMap.setOnCameraChangeListener(mClusterManager);
    mMap.setOnMarkerClickListener(mClusterManager);
    
    
    //Assume that we already have arraylist of jobs
    
    
    
    
    for(final SampleJob job: jobs){
    mClusterManager.addItem(job);
    }
    mClusterManager.cluster();
    }
    
  4. Result

Result

从 lambda 的答案,我已经做了一些更接近的要求。

boolean imageCreated = false;


Bitmap bmp = null;
Marker currentLocationMarker;
private void doSomeCustomizationForMarker(LatLng currentLocation) {
if (!imageCreated) {
imageCreated = true;
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
bmp = Bitmap.createBitmap(400, 400, conf);
Canvas canvas1 = new Canvas(bmp);


Paint color = new Paint();
color.setTextSize(30);
color.setColor(Color.WHITE);


BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inMutable = true;


Bitmap imageBitmap=BitmapFactory.decodeResource(getResources(),
R.drawable.messi,opt);
Bitmap resized = Bitmap.createScaledBitmap(imageBitmap, 320, 320, true);
canvas1.drawBitmap(resized, 40, 40, color);


canvas1.drawText("Le Messi", 30, 40, color);


currentLocationMarker = mMap.addMarker(new MarkerOptions().position(currentLocation)
.icon(BitmapDescriptorFactory.fromBitmap(bmp))
// Specifies the anchor to be at a particular point in the marker image.
.anchor(0.5f, 1));
} else {
currentLocationMarker.setPosition(currentLocation);
}


}