Skip to content

Commit d5856fd

Browse files
drchenraajkumars
authored andcommitted
[Snackbar] Fix the issue that setting margins programmatically does not work
We always use the original margins saved when the snackbar is created to update snackbar's actual margins. Therefore the newly set margins will always be overridden. Fixes this by updating original margins when layout params are set. Resolves #1076 PiperOrigin-RevId: 427795853
1 parent 6c1bdd7 commit d5856fd

File tree

1 file changed

+76
-92
lines changed

1 file changed

+76
-92
lines changed

lib/java/com/google/android/material/snackbar/BaseTransientBottomBar.java

Lines changed: 76 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -300,35 +300,20 @@ public void run() {
300300
}
301301
};
302302

303-
@Nullable private Rect originalMargins;
304303
private int extraBottomMarginWindowInset;
305304
private int extraLeftMarginWindowInset;
306305
private int extraRightMarginWindowInset;
307306
private int extraBottomMarginGestureInset;
308307
private int extraBottomMarginAnchorView;
309308

309+
private boolean pendingShowingView;
310+
310311
private List<BaseCallback<B>> callbacks;
311312

312313
private BaseTransientBottomBar.Behavior behavior;
313314

314315
@Nullable private final AccessibilityManager accessibilityManager;
315316

316-
/** @hide */
317-
// TODO(b/76413401): make package private after the widget migration is finished
318-
@RestrictTo(LIBRARY_GROUP)
319-
protected interface OnLayoutChangeListener {
320-
void onLayoutChange(View view, int left, int top, int right, int bottom);
321-
}
322-
323-
/** @hide */
324-
// TODO(b/76413401): make package private after the widget migration is finished
325-
@RestrictTo(LIBRARY_GROUP)
326-
protected interface OnAttachStateChangeListener {
327-
void onViewAttachedToWindow(View v);
328-
329-
void onViewDetachedFromWindow(View v);
330-
}
331-
332317
/**
333318
* Constructor for the transient bottom bar.
334319
*
@@ -371,24 +356,14 @@ protected BaseTransientBottomBar(
371356
// in the extending Snackbar class. This is to prevent breakage of apps that have custom
372357
// coordinator layout behaviors that depend on that layout.
373358
view = (SnackbarBaseLayout) inflater.inflate(getSnackbarBaseLayoutResId(), targetParent, false);
359+
view.setBaseTransientBottomBar(this);
374360
if (content instanceof SnackbarContentLayout) {
375361
((SnackbarContentLayout) content)
376362
.updateActionTextColorAlphaIfNeeded(view.getActionTextColorAlpha());
377363
((SnackbarContentLayout) content).setMaxInlineActionWidth(view.getMaxInlineActionWidth());
378364
}
379365
view.addView(content);
380366

381-
LayoutParams layoutParams = view.getLayoutParams();
382-
if (layoutParams instanceof MarginLayoutParams) {
383-
MarginLayoutParams marginParams = (MarginLayoutParams) layoutParams;
384-
originalMargins =
385-
new Rect(
386-
marginParams.leftMargin,
387-
marginParams.topMargin,
388-
marginParams.rightMargin,
389-
marginParams.bottomMargin);
390-
}
391-
392367
ViewCompat.setAccessibilityLiveRegion(view, ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
393368
ViewCompat.setImportantForAccessibility(view, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
394369

@@ -438,17 +413,23 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) {
438413

439414
private void updateMargins() {
440415
LayoutParams layoutParams = view.getLayoutParams();
441-
if (!(layoutParams instanceof MarginLayoutParams) || originalMargins == null) {
416+
if (!(layoutParams instanceof MarginLayoutParams) || view.originalMargins == null) {
442417
Log.w(TAG, "Unable to update margins because layout params are not MarginLayoutParams");
443418
return;
444419
}
420+
if (view.getParent() == null) {
421+
// Parent will set layout params to view again. Wait for addView() is done to update layout
422+
// params, in case we save the already updated margins as the original margins.
423+
return;
424+
}
445425

446426
int extraBottomMargin =
447427
getAnchorView() != null ? extraBottomMarginAnchorView : extraBottomMarginWindowInset;
448428
MarginLayoutParams marginParams = (MarginLayoutParams) layoutParams;
449-
marginParams.bottomMargin = originalMargins.bottom + extraBottomMargin;
450-
marginParams.leftMargin = originalMargins.left + extraLeftMarginWindowInset;
451-
marginParams.rightMargin = originalMargins.right + extraRightMarginWindowInset;
429+
marginParams.bottomMargin = view.originalMargins.bottom + extraBottomMargin;
430+
marginParams.leftMargin = view.originalMargins.left + extraLeftMarginWindowInset;
431+
marginParams.rightMargin = view.originalMargins.right + extraRightMarginWindowInset;
432+
marginParams.topMargin = view.originalMargins.top;
452433
view.requestLayout();
453434

454435
if (VERSION.SDK_INT >= VERSION_CODES.Q && shouldUpdateGestureInset()) {
@@ -724,47 +705,15 @@ protected SwipeDismissBehavior<? extends View> getNewBehavior() {
724705
}
725706

726707
final void showView() {
727-
this.view.setOnAttachStateChangeListener(
728-
new BaseTransientBottomBar.OnAttachStateChangeListener() {
729-
@Override
730-
public void onViewAttachedToWindow(View v) {
731-
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
732-
WindowInsets insets = view.getRootWindowInsets();
733-
if (insets != null) {
734-
extraBottomMarginGestureInset = insets.getMandatorySystemGestureInsets().bottom;
735-
updateMargins();
736-
}
737-
}
738-
}
739-
740-
@Override
741-
public void onViewDetachedFromWindow(View v) {
742-
if (isShownOrQueued()) {
743-
// If we haven't already been dismissed then this event is coming from a
744-
// non-user initiated action. Hence we need to make sure that we callback
745-
// and keep our state up to date. We need to post the call since
746-
// removeView() will call through to onDetachedFromWindow and thus overflow.
747-
handler.post(
748-
new Runnable() {
749-
@Override
750-
public void run() {
751-
onViewHidden(BaseCallback.DISMISS_EVENT_MANUAL);
752-
}
753-
});
754-
}
755-
}
756-
});
757-
758708
if (this.view.getParent() == null) {
759709
ViewGroup.LayoutParams lp = this.view.getLayoutParams();
760710

761711
if (lp instanceof CoordinatorLayout.LayoutParams) {
762712
setUpBehavior((CoordinatorLayout.LayoutParams) lp);
763713
}
764714

765-
recalculateAndUpdateMargins();
766-
767715
targetParent.addView(this.view);
716+
recalculateAndUpdateMargins();
768717

769718
// Set view to INVISIBLE so it doesn't flash on the screen before the inset adjustment is
770719
// handled and the enter animation is started
@@ -776,15 +725,41 @@ public void run() {
776725
return;
777726
}
778727

779-
// Otherwise, add one of our layout change listeners and show it in when laid out
780-
this.view.setOnLayoutChangeListener(
781-
new OnLayoutChangeListener() {
782-
@Override
783-
public void onLayoutChange(View view, int left, int top, int right, int bottom) {
784-
BaseTransientBottomBar.this.view.setOnLayoutChangeListener(null);
785-
BaseTransientBottomBar.this.showViewImpl();
786-
}
787-
});
728+
// Otherwise, show it in when laid out
729+
pendingShowingView = true;
730+
}
731+
732+
void onAttachedToWindow() {
733+
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
734+
WindowInsets insets = view.getRootWindowInsets();
735+
if (insets != null) {
736+
extraBottomMarginGestureInset = insets.getMandatorySystemGestureInsets().bottom;
737+
updateMargins();
738+
}
739+
}
740+
}
741+
742+
void onDetachedFromWindow() {
743+
if (isShownOrQueued()) {
744+
// If we haven't already been dismissed then this event is coming from a
745+
// non-user initiated action. Hence we need to make sure that we callback
746+
// and keep our state up to date. We need to post the call since
747+
// removeView() will call through to onDetachedFromWindow and thus overflow.
748+
handler.post(
749+
new Runnable() {
750+
@Override
751+
public void run() {
752+
onViewHidden(BaseCallback.DISMISS_EVENT_MANUAL);
753+
}
754+
});
755+
}
756+
}
757+
758+
void onLayoutChange() {
759+
if (pendingShowingView) {
760+
BaseTransientBottomBar.this.showViewImpl();
761+
pendingShowingView = false;
762+
}
788763
}
789764

790765
private void showViewImpl() {
@@ -1116,7 +1091,6 @@ boolean shouldAnimate() {
11161091
/** @hide */
11171092
@RestrictTo(LIBRARY_GROUP)
11181093
protected static class SnackbarBaseLayout extends FrameLayout {
1119-
11201094
private static final OnTouchListener consumeAllTouchListener =
11211095
new OnTouchListener() {
11221096
@SuppressLint("ClickableViewAccessibility")
@@ -1127,8 +1101,7 @@ public boolean onTouch(View v, MotionEvent event) {
11271101
}
11281102
};
11291103

1130-
private BaseTransientBottomBar.OnLayoutChangeListener onLayoutChangeListener;
1131-
private BaseTransientBottomBar.OnAttachStateChangeListener onAttachStateChangeListener;
1104+
@Nullable private BaseTransientBottomBar<?> baseTransientBottomBar;
11321105
@AnimationMode private int animationMode;
11331106
private final float backgroundOverlayColorAlpha;
11341107
private final float actionTextColorAlpha;
@@ -1137,6 +1110,8 @@ public boolean onTouch(View v, MotionEvent event) {
11371110
private ColorStateList backgroundTint;
11381111
private PorterDuff.Mode backgroundTintMode;
11391112

1113+
@Nullable private Rect originalMargins;
1114+
11401115
protected SnackbarBaseLayout(@NonNull Context context) {
11411116
this(context, null);
11421117
}
@@ -1233,37 +1208,37 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
12331208
@Override
12341209
protected void onLayout(boolean changed, int l, int t, int r, int b) {
12351210
super.onLayout(changed, l, t, r, b);
1236-
if (onLayoutChangeListener != null) {
1237-
onLayoutChangeListener.onLayoutChange(this, l, t, r, b);
1211+
if (baseTransientBottomBar != null) {
1212+
baseTransientBottomBar.onLayoutChange();
12381213
}
12391214
}
12401215

12411216
@Override
12421217
protected void onAttachedToWindow() {
12431218
super.onAttachedToWindow();
1244-
if (onAttachStateChangeListener != null) {
1245-
onAttachStateChangeListener.onViewAttachedToWindow(this);
1219+
if (baseTransientBottomBar != null) {
1220+
baseTransientBottomBar.onAttachedToWindow();
12461221
}
1247-
12481222
ViewCompat.requestApplyInsets(this);
12491223
}
12501224

12511225
@Override
12521226
protected void onDetachedFromWindow() {
12531227
super.onDetachedFromWindow();
1254-
if (onAttachStateChangeListener != null) {
1255-
onAttachStateChangeListener.onViewDetachedFromWindow(this);
1228+
if (baseTransientBottomBar != null) {
1229+
baseTransientBottomBar.onDetachedFromWindow();
12561230
}
12571231
}
12581232

1259-
void setOnLayoutChangeListener(
1260-
BaseTransientBottomBar.OnLayoutChangeListener onLayoutChangeListener) {
1261-
this.onLayoutChangeListener = onLayoutChangeListener;
1262-
}
1263-
1264-
void setOnAttachStateChangeListener(
1265-
BaseTransientBottomBar.OnAttachStateChangeListener listener) {
1266-
onAttachStateChangeListener = listener;
1233+
@Override
1234+
public void setLayoutParams(ViewGroup.LayoutParams params) {
1235+
super.setLayoutParams(params);
1236+
if (params instanceof MarginLayoutParams) {
1237+
updateOriginalMargins((MarginLayoutParams) params);
1238+
if (baseTransientBottomBar != null) {
1239+
baseTransientBottomBar.updateMargins();
1240+
}
1241+
}
12671242
}
12681243

12691244
@AnimationMode
@@ -1291,6 +1266,15 @@ int getMaxInlineActionWidth() {
12911266
return maxInlineActionWidth;
12921267
}
12931268

1269+
private void setBaseTransientBottomBar(BaseTransientBottomBar<?> baseTransientBottomBar) {
1270+
this.baseTransientBottomBar = baseTransientBottomBar;
1271+
}
1272+
1273+
private void updateOriginalMargins(MarginLayoutParams params) {
1274+
originalMargins =
1275+
new Rect(params.leftMargin, params.topMargin, params.rightMargin, params.bottomMargin);
1276+
}
1277+
12941278
@NonNull
12951279
private Drawable createThemedBackground() {
12961280
float cornerRadius =

0 commit comments

Comments
 (0)