将 Fresco 与 StaggeredGridLayoutManager 结合使用
Using Fresco with StaggeredGridLayoutManager
我遵循了 this 不错的教程,本地图像一切顺利。当尝试从本地图像更改为我通过 Fresco 加载到 SimpleDraweeView 中的图像时,我似乎无法让视图调整大小并在其他图像中错开。
我的布局看起来像这样(它是:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardUseCompatPadding="true"
card_view:cardCornerRadius="8dp"
android:layout_marginBottom="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/fresco_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
fresco:actualImageScaleType="fitXY"
/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"
android:text="@string/hello_world"
android:textColor="#ffffff"
android:layout_below="@+id/fresco_image"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:gravity="center_horizontal"
android:layout_alignParentBottom="true"
android:background="#1976D2"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
以上膨胀为以下:
<android.support.v7.widget.RecyclerView
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/text"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
我试过的受票人:
- 将宽度或高度或两者设置为 "match_parent" 和 "wrap_content" 以及所有变体。 wrap_content 设置似乎没有 recommended
- 不同的宽高比,但这似乎会导致图像被裁剪并且全部统一 size/shape(不交错)
- scaleType 与上述的不同组合。
在受票人之外,我一直在查看 GitHub 的一些演示,但我似乎找不到任何交错视图的示例。
我确定这只是获得正确组合的设置的问题,但对于像我这样的人,我无法弄清楚该组合恰好是什么。
如有任何建议(或示例),我们将不胜感激!
更新
我们的后端实现 returns 每张图片的纵横比(以及 width/height)。我能够在 SimpleDraweeView 上设置宽高比并且它尊重这些值:
vDraweeView.setAspectRatio(fAspect);
如果我不必这样做会更酷,但显然视图在图像完全加载之前不知道它的大小。虽然这可能是调整视图大小的好时机,但这可能会导致显示跳动……不可取!!
这听起来对吗?
由于 SimpleDraweeView 在下载之前不知道图像的大小(并且不建议在最初绘制后调整大小,因为移动 UI 原因——也许是其他原因),我不得不设置下载图像之前的纵横比。这假设您在下载图像之前知道宽高比……我们在本例中就是这样做的。我在 recyclerview 适配器中这样做了。
设置纵横比的命令:
vDraweeView.setAspectRatio(fAspect);
您可以使用此替代 WrapContentDraweeView class:
public class WrapContentDraweeView extends GenericDraweeView {
private PipelineDraweeControllerBuilderSupplier mDraweeControllerBuilderSupplier;
private PipelineDraweeControllerBuilder mSimpleDraweeControllerBuilder;
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
this.init(context, (AttributeSet) null);
}
public WrapContentDraweeView(Context context) {
super(context);
this.init(context, (AttributeSet) null);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
this.init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.init(context, attrs);
}
@TargetApi(21)
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
if (!this.isInEditMode()) {
mDraweeControllerBuilderSupplier = Fresco.getDraweeControllerBuilderSupplier();
Preconditions.checkNotNull(mDraweeControllerBuilderSupplier,
"SimpleDraweeView was not initialized!");
this.mSimpleDraweeControllerBuilder = mDraweeControllerBuilderSupplier.get();
if (attrs != null) {
TypedArray gdhAttrs = context.obtainStyledAttributes(attrs,
R.styleable.SimpleDraweeView);
try {
if (gdhAttrs.hasValue(
R.styleable.SimpleDraweeView_actualImageUri)) {
this.setImageURI((Uri) Uri.parse(gdhAttrs.getString(
R.styleable.SimpleDraweeView_actualImageUri)),
(Object) null);
} else if (gdhAttrs.hasValue(
R.styleable.SimpleDraweeView_actualImageResource)) {
int resId = gdhAttrs.getResourceId(
R.styleable.SimpleDraweeView_actualImageResource,
-1);
}
} finally {
gdhAttrs.recycle();
}
}
}
}
protected SimpleDraweeControllerBuilder getControllerBuilder() {
return this.mSimpleDraweeControllerBuilder;
}
public void setImageURI(Uri uri) {
this.setImageURI((Uri) uri, (Object) null);
}
public void setImageURI(@Nullable String uriString) {
this.setImageURI((String) uriString, (Object) null);
}
public void setImageURI(Uri uri, @Nullable Object callerContext) {
setAspectRatio(FrescoUtils.getAspectRatio(uri));
ImageDecodeOptions decoderOptions = ImageDecodeOptions.newBuilder()
.setDecodePreviewFrame(true)
.build();
ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
.setLocalThumbnailPreviewsEnabled(true)
.setImageDecodeOptions(decoderOptions)
.setPostprocessor(new BasePostprocessor() {
@Override
public CloseableReference<Bitmap> process(Bitmap sourceBitmap,
PlatformBitmapFactory bitmapFactory) {
int height = sourceBitmap.getHeight()/2;
int width = sourceBitmap.getWidth()/2;
if (width / (float) height < 0.5f) {
CloseableReference<Bitmap> cropReference = null;
CloseableReference<Bitmap> scaledReference = null;
try {
cropReference = bitmapFactory
.createBitmap(sourceBitmap, 0, 0, width,
(int) (width / 1.66f));
scaledReference = bitmapFactory
.createScaledBitmap(cropReference.get(), getWidth(),
(int) (getWidth() / 1.66f), false);
Bitmap bm = scaledReference.get();
Canvas canvas = new Canvas(bm);
Bitmap badge = getBadgeBitmap("长图");
int padding = (int) (getResources().getDisplayMetrics().density
* 16);
canvas.drawBitmap(badge, bm.getWidth() - badge.getWidth() - padding,
bm.getHeight() - badge.getHeight() - padding, new Paint());
return super.process(bm, bitmapFactory);
} finally {
if (cropReference != null) {
cropReference.close();
}
if (scaledReference != null) {
scaledReference.close();
}
}
}
return super.process(sourceBitmap, bitmapFactory);
}
})
.build();
DraweeController controller = this.mSimpleDraweeControllerBuilder
.setImageRequest(imageRequest)
.setCallerContext(callerContext)
.setOldController(this.getController())
.setAutoPlayAnimations(true)
.setControllerListener(FrescoUtils.createControllerListener(this, uri))
.setOldController(this.getController())
.build();
this.setController(controller);
}
public void setImageURI(@Nullable String uriString, @Nullable Object callerContext) {
Uri uri = uriString != null ? Uri.parse(uriString) : null;
this.setImageURI(uri, callerContext);
}
public void setImageResource(int resId) {
super.setImageResource(resId);
}
private Bitmap getBadgeBitmap(String badge) {
final DisplayMetrics dm = getResources().getDisplayMetrics();
final float density = dm.density;
final float scaledDensity = dm.scaledDensity;
final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint
.SUBPIXEL_TEXT_FLAG);
textPaint.setColor(Color.WHITE);
textPaint.setTypeface(Typeface.create("sans-serif-black", Typeface.NORMAL));
textPaint.setTextSize(12 * scaledDensity);
final float padding = 4 * density;
final float cornerRadius = 2 * density;
final Rect textBounds = new Rect();
textPaint.getTextBounds(badge, 0, badge.length(), textBounds);
final int height = (int) (padding + textBounds.height() + padding);
final int width = (int) (padding + textBounds.width() + padding);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setHasAlpha(true);
final Canvas canvas = new Canvas(bitmap);
final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backgroundPaint.setColor(Color.DKGRAY);
RectF rectF = new RectF(0, 0, width, height);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, backgroundPaint);
// punch out the badge word, leaving transparency
// textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
// 居中
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
int baseline = (int) (rectF.centerY() - (fontMetrics.top + fontMetrics.bottom) / 2);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(badge, rectF.centerX(), baseline, textPaint);
return bitmap;
}
}
然后在您的布局中使用 WrapContentDraweeView 而不是 SimpleDraweeView
并使用您的包名称而不是 fresco package
<com.package.info.WrapContentDraweeView
android:id="@+id/gridThumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
fresco:placeholderImage="@drawable/placeholder"
/>
这比后端实现要好。
我遇到了类似的问题,但我使用的是 Glide。无论如何,请尝试使用下面的此属性设置您的 ImageView。我的 StaggeredGridView 有 2 列,效果很好。
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
android:hardwareAccelerated="true"
app:cardPreventCornerOverlap="false"
android:elevation="0dp">
<ImageView
android:id="@+id/item_cover"
android:layout_width="match_parent"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
为了解决加载图像时的错误,我必须做的另一件事是在加载图像之前使用 PlaceHolder。
Glide.with(context)
.load(url)
.placeholder(R.drawable.transparent_placeholder)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.crossFade()
.into(itemCover)
我遵循了 this 不错的教程,本地图像一切顺利。当尝试从本地图像更改为我通过 Fresco 加载到 SimpleDraweeView 中的图像时,我似乎无法让视图调整大小并在其他图像中错开。
我的布局看起来像这样(它是:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardUseCompatPadding="true"
card_view:cardCornerRadius="8dp"
android:layout_marginBottom="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/fresco_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
fresco:actualImageScaleType="fitXY"
/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"
android:text="@string/hello_world"
android:textColor="#ffffff"
android:layout_below="@+id/fresco_image"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:gravity="center_horizontal"
android:layout_alignParentBottom="true"
android:background="#1976D2"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
以上膨胀为以下:
<android.support.v7.widget.RecyclerView
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/text"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
我试过的受票人:
- 将宽度或高度或两者设置为 "match_parent" 和 "wrap_content" 以及所有变体。 wrap_content 设置似乎没有 recommended
- 不同的宽高比,但这似乎会导致图像被裁剪并且全部统一 size/shape(不交错)
- scaleType 与上述的不同组合。
在受票人之外,我一直在查看 GitHub 的一些演示,但我似乎找不到任何交错视图的示例。
我确定这只是获得正确组合的设置的问题,但对于像我这样的人,我无法弄清楚该组合恰好是什么。
如有任何建议(或示例),我们将不胜感激!
更新 我们的后端实现 returns 每张图片的纵横比(以及 width/height)。我能够在 SimpleDraweeView 上设置宽高比并且它尊重这些值:
vDraweeView.setAspectRatio(fAspect);
如果我不必这样做会更酷,但显然视图在图像完全加载之前不知道它的大小。虽然这可能是调整视图大小的好时机,但这可能会导致显示跳动……不可取!!
这听起来对吗?
由于 SimpleDraweeView 在下载之前不知道图像的大小(并且不建议在最初绘制后调整大小,因为移动 UI 原因——也许是其他原因),我不得不设置下载图像之前的纵横比。这假设您在下载图像之前知道宽高比……我们在本例中就是这样做的。我在 recyclerview 适配器中这样做了。
设置纵横比的命令:
vDraweeView.setAspectRatio(fAspect);
您可以使用此替代 WrapContentDraweeView class:
public class WrapContentDraweeView extends GenericDraweeView {
private PipelineDraweeControllerBuilderSupplier mDraweeControllerBuilderSupplier;
private PipelineDraweeControllerBuilder mSimpleDraweeControllerBuilder;
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
this.init(context, (AttributeSet) null);
}
public WrapContentDraweeView(Context context) {
super(context);
this.init(context, (AttributeSet) null);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
this.init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.init(context, attrs);
}
@TargetApi(21)
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
if (!this.isInEditMode()) {
mDraweeControllerBuilderSupplier = Fresco.getDraweeControllerBuilderSupplier();
Preconditions.checkNotNull(mDraweeControllerBuilderSupplier,
"SimpleDraweeView was not initialized!");
this.mSimpleDraweeControllerBuilder = mDraweeControllerBuilderSupplier.get();
if (attrs != null) {
TypedArray gdhAttrs = context.obtainStyledAttributes(attrs,
R.styleable.SimpleDraweeView);
try {
if (gdhAttrs.hasValue(
R.styleable.SimpleDraweeView_actualImageUri)) {
this.setImageURI((Uri) Uri.parse(gdhAttrs.getString(
R.styleable.SimpleDraweeView_actualImageUri)),
(Object) null);
} else if (gdhAttrs.hasValue(
R.styleable.SimpleDraweeView_actualImageResource)) {
int resId = gdhAttrs.getResourceId(
R.styleable.SimpleDraweeView_actualImageResource,
-1);
}
} finally {
gdhAttrs.recycle();
}
}
}
}
protected SimpleDraweeControllerBuilder getControllerBuilder() {
return this.mSimpleDraweeControllerBuilder;
}
public void setImageURI(Uri uri) {
this.setImageURI((Uri) uri, (Object) null);
}
public void setImageURI(@Nullable String uriString) {
this.setImageURI((String) uriString, (Object) null);
}
public void setImageURI(Uri uri, @Nullable Object callerContext) {
setAspectRatio(FrescoUtils.getAspectRatio(uri));
ImageDecodeOptions decoderOptions = ImageDecodeOptions.newBuilder()
.setDecodePreviewFrame(true)
.build();
ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
.setLocalThumbnailPreviewsEnabled(true)
.setImageDecodeOptions(decoderOptions)
.setPostprocessor(new BasePostprocessor() {
@Override
public CloseableReference<Bitmap> process(Bitmap sourceBitmap,
PlatformBitmapFactory bitmapFactory) {
int height = sourceBitmap.getHeight()/2;
int width = sourceBitmap.getWidth()/2;
if (width / (float) height < 0.5f) {
CloseableReference<Bitmap> cropReference = null;
CloseableReference<Bitmap> scaledReference = null;
try {
cropReference = bitmapFactory
.createBitmap(sourceBitmap, 0, 0, width,
(int) (width / 1.66f));
scaledReference = bitmapFactory
.createScaledBitmap(cropReference.get(), getWidth(),
(int) (getWidth() / 1.66f), false);
Bitmap bm = scaledReference.get();
Canvas canvas = new Canvas(bm);
Bitmap badge = getBadgeBitmap("长图");
int padding = (int) (getResources().getDisplayMetrics().density
* 16);
canvas.drawBitmap(badge, bm.getWidth() - badge.getWidth() - padding,
bm.getHeight() - badge.getHeight() - padding, new Paint());
return super.process(bm, bitmapFactory);
} finally {
if (cropReference != null) {
cropReference.close();
}
if (scaledReference != null) {
scaledReference.close();
}
}
}
return super.process(sourceBitmap, bitmapFactory);
}
})
.build();
DraweeController controller = this.mSimpleDraweeControllerBuilder
.setImageRequest(imageRequest)
.setCallerContext(callerContext)
.setOldController(this.getController())
.setAutoPlayAnimations(true)
.setControllerListener(FrescoUtils.createControllerListener(this, uri))
.setOldController(this.getController())
.build();
this.setController(controller);
}
public void setImageURI(@Nullable String uriString, @Nullable Object callerContext) {
Uri uri = uriString != null ? Uri.parse(uriString) : null;
this.setImageURI(uri, callerContext);
}
public void setImageResource(int resId) {
super.setImageResource(resId);
}
private Bitmap getBadgeBitmap(String badge) {
final DisplayMetrics dm = getResources().getDisplayMetrics();
final float density = dm.density;
final float scaledDensity = dm.scaledDensity;
final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint
.SUBPIXEL_TEXT_FLAG);
textPaint.setColor(Color.WHITE);
textPaint.setTypeface(Typeface.create("sans-serif-black", Typeface.NORMAL));
textPaint.setTextSize(12 * scaledDensity);
final float padding = 4 * density;
final float cornerRadius = 2 * density;
final Rect textBounds = new Rect();
textPaint.getTextBounds(badge, 0, badge.length(), textBounds);
final int height = (int) (padding + textBounds.height() + padding);
final int width = (int) (padding + textBounds.width() + padding);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setHasAlpha(true);
final Canvas canvas = new Canvas(bitmap);
final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backgroundPaint.setColor(Color.DKGRAY);
RectF rectF = new RectF(0, 0, width, height);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, backgroundPaint);
// punch out the badge word, leaving transparency
// textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
// 居中
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
int baseline = (int) (rectF.centerY() - (fontMetrics.top + fontMetrics.bottom) / 2);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(badge, rectF.centerX(), baseline, textPaint);
return bitmap;
}
}
然后在您的布局中使用 WrapContentDraweeView 而不是 SimpleDraweeView 并使用您的包名称而不是 fresco package
<com.package.info.WrapContentDraweeView
android:id="@+id/gridThumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
fresco:placeholderImage="@drawable/placeholder"
/>
这比后端实现要好。
我遇到了类似的问题,但我使用的是 Glide。无论如何,请尝试使用下面的此属性设置您的 ImageView。我的 StaggeredGridView 有 2 列,效果很好。
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
android:hardwareAccelerated="true"
app:cardPreventCornerOverlap="false"
android:elevation="0dp">
<ImageView
android:id="@+id/item_cover"
android:layout_width="match_parent"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
为了解决加载图像时的错误,我必须做的另一件事是在加载图像之前使用 PlaceHolder。
Glide.with(context)
.load(url)
.placeholder(R.drawable.transparent_placeholder)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.crossFade()
.into(itemCover)