52
52
import android .os .Build .VERSION ;
53
53
import android .os .Build .VERSION_CODES ;
54
54
import androidx .core .view .ViewCompat ;
55
+ import android .util .DisplayMetrics ;
55
56
import android .util .Log ;
56
57
import android .view .View ;
57
58
import android .view .ViewGroup ;
59
+ import android .view .WindowManager ;
58
60
import androidx .annotation .ColorInt ;
59
61
import androidx .annotation .FloatRange ;
60
62
import androidx .annotation .IdRes ;
@@ -1032,10 +1034,13 @@ private ProgressThresholdsGroup getThresholdsOrDefault(
1032
1034
*/
1033
1035
private static final class TransitionDrawable extends Drawable {
1034
1036
1035
- // Elevation shadow
1037
+ // Elevation shadow colors
1036
1038
private static final int SHADOW_COLOR = 0x2D000000 ;
1037
1039
private static final int COMPAT_SHADOW_COLOR = 0xFF888888 ;
1038
- private static final float COMPAT_SHADOW_OFFSET_MULTIPLIER = 0.75f ;
1040
+
1041
+ // Elevation shadow offset multiplier adjustments which help approximate native shadows
1042
+ private static final float SHADOW_DX_MULTIPLIER_ADJUSTMENT = 0.3f ;
1043
+ private static final float SHADOW_DY_MULTIPLIER_ADJUSTMENT = 1.5f ;
1039
1044
1040
1045
// Start container
1041
1046
private final View startView ;
@@ -1064,6 +1069,8 @@ private static final class TransitionDrawable extends Drawable {
1064
1069
1065
1070
// Drawing
1066
1071
private final boolean entering ;
1072
+ private final float displayWidth ;
1073
+ private final float displayHeight ;
1067
1074
private final boolean elevationShadowEnabled ;
1068
1075
private final MaterialShapeDrawable compatShadowDrawable = new MaterialShapeDrawable ();
1069
1076
private final RectF currentStartBounds ;
@@ -1084,6 +1091,7 @@ private static final class TransitionDrawable extends Drawable {
1084
1091
private FitModeResult fitModeResult ;
1085
1092
private RectF currentMaskBounds ;
1086
1093
private float currentElevation ;
1094
+ private float currentElevationDy ;
1087
1095
private float progress ;
1088
1096
1089
1097
private TransitionDrawable (
@@ -1121,6 +1129,13 @@ private TransitionDrawable(
1121
1129
this .progressThresholds = progressThresholds ;
1122
1130
this .drawDebugEnabled = drawDebugEnabled ;
1123
1131
1132
+ WindowManager windowManager =
1133
+ (WindowManager ) startView .getContext ().getSystemService (Context .WINDOW_SERVICE );
1134
+ DisplayMetrics displayMetrics = new DisplayMetrics ();
1135
+ windowManager .getDefaultDisplay ().getMetrics (displayMetrics );
1136
+ displayWidth = displayMetrics .widthPixels ;
1137
+ displayHeight = displayMetrics .heightPixels ;
1138
+
1124
1139
containerPaint .setColor (containerColor );
1125
1140
startContainerPaint .setColor (startContainerColor );
1126
1141
endContainerPaint .setColor (endContainerColor );
@@ -1230,8 +1245,7 @@ private void drawElevationShadowWithMaterialShapeDrawable(Canvas canvas) {
1230
1245
(int ) currentMaskBounds .right ,
1231
1246
(int ) currentMaskBounds .bottom );
1232
1247
compatShadowDrawable .setElevation (currentElevation );
1233
- compatShadowDrawable .setShadowVerticalOffset (
1234
- (int ) (currentElevation * COMPAT_SHADOW_OFFSET_MULTIPLIER ));
1248
+ compatShadowDrawable .setShadowVerticalOffset ((int ) currentElevationDy );
1235
1249
compatShadowDrawable .setShapeAppearanceModel (maskEvaluator .getCurrentShapeAppearanceModel ());
1236
1250
compatShadowDrawable .draw (canvas );
1237
1251
}
@@ -1307,10 +1321,6 @@ private void updateProgress(float progress) {
1307
1321
// Fade in/out scrim over non-shared elements
1308
1322
scrimPaint .setAlpha ((int ) (entering ? lerp (0 , 255 , progress ) : lerp (255 , 0 , progress )));
1309
1323
1310
- // Calculate current elevation and set up shadow layer
1311
- currentElevation = lerp (startElevation , endElevation , progress );
1312
- shadowPaint .setShadowLayer (currentElevation , 0 , currentElevation , SHADOW_COLOR );
1313
-
1314
1324
// Calculate position based on motion path
1315
1325
motionPathMeasure .getPosTan (motionPathLength * progress , motionPathPosition , null );
1316
1326
float motionPathX = motionPathPosition [0 ];
@@ -1367,6 +1377,15 @@ private void updateProgress(float progress) {
1367
1377
currentEndBoundsMasked ,
1368
1378
progressThresholds .shapeMask );
1369
1379
1380
+ // Calculate current elevation and set up shadow layer
1381
+ currentElevation = lerp (startElevation , endElevation , progress );
1382
+ float dxMultiplier = calculateElevationDxMultiplier (currentMaskBounds , displayWidth );
1383
+ float dyMultiplier = calculateElevationDyMultiplier (currentMaskBounds , displayHeight );
1384
+ float currentElevationDx = (int ) (currentElevation * dxMultiplier );
1385
+ currentElevationDy = (int ) (currentElevation * dyMultiplier );
1386
+ shadowPaint .setShadowLayer (
1387
+ currentElevation , currentElevationDx , currentElevationDy , SHADOW_COLOR );
1388
+
1370
1389
// Cross-fade images of the start/end states over range of `progress`
1371
1390
float fadeStartFraction = checkNotNull (progressThresholds .fade .start );
1372
1391
float fadeEndFraction = checkNotNull (progressThresholds .fade .end );
@@ -1388,6 +1407,36 @@ private static PointF getMotionPathPoint(RectF bounds) {
1388
1407
return new PointF (bounds .centerX (), bounds .top );
1389
1408
}
1390
1409
1410
+ /**
1411
+ * The dx value for the elevation shadow's horizontal offset should be based on where the
1412
+ * current bounds are located in relation to the horizontal mid-point of the screen, since
1413
+ * that's where the native light source is located.
1414
+ *
1415
+ * <p>If the bounds are at the mid-point, the offset should be 0, which results in even shadows
1416
+ * to the left and right of the view.
1417
+ *
1418
+ * <p>If the bounds are to the left of the mid-point, the offset should be negative, which
1419
+ * results in more shadow to the left of the view.
1420
+ *
1421
+ * <p>If the bounds are to the right of the mid-point, the offset should be positive, which
1422
+ * results in more shadow to the right of the view.
1423
+ */
1424
+ private static float calculateElevationDxMultiplier (RectF bounds , float displayWidth ) {
1425
+ return (bounds .centerX () / (displayWidth / 2 ) - 1 ) * SHADOW_DX_MULTIPLIER_ADJUSTMENT ;
1426
+ }
1427
+
1428
+ /**
1429
+ * The dy value for the elevation shadow's vertical offset should be based on where the current
1430
+ * bounds are located in relation to the top of the screen, since that's where the native light
1431
+ * source is located.
1432
+ *
1433
+ * <p>The offset should be 0 at the top of the screen and increase as the bounds get further
1434
+ * away from the top of the screen.
1435
+ */
1436
+ private static float calculateElevationDyMultiplier (RectF bounds , float displayHeight ) {
1437
+ return bounds .centerY () / displayHeight * SHADOW_DY_MULTIPLIER_ADJUSTMENT ;
1438
+ }
1439
+
1391
1440
private void drawDebugCumulativePath (
1392
1441
Canvas canvas , RectF bounds , Path path , @ ColorInt int color ) {
1393
1442
PointF point = getMotionPathPoint (bounds );
0 commit comments