Facebook Fresco using wrap_content - android

I got a bunch of drawables that I want to load using fresco, I want to use wrap_content size for those images, how can I do it in xml with fresco? Or if xml is not possible how do you do it in code?
<com.facebook.drawee.view.SimpleDraweeView
android:id="#+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="#mipmap/myImage"/>
The above code is not working unless I set a fixed size.

I am part of the Fresco team and I was the one who made the design decision to not support wrap-content. The rationale is explained in the documentation. But in short, the problem is that you can't guarantee that the image will be available immediately (you may need to fetch it first) and that means that the view size would have to change once the image arrives. This is in most cases not desirable and you should probably rethink your UI.
Anyways, if you really really need/want to do that, you can do it like this:
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);
I wrote this code from the top of my head, I haven't actually tested it. But the idea should be clear, and it should work with minor adjustments.

Based on #plamenko's answer, I made a custom view as follows:
/**
* 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());
}
}
}
You can include this class in the XML, an example usage:
<com.example.ui.views.WrapContentDraweeView
android:id="#+id/simple_drawee_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

In Kotlin you can try something like this :
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
For me it was a solution in my RecyclerView because I was looking to set the layoutParams directly in my ViewHolder.

Found a solution by extending SimpleDraweeView, it allows me to use wrap_content and it works just fine! How ever I prevent you from setting the size in setContentView and the preview is not working, I be glad if could edit this answer to fix those.
Usage
<com.gazman.WrapContentDraweeView
android:id="#+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="#mipmap/myImage"/>
Source code
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);
}
}

invoke updateWrapSize in onFinalImageSet
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());
}
}
}

Related

Android Studio: Create Object in custom SwipeButton with attributes from XML

public class SwipeButton extends RelativeLayout {
private ImageView swipeButtonInner;
private float initialX;
private boolean active;
private TextView centerText;
private ViewGroup background;
private Drawable disabledDrawable;
private Drawable enabledDrawable;
private OnStateChangeListener onStateChangeListener;
private OnActiveListener onActiveListener;
private static final int ENABLED = 0;
private static final int DISABLED = 1;
private int collapsedWidth;
private int collapsedHeight;
private LinearLayout layer;
private boolean trailEnabled = false;
private boolean hasActivationState;
public SwipeButton(Context context) {
super(context);
init(context, null, -1, -1);
}
public SwipeButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, -1, -1);
}
public SwipeButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, -1);
}
#TargetApi(21)
public SwipeButton(Context context, AttributeSet attrs, int defStyleAttr, int
defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
public boolean isActive() {
return active;
}
public void setText(String text) {
centerText.setText(text);
}
public void setBackground(Drawable drawable) {
background.setBackground(drawable);
}
public void setSlidingButtonBackground(Drawable drawable) {
background.setBackground(drawable);
}
public void setDisabledDrawable(Drawable drawable) {
disabledDrawable = drawable;
if (!active) {
swipeButtonInner.setImageDrawable(drawable);
}
}
public void setButtonBackground(Drawable buttonBackground) {
if (buttonBackground != null) {
swipeButtonInner.setBackground(buttonBackground);
}
}
public void setEnabledDrawable(Drawable drawable) {
enabledDrawable = drawable;
if (active) {
swipeButtonInner.setImageDrawable(drawable);
}
}
public void setOnStateChangeListener(OnStateChangeListener
onStateChangeListener) {
this.onStateChangeListener = onStateChangeListener;
}
public void setOnActiveListener(OnActiveListener onActiveListener) {
this.onActiveListener = onActiveListener;
}
public void setInnerTextPadding(int left, int top, int right, int bottom) {
centerText.setPadding(left, top, right, bottom);
}
public void setSwipeButtonPadding(int left, int top, int right, int bottom) {
swipeButtonInner.setPadding(left, top, right, bottom);
}
public void setHasActivationState(boolean hasActivationState) {
this.hasActivationState = hasActivationState;
}
My XML:`
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/AnswerRelativeLayout"
android:layout_height="70dp"
android:orientation="vertical"
tools:context="com.example.dynamicobj.FragmentMain">
<com.example.dynamicobj.SwipeButton
android:id="#+id/test_btn"
android:layout_width="match_parent"
android:layout_height="70dp"
app:button_background="#drawable/shape_button"
app:button_image_disabled="#drawable/ic_launcher_background"
app:button_image_height="60dp"
app:button_image_width="100dp"
app:has_activate_state="true"
app:initial_state="disabled"
app:inner_text="Termine buchen"
app:inner_text_background="#drawable/shape_rounded"
app:inner_text_bottom_padding="18dp"
app:inner_text_right_padding="200dp"
app:inner_text_color="#android:color/black"
app:inner_text_size="16sp"
app:inner_text_top_padding="18dp" />
</LinearLayout>`
F
ragmentMain:
public class FragmentMain extends Fragment {
Context context;
View rootView;
public FragmentMain() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(false);
}
#Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_main, container, false);
LinearLayout layout = (LinearLayout)
rootView.findViewById(R.id.AnswerRelativeLayout);
SwipeButton button = new SwipeButton(getContext());
layout.addView(button);
return rootView;
}
}
So I got this custom SwipeButton class from Git (com.ebanx.swipebtn.SwipeButton). Now I want to create an Object from SwipeButton with a layout looking like the one from the xml file. Is there any method, where I just can give the new button the prefinished layout without having to use all these Methods in the SwipeButton class? I am going to create the buttons dynamically later on, but all being the same layout. Help pls?
you forgot to add LayoutParams to your button
use below code before adding button into layout
button.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT))
and set your LinearLayout height wrap_content not 70dp

Cannot resolve method 'super()' error in android studio

I am getting the error: "Cannot resolve method 'super()'" in my code and I am not sure how to resolve this, do you have any clues?
The code is as follows:
public GeoView( double left, double top, double width )
{
super();
this.left = left;
this.top = top;
this.width = width;
this.transform = null;
this.backing_store = null;
this.sink = false;
this.last_size = new Rect(0, 0, 200, 200 );
this.do_tracking = false;
Drawable background = new Drawable() {
#Override
public void draw(Canvas canvas) {
}
#Override
public void setAlpha(int i) {
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return 0;
}
};
What is GeoView class? You extended it from another class?
If yes. Check that parent class have empty constructor, because you call it.
If it's your own class and it didn't extended from any other class. Then you don't need to call super().
View doesn't have empty constructor.
You should Have at least one of this constructor.
public GeoView(Context context) {
super(context);
}
public GeoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GeoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
If you want to create view by yourself, you need first constructor. Then you have to
GeoView geoView = new GeoView(YourActivity.this);
Or you can simply add your view to layout. Then You second will be called constructor. I think you should add all of them.
<com.example.yourapp.GeoView
android:layout_height="300dp"
android:layout_width="match_parent"/>

How to create custom imageview with image load listener in android?

I want to load images into gridView and want to show progress bar during loading and dismiss it when the loading finished.
How to create image load listener for the images without using third party libraries?
Suggest me any sample code for reference?
Create a common class CustomNetworkImageView
public class CustomNetworkImageView extends NetworkImageView {
private Bitmap mLocalBitmap;
private boolean mShowLocal;
public void setLocalImageBitmap(Bitmap bitmap) {
if (bitmap != null) {
mShowLocal = true;
}
this.mLocalBitmap = bitmap;
requestLayout();
}
#Override
public void setImageUrl(String url, ImageLoader imageLoader) {
mShowLocal = false;
super.setImageUrl(url, imageLoader);
}
public CustomNetworkImageView(Context context) {
this(context, null);
}
public CustomNetworkImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomNetworkImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mShowLocal) {
setImageBitmap(mLocalBitmap);
}
}
}
In your xml use like this
<urpackagename.CustomNetworkImageView
android:id="#+id/uploadImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:scaleType="fitCenter"
android:src="#drawable/urDrawable" />
In your Activity or fragment initialize customImage View
public CustomNetworkImageView userImage;
userImage = (CustomNetworkImageView)getActivity().findViewById(R.id.uploadImage);

Getting resource ID or InputStream from ImageView Drawable

I am developing a GifView class, extending android.view.ImageView to show an animation gif in Android.
The problem is getting android:src="#drawable/myGif" from XML layout to load it in Movie automatically. When I override setImageDrawable(Drawable drawable) to intercept default android behaviour, I have two differents ways to do it: getting the id resource from #drawable/myGif and save it for later use, or load it on demand in Movie. But with the latter option I need to convert Drawable in InputStream unused compress method from Bitmap class to can safe GIF layers.
How can I do it?
public class GifView extends ImageView {
...
private Movie movie;
private int resId;
public GifView(Context context) {
super(context);
this.context = context;
}
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public GifView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
#Override
public void setImageDrawable(Drawable drawable) {
// ONE OF THOSE WAYS TO DO
// this.redId = ...
// this.movie = Movie.decodeStream(...);
}
...
}
ok, you can do it automatically, but does it really pay of?
public class GifImageView extends ImageView {
private final static String TAG = "GifImageView";
public GifImageView(Context context, AttributeSet attrs) {
super(context, attrs);
int[] attrArray = {
android.R.attr.src
};
TypedArray a = context.obtainStyledAttributes(attrs, attrArray);
int id = a.getResourceId(0, 0);
a.recycle();
if (id != 0) {
try {
Drawable d = new GifDrawable(getResources(), id);
setImageDrawable(d);
} catch (Exception e) {
Log.d(TAG, "GifImageView " + e.getMessage());
}
}
}
class GifDrawable extends Drawable implements Runnable {
...
}
}

custom navigation bar android

I want to do a custom horizontal navigation bar, which consists of as many dots as i have pages.
My thoughts are:
Create a Custom ListView
Create Class Dot extends from View
Add all these dots to the custom listview dynamically..
Is this right to do so?
EDIT:
public class NavigationBarLesson extends LinearLayout {
private LessonConfig config = LessonConfig.getInstance();
private ArrayList<NavigationCircle> navigationCircles;
private int pageCount;
public NavigationBarLesson(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public NavigationBarLesson(Context context) {
super(context);
init(context);
}
private void init(Context context) {
Log.i("init","yes");
pageCount = config.getLektionCount();
navigationCircles = new ArrayList<NavigationCircle>();
for(int i=0; i < pageCount; i++){
this.addView(new NavigationCircle(context));
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = View.MeasureSpec.getSize(widthMeasureSpec);
int height = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
}
public class NavigationCircle extends ImageView{
private static Bitmap img;
private Bitmap activeImg;
public NavigationCircle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public NavigationCircle(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public NavigationCircle(Context context) {
super(context);
init();
}
public void init() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, 0, 5, 0);
this.setLayoutParams(lp);
this.setBackgroundResource(R.drawable.upcoming_pages);
}
public Bitmap getImg() {
return img;
}
public Bitmap getActiveImg() {
return this.activeImg;
}
}
Well, I do not see why you would need a ListView. Create a horizontal LinearLayout with some simple views like TextView's or ImageView's etc added dynamically to the LinearLayout.

Categories

Resources