简体   繁体   中英

Android Fresco: drawing different kind of image shapes

Fresco has built-in support for circular images and rounded corner, but what about other shapes such as diamond or parallelogram, etc?

It's simple to do with the standard ImageView via custom drawable that uses BitmapShader. For example, the following custom Drawable receives the image Bitmap and a slope height to make a an ImageView look like this picture:

在此处输入图片说明

public class MaskDrawable extends Drawable {
    private Paint mPaint;
    private Path mPath;
    private int mSlopeHeight;

    public MaskDrawable(Bitmap bitmap, int slopeHeight) {
        BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(shader);
        mSlopeHeight = slopeHeight;

        mPath = new Path();
    }

    @Override
    public void draw(Canvas canvas) {
        Rect bounds = getBounds();

        mPath.moveTo(0, 0);
        mPath.lineTo(0, bounds.bottom);
        mPath.lineTo(bounds.right, bounds.bottom - mSlopeHeight);
        mPath.lineTo(bounds.right, 0);
        canvas.drawPath(mPath, mPaint);
    }

To do that with Fresco, I need the Bitmap of the image, but I'm not sure how to do that. I read that I can get the Bitmap directly from the ImagePipeline, but that are many gotchas that comes with it. In one case, the returned Bitmap is short lived and shouldn't be used to draw on the screen where in the other case I get a CloseableReference which I need to release at some point which isn't clear to me. What I have seen on the net so far is code similar to this for getting the Bitmap:

ImagePipeline imagePipeline = Fresco.getImagePipeline();

        ImageRequest imageRequest = ImageRequestBuilder
                .newBuilderWithSource(uri)
                .setRequestPriority(Priority.HIGH)
                .setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
                .build();

        DataSource<CloseableReference<CloseableBitmap>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, getContext());

        DataSubscriber<CloseableReference<CloseableBitmap>> dataSubscriber =
                new BaseDataSubscriber<CloseableReference<CloseableBitmap>>() {
                    @Override
                    protected void onNewResultImpl(DataSource<CloseableReference<CloseableBitmap>> dataSource) {
                        mBitmapRef = dataSource.getResult();
                        // Get the bitmap here and use it in my custom drawable?
                    }

                    @Override
                    protected void onFailureImpl(DataSource<CloseableReference<CloseableBitmap>> dataSource) {
                    }
                };

        dataSource.subscribe(dataSubscriber, UiThreadImmediateExecutorService.getInstance());

I haven't tried that yet and was wondering if somebody can provide a working solution instead of the bits and bytes I've gathered so far from different places. It has to be done right or else I can easily leak memory, which beats the whole idea of using Fresco from the first place.

You don't need to and also not recommend to use imagepipeline as you're dealing with view.

One way is to manage those bitmap in postprocessor. You need to override the process method, use the same BitmapShader, paint, canvas implementation, use PlatformBitmapFactory createBitmap to create purgeable bitmap CloseableReference, and finally close the reference when you're done with the bitmap.

See more in http://frescolib.org/docs/modifying-image.html

EDIT

Below is the final implementation I came up with after getting help from Jie Wang. The following code snippet places the image in the shape I presented in the question.

mSimpleDraweeView = (SimpleDraweeView) findViewById(R.id.shaped_picture);
final int slopeHeight = 100;

Postprocessor maskProcessor = new BasePostprocessor() {
    @Override
    public CloseableReference<Bitmap> process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) {
        // Get the size of the downloaded bitmap
        final int width = sourceBitmap.getWidth();
        final int height = sourceBitmap.getHeight();

        // Create a new bitmap and use it to draw the shape that we want.
        CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap(width, height);
        try {
            Bitmap destBitmap = bitmapRef.get();

            // Create canvas using the new bitmap we created earlier
            Canvas canvas = new Canvas(destBitmap);

            // Set up the Paint we will use for filling in the shape
            // BitmapShader will fill the shape with the downloaded bitmap
            BitmapShader shader = new BitmapShader(sourceBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setShader(shader);

            // Set up the actual shape. Modify this part with any shape you want to have.
            Path path = new Path();
            path.moveTo(0, 0);
            path.lineTo(0, height);
            path.lineTo(width, height - slopeHeight);
            path.lineTo(width, 0);

            // Draw the shape and fill it with the paint
            canvas.drawPath(path, paint);

            return CloseableReference.cloneOrNull(bitmapRef);
        }
        finally {
            CloseableReference.closeSafely(bitmapRef);
        }
    }
};

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
        .setPostprocessor(maskProcessor)
        .build();

DraweeController controller = Fresco.newDraweeControllerBuilder()
        .setImageRequest(request)
        .setOldController(mSimpleDraweeView.getController())
        .build();

mSimpleDraweeView.setController(controller);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM