How to get ImageView full width when inside a HorizontalScrollView - android

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.

Related

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

ImageView expanding container despite adjustViewBounds

I want to wrap an ImageView inside a LinearLayout so that I can center a group of views. However, the original image needs to be scaled down to fit in the ImageView, and the original size expands the LinearLayout, despite my use of adjustViewBounds="true" and an enclosing FrameLayout as suggested by previous questions on SO.
The desired layout should look like this,
but the observed layout looks like this,
as produced by the XML below:
<android.support.percent.PercentRelativeLayout
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:layout_height="match_parent"
tools:context="project.MainActivity">
<LinearLayout
android:layout_width="wrap_content"
app:layout_heightPercent="32%"
android:orientation="horizontal"
android:background="#b44343"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sample Text"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#5555ae">
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#2c8c4c"
android:src="#drawable/spades_icon"
android:adjustViewBounds="true"
android:scaleType="centerInside"/>
</FrameLayout>
</LinearLayout>
</android.support.percent.PercentRelativeLayout>
I can't use the other suggestion of setting android:maxHeight="100dp" because I need the height to be relative to that of the screen.
I see that you have added android:adjustViewBounds="true".
You can combine that with android:maxWidth="60dp"
So your imageView should look like this.
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#2c8c4c"
android:src="#drawable/spades_icon"
android:adjustViewBounds="true"
android:maxWidth="60dp"
android:scaleType="centerInside"/>
You can change the max width to any number you want.
Things you can do:
1) Set a specific width / height to the FrameLayout enclosing the ImageView and set android:scaleType to centerInside, fitCenter, or fitXY for the ImageViwe.
2) Programatically, in your activity, after onCreate, in onResume for example, you can get the LayoutParams and change the width and height of the ImageView doing you own scaleing. I take this aproach when I scale against the screen widht or height at run time.
EDIT 1
Example of second alternative:
public class TestActivity extends AppCompatActivity {
ImageView imgView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testactivity_layout);
imgView = (ImageView) findViewById(R.id.imgview);
}
#Override
protected void onResume() {
super.onResume();
ViewGroup.LayoutParams params = imgView.getLayoutParams();
params.width = 100;
}
}
Notes:
The width is expressed in pixel.
To get the display metrics:
How to get screen display metrics in application class
To establish a relative width for the ImageView, get the width of the display and calculate the desireed % as the width of the image.
Based on this answer to another question, a solution that removes the whitespace in the LinearLayout while preserving the height and aspect ratio of the image is:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
LinearLayout layout = (LinearLayout) findViewById(R.id.mLinearLayout);
ImageView imageView = (ImageView) findViewById(R.id.mImageView);
TextView textView = (TextView) findViewById(R.id.mTextView);
layout.getLayoutParams().width = textView.getWidth()+imageView.getWidth();
layout.requestLayout();
}
EDIT:
Based on #Juan's answer and these instructions, the following code also achieves the desired result:
#Override
protected void onResume() {
DisplayMetrics displayMetrics = new DisplayMetrics();
this.getWindowManager()
.getDefaultDisplay()
.getMetrics(displayMetrics);
ViewGroup.LayoutParams params = imgView.getLayoutParams();
params.height = (int)Math.floor(displayMetrics.heightPixels * 0.32);
}

Image from network with Glide too small on Android

I am trying to download an image from the network and display in the ImageView with Glide using scaleType="centerInside" option.
For some reason, the image, when downloaded from the network, looks much smaller on the screen than when the same image is put into the ImageView from resources.
Example:
Both images can be found here. I would argue that even those images that have been set from resources look smaller than they could actually be when compared to what I see on my laptop. I understand that there is something related to the screen density in play, but how can I make these images be of "user-friendly size", e.g., a bit larger?
Even a different image of 600x250 px size is ridiculously small on the phone (with ImageView's layout_height and layout_width set to "wrap_content").
Code from the Activity:
public class DisplayImagesActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.display_image_activity);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
setTitle("Hello StackOverflow!");
ImageView top_left = (ImageView) findViewById(R.id.top_left);
ImageView top_right = (ImageView) findViewById(R.id.top_right);
ImageView bottom_left = (ImageView) findViewById(R.id.bottom_left);
ImageView bottom_right = (ImageView) findViewById(R.id.bottom_right);
String[] urls = new String[] {
"http://imgur.com/6jMOdg0.png",
"http://imgur.com/AhIziYr.png"
};
top_left.setImageResource(R.drawable.top_left);
top_right.setImageResource(R.drawable.top_right);
Glide.with(this)
.load(urls[0])
.signature(new StringSignature(new Date().toString()))
.into(bottom_left);
Glide.with(this)
.load(urls[1])
.signature(new StringSignature(new Date().toString()))
.into(bottom_right);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
this.finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
display_image_activity.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/match_parent"
android:orientation="vertical">
<include layout="#layout/_toolbar" />
<ScrollView
style="#style/match_parent">
<RelativeLayout
style="#style/match_parent"
android:padding="16dp">
<TextView
style="#style/wrap_content"
android:id="#+id/text_resources"
android:layout_marginBottom="10dp"
android:text="From Resources"/>
<ImageView
android:id="#+id/top_left"
android:background="#color/Linen"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_marginBottom="20dp"
android:layout_below="#id/text_resources"
android:scaleType="centerInside"/>
<ImageView
android:id="#+id/top_right"
android:background="#color/Linen"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_toRightOf="#id/top_left"
android:layout_toEndOf="#id/top_left"
android:layout_below="#id/text_resources"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:scaleType="centerInside"/>
<TextView
style="#style/wrap_content"
android:id="#+id/text_network"
android:layout_below="#id/top_left"
android:layout_marginBottom="10dp"
android:text="From Network"/>
<ImageView
android:id="#+id/bottom_left"
android:background="#color/Linen"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_below="#id/text_network"
android:scaleType="centerInside" />
<ImageView
android:id="#+id/bottom_right"
android:background="#color/Linen"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_toRightOf="#id/bottom_left"
android:layout_toEndOf="#id/bottom_left"
android:layout_below="#id/text_network"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:scaleType="centerInside" />
</RelativeLayout>
</ScrollView>
</LinearLayout>
I faced the same problem. Glide tries to interpret what my app needs and transforms the images accordingly, resulting in too small images in some places. In my case the ImageViews use adjustViewBounds="true" and MaxWdth/Height leading to problems
While I am not anything close to being a Glide Expert, I found a quick fix working for me.
I simply added a .dontTransform() mehod call, which in my case is OK since I use thumbnails that already have been pre-scaled.
GlideApp.with(context).load(fireStorage).dontTransform().into(imgView);
(Using a Placeholder would probably also have helped, but again, for me this was the easiest way)
Your images cannot be bigger than what you defined:
android:layout_width="150dp"
android:layout_height="120dp"
Try
android:layout_width="match_parent"
android:layout_height="wrap_content"
Your ImageViews can be that fixed size if you want although they should be flexible with match_parent/wrap_content.
I don't know what Glide does for sure but it looks like the resolution of the images from the network is smaller than the ones from resources. The android:scaleType="centerInside" gives you the behaviour that the image will be SHRUNK until both dimensions of the image fit in the ImageView and it's aspect ratio is maintained. If you want the images to expand to fit the ImageView you probably want android:scaleType="fitCenter" instead. You might also want android:adjustViewBounds to be true/false depending on how you want it to behave if you decide to make the dimensions flexible.
The documentation for scaleType is useful here:
https://developer.android.com/reference/android/widget/ImageView.ScaleType.html
https://developer.android.com/reference/android/graphics/Matrix.ScaleToFit.html#CENTER
This code saved my time. It works for me!
//Get actual width and height of image
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Bitmap bitmap = null;
try {
URL url = new URL(imgUrl);
bitmap = BitmapFactory.decodeStream((InputStream)url.getContent());
} catch (IOException e) {
Timber.e("Image Loading Error %s", e.getLocalizedMessage());
}
if (bitmap != null) {
final float scale = resources.getDisplayMetrics().density;
final int dpWidthInPx = (int) (bitmap.getWidth() * scale + 0.5f);
final int dpHeightInPx = (int) (bitmap.getHeight() * scale + 0.5f);
//Set result width and height to image
GlideApp.with(imgAns.getContext())
.load(imgUrl)
.override(dpWidthInPx, dpHeightInPx)
.into(imgAns);
}

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

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!

Dynamically setting relative dimension of a linearlayout

I want to resize my LinearLayout (or a view) to a dimension which is relative to the parent or itself. For example, I want the width to be 1/3 of the parent's width. Or, the height should be same as its own width. I don't want to use any constants , so that it works for all devices.
adding code:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<LinearLayout android:id="#+id/ll_board"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
...
</LinearLayout>
code:
public class GMActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
LinearLayout board_layout = (LinearLayout)findViewById(R.id.ll_board);
// I wanted to resize board_layout here ..
// getParent().getWidth() returns 0
Log.d("gm", "layout: " + ((LinearLayout) board_layout.getParent()).getWidth());
// ..
}
}
getWidth() is giving 0. Is it too early to call this? If yes, what is the correct place to call this?
Basically my intention is to make the width of the layout a fraction of the screen size width, and, height same as its own width.
Considering layout your LinearLayout and that its parent it's another LinearLayout:
Get the parent's width:
int parentWidth = ((LinearLayout) layout.getParent()).getWidth();
Get the view's width:
int viewWidth = ((LinearLayout) layout).getWidth();
set the
view.setHeight(viewWidth );
view.setWidth(parentWidth / 3);
I found height=width solution (square shaped layout) in LinearLayout in Square Form

Categories

Resources