Android Fresco:绘制不同种类的图像形状

Android Fresco: drawing different kind of image shapes

Fresco 内置了对圆形图像和圆角的支持,但其他形状(例如菱形或平行四边形等)呢?

通过使用 BitmapShader 的自定义可绘制对象来处理标准 ImageView 很简单。例如,下面的自定义 Drawable 接收图像 Bitmap 和坡度高度,使 ImageView 看起来像这样:

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);
    }

要使用 Fresco 做到这一点,我需要图像的位图,但我不确定该怎么做。我读到我可以直接从 ImagePipeline 获取位图,但是它有很多陷阱。在一种情况下,返回的 Bitmap 是短暂的,不应该用于在屏幕上绘制,在另一种情况下,我得到一个 CloseableReference,我需要在我不清楚的某个时候释放它。到目前为止,我在网上看到的是与此类似的用于获取位图的代码:

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());

我还没有尝试过,想知道是否有人可以提供一个可行的解决方案,而不是我到目前为止从不同地方收集的比特和字节。它必须正确完成,否则我很容易泄漏内存,这打败了从一开始就使用 Fresco 的整个想法。

您不需要也不建议在处理视图时使用 imagepipeline。

一种方法是在后处理器中管理这些位图。您需要重写 process 方法,使用相同的 BitmapShader、paint、canvas 实现,使用 PlatformBitmapFactory createBitmap 创建可清除位图 CloseableReference,最后在完成位图后关闭引用。

查看更多 http://frescolib.org/docs/modifying-image.html

编辑

下面是我在王杰的帮助下最终实现的。以下代码片段将图像置于我在问题中呈现的形状中。

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);