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

Posted

技术标签:

【中文标题】Android - View.requestLayout 在 OnLayoutChangeListener 中不起作用【英文标题】:Android - View.requestLayout doesn't work in OnLayoutChangeListener 【发布时间】:2014-10-09 09:31:11 【问题描述】:

我的应用程序必须使用GridLayoutGridLayout 的问题是重量的限制,所以我必须在运行时缩放它的孩子大小。我在OnGlobalLayoutListener 的帮助下做到了这一点。

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

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 刷新自己和它的孩子?我正在寻找一个优雅的解决方案。

非常感谢您的回答!

问候! :-)

【问题讨论】:

【参考方案1】:

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

我不太确定requestLayout(),也许用另一个电话setLayoutParams() 替换它。应该是一样的效果。

因此,在实现Runnable 的类中实现您的重新布局,在onCreate()onLayoutChanges() 中实例化一个Handler,实例化Runnable 类并将其发布到处理程序:

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

【讨论】:

【参考方案2】:

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

您的“解决方案”很有趣,但不幸的是有点错误。光学上它工作得很好,但在后台你有一个无限循环...... 如果您在Buttons 之一的文本中设置计数器,您将看到它。计数器不断增加。 在 Handler 中设置 Buttons 的新布局大小之后是对 OnLayoutChangeListener 的新调用,此 Listener 再次启动 Handler,依此类推…… 如果您使用OnGlobalLayoutListener 而不是OnLayoutChangeListenerHandler,您将遇到同样的问题。 原因如下。 view.requestLayout()view.setLayoutParams(…)onLayoutChange 方法中不起作用,但它在 onGlobalLayout 方法或 Handler 中起作用,这会创建一个新线程。 现在我真的不明白的部分: 对 child.requestLayout() 的成功调用会对父级的 onLayoutChange 方法进行新的调用。这很奇怪,因为父级的布局不应再次更改。 然而,如果/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 中不起作用,仍然是开放的,但这里提出了一个令人满意的解决方案,所以我很高兴。

再次感谢您的关心!

【讨论】:

像魅力一样工作。我已经为这个问题苦苦挣扎了好几个小时,这很奏效。我什至不需要使布局无效和/或请求布局。谢谢!【参考方案3】:

handler 很危险。建议使用 view.getViewTreeObserver().addOnPreDrawListener();

这不是冲突;

【讨论】:

以上是关于Android - View.requestLayout 在 OnLayoutChangeListener 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

android 21 是啥版本

Android逆向-Android基础逆向(2-2)

【Android笔记】android Toast

图解Android - Android核心机制

Android游戏开发大全的目录