In this answer I wrote
forceLayout()
Call
forceLayout()
if you only want to relayout your own view's content, but don't need to trigger remeasuring of the entire view tree (all the parent views). If you had a customViewGroup
that wasn't changing its own size but needed to remeasure and relayout its children, this would be an appropriate situation to callforceLayout()
.Basically, calling
requestLayout()
results in a call toparent.requestLayout()
, but callingforceLayout()
doesn't.
As I recall I wrote my answer by reading the documentation and source code. However, I didn't experience using forceLayout
. A user commented that it was not working as I described.
Testing forceLayout
I am finally getting around to researching the cause for this. I set up a simple project with a grandparent ViewGroup
, a parent ViewGroup
, and a child View
. I used custom views for each of them so that I could watch the log statements in onMeasure
, onLayout
, and onDraw
.
When the layout is first created from xml I get the following log:
ViewGroupGrandparent onMeasure called
ViewGroupParent onMeasure called
MyChildView onMeasure called
ViewGroupGrandparent onMeasure called
ViewGroupParent onMeasure called
MyChildView onMeasure called
ViewGroupGrandparent onLayout called
ViewGroupParent onLayout called
MyChildView onLayout called
MyChildView onDraw called
forceLayout
This looks like reasonable output. However, when I subsequently call forceLayout
individually on any of the views I get nothing. If I call them all at once, then the child view's onDraw
gets called.
child
childView.forceLayout();
// (no log output)
parent
viewGroupParent.forceLayout();
// (no log output)
grandparent
viewGroupGrandparent.forceLayout();
// (no log output)
all together
childView.forceLayout();
viewGroupParent.forceLayout();
viewGroupGrandparent.forceLayout();
// MyChildView onDraw called
requestLayout
On the other hand, calling requestLayout
has a much bigger effect.
child
childView.requestLayout();
// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// MyChildView onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called
// MyChildView onLayout called
// MyChildView onDraw called
parent
viewGroupParent.requestLayout();
// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called
grandparent
viewGroupGrandparent.requestLayout();
// ViewGroupGrandparent onMeasure called
// ViewGroupGrandparent onLayout called
Question
When does forceLayout
have any effect? Why doesn't it seem to work as it is supposed to in my examples above?
Supplemental code
Here is the code I used to make the tests above.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.forcelayout.MainActivity">
<com.example.forcelayout.ViewGroupGrandparent
android:id="@+id/view_group_grandparent"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.example.forcelayout.ViewGroupParent
android:id="@+id/view_group_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.example.forcelayout.MyChildView
android:id="@+id/child_view"
android:layout_width="100dp"
android:layout_height="100dp"/>
</com.example.forcelayout.ViewGroupParent>
</com.example.forcelayout.ViewGroupGrandparent>
<Button
android:text="Click me"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="buttonClick"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void buttonClick(View view) {
Log.i("TAG", "buttonClick: ");
ViewGroupGrandparent viewGroupGrandparent = (ViewGroupGrandparent) findViewById(R.id.view_group_grandparent);
ViewGroupParent viewGroupParent = (ViewGroupParent) findViewById(R.id.view_group_parent);
MyChildView childView = (MyChildView) findViewById(R.id.child_view);
childView.forceLayout();
//viewGroupParent.forceLayout();
//viewGroupGrandparent.forceLayout();
//childView.requestLayout();
//viewGroupParent.requestLayout();
//viewGroupGrandparent.requestLayout();
}
}
ViewGroupGrandparent.java
public class ViewGroupGrandparent extends LinearLayout {
public ViewGroupGrandparent(Context context) {
super(context);
}
public ViewGroupGrandparent(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewGroupGrandparent(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.i("TAG", "ViewGroupGrandparent onMeasure called");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("TAG", "ViewGroupGrandparent onLayout called");
super.onLayout(changed, l, t, r, b);
}
@Override
protected void onDraw(Canvas canvas) {
Log.i("TAG", "ViewGroupGrandparent onDraw called");
super.onDraw(canvas);
}
}
ViewGroupParent.java
public class ViewGroupParent extends LinearLayout {
public ViewGroupParent(Context context) {
super(context);
}
public ViewGroupParent(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewGroupParent(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.i("TAG", "ViewGroupParent onMeasure called");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("TAG", "ViewGroupParent onLayout called");
super.onLayout(changed, l, t, r, b);
}
@Override
protected void onDraw(Canvas canvas) {
Log.i("TAG", "ViewGroupParent onDraw called");
super.onDraw(canvas);
}
}
MyChildView.java
public class MyChildView extends View {
public MyChildView(Context context) {
super(context);
}
public MyChildView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyChildView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.i("TAG", "MyChildView onMeasure called");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Log.i("TAG", "MyChildView onLayout called");
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
Log.i("TAG", "MyChildView onDraw called");
super.onDraw(canvas);
}
}