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