16

I have a class called FractalView that extends ImageView. My goal is to draw a fractal with this class that has the size of the whole screen. It is the only View in my activity's layout file, other than the top-level LinearLayout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
    android:background="#FFFFFFFF">

    <com.antonc.fractal_wallpaper.FractalView
        android:id="@+id/fractal"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"/>
</LinearLayout>

As you can see, I'm using "match_parent" for both layout_width and layout_height of both the FractalView and the LinearLayout. At this point I assumed that the size of the FractalView is set to a certain value, since it's matching it's parent, which is matching it's parent(which is the whole screen). Then I try to detect the size that is available to FractalView by using getWidth() and getHeight():

public class FractalView extends ImageView {

private Context mContext;
private Handler mHandler;

private int mScrWidth;
private int mScrHeight;

public FractalView(Context context) {
    super(context);
    mContext = context;
    init();
}

private void init() {

    mScrWidth = getWidth();
    mScrHeight = getHeight();
    // Breakpoint
}

@Override
protected void onDraw(Canvas canvas) {
    [...]
}

}

But when my code reaches the breakpoint, the debugger shows that mScrWidth and mScrHeight are zero. I've tried using getMeasuredWidth() and getMaxWidth(), but they also return incorrect values. I've read several answers to similar questions, i.e. this one and they all explain that getWidth() returns the size of an image after it has been drawn, while what I actually need is the width of the area which is available to this view, before I start drawing.

Is there a way to retrieve such values for width and height?

Community
  • 1
  • 1
Anton Cherkashyn
  • 5,719
  • 6
  • 43
  • 80
  • 2
    Override onMeasure. http://stackoverflow.com/questions/7423082/authorative-way-to-override-onmeasure – Simon Jan 29 '13 at 21:50
  • Since you are willing to use the whole screen, you can take a look at getting the device width and height (this is not adviceable). If getMeasuredWidth() is returning the wrong value you should wait until its get meaured and call that function again. e.g. call that function in another Activity lifecycle method (e.g. onResume instead of onCreate), hope this helps – Tobrun Jan 29 '13 at 21:52
  • The other thing you can do, since you are using an ImageView, is to override setImageBitmap(). – Simon Jan 29 '13 at 21:56
  • I've found a way to do this and I've posted it as an answer. I'm not going to accept my own answer for now, because maybe someone knows a better way to do this. Simon, user1281750 - Thank you, I'll take a look at the methods that you suggested. – Anton Cherkashyn Jan 29 '13 at 22:08

2 Answers2

32

A view's size isn't available until after onMeasure(), particularly if its set to wrap_content, or fill_parent.

You should either access the size in or after onMeasure() in the View code, or in a a Layout Tree Observer:

LinearLayout layout = (LinearLayout)findViewById(R.id.mylayout);
ViewTreeObserver vto = layout.getViewTreeObserver(); 
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
    @Override 
    public void onGlobalLayout() { 
        this.layout.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
        int width  = layout.getMeasuredWidth();
        int height = layout.getMeasuredHeight(); 

    } 
});

You will also need to add android:id="@+id/mylayout" to your LinearLayout for the second one to work.

Raghav Sood
  • 81,899
  • 22
  • 187
  • 195
  • 1
    I ended up overriding onMeasure() and putting calls to `getMeasuredWidth()` and `getMeasuredHeight()` right after the call to `super.onMeasure(widthMeasureSpec, heightMeasureSpec)`. This seems to do the trick. – Anton Cherkashyn Jan 30 '13 at 23:06
0

I figured out a way to do this while I was typing the question. Instead of trying to retrieve the size in the constructor method, I moved the code to the onDraw() method like this:

private void init() {

}

@Override
protected void onDraw(Canvas canvas) {
    mScrWidth = canvas.getWidth();
    mScrHeight = canvas.getHeight();
    [...]
}

This returns correct dimensions.

Anton Cherkashyn
  • 5,719
  • 6
  • 43
  • 80
  • 7
    See Raghav's answer (and my comments). Any code that you do not need in onDraw is bad code, since it is executed everytime your view is drawn. If you don't update the view often, it might not matter but that should not be an excuse for doing things in the wrong place. – Simon Jan 29 '13 at 22:12