问题
I have this function in my main activity which doesn't display a badge:
private fun setFindShiftBadge(state: HomeState) {
val findShiftsBadge = BadgeDrawable.create(this)
home_framelayout.foreground = findShiftsBadge
findShiftsBadge.badgeGravity = BadgeDrawable.TOP_END
findShiftsBadge.backgroundColor = resources.getColor(R.color.colorWhite)
findShiftsBadge.badgeTextColor = resources.getColor(R.color.colorPrimary)
findShiftsBadge.number = state.availableShifts.size
}
In the same activity, I have this function which displays a badge:
private fun setMyPostedShiftBadge(state: HomeState) {
val userShiftsBadge: BadgeDrawable =
bottom_navigation_view.getOrCreateBadge(R.id.bottom_navigation_my_posted_shifts_text)
userShiftsBadge.number = state.userShifts.size
userShiftsBadge.backgroundColor = resources.getColor(R.color.colorPrimary)
}
Now I understand that the second function works because the badge is set within a BottomNavigationView
. Ironically, BottomNavigationView
extends FrameLayout
. In the Android Documentation:
Add BadgeDrawable as a ViewOverlay to the desired anchor view using attachBadgeDrawable(BadgeDrawable, View, FrameLayout).
Update the BadgeDrawable BadgeDrawable's coordinates (center and bounds) based on its anchor view using updateBadgeCoordinates(View, ViewGroup).
They say to use this code, For API 18+ (APIs supported by ViewOverlay)
which I use in the first function that doesn't work:
BadgeDrawable badgeDrawable = BadgeDrawable.create(context);
BadgeUtils.attachBadgeDrawable(badgeDrawable, anchor, null);
I have also tried this solution but it didn't work either. I'm aware there are workarounds such as:
- Creating a drawable then setting that onto of the view you want
- Placing a TextView within that drawable that you then update with the number you want
- Toggle the visibility of the drawable when there are any numbers.
I am on implementation 'com.google.android.material:material:1.2.0-alpha04'
This seems dirty to me since I know that there is a better way to do this. I just can't seem to figure it out. What am I missing? Is this even possible? How have you been able to do this without having to write a workaround? Thanks for your time! Looking forward to learning from your questions and answers!! Have a fantastic day!
回答1:
BottomNavigationView.getOrCreateBadge()
does not only assign the BadgeDrawable
as foreground Drawable
, it also sets the drawable bounds. Without this step, they stay at (0,0,0,0), so there is nothing to draw.
In order to set the bounds, let's introduce an extension function for BadgeDrawable
/**
* Inspired by BadgeUtils in com.google.android.material library
*
* Sets the bounds of a BadgeDrawable
*/
private fun BadgeDrawable.setBoundsFor(@NonNull anchor: View, @NonNull parent: FrameLayout){
val rect = Rect()
parent.getDrawingRect(rect)
this.setBounds(rect)
this.updateBadgeCoordinates(anchor, parent)
}
Use this function with your BadgeDrawable
for FrameLayout
:
private fun setFindShiftBadge(state: HomeState) {
val findShiftsBadge = BadgeDrawable.create(this)
// configure the badge drawable
findShiftsBadge.badgeGravity = BadgeDrawable.TOP_END
findShiftsBadge.backgroundColor = resources.getColor(R.color.colorWhite)
findShiftsBadge.badgeTextColor = resources.getColor(R.color.colorPrimary)
findShiftsBadge.number = state.availableShifts.size
// set bounds inside which it will be drawn
findShiftsBadge.setBoundsFor(btn_badge, home_framelayout)
// assign as foreground drawable
home_framelayout.foreground = findShiftsBadge
}
回答2:
BadgeDrawable badgeDrawable = BadgeDrawable.create(this);
badgeDrawable.setNumber(15);
FrameLayout frameLayout = findViewById(R.id.framelayout);
ImageView imageView = findViewById(R.id.iv_center);
//core code
frameLayout.setForeground(badgeDrawable);
frameLayout.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
//either of the following two lines of code work
//badgeDrawable.updateBadgeCoordinates(imageView, frameLayout);
BadgeUtils.attachBadgeDrawable(badgeDrawable, imageView, frameLayout);
});
<FrameLayout
android:id="@+id/framelayout"
android:layout_width="150dp"
android:layout_height="150dp">
<ImageView
android:layout_gravity="center"
android:id="@+id/iv_center"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitXY"
android:src="@drawable/ic_beard" />
</FrameLayout>
来源:https://stackoverflow.com/questions/60009600/does-badgedrawable-not-work-for-views-within-a-framelayout-such-as-buttons-imag