Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 730b469

Browse files
authored
Support opacity layers for platform-views using hybrid-composition (#30264)
1 parent be81d61 commit 730b469

File tree

6 files changed

+124
-1
lines changed

6 files changed

+124
-1
lines changed

shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import android.content.Context;
77
import android.graphics.Canvas;
88
import android.graphics.Matrix;
9+
import android.graphics.Paint;
910
import android.graphics.Path;
1011
import android.view.MotionEvent;
1112
import android.view.View;
@@ -28,6 +29,7 @@ public class FlutterMutatorView extends FrameLayout {
2829
private int top;
2930
private int prevLeft;
3031
private int prevTop;
32+
private Paint paint;
3133

3234
private final AndroidTouchProcessor androidTouchProcessor;
3335

@@ -42,6 +44,7 @@ public FlutterMutatorView(
4244
super(context, null);
4345
this.screenDensity = screenDensity;
4446
this.androidTouchProcessor = androidTouchProcessor;
47+
this.paint = new Paint();
4548
}
4649

4750
/** Initialize the FlutterMutatorView. */
@@ -142,6 +145,11 @@ public void draw(Canvas canvas) {
142145
pathCopy.offset(-left, -top);
143146
canvas.clipPath(pathCopy);
144147
}
148+
149+
// Apply the final opacity value on the parent canvas.
150+
paint.setAlpha((int) (mutatorsStack.getFinalOpacity() * 255));
151+
this.setLayerType(LAYER_TYPE_HARDWARE, paint);
152+
145153
super.draw(canvas);
146154
canvas.restore();
147155
}

shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorsStack.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class FlutterMutator {
4747
@Nullable private Rect rect;
4848
@Nullable private Path path;
4949
@Nullable private float[] radiis;
50+
@Nullable private float opacity;
5051

5152
private FlutterMutatorType type;
5253

@@ -93,6 +94,16 @@ public FlutterMutator(Matrix matrix) {
9394
this.matrix = matrix;
9495
}
9596

97+
/**
98+
* Initialize an opacity mutator.
99+
*
100+
* @param opacity the opacity to apply.
101+
*/
102+
public FlutterMutator(float opacity) {
103+
this.type = FlutterMutatorType.OPACITY;
104+
this.opacity = opacity;
105+
}
106+
96107
/**
97108
* Get the mutator type.
98109
*
@@ -128,18 +139,29 @@ public Path getPath() {
128139
public Matrix getMatrix() {
129140
return matrix;
130141
}
142+
143+
/**
144+
* Get the opacity of the mutator if {@link #getType()} returns FlutterMutatorType.OPACITY.
145+
*
146+
* @return the opacity if the type is FlutterMutatorType.OPACITY; otherwise null.
147+
*/
148+
public float getOpacity() {
149+
return opacity;
150+
}
131151
}
132152

133153
private @NonNull List<FlutterMutator> mutators;
134154

135155
private List<Path> finalClippingPaths;
136156
private Matrix finalMatrix;
157+
private float finalOpacity;
137158

138159
/** Initialize the mutator stack. */
139160
public FlutterMutatorsStack() {
140161
this.mutators = new ArrayList<FlutterMutator>();
141162
finalMatrix = new Matrix();
142163
finalClippingPaths = new ArrayList<Path>();
164+
finalOpacity = 1.f;
143165
}
144166

145167
/**
@@ -187,6 +209,17 @@ public void pushClipRRect(int left, int top, int right, int bottom, float[] radi
187209
finalClippingPaths.add(path);
188210
}
189211

212+
/**
213+
* Push an opacity {@link FlutterMutatorsStack.FlutterMutator} to the stack.
214+
*
215+
* @param opacity the opacity value.
216+
*/
217+
public void pushOpacity(float opacity) {
218+
FlutterMutator mutator = new FlutterMutator(opacity);
219+
mutators.add(mutator);
220+
finalOpacity *= opacity;
221+
}
222+
190223
/**
191224
* Get a list of all the raw mutators. The 0 index of the returned list is the top of the stack.
192225
*/
@@ -214,4 +247,9 @@ public List<Path> getFinalClippingPaths() {
214247
public Matrix getFinalMatrix() {
215248
return finalMatrix;
216249
}
250+
251+
/** Returns the final opacity value. Apply this value to the canvas of the view. */
252+
public float getFinalOpacity() {
253+
return finalOpacity;
254+
}
217255
}

shell/platform/android/platform_view_android_jni_impl.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ static jmethodID g_mutators_stack_init_method = nullptr;
121121
static jmethodID g_mutators_stack_push_transform_method = nullptr;
122122
static jmethodID g_mutators_stack_push_cliprect_method = nullptr;
123123
static jmethodID g_mutators_stack_push_cliprrect_method = nullptr;
124+
static jmethodID g_mutators_stack_push_opacity_method = nullptr;
124125

125126
// Called By Java
126127
static jlong AttachJNI(JNIEnv* env, jclass clazz, jobject flutterJNI) {
@@ -1019,6 +1020,14 @@ bool PlatformViewAndroid::Register(JNIEnv* env) {
10191020
return false;
10201021
}
10211022

1023+
g_mutators_stack_push_opacity_method =
1024+
env->GetMethodID(g_mutators_stack_class->obj(), "pushOpacity", "(F)V");
1025+
if (g_mutators_stack_push_opacity_method == nullptr) {
1026+
FML_LOG(ERROR)
1027+
<< "Could not locate FlutterMutatorsStack.pushOpacity method";
1028+
return false;
1029+
}
1030+
10221031
g_on_display_platform_view_method =
10231032
env->GetMethodID(g_flutter_jni_class->obj(), "onDisplayPlatformView",
10241033
"(IIIIIIILio/flutter/embedding/engine/mutatorsstack/"
@@ -1453,10 +1462,15 @@ void PlatformViewAndroidJNIImpl::FlutterViewOnDisplayPlatformView(
14531462
(int)rect.bottom(), radiisArray.obj());
14541463
break;
14551464
}
1465+
case opacity: {
1466+
float opacity = (*iter)->GetAlphaFloat();
1467+
env->CallVoidMethod(mutatorsStack, g_mutators_stack_push_opacity_method,
1468+
opacity);
1469+
break;
1470+
}
14561471
// TODO(cyanglaz): Implement other mutators.
14571472
// https://github.com/flutter/flutter/issues/58426
14581473
case clip_path:
1459-
case opacity:
14601474
break;
14611475
}
14621476
++iter;

shell/platform/android/test/io/flutter/FlutterTestSuite.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest;
2525
import io.flutter.embedding.engine.loader.FlutterLoaderTest;
2626
import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorViewTest;
27+
import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStackTest;
2728
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest;
2829
import io.flutter.embedding.engine.renderer.FlutterRendererTest;
2930
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannelTest;
@@ -73,6 +74,7 @@
7374
FlutterJNITest.class,
7475
FlutterLaunchTests.class,
7576
FlutterLoaderTest.class,
77+
FlutterMutatorsStackTest.class,
7678
FlutterMutatorViewTest.class,
7779
FlutterShellArgsTest.class,
7880
FlutterRendererTest.class,

shell/platform/android/test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package io.flutter.embedding.engine.mutatorsstack;
22

3+
import static android.view.View.LAYER_TYPE_HARDWARE;
34
import static android.view.View.OnFocusChangeListener;
45
import static junit.framework.TestCase.*;
56
import static org.mockito.Mockito.*;
67

8+
import android.graphics.Canvas;
79
import android.graphics.Matrix;
10+
import android.graphics.Paint;
811
import android.view.MotionEvent;
912
import android.view.View;
1013
import android.view.ViewGroup;
@@ -250,4 +253,20 @@ public ViewTreeObserver getViewTreeObserver() {
250253
view.unsetOnDescendantFocusChangeListener();
251254
verify(viewTreeObserver, times(1)).removeOnGlobalFocusChangeListener(activeFocusListener);
252255
}
256+
257+
@Test
258+
public void draw_opacityApplied() {
259+
final FlutterMutatorView view = new FlutterMutatorView(RuntimeEnvironment.systemContext);
260+
final FlutterMutatorView spy = spy(view);
261+
262+
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
263+
mutatorsStack.pushOpacity(.3f);
264+
265+
spy.readyToDisplay(mutatorsStack, /*left=*/ 1, /*top=*/ 2, /*width=*/ 0, /*height=*/ 0);
266+
spy.draw(new Canvas());
267+
verify(spy)
268+
.setLayerType(
269+
intThat((Integer layerType) -> layerType == LAYER_TYPE_HARDWARE),
270+
argThat((Paint paint) -> paint.getAlpha() == (int) (.3f * 255)));
271+
}
253272
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.flutter.embedding.engine.mutatorsstack;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import org.junit.Test;
6+
import org.junit.runner.RunWith;
7+
import org.robolectric.RobolectricTestRunner;
8+
import org.robolectric.annotation.Config;
9+
10+
@Config(manifest = Config.NONE)
11+
@RunWith(RobolectricTestRunner.class)
12+
public class FlutterMutatorsStackTest {
13+
14+
@Test
15+
public void pushOpacity() {
16+
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
17+
mutatorsStack.pushOpacity(.5f);
18+
19+
assertEquals(mutatorsStack.getMutators().size(), 1);
20+
assertEquals(
21+
mutatorsStack.getMutators().get(0).getType(),
22+
FlutterMutatorsStack.FlutterMutatorType.OPACITY);
23+
assertEquals(mutatorsStack.getMutators().get(0).getOpacity(), .5f, 0f);
24+
}
25+
26+
@Test
27+
public void defaultOpacity() {
28+
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
29+
30+
assertEquals(1f, mutatorsStack.getFinalOpacity(), 0f);
31+
}
32+
33+
@Test
34+
public void layeredOpacity() {
35+
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
36+
mutatorsStack.pushOpacity(.5f);
37+
mutatorsStack.pushOpacity(.6f);
38+
mutatorsStack.pushOpacity(1f);
39+
40+
assertEquals(.3f, mutatorsStack.getFinalOpacity(), 1 / 255);
41+
}
42+
}

0 commit comments

Comments
 (0)