Android - View.requestLayout not working in OnLayoutChangeListener

I need to use GridLayout

for my application. The problem GridLayout

is weight limitation, so I have to scale the size of my children at runtime I do this with OnGlobalLayoutListener

.

My children GridLayout

are two Button

that are the width of the parent and half the height of the parents. One Button

above and one Button

below. If I click the top one Button

, I want to resize GridLayout

to 500 in width and height and 700 in width and height. After clicking on the top Button

value it Button

should scale correctly, but they don't.

public class HauptAktivitaet extends Activity implements OnLayoutChangeListener, OnClickListener{

  /** ContentView its sizes I will change on a click later */

  private GridLayout mGridLayout;

  /** LayoutParams of the above child */

  private GridLayout.LayoutParams mGridLayout_LayoutParams_Above;

  /** LayoutParams of the below child */

  private GridLayout.LayoutParams mGridLayout_LayoutParams_Below;

  /** Children of the ContentView */

  private Button mButtonAbove;
  private Button mButtonBelow;


  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    inline();

    setContentView(mGridLayout);

  }

  private void inline(){

    /* instantiation of ContentView which is named mGridLayout */

    mGridLayout = new GridLayout(this);

    /* set the count of rows and columns of ContentView */

    mGridLayout.setRowCount(2);
    mGridLayout.setColumnCount(1);

      /* set OnGlobalLayoutListener for observe a change of its layout */

    mGridLayout.addOnLayoutChangeListener(this);

    /* instantiation of the LayoutParams for the children */

    mGridLayout_LayoutParams_Above = new GridLayout.LayoutParams(GridLayout.spec(0), GridLayout.spec(0));
    mGridLayout_LayoutParams_Below = new GridLayout.LayoutParams(GridLayout.spec(1), GridLayout.spec(0));

    /* instantiation of the children and setting of their LayoutParams */

    mButtonAbove = new Button(this);
    mButtonAbove.setLayoutParams(mGridLayout_LayoutParams_Above);
    mButtonAbove.setOnClickListener(this); // A click on this Button changes the Size of its parent ViewGroup.

    /* instantiation of ContentView */

    mButtonBelow = new Button(this);
    mButtonBelow.setLayoutParams(mGridLayout_LayoutParams_Below);

    /* add children to the ContentView */

    mGridLayout.addView(mButtonAbove);
    mGridLayout.addView(mButtonBelow);

  }

  @Override
  public void onLayoutChange(View v, int left, int top, int right, int bottom,
      int oldLeft, int oldTop, int oldRight, int oldBottom) {

    /* Width and height on this time are known */

    int width = mGridLayout.getWidth();
    int height = mGridLayout.getHeight();

    /* Changes the LayoutParams of ContentViews children dynamicly*/

    mGridLayout_LayoutParams_Above.width = width;
    mGridLayout_LayoutParams_Above.height = height / 2;

    mGridLayout_LayoutParams_Below.width = width;
    mGridLayout_LayoutParams_Below.height = height / 2;

    /* ISSUE:
     * should update the rendering of the buttons but it doesn't work correctly */

    mButtonAbove.requestLayout();
    mButtonBelow.requestLayout();

    /* A little debug info for knowing of the ContentViews size */

    mButtonBelow.setText("Own Width = " + mButtonBelow.getWidth() + "\n" + 
                         "Own Height = " + mButtonBelow.getHeight() + "\n" +
                         "Perents Width = " + width + "\n" +
                         "Perents Height = " + height);


  }

  private boolean switcher = true;

  /** 
   * Works correctly
   * */

  @Override
  public void onClick(View v) {

    int width = 0;
    int height = 0;

    if(switcher){
      width = 500;
      height = 500;
      switcher = false;
    }else{
      width = 700;
      height = 700;
      switcher = true;
    }

    mGridLayout.getLayoutParams().width = width;
    mGridLayout.getLayoutParams().height = height;

    mGridLayout.requestLayout();

  }

}

      

Can anyone tell me what to do?

How can I say to ContentView

renew myself and my children? I am looking for an elegant solution.

Thanks for answers!

Hello!: -)

+3


source to share


2 answers


@ user3492470
Thank you so much for your answer. I thought no one had an idea.

Your "solution" is interesting, but unfortunately a little wrong. Optically it works fine, but in the background you have an infinite loop ...
You can see this if you set a counter in the text of one of the Button

s. The counter increases infinitely.
Installing a new layout size Button

s' in Handler

is accompanied by a new challenge OnLayoutChangeListener

, and it Listener

restarts Handler

and so on ...
you get the same problem if you use OnGlobalLayoutListener

instead of OnLayoutChangeListener

a Handler

.
The reason is as follows. view.requestLayout()

or view.setLayoutParams(…)

does not work in a method onLayoutChange

, but works in a method onGlobalLayout

or in Handler

which creates a new thread.
Now the part I don't really understand:
A successful call child.requestLayout()

causes a new call to the parent method onLayoutChange

. This is typical because the parent layout should not change again.
Be that as it may, a little if / else logic solves infinite calls.

  private int counter = 0;
  @Override
  public void onGlobalLayout() {
    // TODO Auto-generated method stub

    /* Width and height on this time are known */

    int width  = mGridLayout.getWidth();
    int height = mGridLayout.getHeight();

    /* Buttons width and height are supposed to be */

    int mButtons_width  = width;
    int mButtons_height = height / 2;

    if(!(mButtonAbove.getWidth()  == mButtons_width  &&
         mButtonAbove.getHeight() == mButtons_height &&
         mButtonBelow.getWidth()  == mButtons_width  &&
         mButtonBelow.getHeight() == mButtons_height)
      ){

      /* Changes the LayoutParams of ContentViews children dynamicly */

      mGridLayout_LayoutParams_Above.width = mButtons_width;
      mGridLayout_LayoutParams_Above.height = mButtons_height;

      mGridLayout_LayoutParams_Below.width = mButtons_width;
      mGridLayout_LayoutParams_Below.height = mButtons_height;

      /* NO ISSUE ANYMORE:
       * updates the rendering of the buttons */

      mButtonAbove.requestLayout();
      mButtonBelow.requestLayout();

    }

    /* A little debug info for knowing the ContentView and Children size */

    mButtonBelow.setText("Own Width = " + mButtonBelow.getWidth() + "\n" + 
                         "Own Height = " + mButtonBelow.getHeight() + "\n" +
                         "Perents Width = " + width + "\n" +
                         "Perents Height = " + height + "\n" +
                         "Calls = " + counter++);

  }

      



In this case, I use / OnGlobalLayoutListener

instead of combination . ;-) The if / else logic is the same.OnLayoutChangeListener

uiHandler

My original question why requestLayout

doesn't work in onLayoutChange

is still open, but a satisfactory solution has been provided here, so I'm happy.

Thanks again for your concern!

+4


source


I faced the same problem. My guess is that Android has not finished calculating the new layout when called onLayoutChanges()

and the recalculation cannot be called from the method. The solution is to use a Handler to post the layout changes at the end of the UI thread.

I'm not sure if I requestLayout()

may have replaced it with another call setLayoutParams()

. It probably has the same effect.



So, implement your re-layout in the implementing class, Runnable

instantiate Handler

in onCreate()

and in onLayoutChanges()

instantiate the Runnable class and place it in your handler:

public class HauptAktivitaet extends Activity implements OnLayoutChangeListener, OnClickListener{

  // this class updates the layout
  private class LayoutUpdater implements Runnable {
    private final int width;
    private final int height;

    private LayoutUpdater(int width, int height) {
      this.width = width;
      this.height = height;
    }

    public void run() {
      mGridLayout_LayoutParams_Above.width = width;
      mGridLayout_LayoutParams_Above.height = height;

      mGridLayout_LayoutParams_Below.width = width;
      mGridLayout_LayoutParams_Below.height = height;

      // might be necessary or not: set layout parameters to trigger update
      mButtonAbove.setLayoutParams(mGridLayout_LayoutParams_Above);   
      mButtonBelow.setLayoutParams(mGridLayout_LayoutParams_Below);
    }
  }

  /* snip */

  // add handler running in UI thread
  private Handler uiHandler;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // instantiate handler
    uiHandler = new Handler();

    inline();

    setContentView(mGridLayout);

  }

  /* snip */

  @Override
  public void onLayoutChange(View v, int left, int top, int right, int bottom,
      int oldLeft, int oldTop, int oldRight, int oldBottom) {

    /* Width and height on this time are known */

    int width = mGridLayout.getWidth();
    int height = mGridLayout.getHeight();

    // post layout update to the end of queue
    uiHandler.post(new LayoutUpdater(width, height / 2);
  }
}

      

+5


source







All Articles