4

I've been trying to find a solution to this, but perhaps I'm not understanding how the "onMeasure()" method works so well in Android!

This is the first time I've made custom views "dynamic" in Android, so I learned that you need to override the "onMeasure()" method in order to resize your view depending upon how large the screen is. I would like the my view to be half the width of the screen, plus a little extra (this number is arbitrary, as long as its relatively small, between 3 and 5), so I wrote this code for the onMeasure method:

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    int halfWidth = (MeasureSpec.getSize(widthMeasureSpec) / 2) - 5;

    this.setMeasuredDimension((int) halfWidth, (int) halfWidth);
}

I'm sure this a terrible way of going about things because everywhere I've found online has so much more code than I do while trying to accomplish something similar!

When I place these views next to each other in the XML, this first view is correctly sized (it is indeed half the size of the width minus a little more), but the second view is much smaller, probably half the size of the width of the last view minus a little more! (I would post a picture of it but unfortunately I don't have enough reputation to do so...)

What should occur is the two boxes should be the same size, no matter where in the XML I place them. They should all be one-half the size of the width of the screen, minus a little extra.

I know it has something to do with how I'm using "setMeasuredDimension()", but I don't know what it is. I really hope someone could clarify things a bit for me! Thanks! :)

Thanizer
  • 382
  • 1
  • 6
  • 21
  • Hi take a look at "layout_weight" http://stackoverflow.com/questions/3995825/what-does-androidlayout-weight-mean – GHz May 20 '12 at 00:18

4 Answers4

3

I actually accomplished this today through a little bit of experimentation based upon what Tim said. I moved away from "onMeasure()" and only wrote this code for it:

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    // Call the super class.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    // Set the dimensions of this view.
    this.setMeasuredDimension((int) width, (int) width);
}

Width is a global float. Where does width come from? I derived width from this method that I wrote in the view to resize:

public void initSize(float size)
{
    width = size;
    this.invalidate();
}

I call "invalidate()" to redraw the view. Something has to call this method though, so I do so in the activity. First, I get the size of the screen in the Activity, not the View, using this code:

Display thisDisplay = this.getWindowManager().getDefaultDisplay();
Point desiredSize = new Point();
display.getSize(desiredSize);
int width = size.x;

You used to be able to call a method called "getWidth()" on "thisDisplay" but this method is now depreciated, so you should use "getSize()" instead. Finally, right after you initialize the constructor, call the "initSize()" method:

nameBox = (InfoBoxView) findViewById(R.id.nameBox);
    nameBox.initSize(width);

Now, all the views will be the same size. I don't know if this is the best way to do this, but it works for now and I figured I'd post it here so everyone knew. :)

Thanizer
  • 382
  • 1
  • 6
  • 21
2

Old question.. but here is onMeasure which makes view square and smaller than half of the screen without need to code anything extra in Activity. You can change it to produce any other dimension ratio.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // get view dimensions suggested by layout
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    // width should never be more than half of a screen
    int displayWidth = getContext().getResources().getDisplayMetrics().widthPixels;
    width = Math.min(width, displayWidth/2);
    // always make it a square (use the smaller dimension of the two)
    if (width > height) {
        width = height;
    } else {
        height = width;
    }
    // use the values
    super.onMeasure(
            MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
    );
}
Tono Wiedermann
  • 608
  • 6
  • 13
1

I think the problem is the size of the parent view has been reduced also, perhaps about half the size, per your claim. Therefore I propose code:

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
   // Prevent reducing the size of the parent view
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);

   int halfWidth = (MeasureSpec.getSize(widthMeasureSpec) / 2) - 5;

   this.setMeasuredDimension((int) halfWidth, (int) halfWidth);
}

Also, what is your XML layout like? That may need fixing also, though doubted that is the issue.

I did not try this with my Android SDK.

The Original Android
  • 6,147
  • 3
  • 26
  • 31
  • Can you say exactly what you think calling `super.onMeasure` will achieve here? It will just call the unoverriden method onMeasure, which will set the measured dimensions, which will be immediately overwritten again after returning to the Override function. What you've described there makes no sense to me, though you can try to explain your answer if you want. – Tim May 20 '12 at 05:33
  • I know it is convoluted to call the parent onMeasure. But basically I think this would fix the issue of the original problem of “but the second view is much smaller, probably half the size of the width of the last view…”, as Thanizer states. In summary, I think View.onMeasure should have implemented the way we're all thinking :) And then of course the code afterwards is the actual desired implementation. – The Original Android May 20 '12 at 06:58
  • As Tim said, I'm a little confused as to how that will accomplish anything. I did try it on the SDK but it didn't do anything. I appreciate the thought, though! – Thanizer May 20 '12 at 12:54
0

I think you need to read up in the documentation on onMeasure and see how it works.

It is actually called multiple times depending on the type of layout container that the view is in, and the exact specifics will vary from one ViewGroup to another. Generally the dimension are loosest the first time around, and then get more fine grained as more components are laid out.

When onMeasure is called, you first want to see what kind of MeasureSpec you have received by calling MeasureSpec.getMode(widthMeasureSpec). This will return either MeasureSpec.AT_MOST or MeasureSpec.EXACTLY.

If it returns AT_MOST, that tells you the largest possible area the view could fill at this time. When you're laying out the first view, it's likely saying that your first view can use at most the entire width of the screen, because no other components are laid out yet. Dividing the entire width of the container by two, you then tell the container that you're happy with using 50% of the horizontal layout by calling setMeasuredDimension.

Next when onMeasure is called for your second view, only half of the horizontal width of the container is now available, because you've already requested half of the screen for the first view.

You're then dividing that number by two, leaving the second view with only 25% of the horizontal width of the container, while the first view got 50% of the horizontal width.

I'd suggest that overriding onMeasure is not the proper way to get a view to use half the screen, though I wanted to explain how it works. Is there some reason you think you can't achieve this with just standard xml layouts?

EDIT: Reread your question again, I think your problem is that you're assuming that widthMeasureSpec means the same thing as the screen width (which it does not). You could try to properly query the width of the screen and call setMeasureDimension based on that, though it might not work in every case. If you're not careful you may request a width greater than what's allotted to you based onMeasure. Or sometimes you will get called an onMeasure with MeasureSpec.EXACTLY, at which point you have no choice but to use the supplied width or height.

Tim
  • 35,413
  • 11
  • 95
  • 121
  • I've actually looked up querying the screen width, but it seems everyone has a different solution to this. What's the proper solution when it comes to asking for the screen width, in your opinion? – Thanizer May 20 '12 at 12:57