Android - View.requestLayout在OnLayoutChangeListener中不起作用

时间:2014-08-15 16:04:50

标签: java android layout viewgroup

我必须使用GridLayout作为我的应用程序。 GridLayout的问题是权重的限制因此我必须在运行时缩放其子项大小。我是在OnGlobalLayoutListener的帮助下完成的。

我的GridLayout的孩子是两个Button,它们有父母的宽度和父母身高的一半。一个Button以上,一个Button以下。 如果单击上部Button,我想将宽度和高度GridLayout的大小切换为500,宽度和高度切换为700。 在Button上方点击后,Button应该正确缩放,但他们不会这样做。

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();

  }

}

有人可以告诉我该怎么做吗?

如何告诉ContentView刷新自己及其子女?我正在寻找一个优雅的解决方案。

非常感谢您的回答!

问候! : - )

3 个答案:

答案 0 :(得分:6)

我遇到了同样的问题。我猜Android在调用onLayoutChanges()时还没有完成计算新布局,并且无法从方法中触发重新计算。 解决方案是使用Handler将布局更改发布到UI线程的末尾。

我对requestLayout()并不十分确定,可能会将其替换为setLayoutParams()。它可能具有相同的效果。

因此,在实现Runnable的类中实现重新布局,在Handler中实例化onCreate()并在onLayoutChanges()实例化Runnable类并将其发布到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);
  }
}

答案 1 :(得分:6)

@ user3492470
非常感谢你的回答。我以为没人知道。

你的“解决方案”很有意思,但遗憾的是有点不对劲。光学上它很有效,但在后台你有一个无限循环...
如果您在其中一个Button的文本处设置计数器,您将看到它。柜台无休止地增加 在Button中设置Handler s'新布局尺寸之后是OnLayoutChangeListener的新调用,此Listener再次启动Handler,因此在...上 如果您使用OnGlobalLayoutListener而不是OnLayoutChangeListener并使用Handler,则会遇到同样的问题。
原因如下。 view.requestLayout()view.setLayoutParams(…)onLayoutChange方法中不起作用,但它适用于onGlobalLayout方法或创建新主题的Handler。<登记/> 现在这部分我真的不明白:
child.requestLayout()上的成功通话会对父母的onLayoutChange方法进行新的调用。这很奇怪,因为父母的布局不应该再改变 然而,有一点if / else逻辑解决了无休止的调用。

  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's and Children's size */

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

  }

在这种情况下,我使用OnGlobalLayoutListener代替OnLayoutChangeListener / uiHandler组合。 ;-) if / else逻辑是相同的。

我原来的问题,为什么requestLayoutonLayoutChange中不起作用,仍然是开放的,但这里提出了一个令人满意的解决方案,所以我很高兴。

再次感谢您的关注!

答案 2 :(得分:0)

处理程序很危险。建议使用
view.getViewTreeObserver().addOnPreDrawListener();

这不是冲突;