使用 wrap_content 的 Facebook Fresco
Facebook Fresco using wrap_content
我有一堆要使用 fresco 加载的可绘制对象,我想为这些图像使用 wrap_content
大小,如何在 xml 中使用 fresco 进行加载?或者如果 xml 不可能,你如何在代码中做到这一点?
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="@mipmap/myImage"/>
除非我设置固定大小,否则上面的代码不起作用。
通过扩展 SimpleDraweeView
找到了一个解决方案,它允许我使用 wrap_content
,而且效果很好!我如何阻止您在 setContentView
中设置大小并且预览不起作用,如果可以编辑此答案以修复这些问题,我很高兴。
用法
<com.gazman.WrapContentDraweeView
android:id="@+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="@mipmap/myImage"/>
源代码
public class WrapContentDraweeView extends SimpleDraweeView {
private int outWidth;
private int outHeight;
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.GenericDraweeView);
try {
int placeholderId = gdhAttrs.getResourceId(
R.styleable.GenericDraweeView_placeholderImage,
0);
if(placeholderId != 0){
if(isInEditMode()){
setImageResource(placeholderId);
}
else {
loadSize(placeholderId, context.getResources());
}
}
} finally {
gdhAttrs.recycle();
}
}
private void loadSize(int placeholderId, Resources resources) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, placeholderId, options);
outWidth = options.outWidth;
outHeight = options.outHeight;
}
@Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
params.width = outWidth;
params.height = outHeight;
super.setLayoutParams(params);
}
}
我是 Fresco 团队的一员,是我做出了不支持 wrap-content 的设计决定。 documentation 中解释了基本原理。但简而言之,问题是您不能保证图像将立即可用(您可能需要先获取它),这意味着一旦图像到达,视图大小就必须改变。这在大多数情况下是不可取的,您可能应该重新考虑您的 UI.
无论如何,如果你真的need/want要那样做,你可以这样做:
void updateViewSize(@Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
draweeView.getLayoutParams().width = imageInfo.getWidth();
draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
ControllerListener listener = new BaseControllerListener {
@Override
public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
@Override
public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
DraweeController controller = draweeControllerBuilder
.setUri(uri)
.setControllerListener(listener)
.build();
draweeView.setController(controller);
这段代码是我凭空写的,我还没有实际测试过。但是思路应该很清晰,稍微调整一下就可以了。
根据@plamenko的回答,我做了一个自定义view如下:
/**
* Works when either height or width is set to wrap_content
* The view is resized based on the image fetched
*/
public class WrapContentDraweeView extends SimpleDraweeView {
// we set a listener and update the view's aspect ratio depending on the loaded image
private final ControllerListener listener = new BaseControllerListener<ImageInfo>() {
@Override
public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
@Override
public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void setImageURI(Uri uri, Object callerContext) {
DraweeController controller = ((PipelineDraweeControllerBuilder)getControllerBuilder())
.setControllerListener(listener)
.setCallerContext(callerContext)
.setUri(uri)
.setOldController(getController())
.build();
setController(controller);
}
void updateViewSize(@Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}
您可以将此 class 包含在 XML
中,示例用法:
<com.example.ui.views.WrapContentDraweeView
android:id="@+id/simple_drawee_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
在 Kotlin 中,您可以尝试这样的操作:
val listener = object : BaseControllerListener<ImageInfo>() {
override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) {
super.onFinalImageSet(id, imageInfo, animatable)
itemView.draweeGif.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.draweeGif.aspectRatio = (imageInfo?.width?.toFloat() ?: 0.toFloat()) / (imageInfo?.height?.toFloat() ?: 0.toFloat())
}
}
val controller = Fresco.newDraweeControllerBuilder()
.setUri(uriGif)
.setControllerListener(listener)
.setAutoPlayAnimations(true)
.build()
itemView.draweeGif.controller = controller
对我来说,这是我的 RecyclerView 中的一个解决方案,因为我希望直接在我的 ViewHolder 中设置布局参数。
在onFinalImageSet
中调用updateWrapSize
void updateWrapSize(@Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
boolean wrapH = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
boolean wrapW = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT;
if (wrapH || wrapW) {
if (wrapW && !wrapH) {
getLayoutParams().width = (int) (imageInfo.getWidth() * (float) getLayoutParams().height / imageInfo.getHeight());
} else if (wrapH && !wrapW) {
getLayoutParams().height = (int) (imageInfo.getHeight() * (float) getLayoutParams().width / imageInfo.getWidth());
} else {
getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}
我有一堆要使用 fresco 加载的可绘制对象,我想为这些图像使用 wrap_content
大小,如何在 xml 中使用 fresco 进行加载?或者如果 xml 不可能,你如何在代码中做到这一点?
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="@mipmap/myImage"/>
除非我设置固定大小,否则上面的代码不起作用。
通过扩展 SimpleDraweeView
找到了一个解决方案,它允许我使用 wrap_content
,而且效果很好!我如何阻止您在 setContentView
中设置大小并且预览不起作用,如果可以编辑此答案以修复这些问题,我很高兴。
用法
<com.gazman.WrapContentDraweeView
android:id="@+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="@mipmap/myImage"/>
源代码
public class WrapContentDraweeView extends SimpleDraweeView {
private int outWidth;
private int outHeight;
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.GenericDraweeView);
try {
int placeholderId = gdhAttrs.getResourceId(
R.styleable.GenericDraweeView_placeholderImage,
0);
if(placeholderId != 0){
if(isInEditMode()){
setImageResource(placeholderId);
}
else {
loadSize(placeholderId, context.getResources());
}
}
} finally {
gdhAttrs.recycle();
}
}
private void loadSize(int placeholderId, Resources resources) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, placeholderId, options);
outWidth = options.outWidth;
outHeight = options.outHeight;
}
@Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
params.width = outWidth;
params.height = outHeight;
super.setLayoutParams(params);
}
}
我是 Fresco 团队的一员,是我做出了不支持 wrap-content 的设计决定。 documentation 中解释了基本原理。但简而言之,问题是您不能保证图像将立即可用(您可能需要先获取它),这意味着一旦图像到达,视图大小就必须改变。这在大多数情况下是不可取的,您可能应该重新考虑您的 UI.
无论如何,如果你真的need/want要那样做,你可以这样做:
void updateViewSize(@Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
draweeView.getLayoutParams().width = imageInfo.getWidth();
draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
ControllerListener listener = new BaseControllerListener {
@Override
public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
@Override
public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
DraweeController controller = draweeControllerBuilder
.setUri(uri)
.setControllerListener(listener)
.build();
draweeView.setController(controller);
这段代码是我凭空写的,我还没有实际测试过。但是思路应该很清晰,稍微调整一下就可以了。
根据@plamenko的回答,我做了一个自定义view如下:
/**
* Works when either height or width is set to wrap_content
* The view is resized based on the image fetched
*/
public class WrapContentDraweeView extends SimpleDraweeView {
// we set a listener and update the view's aspect ratio depending on the loaded image
private final ControllerListener listener = new BaseControllerListener<ImageInfo>() {
@Override
public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
@Override
public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void setImageURI(Uri uri, Object callerContext) {
DraweeController controller = ((PipelineDraweeControllerBuilder)getControllerBuilder())
.setControllerListener(listener)
.setCallerContext(callerContext)
.setUri(uri)
.setOldController(getController())
.build();
setController(controller);
}
void updateViewSize(@Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}
您可以将此 class 包含在 XML
中,示例用法:
<com.example.ui.views.WrapContentDraweeView
android:id="@+id/simple_drawee_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
在 Kotlin 中,您可以尝试这样的操作:
val listener = object : BaseControllerListener<ImageInfo>() {
override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) {
super.onFinalImageSet(id, imageInfo, animatable)
itemView.draweeGif.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.draweeGif.aspectRatio = (imageInfo?.width?.toFloat() ?: 0.toFloat()) / (imageInfo?.height?.toFloat() ?: 0.toFloat())
}
}
val controller = Fresco.newDraweeControllerBuilder()
.setUri(uriGif)
.setControllerListener(listener)
.setAutoPlayAnimations(true)
.build()
itemView.draweeGif.controller = controller
对我来说,这是我的 RecyclerView 中的一个解决方案,因为我希望直接在我的 ViewHolder 中设置布局参数。
在onFinalImageSet
updateWrapSize
void updateWrapSize(@Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
boolean wrapH = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
boolean wrapW = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT;
if (wrapH || wrapW) {
if (wrapW && !wrapH) {
getLayoutParams().width = (int) (imageInfo.getWidth() * (float) getLayoutParams().height / imageInfo.getHeight());
} else if (wrapH && !wrapW) {
getLayoutParams().height = (int) (imageInfo.getHeight() * (float) getLayoutParams().width / imageInfo.getWidth());
} else {
getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}