23

I get imageView's width 0. Below is code.

xml file :

<ImageView
        android:id="@+id/img"
        android:layout_width="200dp"
        android:layout_height="200dp" />

Activity :

@Override
protected void onResume() {
    super.onResume();
    ImageView img = (ImageView) findViewById(R.id.img);
    Log.d(TAG, "width : " + img.getWidth());
}

I don't want to use below code as I need to place this code in so many places.

ViewTreeObserver vto = img.getViewTreeObserver();
    vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        public boolean onPreDraw() {
            ImageView img = (ImageView) findViewById(R.id.img);
                    Log.d(TAG, "width : " + img.getWidth());
            return true;
        }
    });

My workaround : I actually later did not need to add this code in an activity. I added it to a non-activity class and instead of ImageView's width I used Bitmap image's width and now does not have this problem.

Geek
  • 8,280
  • 17
  • 73
  • 137
  • in onResume, the screen has not been laid out yet, all components are still 0x0. Consider calling getMeasuredWidth instead, or calling it after the layout is done – njzk2 Oct 09 '13 at 12:12
  • 1
    @njzk2 getMeasuredWidth() also returns 0. – Geek Oct 09 '13 at 12:24

5 Answers5

50

Where you calling getWidth() and getHeight() on ImageView? If you calling from onCreate() in activity, it won't work. You need to wait for activity window to attached and then call getWidth() and getHeight() on ImageView. You can try calling getWidth() and getHeight() from onWindowFocusChanged() method of your activity.

call on onWindowFocusChanged like this way

public void onWindowFocusChanged(boolean hasFocus) {
    // TODO Auto-generated method stub
    super.onWindowFocusChanged(hasFocus);
     if (hasFocus) {
        ImageView img = (ImageView) findViewById(R.id.img);
        Log.d(TAG, "width : " + img.getWidth());
     }

    }
Sanket Kachhela
  • 10,861
  • 8
  • 50
  • 75
9

Since you already have a "fixed" width imageView of 200dp why don't you just put it in dimens.xml

<dimen name="image_width">200dp</dimen>

and then simply

int width = (int) getResources().getDimension(R.dimen.image_Width)
linakis
  • 1,203
  • 10
  • 19
  • It is just an example. I use this code for manyImageViews and not all have fix size. Some use wrap_content. – Geek Oct 09 '13 at 12:26
  • 1
    getWidth and getHeight may vary as per device resolution for same images so you can not use like this way – Sanket Kachhela Oct 09 '13 at 12:26
  • Ok... and you down vote for that? You should use better examples :P @SanketKachhela that is not true, dimens resource takes care of that (if we are talking about predefined sizes (like the ones used in the original question) – linakis Oct 09 '13 at 12:30
  • Probably not the best practice, but nothing else is working for me, and I really need to move on and do other work in my app. I will note to revisit this problem if I need a UI for different devices, but for now a fixed `ImageView` with relative alignment in the UI works, so thanks. – Azurespot Apr 08 '15 at 02:53
4

You need to wait for ImageView inflated. To try OnLayoutChangeListener.

        imageView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
            //
            if (imageView.getWidth() > 0 && imageView.getHeight() > 0) {
                // put your code here...
            }
        });
尹劍平
  • 184
  • 1
  • 1
  • 5
3

well i found many many questions related that are duplicate to this question, and most of the answers were not fit for my case.

I have come up with a solution that may fit for yours as it does for me. Simply, i have created a custom ImageView and added a new callback OnSizeChangedCallback that is being called whenever the size of the view is changed to non zero values.

Here's a code sample of my own:

public class AvatarImageView extends ImageView{

    private OnImageViewSizeChanged sizeCallback = null;

    public AvatarImageView(Context context) {
        super(context);
    }

    public AvatarImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public AvatarImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w == 0 || h == 0) {
            return;
        }
        else{
            if(sizeCallback != null)
                sizeCallback.invoke(this, w, h);
        }

    }

    public void setOnImageViewSizeChanged(OnImageViewSizeChanged _callback){
        this.sizeCallback = _callback;

        if (getWidth() != 0 && getHeight() != 0) {
            _callback.invoke(this, getWidth(), getHeight());
        }
    }

    public interface OnImageViewSizeChanged{
        public void invoke(ImageView v, int w, int h);
    }
}

and you can simply use it as follows:

final AvatarImageView avatarIcon = (AvatarImageView) findViewById(R.id.avatarImg);
avatarIcon.setOnImageViewSizeChanged(new AvatarImageView.OnImageViewSizeChanged() {
    @Override
    public void invoke(ImageView v, final int w, final int h) {
            // Do whatever you want with w and h which are non zero values ...
    }
});

related questions:

  1. views-getwidth-and-getheight-returning-0
  2. android-imageview-return-getwidth-getheight-zero
  3. android-get-width-returns-0
Community
  • 1
  • 1
M.ElSaka
  • 1,264
  • 13
  • 20
0

Just came around this old question. Basically, the solution is to wait for the ImageView (or any other View, really) to be measured and "drawn". You can achieve that with implementing some late lifecycle callback (e.g. onGlobalLayout), but I found a simpler solution that works well for me: just wait for the attributes to become non-zero.

The caveat here is that you mustn't block the UI thread while waitig. It's very easy to achieve if you use Kotlin Coroutines:

override fun onStart() {
        super.onStart()
       
        coroutineScope.launch {
            while (imgBarcode.width == 0 || imgBarcode.height == 0) {
                MyLogger.d("waiting for ImageView to get its eventual size")
                delay(50)
            }

            val barcodeBitmap = generateBarcodeUseCase.generateBarcode(
                data, imgBarcode.width, imgBarcode.height
            )

            imgBarcode.setImageBitmap(barcodeBitmap)
        }
    }

If you don't use Coroutines, you'll need to use e.g. Handler.postDelayed() calls to implement the same feature.

Vasiliy
  • 16,221
  • 11
  • 71
  • 127