Home » Android » android – How can I create a speech-bubble border for a Google Marker Custom Icon using Picasso?

android – How can I create a speech-bubble border for a Google Marker Custom Icon using Picasso?

Posted by: admin June 15, 2020 Leave a comment

Questions:

How can I use Picasso and Google Marker Custom Icon to achieve this feature?

enter image description here

I know how to use Picasso for the image, but I don’t know how to add that “marker icon” on the bottom and the borders.

Picasso.with(mContext)
            .load(url)
            .resize(250, 250)
            .centerInside()
            .into(new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    Marker driver_marker = mMap.addMarker(new MarkerOptions()
                            .position(new LatLng(Double.parseDouble(lat), Double.parseDouble(lng)))
                            .icon(BitmapDescriptorFactory.fromBitmap(bitmap))
                            .title(name)
                            .snippet(address)
                    );


                    @Override
                    public void onBitmapFailed (Drawable errorDrawable){
                    }

                    @Override
                    public void onPrepareLoad (Drawable placeHolderDrawable){
                    }
                });
            }

I added this inside the onBitmapLoaded:

Paint paint = new Paint();
    paint.setColor(Color.YELLOW);
    paint.setStrokeWidth(10);
    paint.setShadowLayer(5, 0, 1, Color.RED);

    Canvas canvas = new Canvas(bitmap);
    canvas.drawLine(0, 0, canvas.getWidth(), 0, paint);
    canvas.drawLine(0, 0, 0, canvas.getHeight(), paint);
    canvas.drawLine(0, canvas.getHeight(), canvas.getWidth(), canvas.getHeight(), paint);
    canvas.drawLine(canvas.getWidth(), 0, canvas.getWidth(), canvas.getHeight(), paint);

And that seems to added the borders, but how do I add that inverted pyramid with the Canvas? Thanks, after that, I’m pretty much done! 😀

How to&Answers:

Here is a transformation class that I got working. It’s lacking the corner radius and any gradients, but it has the inverted pyramid on the bottom, and it should serve as a good starting point.

Here is the transformation class:

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;

public class BubbleTransformation implements com.squareup.picasso.Transformation {
    private static final int outerMargin = 40;
    private final int margin;  // dp

    // margin is the board in dp
    public BubbleTransformation(final int margin) {
        this.margin = margin;
    }

    @Override
    public Bitmap transform(final Bitmap source) {
        Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        Paint paintBorder = new Paint();
        paintBorder.setColor(Color.CYAN);
        paintBorder.setStrokeWidth(margin);
        canvas.drawRoundRect(new RectF(outerMargin, outerMargin, source.getWidth() - outerMargin, source.getHeight() - outerMargin), 0, 0, paintBorder);

        Paint trianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        trianglePaint.setStrokeWidth(2);
        trianglePaint.setColor(Color.CYAN);
        trianglePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        trianglePaint.setAntiAlias(true);

        Path triangle = new Path();
        triangle.setFillType(Path.FillType.EVEN_ODD);
        triangle.moveTo(outerMargin, source.getHeight() / 2);
        triangle.lineTo(source.getWidth()/2,source.getHeight());
        triangle.lineTo(source.getWidth()-outerMargin,source.getHeight()/2);
        triangle.close();

        canvas.drawPath(triangle, trianglePaint);

        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
        canvas.drawRoundRect(new RectF(margin+outerMargin, margin+outerMargin, source.getWidth() - (margin + outerMargin), source.getHeight() - (margin + outerMargin)), 0, 0, paint);

        if (source != output) {
            source.recycle();
        }

        return output;
    }

    @Override
    public String key() {
        return "rounded";
    }
}

The call to Picasso:

 Picasso.with(getActivity())
            .load(user_photo_url)
            .resize(250,250)
            .centerCrop()
            .transform(new BubbleTransformation(20))
            .into(mTarget);

The Target:

Target mTarget = new Target() {
    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        Marker driver_marker = mMap.addMarker(new MarkerOptions()
                        .position(latLng)
                        .icon(BitmapDescriptorFactory.fromBitmap(bitmap))
                        .title("test")
                        .snippet("test address")
        );
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
        Log.d("picasso", "onBitmapFailed");
    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {

    }
};

Result:

enter image description here

Answer:

here marker view is your custom marker layout

    val view = LayoutInflater.from(context).inflate(R.layout.marker_view, 
    null,false)

    val bitmap = getBitmapFromView(view)

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

use this function to convert bitmap from view

private fun getBitmapFromView(view: View): Bitmap {
        view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
        val bitmap = Bitmap.createBitmap(view.measuredWidth, view.measuredHeight,
                Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        view.layout(0, 0, view.measuredWidth, view.measuredHeight)
        view.draw(canvas)
        return bitmap
    }

Answer:

You can set in kotlin like this..

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        mapView.getMapAsync { googleMap ->
            googleMap1 = googleMap as GoogleMap
            addCustomMarker()
        }

    }


 private fun addCustomMarker() {
        Log.d("addCustomMarker", "addCustomMarker()")
        if (googleMap1 == null) {
            return
        }
        // adding a marker on map with image from  drawable
        googleMap1.addMarker(
            MarkerOptions()
                .position(LatLng(23.0225 , 72.5714))
                .icon(BitmapDescriptorFactory.fromBitmap(getMarkerBitmapFromView()))
        )
    }



    private fun getMarkerBitmapFromView(): Bitmap? {
        val customMarkerView: View? = layoutInflater.inflate(R.layout.view_custom_marker, null)
//        val markerImageView: ImageView =
//            customMarkerView.findViewById<View>(R.id.profile_image) as ImageView
        customMarkerView?.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED );
        customMarkerView?.layout(0, 0, customMarkerView.measuredWidth, customMarkerView.measuredHeight);
        customMarkerView?.buildDrawingCache();
        val returnedBitmap = Bitmap.createBitmap(
            customMarkerView!!.measuredWidth, customMarkerView.measuredHeight,
            Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(returnedBitmap)
        canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN)
        val drawable = customMarkerView.background

        drawable?.draw(canvas);
        customMarkerView.draw(canvas);
        return returnedBitmap;

    }