Change ImageView size in a recyclerview after Picasso.load()? - android

I have:
a recyclerview with GridLayoutManager
grid item layout with an ImageView in it (with height wrap_content and width match_parent) wrapped in a Framelayout, so the image is bottom|center_horizontal aligned
Picasso loading an image into ImageView asynchronously from the web
Current situation:
The image is loaded into the imageView, but the imageview's size is the recycled grid item's imageview's size.
What I would like to achieve:
After loading the image into the imageview, resize the imageview on runtime (redraw).
What I have tried:
notifyItemChanged() - could do the trick (in theory at least), but I
am not able to check if the current grid item's view is in layout
state, so my app crashed with IllegalStateException
listening to Picasso's load with a Callback, and onSuccess() check the imageview drawable aspectratio and try to resize the imageview itself with
requestLayout(). Did not work. (Well it worked, but only when there
is an animation or something triggering the redraw of the layout. If
there is nothing, then the imageview is not redrawn.)
listening to Picasso's load with a Callback, and onSuccess() start an animation animating the alpha of the imageview. This would trigger the redraw. But this sometimes worked sometimes not(and I don't know why).

What I did was putting an ImageView into FrameLayout and then changing this FrameLayout's size to needed. Hope it would help you.
<FrameLayout
android:id="#+id/frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true">
<ImageView
android:id="#+id/video_thumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitStart"
android:adjustViewBounds="true" />
</FrameLayout>
P.S. changing FrameLayout's size:
viewHolder.frame.setMinimumWidth(neededWidth);

Picasso has a function to resize your image. Something like this:
Picasso.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.into(imageView)
You can change centerCrop, to play with aspect ratio

I wanna show this using a
Campaign Banner List Using Dynamic Width - Height Example
that:
Create an item_campaign_banner.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white">
<RelativeLayout
android:id="#+id/campaignImageSuperView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/campaignImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="#drawable/campaign_image_error_icon"
android:visibility="visible"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"/>
<View
android:id="#+id/campaignSectionLine"
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_below="#+id/campaignImage"
android:background="#F2F2F2" />
</RelativeLayout>
</RelativeLayout>
Than create a BannerHolder.java
public class BannerHolder extends RecyclerView.ViewHolder {
private ImageView campaignImage;
private View campaignSectionLine;
private ViewGroup campaignImageSuperView;
public BannerHolder(#NonNull View itemView) {
super(itemView);
campaignImage = (ImageView) itemView.findViewById(R.id.campaignImage);
campaignSectionLine = itemView.findViewById(R.id.campaignSectionLine);
campaignImageSuperView = itemView.findViewById(R.id.campaignImageSuperView);
}
public ImageView getCampaignImage() {
return campaignImage;
}
public ViewGroup getCampaignImageSuperView() {
return campaignImageSuperView;
}
}
Than apply this in onBindViewHolder(#NonNull RecyclerView.ViewHolder incomingHolder, int position) method that
your created YourAdapter.java
if (incomingHolder instanceof BannerHolder) {
BannerHolder bannerHolder = (BannerHolder) incomingHolder;
Banner banner = (Banner) campaigns.get(position);
if(banner != null && banner.getImage() != null && banner.getImage().getWidth() != null && banner.getImage().getHeight() != null) {
Picasso.with(activity)
.load(banner.getImage().getUrl())
.error(R.drawable.campaign_image_error_icon)
.into(bannerHolder.getCampaignImage());
bannerHolder.getCampaignImageSuperView().setMinimumWidth(banner.getImage().getWidth());
bannerHolder.getCampaignImageSuperView().setMinimumHeight(banner.getImage().getHeight());
}
}
That's all!

Related

Multiple images in card views with Glide only shows the last loaded items

I am using Glide (also made a try with Picasso with the same behavior) to load a list of image urls into cards that should be shown in a camera view. Everything is working like a charm, but if i try to load more then around 8-12 images, only some of the last created cardviews are shown. Views created before are completely removed or shown as squashed image without card layout. There are no loading errors or further informations in the console log.
Now comes the crux of the story. When i exclude the images from the cardviews or use a local drawable as the image content, even loaded with Glide, all cards are displayed as expected.
I thought about a caching issue and added android:largeHeap="true" with no success. Also i've used a small thumbnail for all cards. Same scenario.
This is the function i am using to create the cardview's from a list of pois:
for (PlacesModel placesModel : mPlacesModelList) {
if ((placesModel.getDistance() / 1000f) < showDist) {
View view = layoutInflator.inflate(R.layout.ar_view, null);
view.setTag(placesModel.getPlaceId());
view.setVisibility(View.INVISIBLE);
ImageView img = view.findViewById(R.id.card_view_image);
view.setScaleY(setSize(placesModel.getDistance()) / 8.0f);
view.setScaleX(setSize(placesModel.getDistance()) / 8.0f);
TextView text = view.findViewById(R.id.t);
TextView desc = view.findViewById(R.id.d);
text.setMinWidth(200);
text.setText(placesModel.getPlaceName());
desc.setText(Config.cDist(placesModel.getDistance()));
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, setSize(placesModel.getDistance()) / 3.0f);
desc.setTextSize(TypedValue.COMPLEX_UNIT_SP, (setSize(placesModel.getDistance()) - 1) / 2.0f);
Glide.with(getApplicationContext())
.load(placesModel.getImageThumbnail())
.thumbnail(0.1f)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.error(R.drawable.no_image)
.listener(new RequestListener<Drawable>() {
#Override
public boolean onLoadFailed(#Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
view.setVisibility(View.GONE);
return false;
}
#Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
view.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
view.setVisibility(View.GONE);
return false;
}
})
.into(img);
infl.addView(view);
}
}
}
This is the according layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/marker"
android:clickable="true"
android:focusable="true"
android:onClick="cardViewClicked"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="#+id/mainMarker"
android:orientation="vertical"
android:padding="4dp"
android:background="#ffffff"
app:cardBackgroundColor="#ffffff"
app:cardCornerRadius="1dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:padding="2dp"
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/card_view_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"/>
<TextView
android:id="#+id/t"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="1"
android:maxWidth="80dp"
android:textAppearance="#style/TextAppearance.FontPath.Bold"
android:textColor="#android:color/black"/>
<TextView
android:id="#+id/d"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="1"
android:maxWidth="80dp"
android:text=""/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_below="#+id/mainMarker"
android:layout_centerHorizontal="true"
android:scaleType="fitXY"
android:src="#drawable/ic_marker" />
</RelativeLayout>
Create CardView and its contents dynamically (programmatically). In XML file, just create a layout and add the cards using CardView dynamically inside java file.
Create object inside class, but outside onCreate method.
RelativeLayout relativeLayout = new RelativeLayout;
Then, link the layout using its id
relativeLayout = findViewById(R.id.marker);
Then create a function which will be called while creating a card. Code inside that function will look like:
TextView textView = new TextView(this);
textView.setText("Something...");
CardView cardView = new CardView(this);
cardView.addView(textView);
relativeLayout.addView(cardView);
I've found the solution.
There was a Glide info in Logcat where others mentioned i can be slightly ignored.
Glide treats LayoutParams.WRAP_CONTENT as a request for an image the size of this device's screen dimensions. If you want to load the original image and are ok with the corresponding memory cost and OOMs (depending on the input size), use override(Target.SIZE_ORIGINAL). Otherwise, use LayoutParams.MATCH_PARENT, set layout_width and layout_height to fixed dimension, or use .override() with fixed dimensions.
Now i had only to set the layout parameters to Target.SIZE_ORIGINAL like this
view.setLayoutParams(new LinearLayout.LayoutParams(
Target.SIZE_ORIGINAL,
Target.SIZE_ORIGINAL));

How to get ImageView full width when inside a HorizontalScrollView

I'm placing a very wide image inside a HorizontalScrollView.
The ImageView/ScrollView height is dynamic as I've set the height to 0dp and added constraints. Since the ImageView's scale type is fitStart and adjustViewBounds is true - the image's width is being resized.
XML:
<HorizontalScrollView
android:id="#+id/mapScrollView"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toTopOf="#id/playButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<android.support.constraint.ConstraintLayout
android:id="#+id/mapLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView
android:id="#+id/mapImage"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:scaleType="fitStart"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.gigi.testmap.activities.quest.QuestMapPathView
android:id="#+id/mapLinesView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#id/mapImage"
app:layout_constraintEnd_toEndOf="#id/mapImage"
app:layout_constraintStart_toStartOf="#id/mapImage"
app:layout_constraintTop_toTopOf="#id/mapImage" />
</android.support.constraint.ConstraintLayout>
</HorizontalScrollView>
I'm trying to get the ImageView width (total width, visible & invisible part). Getting the ScrollView total width will also help.
My goal is to place buttons on top of the map in positions calculated according to width & height of the rendered ImageView.
I'm loading the image using Glide:
final ImageView mapImage = mActivity.findViewById(R.id.mapImage);
Glide.with(mActivity).load(R.drawable.fullmap).into(mapImage);
mapImage.getViewTreeObserver().addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mapImage.getWidth(); // --> returns 0
mapImage.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
});
I've tried getting the width using tree view observer's global layout listener but all I got is 0. The height is returned correctly though.
Any help will be much appreciated,
Thank you very much.
I have no experience with Glide.
You are setting the all views / containers android:layout_height="0dp"
try first to change it to any other arbitrary value or wrap_content.
you did not attach the code of how you are trying to get the height.
Have you tryed mapImage.getHeight()
For Bitmap, I used to get this way
Glide.with(mContext())
.asBitmap()
.load(path)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap bitmap,
Transition<? super Bitmap> transition) {
int w = bitmap.getWidth();
int h = bitmap.getHeight()
}
});
It seems that the image is not fully rendered once the tree view global layout listener is first called (I guess it's because the image width is pretty big) - What I did was to have a check for the image width and only if greater than 0, remove the listener and continue with my code.
final ImageView mapImage = mActivity.findViewById(R.id.mapImage);
Glide.with(mActivity).load(R.drawable.fullmap).into(mapImage);
mapImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (mapImage.getWidth() > 0) {
mapImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
//continue width related code here
}
}
});
Maybe not the prefect solution and there is a small delay until the image shows up - but it is ok for my use case.

Cannot maintain image aspect ratio/quality in android application

As the title suggest, I am having a lot of issues maintaining the quality and aspect ratio of my images on my android application.
Basically I have an image view and I am trying to get images to fit the image view and maintain it's image quality. I cant use fitxy because that will stretch the image to fill the entire imageview, but I only desire the image to automatically scale it's self and show as crisp as possible like your phone gallery.
In my code, I use fitCenter as the scale type and the size of the image seems alright, however the image is terrible distorted. This is the code.
Ive also included a snapshot of the android and iphone version to show how the exact same image is showing differently on the same image.
<?xml version="1.0" encoding="utf-8"?>
<!-- This controls layout for the slider in Gallery -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView
android:id="#+id/pagerimage"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="center_horizontal"
/>
</RelativeLayout>
</LinearLayout>
iPhone
Android
public void handleGalleryImages() {
try {
for (int j = 0; j < imagesNaturalItemlist.size(); j++) {
Log.e("ImagePATH>>>>>", imagesNaturalItemlist.get(j).getImage());
String imageid = imagesNaturalItemlist.get(j).getUniqueId();
downgalleryimage = new DownloadImage(imagesNaturalItemlist.get(j).getImage(), imageid);
downgallerylist.add(downgalleryimage);
beachimages.add(imagesNaturalItemlist.get(j).getImage());
}
} catch (NullPointerException e) {
e.printStackTrace();
}
adapter
public Object instantiateItem(ViewGroup container, int position) {
View itemView = LayoutInflater.from(context).inflate(R.layout.sliding_pager_row, container, false);
ImageView imageView = (ImageView) itemView.findViewById(R.id.pagerimage);
if (images.get(position) != null && !images.get(position).equals("")) {
//Picasso.with(context).load(images.get(position)).into(imageView);
Glide.with(context)
.load(images.get(position))
.apply(new RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL).override(600, 250))
.into(imageView);
}
container.addView(itemView);
return itemView;
}

View width is zero when set to MATCH_PARENT

I have an ImageView inside of a LinearLayout which is an item of a RecyclerView. I have all these views' width as MATCH_PARENT.
Now, I'm trying to calculate the heigth using the aspect ratio before the image is loaded so there's no resizing.
But, what I've seen is that before the image is loaded, its width is zero.
The thing is that I'm doing similar things in other parts of the app and it's working fine, I don't really know what I'm missing here.
RecyclerView item:
<?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"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white">
<LinearLayout
...
</LinearLayout>
<!-- This is the image -->
<ImageView
android:id="#+id/descubre_shop_banner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/colorLightText"
android:layout_marginRight="16dp"
android:layout_marginLeft="16dp"
android:layout_marginBottom="8dp"/>
</LinearLayout>
ViewHolder
mBannerTarget = new Target()
{
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)
{
mShopBannerImageView.setImageBitmap(bitmap);
// Wrap content when the images is loaded.
mShopBannerImageView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
mShopBannerImageView.setBackgroundColor(-1);
mShopBannerImageView.setAlpha(1.0f);
Animation fadeOut = new AlphaAnimation(0, 1);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(250);
mShopBannerImageView.startAnimation(fadeOut);
LOADED = true;
}
#Override
public void onBitmapFailed(Drawable errorDrawable)
{
mShopBannerImageView.setBackgroundColor(
mContext.getResources().getColor(android.R.color.holo_red_dark));
mShopBannerImageView.setAlpha(0.2f);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable)
{
// This is 0
Log.d(Properties.TAG, "" + mShopBannerImageView.getWidth());
// Remove the old bitmap
mShopBannerImageView.setImageBitmap(null);
// Set the height using the width and the aspect ratio.
mShopBannerImageView.getLayoutParams().height =
(int) (mShopBannerImageView.getWidth() * shop.getAspectRatio());
// Set a color while loading.
mShopBannerImageView.setBackgroundColor(
mContext.getResources().getColor(R.color.colorAccent));
mShopBannerImageView.setAlpha(0.25f);
}
};
I tried to set the width of the ImageView and its parent as MATCH_PARENT in code but no luck. Any clue of what's going on?
Regards,
The views dimensions is always 0 before being fully created, if you wanted to know the actual width, you have to use the method: post():
view.post(new Runnable() {
#Override
public void run() {
// This is where width would be different than 0
}
});
That being said, you should actually use an other method to keep the ratio than actually calculating it yourself!
See this thread for further information How to scale an Image in ImageView to keep the aspect ratio

Fit image to screen width in viewpager in landscape mode in android

I have a VerticalViewPager and I have some images in it. When I rotate my device in landscape mode my ImageView doesn't scale to width of my screen. It fits the height if image instead. I used AspectRatioImageView. It fits the width but VerticalViewPager doesn't scroll down.
Thank you.
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<HackyViewPager
android:id="#+id/vvp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
>
</HackyViewPager>
</RelativeLayout>
Here is rowitemview.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:pixlui="http://schemas.android.com/apk/com.neopixl.pixlui"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#fff" >
<AspectRatioImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="#drawable/m4_3" />
</LinearLayout>
and here is my instaniateItem of my ImagePageAdapter that extends PagerAdapter:
#Override
public Object instantiateItem(ViewGroup container, int position)
{
Context context = MainActivity.this;
container.requestLayout();
AspectRatioImageView imageView = new AspectRatioImageView(context);
imageView.setImageDrawable(getResources().getDrawable(R.drawable.m4_3));
mAttacher = new PhotoViewAttacher(imageView);
mAttacher.setOnMatrixChangeListener(new OnMatrixChangedListener() {
#Override
public void onMatrixChanged(RectF rect) {
if((int)rect.width()>_width)
viewPager.setLocked(true);
else
viewPager.setLocked(false);
}
});
P.S : The issue is with viewPager height. When I scroll down the image it just goes to the other page instead of scrolling the scaled image.
Here's what you need.
This is a good solution I found out.
Android Crop Center of Bitmap
This allows to set the height and your width according to your orientation.
To change the image's scale to full screen, you generally use center crop like this:
yourImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
Hope this information was useful..:)
Use android:scalType = "fitxy" in activity code like this imageView.setScaleType(ImageView.ScaleType.FIT_XY);

Categories

Resources