mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Support opacity layers for platform-views using hybrid-composition (flutter/engine#30264)
This commit is contained in:
parent
66bbb434e5
commit
4be91c61de
@ -6,6 +6,7 @@ import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@ -28,6 +29,7 @@ public class FlutterMutatorView extends FrameLayout {
|
||||
private int top;
|
||||
private int prevLeft;
|
||||
private int prevTop;
|
||||
private Paint paint;
|
||||
|
||||
private final AndroidTouchProcessor androidTouchProcessor;
|
||||
|
||||
@ -42,6 +44,7 @@ public class FlutterMutatorView extends FrameLayout {
|
||||
super(context, null);
|
||||
this.screenDensity = screenDensity;
|
||||
this.androidTouchProcessor = androidTouchProcessor;
|
||||
this.paint = new Paint();
|
||||
}
|
||||
|
||||
/** Initialize the FlutterMutatorView. */
|
||||
@ -142,6 +145,11 @@ public class FlutterMutatorView extends FrameLayout {
|
||||
pathCopy.offset(-left, -top);
|
||||
canvas.clipPath(pathCopy);
|
||||
}
|
||||
|
||||
// Apply the final opacity value on the parent canvas.
|
||||
paint.setAlpha((int) (mutatorsStack.getFinalOpacity() * 255));
|
||||
this.setLayerType(LAYER_TYPE_HARDWARE, paint);
|
||||
|
||||
super.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@ public class FlutterMutatorsStack {
|
||||
@Nullable private Rect rect;
|
||||
@Nullable private Path path;
|
||||
@Nullable private float[] radiis;
|
||||
@Nullable private float opacity;
|
||||
|
||||
private FlutterMutatorType type;
|
||||
|
||||
@ -93,6 +94,16 @@ public class FlutterMutatorsStack {
|
||||
this.matrix = matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an opacity mutator.
|
||||
*
|
||||
* @param opacity the opacity to apply.
|
||||
*/
|
||||
public FlutterMutator(float opacity) {
|
||||
this.type = FlutterMutatorType.OPACITY;
|
||||
this.opacity = opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mutator type.
|
||||
*
|
||||
@ -128,18 +139,29 @@ public class FlutterMutatorsStack {
|
||||
public Matrix getMatrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opacity of the mutator if {@link #getType()} returns FlutterMutatorType.OPACITY.
|
||||
*
|
||||
* @return the opacity if the type is FlutterMutatorType.OPACITY; otherwise null.
|
||||
*/
|
||||
public float getOpacity() {
|
||||
return opacity;
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull List<FlutterMutator> mutators;
|
||||
|
||||
private List<Path> finalClippingPaths;
|
||||
private Matrix finalMatrix;
|
||||
private float finalOpacity;
|
||||
|
||||
/** Initialize the mutator stack. */
|
||||
public FlutterMutatorsStack() {
|
||||
this.mutators = new ArrayList<FlutterMutator>();
|
||||
finalMatrix = new Matrix();
|
||||
finalClippingPaths = new ArrayList<Path>();
|
||||
finalOpacity = 1.f;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,6 +209,17 @@ public class FlutterMutatorsStack {
|
||||
finalClippingPaths.add(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an opacity {@link FlutterMutatorsStack.FlutterMutator} to the stack.
|
||||
*
|
||||
* @param opacity the opacity value.
|
||||
*/
|
||||
public void pushOpacity(float opacity) {
|
||||
FlutterMutator mutator = new FlutterMutator(opacity);
|
||||
mutators.add(mutator);
|
||||
finalOpacity *= opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all the raw mutators. The 0 index of the returned list is the top of the stack.
|
||||
*/
|
||||
@ -214,4 +247,9 @@ public class FlutterMutatorsStack {
|
||||
public Matrix getFinalMatrix() {
|
||||
return finalMatrix;
|
||||
}
|
||||
|
||||
/** Returns the final opacity value. Apply this value to the canvas of the view. */
|
||||
public float getFinalOpacity() {
|
||||
return finalOpacity;
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +121,7 @@ static jmethodID g_mutators_stack_init_method = nullptr;
|
||||
static jmethodID g_mutators_stack_push_transform_method = nullptr;
|
||||
static jmethodID g_mutators_stack_push_cliprect_method = nullptr;
|
||||
static jmethodID g_mutators_stack_push_cliprrect_method = nullptr;
|
||||
static jmethodID g_mutators_stack_push_opacity_method = nullptr;
|
||||
|
||||
// Called By Java
|
||||
static jlong AttachJNI(JNIEnv* env, jclass clazz, jobject flutterJNI) {
|
||||
@ -1019,6 +1020,14 @@ bool PlatformViewAndroid::Register(JNIEnv* env) {
|
||||
return false;
|
||||
}
|
||||
|
||||
g_mutators_stack_push_opacity_method =
|
||||
env->GetMethodID(g_mutators_stack_class->obj(), "pushOpacity", "(F)V");
|
||||
if (g_mutators_stack_push_opacity_method == nullptr) {
|
||||
FML_LOG(ERROR)
|
||||
<< "Could not locate FlutterMutatorsStack.pushOpacity method";
|
||||
return false;
|
||||
}
|
||||
|
||||
g_on_display_platform_view_method =
|
||||
env->GetMethodID(g_flutter_jni_class->obj(), "onDisplayPlatformView",
|
||||
"(IIIIIIILio/flutter/embedding/engine/mutatorsstack/"
|
||||
@ -1453,10 +1462,15 @@ void PlatformViewAndroidJNIImpl::FlutterViewOnDisplayPlatformView(
|
||||
(int)rect.bottom(), radiisArray.obj());
|
||||
break;
|
||||
}
|
||||
case opacity: {
|
||||
float opacity = (*iter)->GetAlphaFloat();
|
||||
env->CallVoidMethod(mutatorsStack, g_mutators_stack_push_opacity_method,
|
||||
opacity);
|
||||
break;
|
||||
}
|
||||
// TODO(cyanglaz): Implement other mutators.
|
||||
// https://github.com/flutter/flutter/issues/58426
|
||||
case clip_path:
|
||||
case opacity:
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
|
||||
@ -24,6 +24,7 @@ import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponent
|
||||
import io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest;
|
||||
import io.flutter.embedding.engine.loader.FlutterLoaderTest;
|
||||
import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorViewTest;
|
||||
import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStackTest;
|
||||
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest;
|
||||
import io.flutter.embedding.engine.renderer.FlutterRendererTest;
|
||||
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannelTest;
|
||||
@ -73,6 +74,7 @@ import test.io.flutter.embedding.engine.PluginComponentTest;
|
||||
FlutterJNITest.class,
|
||||
FlutterLaunchTests.class,
|
||||
FlutterLoaderTest.class,
|
||||
FlutterMutatorsStackTest.class,
|
||||
FlutterMutatorViewTest.class,
|
||||
FlutterShellArgsTest.class,
|
||||
FlutterRendererTest.class,
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
package io.flutter.embedding.engine.mutatorsstack;
|
||||
|
||||
import static android.view.View.LAYER_TYPE_HARDWARE;
|
||||
import static android.view.View.OnFocusChangeListener;
|
||||
import static junit.framework.TestCase.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -250,4 +253,20 @@ public class FlutterMutatorViewTest {
|
||||
view.unsetOnDescendantFocusChangeListener();
|
||||
verify(viewTreeObserver, times(1)).removeOnGlobalFocusChangeListener(activeFocusListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void draw_opacityApplied() {
|
||||
final FlutterMutatorView view = new FlutterMutatorView(RuntimeEnvironment.systemContext);
|
||||
final FlutterMutatorView spy = spy(view);
|
||||
|
||||
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
|
||||
mutatorsStack.pushOpacity(.3f);
|
||||
|
||||
spy.readyToDisplay(mutatorsStack, /*left=*/ 1, /*top=*/ 2, /*width=*/ 0, /*height=*/ 0);
|
||||
spy.draw(new Canvas());
|
||||
verify(spy)
|
||||
.setLayerType(
|
||||
intThat((Integer layerType) -> layerType == LAYER_TYPE_HARDWARE),
|
||||
argThat((Paint paint) -> paint.getAlpha() == (int) (.3f * 255)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package io.flutter.embedding.engine.mutatorsstack;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@Config(manifest = Config.NONE)
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class FlutterMutatorsStackTest {
|
||||
|
||||
@Test
|
||||
public void pushOpacity() {
|
||||
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
|
||||
mutatorsStack.pushOpacity(.5f);
|
||||
|
||||
assertEquals(mutatorsStack.getMutators().size(), 1);
|
||||
assertEquals(
|
||||
mutatorsStack.getMutators().get(0).getType(),
|
||||
FlutterMutatorsStack.FlutterMutatorType.OPACITY);
|
||||
assertEquals(mutatorsStack.getMutators().get(0).getOpacity(), .5f, 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultOpacity() {
|
||||
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
|
||||
|
||||
assertEquals(1f, mutatorsStack.getFinalOpacity(), 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void layeredOpacity() {
|
||||
final FlutterMutatorsStack mutatorsStack = new FlutterMutatorsStack();
|
||||
mutatorsStack.pushOpacity(.5f);
|
||||
mutatorsStack.pushOpacity(.6f);
|
||||
mutatorsStack.pushOpacity(1f);
|
||||
|
||||
assertEquals(.3f, mutatorsStack.getFinalOpacity(), 1 / 255);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user