Search code examples
androidgoogle-mapsandroid-layoutgoogle-maps-markers

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


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! :D


Solution

  • 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