Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - View.requestLayout doesn't work in OnLayoutChangeListener

I have to use the GridLayout for my application. The problem of GridLayout is the limitation of weight so I must scale its children size at runtime. I do this by the help of an OnGlobalLayoutListener.

The children of my GridLayout are two Buttons which have got the width of the parent and the half of parents height. One Button above and one Button below. If the upper Button is clicked I want to switch the size of the GridLayout to 500 in width and height and 700 in width and height. After the click at the upper Button the Buttons should scale correctly but they don't do that.

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 anybody tell me what to do?

How can I tell the ContentView to refresh itself and its children? I am searching for an elegant solution.

Thank you very much for answers!

Regards! :-)

like image 554
Barock Avatar asked Aug 15 '14 16:08

Barock


People also ask

Why is requestlayout () not working on my Descendents?

Now when calling requestLayout () on a descendent of that view, Android will recursively call requestLayout () on that descendent's ancestors. The problem is that it will stop the recursion at the view on which you've called forceLayout (). So the requestLayout () call will never reach the view root and thus never schedule a layout pass.

Why requestlayout () does not schedule a layout pass?

So the requestLayout () call will never reach the view root and thus never schedule a layout pass. An entire subtree of the view hierarchy is waiting for a layout and calling requestLayout () on any view of that subtree will not cause a layout.

When would a custom view call requestlayout?

An example of when a custom view would call it is when a text or background color property has changed. The view will be redrawn but the size will not change. If something about your view changes that will affect the size, then you should call requestLayout ().

What is the difference between forcelayout () and requestlayout () in Android?

There is also a much bigger issue when using forceLayout () and requestLayout (): Let's say you've called forceLayout () on a view. Now when calling requestLayout () on a descendent of that view, Android will recursively call requestLayout () on that descendent's ancestors.


1 Answers

I ran into the same problem. I guess Android has not finished calculating the new layout when onLayoutChanges() is called, and a recalculation cannot be triggered from within the method. The solution is to use a Handler to post layout changes to the end of the UI thread.

I am not really sure about requestLayout(), maybe replace it with another call of setLayoutParams(). It probably has the same effect.

So implement your re-layout in a class implementing Runnable, instantiate a Handler in onCreate() and in onLayoutChanges() instantiate the Runnable class and post it to the 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);
  }
}
like image 102
user3492470 Avatar answered Sep 21 '22 14:09

user3492470