Android - View.requestLayout 在 OnLayoutChangeListener 中不起作用
Posted
技术标签:
【中文标题】Android - View.requestLayout 在 OnLayoutChangeListener 中不起作用【英文标题】:Android - View.requestLayout doesn't work in OnLayoutChangeListener 【发布时间】:2014-10-09 09:31:11 【问题描述】:我的应用程序必须使用GridLayout
。 GridLayout
的问题是重量的限制,所以我必须在运行时缩放它的孩子大小。我在OnGlobalLayoutListener
的帮助下做到了这一点。
我的GridLayout
的孩子是两个Button
s,它们得到了父母的宽度和父母高度的一半。一个Button
在上面,一个Button
在下面。
如果单击上部Button
,我想将GridLayout
的大小切换为宽高500,宽高700。
单击上方的Button
后,Button
s 应该可以正确缩放,但他们不会这样做。
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 非常感谢您的回答。我以为没有人知道。
您的“解决方案”很有趣,但不幸的是有点错误。光学上它工作得很好,但在后台你有一个无限循环......
如果您在Button
s 之一的文本中设置计数器,您将看到它。计数器不断增加。
在 Handler
中设置 Button
s 的新布局大小之后是对 OnLayoutChangeListener
的新调用,此 Listener
再次启动 Handler
,依此类推……
如果您使用OnGlobalLayoutListener
而不是OnLayoutChangeListener
和Handler
,您将遇到同样的问题。
原因如下。 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 逻辑是一样的。
我最初的问题,为什么requestLayout
在onLayoutChange
中不起作用,仍然是开放的,但这里提出了一个令人满意的解决方案,所以我很高兴。
再次感谢您的关心!
【讨论】:
像魅力一样工作。我已经为这个问题苦苦挣扎了好几个小时,这很奏效。我什至不需要使布局无效和/或请求布局。谢谢!【参考方案3】:handler 很危险。建议使用 view.getViewTreeObserver().addOnPreDrawListener();
这不是冲突;
【讨论】:
以上是关于Android - View.requestLayout 在 OnLayoutChangeListener 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )