Skip to content

Commit 810a867

Browse files
committed
Corrects invalidation logic for layered views
A bug in the invalidation logic meant that changes to a view would not cause parents in the view hiearchy that were set to have a layer (e.g., View.LAYER_TYPE_HARDWARE) to get invalidated properly. So even though the child view was all set to recreate its display list according to the property change, the layer in the tree above it would stay as-is, meaning that the change would not show up on the screen. Issue #5887530 DropTarget text does not change color with the icon Change-Id: Ie6eac4f406d172cb437822d9fe76340ab2afaf1c
1 parent 73b61d6 commit 810a867

File tree

4 files changed

+271
-59
lines changed

4 files changed

+271
-59
lines changed

core/java/android/view/ViewGroup.java

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3938,59 +3938,6 @@ public final void invalidateChild(View child, final Rect dirty) {
39383938
// through
39393939
final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
39403940

3941-
//noinspection PointlessBooleanExpression
3942-
if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
3943-
if (dirty == null) {
3944-
if (child.mLayerType != LAYER_TYPE_NONE) {
3945-
mPrivateFlags |= INVALIDATED;
3946-
mPrivateFlags &= ~DRAWING_CACHE_VALID;
3947-
child.mLocalDirtyRect.setEmpty();
3948-
}
3949-
do {
3950-
View view = null;
3951-
if (parent instanceof View) {
3952-
view = (View) parent;
3953-
if (view.mLayerType != LAYER_TYPE_NONE) {
3954-
view.mLocalDirtyRect.setEmpty();
3955-
if (view.getParent() instanceof View) {
3956-
final View grandParent = (View) view.getParent();
3957-
grandParent.mPrivateFlags |= INVALIDATED;
3958-
grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3959-
}
3960-
}
3961-
if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
3962-
// already marked dirty - we're done
3963-
break;
3964-
}
3965-
}
3966-
3967-
if (drawAnimation) {
3968-
if (view != null) {
3969-
view.mPrivateFlags |= DRAW_ANIMATION;
3970-
} else if (parent instanceof ViewRootImpl) {
3971-
((ViewRootImpl) parent).mIsAnimating = true;
3972-
}
3973-
}
3974-
3975-
if (parent instanceof ViewRootImpl) {
3976-
((ViewRootImpl) parent).invalidate();
3977-
parent = null;
3978-
} else if (view != null) {
3979-
if ((view.mPrivateFlags & DRAWN) == DRAWN ||
3980-
(view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
3981-
view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3982-
view.mPrivateFlags |= DIRTY;
3983-
parent = view.mParent;
3984-
} else {
3985-
parent = null;
3986-
}
3987-
}
3988-
} while (parent != null);
3989-
}
3990-
3991-
return;
3992-
}
3993-
39943941
// Check whether the child that requests the invalidate is fully opaque
39953942
// Views being animated or transformed are not considered opaque because we may
39963943
// be invalidating their old position and need the parent to paint behind them.
@@ -4025,12 +3972,6 @@ public final void invalidateChild(View child, final Rect dirty) {
40253972
View view = null;
40263973
if (parent instanceof View) {
40273974
view = (View) parent;
4028-
if (view.mLayerType != LAYER_TYPE_NONE &&
4029-
view.getParent() instanceof View) {
4030-
final View grandParent = (View) view.getParent();
4031-
grandParent.mPrivateFlags |= INVALIDATED;
4032-
grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
4033-
}
40343975
}
40353976

40363977
if (drawAnimation) {
@@ -4103,6 +4044,7 @@ public ViewParent invalidateChildInParent(final int[] location, final Rect dirty
41034044
location[CHILD_TOP_INDEX] = top;
41044045

41054046
if (mLayerType != LAYER_TYPE_NONE) {
4047+
mPrivateFlags |= INVALIDATED;
41064048
mLocalDirtyRect.union(dirty);
41074049
}
41084050

@@ -4121,6 +4063,7 @@ public ViewParent invalidateChildInParent(final int[] location, final Rect dirty
41214063
}
41224064

41234065
if (mLayerType != LAYER_TYPE_NONE) {
4066+
mPrivateFlags |= INVALIDATED;
41244067
mLocalDirtyRect.union(dirty);
41254068
}
41264069

tests/HwAccelerationTest/AndroidManifest.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,5 +675,14 @@
675675
</intent-filter>
676676
</activity>
677677

678+
<activity
679+
android:name="ViewLayerInvalidationActivity"
680+
android:label="_ViewLayerInvalidation">
681+
<intent-filter>
682+
<action android:name="android.intent.action.MAIN" />
683+
<category android:name="android.intent.category.LAUNCHER" />
684+
</intent-filter>
685+
</activity>
686+
678687
</application>
679688
</manifest>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
**
4+
** Copyright 2012, The Android Open Source Project
5+
**
6+
** Licensed under the Apache License, Version 2.0 (the "License");
7+
** you may not use this file except in compliance with the License.
8+
** You may obtain a copy of the License at
9+
**
10+
** http://www.apache.org/licenses/LICENSE-2.0
11+
**
12+
** Unless required by applicable law or agreed to in writing, software
13+
** distributed under the License is distributed on an "AS IS" BASIS,
14+
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
** See the License for the specific language governing permissions and
16+
** limitations under the License.
17+
*/
18+
-->
19+
20+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
21+
android:orientation="vertical"
22+
android:layout_width="fill_parent"
23+
android:layout_height="fill_parent">
24+
<LinearLayout android:orientation="horizontal"
25+
android:layout_width="wrap_content"
26+
android:layout_height="wrap_content">
27+
<TextView
28+
android:layout_width="wrap_content"
29+
android:layout_height="wrap_content"
30+
android:text="This is some text"
31+
android:id="@+id/nestedStatus"/>
32+
<TextView
33+
android:layout_width="wrap_content"
34+
android:layout_height="wrap_content"
35+
android:text="This is some text"
36+
android:id="@+id/invalidateStatus"/>
37+
</LinearLayout>
38+
<LinearLayout android:orientation="vertical"
39+
android:layout_width="fill_parent"
40+
android:layout_height="fill_parent"
41+
android:id="@+id/container">
42+
<TextView
43+
android:layout_width="wrap_content"
44+
android:layout_height="wrap_content"
45+
android:text="This is some text"
46+
android:id="@+id/textview"/>
47+
<TextView
48+
android:layout_width="wrap_content"
49+
android:layout_height="wrap_content"
50+
android:text="This is some text"
51+
android:id="@+id/textviewa"/>
52+
<LinearLayout android:orientation="vertical"
53+
android:layout_width="wrap_content"
54+
android:layout_height="wrap_content"
55+
android:id="@+id/container1">
56+
<TextView
57+
android:layout_width="wrap_content"
58+
android:layout_height="wrap_content"
59+
android:text="This is some text"
60+
android:id="@+id/textview1"/>
61+
</LinearLayout>
62+
<LinearLayout android:orientation="vertical"
63+
android:layout_width="wrap_content"
64+
android:layout_height="wrap_content"
65+
android:id="@+id/container2">
66+
<LinearLayout android:orientation="vertical"
67+
android:layout_width="wrap_content"
68+
android:layout_height="wrap_content"
69+
android:id="@+id/container2a">
70+
<TextView
71+
android:layout_width="wrap_content"
72+
android:layout_height="wrap_content"
73+
android:text="This is some text"
74+
android:id="@+id/textview2"/>
75+
</LinearLayout>
76+
</LinearLayout>
77+
<LinearLayout android:orientation="vertical"
78+
android:layout_width="wrap_content"
79+
android:layout_height="wrap_content"
80+
android:id="@+id/container3">
81+
<LinearLayout android:orientation="vertical"
82+
android:layout_width="wrap_content"
83+
android:layout_height="wrap_content"
84+
android:id="@+id/container3a">
85+
<LinearLayout android:orientation="vertical"
86+
android:layout_width="wrap_content"
87+
android:layout_height="wrap_content"
88+
android:id="@+id/container3b">
89+
<TextView
90+
android:layout_width="wrap_content"
91+
android:layout_height="wrap_content"
92+
android:text="This is some text"
93+
android:id="@+id/textview3"/>
94+
</LinearLayout>
95+
</LinearLayout>
96+
</LinearLayout>
97+
</LinearLayout>
98+
</LinearLayout>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.android.test.hwui;
18+
19+
import android.app.Activity;
20+
import android.graphics.Color;
21+
import android.os.Bundle;
22+
import android.view.View;
23+
import android.view.ViewGroup;
24+
import android.widget.LinearLayout;
25+
import android.widget.TextView;
26+
27+
import java.util.ArrayList;
28+
29+
public class ViewLayerInvalidationActivity extends Activity {
30+
31+
int currentColor = Color.WHITE;
32+
boolean nestedLayersOn = false;
33+
ArrayList<LinearLayout> linearLayouts = new ArrayList<LinearLayout>();
34+
ArrayList<LinearLayout> topLayouts = new ArrayList<LinearLayout>();
35+
ArrayList<TextView> textViews = new ArrayList<TextView>();
36+
LinearLayout container = null;
37+
boolean randomInvalidates = false;
38+
TextView nestedStatusTV, invalidateStatusTV;
39+
static final String NO_NESTING = "Nested Layer: NO ";
40+
static final String NESTING = "Nested Layers: YES ";
41+
static final String NO_INVALIDATING = "Random Invalidating: NO ";
42+
static final String INVALIDATING = "Random Invalidating: YES ";
43+
static final int TEXT_COLOR_INTERVAL = 400;
44+
static final int INVALIDATING_INTERVAL = 1000;
45+
static final int NESTING_INTERVAL = 2000;
46+
47+
@Override
48+
public void onCreate(Bundle savedInstanceState) {
49+
super.onCreate(savedInstanceState);
50+
setContentView(R.layout.view_layer_invalidation);
51+
52+
container = (LinearLayout) findViewById(R.id.container);
53+
final LinearLayout container1 = (LinearLayout) findViewById(R.id.container1);
54+
final LinearLayout container2 = (LinearLayout) findViewById(R.id.container2);
55+
final LinearLayout container3 = (LinearLayout) findViewById(R.id.container3);
56+
nestedStatusTV = (TextView) findViewById(R.id.nestedStatus);
57+
invalidateStatusTV = (TextView) findViewById(R.id.invalidateStatus);
58+
final TextView tva = (TextView) findViewById(R.id.textviewa);
59+
60+
topLayouts.add(container1);
61+
topLayouts.add(container2);
62+
topLayouts.add(container3);
63+
64+
collectLinearLayouts(container);
65+
collectTextViews(container);
66+
67+
nestedStatusTV.setText(NO_NESTING);
68+
invalidateStatusTV.setText(NO_INVALIDATING);
69+
70+
tva.setLayerType(View.LAYER_TYPE_HARDWARE, null);
71+
container1.setLayerType(View.LAYER_TYPE_HARDWARE, null);
72+
container2.setLayerType(View.LAYER_TYPE_HARDWARE, null);
73+
container3.setLayerType(View.LAYER_TYPE_HARDWARE, null);
74+
75+
container.postDelayed(textColorSetter, TEXT_COLOR_INTERVAL);
76+
container.postDelayed(nestedLayerSetter, NESTING_INTERVAL);
77+
container.postDelayed(randomInvalidatesSetter, INVALIDATING_INTERVAL);
78+
}
79+
80+
private Runnable textColorSetter = new Runnable() {
81+
@Override
82+
public void run() {
83+
currentColor = (currentColor == Color.WHITE) ? Color.RED : Color.WHITE;
84+
for (TextView tv : textViews) {
85+
tv.setTextColor(currentColor);
86+
}
87+
if (randomInvalidates) {
88+
randomInvalidator(container);
89+
}
90+
container.postDelayed(textColorSetter, TEXT_COLOR_INTERVAL);
91+
}
92+
};
93+
94+
private Runnable randomInvalidatesSetter = new Runnable() {
95+
@Override
96+
public void run() {
97+
randomInvalidates = !randomInvalidates;
98+
invalidateStatusTV.setText(randomInvalidates ? INVALIDATING : NO_INVALIDATING);
99+
container.postDelayed(randomInvalidatesSetter, INVALIDATING_INTERVAL);
100+
}
101+
};
102+
103+
private Runnable nestedLayerSetter = new Runnable() {
104+
@Override
105+
public void run() {
106+
nestedLayersOn = !nestedLayersOn;
107+
nestedStatusTV.setText(nestedLayersOn ? NESTING : NO_NESTING);
108+
for (LinearLayout layout : linearLayouts) {
109+
layout.setLayerType(nestedLayersOn ?
110+
View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null);
111+
}
112+
if (!nestedLayersOn) {
113+
for (LinearLayout layout : topLayouts) {
114+
layout.setLayerType(View.LAYER_TYPE_HARDWARE, null);
115+
}
116+
}
117+
container.postDelayed(nestedLayerSetter, NESTING_INTERVAL);
118+
}
119+
};
120+
121+
/**
122+
* Invalidates views based on random chance (50%). This is meant to test
123+
* invalidating several items in the hierarchy at the same time, which can cause artifacts
124+
* if our invalidation-propagation logic is not sound.
125+
*/
126+
private void randomInvalidator(ViewGroup parent) {
127+
for (int i = 0; i < parent.getChildCount(); ++i) {
128+
View child = parent.getChildAt(i);
129+
if (Math.random() < .5) {
130+
child.invalidate();
131+
}
132+
if (child instanceof ViewGroup) {
133+
randomInvalidator((ViewGroup) child);
134+
}
135+
}
136+
}
137+
138+
private void collectLinearLayouts(View view) {
139+
if (!(view instanceof LinearLayout)) {
140+
return;
141+
}
142+
LinearLayout parent = (LinearLayout) view;
143+
linearLayouts.add(parent);
144+
for (int i = 0; i < parent.getChildCount(); ++i) {
145+
collectLinearLayouts(parent.getChildAt(i));
146+
}
147+
}
148+
149+
private void collectTextViews(View view) {
150+
if (view instanceof TextView) {
151+
textViews.add((TextView) view);
152+
return;
153+
}
154+
if (!(view instanceof ViewGroup)) {
155+
return;
156+
}
157+
ViewGroup parent = (ViewGroup) view;
158+
for (int i = 0; i < parent.getChildCount(); ++i) {
159+
collectTextViews(parent.getChildAt(i));
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)