0

This has been driving me crazy for the past couple of hours.
I have a FrameLayout, that has a single button on it. When you click the button, I want to draw a new word, and get it's measurements so I can draw a rectangle around it.

This is the code that runs when you click on the 'Next' button:

private OnClickListener m_onClickNext = new OnClickListener()
{
    public void onClick(View v)
    {
        TextView tv = new TextView(context);                
        tv.setTextColor(0xFF000000);
        tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 100);                
        tv.setText("TEXTSTRING");

        m_frameLayout.addView(tv, new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER));

        //tv.measure(0, 0);
        int left = tv.getLeft();
        int top = tv.getTop();
        int right = left + tv.getMeasuredWidth();
        int bottom = top + tv.getMeasuredHeight();
    }
};

left and top are 0 (zero).
Adding tv.measure(0, 0) atleast updates the width and height.

I tried calling invalidate and requestLayout on tv and m_frameLayout, with no success.

I understand the layout hasn't finish drawing.. but WHY ????

Itsik
  • 3,920
  • 1
  • 25
  • 37

1 Answers1

0

I understand the layout hasn't finished drawing.. but, WHY ????

The reason the layout is not done drawing is because you are using the same thread that the UI drawing uses. Everything you do is using a single thread (the UI Thread) unless you specify otherwise with an AsyncTask or another Thread. Invalidate() is not a function that forces an immediate redraw. Invalidate() flags everything so that when the UI Thread is free, it will redraw whatever was invalidated. If you keep giving the UI Thread instructions, then the redraw doesn't have a chance to happen.

In other words, Invalidate will not change its numbers until the drawing has a chance to run, and since it is not given a chance to run before you ask for its numbers it can't possibly give you the right numbers. In order to make this behave as desired, a couple of things need to happen:

  1. You have to allow the layout to draw. This can be done in three ways: a) You can run your click on a different thread, but this doesn't guarantee when it is going to be done, just that it will. b) You can override invalidate() so that it performs a callback to let you know to add the rectangle. c) You can implement a listener so that your code runs when the size of the TextView has changed.

  2. AFTER the layout has redrawn, then you can apply your special rectangle. By using method c) above, you can do this basically right away. However, you have to make sure that you invalidate() again, causing a potential loop in processing. So, make sure to have an indicator of some kind to tell you that you already drew the rectangle and not to invalidate() a third time.

Hope this helps,

FuzzicalLogic

Fuzzical Logic
  • 12,947
  • 2
  • 30
  • 58