Skip to content

Commit

Permalink
[Badge] Remove BadgeUtils.USE_COMPAT_PARENT
Browse files Browse the repository at this point in the history
Resolves #4170

GIT_ORIGIN_REV_ID=35f11c97b64f22b9c3994b41b35c955f7a4261ed
PiperOrigin-RevId: 634877849
  • Loading branch information
MGaetan89 authored and leticiarossi committed May 17, 2024
1 parent 182a507 commit 28dc750
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 187 deletions.
86 changes: 4 additions & 82 deletions lib/java/com/google/android/material/badge/BadgeDrawable.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
Expand Down Expand Up @@ -296,11 +295,6 @@ public void setVisible(boolean visible) {
private void onVisibilityUpdated() {
boolean visible = state.isVisible();
setVisible(visible, /* restart= */ false);
// When hiding a badge in pre-API 18, invalidate the custom parent in order to trigger a draw
// pass to remove this badge from its foreground.
if (BadgeUtils.USE_COMPAT_PARENT && getCustomBadgeParent() != null && !visible) {
((ViewGroup) getCustomBadgeParent().getParent()).invalidate();
}
}

/**
Expand Down Expand Up @@ -386,11 +380,6 @@ public void updateBadgeCoordinates(
* also updates this {@code BadgeDrawable BadgeDrawable's} bounds, because they are dependent on
* the center coordinates.
*
* <p>For pre API-18, optionally wrap the anchor in a {@code FrameLayout} (if it's not done
* already) that will be inserted into the anchor's view hierarchy and calculate the badge's
* coordinates the parent {@code FrameLayout} because the {@code BadgeDrawable} will be set as the
* parent's foreground.
*
* @param anchorView This badge's anchor.
*/
public void updateBadgeCoordinates(@NonNull View anchorView) {
Expand All @@ -402,85 +391,26 @@ public void updateBadgeCoordinates(@NonNull View anchorView) {
* also updates this {@code BadgeDrawable BadgeDrawable's} bounds, because they are dependent on
* the center coordinates.
*
* <p>For pre API-18, if no {@code customBadgeParent} is specified, optionally wrap the anchor in
* a {@code FrameLayout} (if it's not done already) that will be inserted into the anchor's view
* hierarchy and calculate the badge's coordinates the parent {@code FrameLayout} because the
* {@code BadgeDrawable} will be set as the parent's foreground.
*
* @param anchorView This badge's anchor.
* @param customBadgeParent An optional parent view that will set this {@code BadgeDrawable} as
* its foreground.
*/
public void updateBadgeCoordinates(
@NonNull View anchorView, @Nullable FrameLayout customBadgeParent) {
this.anchorViewRef = new WeakReference<>(anchorView);
this.customBadgeParentRef = new WeakReference<>(customBadgeParent);

if (BadgeUtils.USE_COMPAT_PARENT && customBadgeParent == null) {
tryWrapAnchorInCompatParent(anchorView);
} else {
this.customBadgeParentRef = new WeakReference<>(customBadgeParent);
}
if (!BadgeUtils.USE_COMPAT_PARENT) {
updateAnchorParentToNotClip(anchorView);
}
updateAnchorParentToNotClip(anchorView);
updateCenterAndBounds();
invalidateSelf();
}

private boolean isAnchorViewWrappedInCompatParent() {
View customBadgeAnchorParent = getCustomBadgeParent();
return customBadgeAnchorParent != null
&& customBadgeAnchorParent.getId() == R.id.mtrl_anchor_parent;
}

/** Returns a {@link FrameLayout} that will set this {@code BadgeDrawable} as its foreground. */
@Nullable
public FrameLayout getCustomBadgeParent() {
return customBadgeParentRef != null ? customBadgeParentRef.get() : null;
}

/**
* ViewOverlay is not supported below api 18, wrap the anchor view in a {@code FrameLayout} in
* order to support scrolling.
*/
private void tryWrapAnchorInCompatParent(final View anchorView) {
ViewGroup anchorViewParent = (ViewGroup) anchorView.getParent();
if ((anchorViewParent != null && anchorViewParent.getId() == R.id.mtrl_anchor_parent)
|| (customBadgeParentRef != null && customBadgeParentRef.get() == anchorViewParent)) {
return;
}
// Must call this before wrapping the anchor in a FrameLayout.
updateAnchorParentToNotClip(anchorView);

// Create FrameLayout and configure it to wrap the anchor.
final FrameLayout frameLayout = new FrameLayout(anchorView.getContext());
frameLayout.setId(R.id.mtrl_anchor_parent);
frameLayout.setClipChildren(false);
frameLayout.setClipToPadding(false);
frameLayout.setLayoutParams(anchorView.getLayoutParams());
frameLayout.setMinimumWidth(anchorView.getWidth());
frameLayout.setMinimumHeight(anchorView.getHeight());

int anchorIndex = anchorViewParent.indexOfChild(anchorView);
anchorViewParent.removeViewAt(anchorIndex);
anchorView.setLayoutParams(
new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

frameLayout.addView(anchorView);
anchorViewParent.addView(frameLayout, anchorIndex);
customBadgeParentRef = new WeakReference<>(frameLayout);

// Update the badge's coordinates after the FrameLayout has been added to the view hierarchy and
// has a size.
frameLayout.post(
new Runnable() {
@Override
public void run() {
updateBadgeCoordinates(anchorView, frameLayout);
}
});
}

private static void updateAnchorParentToNotClip(View anchorView) {
ViewGroup anchorViewParent = (ViewGroup) anchorView.getParent();
anchorViewParent.setClipChildren(false);
Expand Down Expand Up @@ -1230,11 +1160,9 @@ private void updateCenterAndBounds() {
anchorView.getDrawingRect(anchorRect);

ViewGroup customBadgeParent = customBadgeParentRef != null ? customBadgeParentRef.get() : null;
if (customBadgeParent != null || BadgeUtils.USE_COMPAT_PARENT) {
if (customBadgeParent != null) {
// Calculates coordinates relative to the parent.
ViewGroup viewGroup =
customBadgeParent == null ? (ViewGroup) anchorView.getParent() : customBadgeParent;
viewGroup.offsetDescendantRectToMyCoords(anchorView, anchorRect);
customBadgeParent.offsetDescendantRectToMyCoords(anchorView, anchorRect);
}

calculateCenterAndBounds(anchorRect, anchorView);
Expand Down Expand Up @@ -1389,10 +1317,6 @@ private void autoAdjustWithinViewBounds(@NonNull View anchorView, @Nullable View
totalAnchorYOffset = anchorView.getY();
totalAnchorXOffset = anchorView.getX();
anchorParent = anchorView.getParent();
} else if (isAnchorViewWrappedInCompatParent()) {
totalAnchorYOffset = ((View) customAnchorParent).getY();
totalAnchorXOffset = ((View) customAnchorParent).getX();
anchorParent = customAnchorParent.getParent();
} else {
totalAnchorYOffset = 0;
totalAnchorXOffset = 0;
Expand Down Expand Up @@ -1446,8 +1370,6 @@ private void autoAdjustWithinGrandparentBounds(@NonNull View anchorView) {
ViewParent anchorParent = null;
if (customAnchor == null) {
anchorParent = anchorView.getParent();
} else if (isAnchorViewWrappedInCompatParent()) {
anchorParent = customAnchor.getParent();
} else {
anchorParent = customAnchor;
}
Expand Down
39 changes: 17 additions & 22 deletions lib/java/com/google/android/material/badge/BadgeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@
@ExperimentalBadgeUtils
public class BadgeUtils {

public static final boolean USE_COMPAT_PARENT = VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN_MR2;

private static final String LOG_TAG = "BadgeUtils";

private BadgeUtils() {
Expand Down Expand Up @@ -80,9 +78,9 @@ public static void attachBadgeDrawable(

/**
* Attaches a BadgeDrawable to its associated anchor and update the BadgeDrawable's coordinates
* based on the anchor. For API 18+, the BadgeDrawable will be added as a view overlay. For
* pre-API 18, the BadgeDrawable will be set as the foreground of a FrameLayout that is an
* ancestor of the anchor.
* based on the anchor. The BadgeDrawable will be added as a view overlay as default. If it has a
* FrameLayout custom parent that is an ancestor of the anchor, then the BadgeDrawable will be set
* as the foreground of that.
*/
public static void attachBadgeDrawable(
@NonNull BadgeDrawable badgeDrawable,
Expand All @@ -93,11 +91,7 @@ public static void attachBadgeDrawable(
if (badgeDrawable.getCustomBadgeParent() != null) {
badgeDrawable.getCustomBadgeParent().setForeground(badgeDrawable);
} else {
if (USE_COMPAT_PARENT) {
throw new IllegalArgumentException("Trying to reference null customBadgeParent");
} else {
anchor.getOverlay().add(badgeDrawable);
}
anchor.getOverlay().add(badgeDrawable);
}
}

Expand All @@ -117,9 +111,9 @@ public static void attachBadgeDrawable(
/**
* Attaches a BadgeDrawable to its associated action menu item on a toolbar, update the
* BadgeDrawable's coordinates based on this anchor and adjust the BadgeDrawable's offset so it is
* not clipped off by the toolbar. For API 18+, the BadgeDrawable will be added as a view overlay.
* For pre-API 18, the BadgeDrawable will be set as the foreground of a FrameLayout that is an
* ancestor of the anchor.
* not clipped off by the toolbar. The BadgeDrawable will be added as a view overlay as default.
* If it has a FrameLayout custom parent that is an ancestor of the anchor, then the BadgeDrawable
* will be set as the foreground of that.
*
* <p>Menu item views are reused by the menu, so any structural changes to the menu may require
* detaching the BadgeDrawable and re-attaching it to the correct item.
Expand Down Expand Up @@ -173,26 +167,27 @@ public void onInitializeAccessibilityNodeInfo(
}

/**
* Detaches a BadgeDrawable from its associated anchor. For API 18+, the BadgeDrawable will be
* removed from its anchor's ViewOverlay. For pre-API 18, the BadgeDrawable will be removed from
* the foreground of a FrameLayout that is an ancestor of the anchor.
* Detaches a BadgeDrawable from its associated anchor. The BadgeDrawable will be removed from its
* anchor's ViewOverlay. If it has a FrameLayout custom parent that is an ancestor of the anchor,
* then the BadgeDrawable will be removed from the parent's foreground instead.
*/
public static void detachBadgeDrawable(
@Nullable BadgeDrawable badgeDrawable, @NonNull View anchor) {
if (badgeDrawable == null) {
return;
}
if (USE_COMPAT_PARENT || badgeDrawable.getCustomBadgeParent() != null) {
if (badgeDrawable.getCustomBadgeParent() != null) {
badgeDrawable.getCustomBadgeParent().setForeground(null);
} else {
anchor.getOverlay().remove(badgeDrawable);
}
}

/**
* Detaches a BadgeDrawable from its associated action menu item on a toolbar, For API 18+, the
* BadgeDrawable will be removed from its anchor's ViewOverlay. For pre-API 18, the BadgeDrawable
* will be removed from the foreground of a FrameLayout that is an ancestor of the anchor.
* Detaches a BadgeDrawable from its associated action menu item on a toolbar, The BadgeDrawable
* will be removed from its anchor's ViewOverlay. If it has a FrameLayout custom parent that is an
* ancestor of the anchor, then the BadgeDrawable will be removed from the parent's foreground
* instead.
*/
public static void detachBadgeDrawable(
@Nullable BadgeDrawable badgeDrawable, @NonNull Toolbar toolbar, @IdRes int menuItemId) {
Expand Down Expand Up @@ -243,8 +238,8 @@ static void removeToolbarOffset(BadgeDrawable badgeDrawable) {
}

/**
* Sets the bounds of a BadgeDrawable to match the bounds of its anchor (for API 18+) or its
* anchor's FrameLayout ancestor (pre-API 18).
* Sets the bounds of a BadgeDrawable to match the bounds of its anchor or its anchor's
* FrameLayout ancestor if it has a custom parent set.
*/
public static void setBadgeDrawableBounds(
@NonNull BadgeDrawable badgeDrawable,
Expand Down
22 changes: 0 additions & 22 deletions lib/java/com/google/android/material/badge/res/values/ids.xml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -910,8 +910,7 @@ private void tryUpdateBadgeBounds(View anchorView) {
if (!hasBadge()) {
return;
}
BadgeUtils.setBadgeDrawableBounds(
badgeDrawable, anchorView, getCustomParentForBadge(anchorView));
BadgeUtils.setBadgeDrawableBounds(badgeDrawable, anchorView, null);
}

private void tryAttachBadgeToAnchor(@Nullable View anchorView) {
Expand All @@ -923,8 +922,7 @@ private void tryAttachBadgeToAnchor(@Nullable View anchorView) {
setClipChildren(false);
setClipToPadding(false);

BadgeUtils.attachBadgeDrawable(
badgeDrawable, anchorView, getCustomParentForBadge(anchorView));
BadgeUtils.attachBadgeDrawable(badgeDrawable, anchorView);
}
}

Expand All @@ -942,15 +940,6 @@ private void tryRemoveBadgeFromAnchor(@Nullable View anchorView) {
badgeDrawable = null;
}

@Nullable
private FrameLayout getCustomParentForBadge(View anchorView) {
if (anchorView == icon) {
return BadgeUtils.USE_COMPAT_PARENT ? ((FrameLayout) icon.getParent()) : null;
}
// TODO(b/138148581): Support displaying a badge on label-only bottom navigation views.
return null;
}

private int getSuggestedIconWidth() {
int badgeWidth =
badgeDrawable == null
Expand Down
Loading

0 comments on commit 28dc750

Please sign in to comment.