2

I have a ListView. On each row is an image and text. I want to scale image so it fits the row height i.e.

  • image shouldn't cause the row height is bigger
  • image height should be the same as the row height
  • image width should be calculated so the original width height ratio is maintained

All solutions that I figured out seem too complex for which I'd assume is a relatively common requirement so I'd like to check if I'm missing something - do you have an idea how to achieve this in a simpler way ?

The following are solutions I considered.

Solution 1:

Nested views for ListView item and set android:layout_height="match_parent". Image is resized correctly but ImageView occupies the width as if it was not resized. (See the following picture. I added black background to see how much space occupies ImageView.)

enter image description here

<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">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView android:id="@+id/Image"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:adjustViewBounds="true"
            android:scaleType="fitStart"

            android:background="#000000"
            android:padding="1dp"/>
        <TextView
            android:id="@+id/Text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/Image" />
    </LinearLayout>
</RelativeLayout>

Solution 2: Using

ViewTreeObserver observer = MyTextView.getViewTreeObserver() observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {

public boolean onPreDraw(){
    observer.removeOnPreDrawListener(this);
    // Finding out height of TextView and then calculating width and height of image and setting size of ImageView. Of course, I could also get observer from ImageView and then just set width as height is correct
}

}

This seems like too complicated.

Solution 3:

Use, e.g., Paint.FontMetrics to calculate height but I'd also need to find out the font used (e.g., system one ...) Also ListView probably has some padding, etc. so kind of lots of things to retrieve.

Any other simpler solution please ?

Edit - clarification

The image has the max. size, which if reached, stops any further increasing of the size of ImageView.

  • There's a circular dependency in your requirements: the width of the image depends on the height of the image, the height of the image depends on the height of the text, and the height of the text depends on the width of the image. Imagine some long text (multiple lines). It would be possible for both the image and text to "fill" the row at multiple sizes, because as the image gets taller it also gets wider, which in turn makes the text have less size, so it will wrap lines more, and make the row taller, and so on. This will need to be addressed before I can try to give a helpful answer. – Ben P. Dec 18 '17 at 21:46
  • @Ben P. It's not circular - "the height of the text depends on the width of the image" isn't completely true but I see what you probably mean. Text determines height of the row. Then I need to scale down image so its height is the same as the height of the row. Width of image maintains aspect ratio so I can calculate it too. Now, I expand width of row so also image goes there based on calculated width. Yes, now it can happen that after factoring in image width, the text won't fit but onMeasure for that purpose can be called multiple times. After a definite rounds final size will be calculated. –  Dec 19 '17 at 15:37
  • I'm imagining a scenario where the first pass to lay out text says "this is 5 lines tall", so you lay out the image, which reflows the text, which is now 6 lines tall, so you resize the image, which reflows the text, which is now 7 lines tall, ad infinitum. I can further be certain that this is a problem as the `ConstraintLayout` I tried to use to solve your issue incorrectly draws the text due to this circular dependency (it just stops reflowing the text after some fixed number of cycles). – Ben P. Dec 19 '17 at 16:16
  • @Ben P. Yeah, I got it. ;-) I should've said the image has some max. size so it won't infinitively increase its size. But point taken - let me add state this explicitly in the question. Thank you. :-) –  Dec 19 '17 at 19:34

2 Answers2

0

If you are wanting to set the width and height of the ImageView based on the width and height, you can always get the width and height of the TextView programmatically, then implement your own algorithm to scale the ImageView accordingly.

To get the width and height from a textView, let's call it item_textView, it's fairly simple:

TextView item_textView = findViewById(R.id.item_textView);
int text_width = item_textView.getWidth();
int text_height  = item_textView.getHeight();

At this point, you can perform whatever math you need to perform to scale the imageView down to the correct size. To set the size of the imageView:

my_imageView.getLayoutParams().width = text_width/2;
my_imageView.getLayoutParams().height = text_height/2;
0

Since you defined the parent linear layout height as wrap content it will take the size of its content and may vary based on its children element dimensions.

Try to define exact size for the image in your row layout. If you can define its width and height as 40dp or 48dp you might be able to achieve your goal.

If all elements depend on each other's size you might be able to see variations. Try to define the avatar/image sizes in specific dp.

I tried to tweak your layout and got some results. Check out the following code.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp">
    <ImageView
        android:id="@+id/earthquake_magnitude"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:background="@android:color/black"
        android:fontFamily="sans-serif-medium"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="16dp"
        android:layout_marginStart="16dp"
        android:layout_weight="1"
        android:text="CHROME" />
</LinearLayout>
Hemant
  • 38
  • 8
  • @Hermant. Thank you for the answer. I know that if I defined fixed size, it'd be simple as you say but my text size can change so the images size too - so I can't just fix it. Imagine, e.g., Chrome in Android. You zoom in and everything's well scaled up. So the circular dependency is really only a myth. ;-) –  Dec 19 '17 at 15:44