Skip to content

Commit 3c6331e

Browse files
committed
Merge branch 'redwerk-bootstrap_badges' into upstream/master
2 parents 2d2aeb9 + d21ad2b commit 3c6331e

11 files changed

Lines changed: 361 additions & 5 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.beardedhen.androidbootstrap;
2+
3+
import android.content.Context;
4+
import android.content.res.TypedArray;
5+
import android.graphics.drawable.Drawable;
6+
import android.os.Build;
7+
import android.util.AttributeSet;
8+
import android.widget.ImageView;
9+
10+
import com.beardedhen.androidbootstrap.api.attributes.BootstrapBrand;
11+
import com.beardedhen.androidbootstrap.api.defaults.DefaultBootstrapBrand;
12+
import com.beardedhen.androidbootstrap.api.defaults.DefaultBootstrapSize;
13+
import com.beardedhen.androidbootstrap.api.view.BootstrapSizeView;
14+
import com.beardedhen.androidbootstrap.utils.DimenUtils;
15+
16+
public class BootstrapBadge extends ImageView implements BootstrapSizeView {
17+
18+
private int badgeCount;
19+
private int size;
20+
private boolean allowZeroValue;
21+
private boolean insideContainer;
22+
private BootstrapBrand bootstrapBrand = DefaultBootstrapBrand.REGULAR;
23+
private float bootstrapSize;
24+
private Drawable badgeDrawable;
25+
26+
public BootstrapBadge(Context context) {
27+
super(context);
28+
init(null);
29+
}
30+
31+
public BootstrapBadge(Context context, AttributeSet attrs) {
32+
super(context, attrs);
33+
init(attrs);
34+
}
35+
36+
public BootstrapBadge(Context context, AttributeSet attrs, int defStyleAttr) {
37+
super(context, attrs, defStyleAttr);
38+
init(attrs);
39+
}
40+
41+
private void init(AttributeSet attrs) {
42+
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BootstrapBadge);
43+
44+
try {
45+
int sizeOrdinal = a.getInt(R.styleable.BootstrapBadge_bootstrapSize, -1);
46+
47+
allowZeroValue = a.getBoolean(R.styleable.BootstrapBadge_allowZeroValue, false);
48+
bootstrapSize = DefaultBootstrapSize.fromAttributeValue(sizeOrdinal).scaleFactor();
49+
} finally {
50+
a.recycle();
51+
}
52+
53+
size = (int) DimenUtils.pixelsFromDpResource(getContext(), R.dimen.bootstrap_badge_default_size);
54+
updateBootstrapState();
55+
}
56+
57+
private void updateBootstrapState() {
58+
badgeDrawable = BootstrapDrawableFactory.createBadgeDrawable(getContext(),
59+
bootstrapBrand,
60+
(int) (size * bootstrapSize),
61+
(int) (size * bootstrapSize),
62+
badgeCount,
63+
allowZeroValue,
64+
insideContainer);
65+
66+
if (Build.VERSION.SDK_INT >= 16) {
67+
setBackground(badgeDrawable);
68+
} else {
69+
setBackgroundDrawable(badgeDrawable);
70+
}
71+
}
72+
73+
public void setBootstrapBrand(BootstrapBrand bootstrapBrand, boolean insideContainer) {
74+
this.bootstrapBrand = bootstrapBrand;
75+
this.insideContainer = insideContainer;
76+
updateBootstrapState();
77+
}
78+
79+
public void setBadgeCount(int badgeCount) {
80+
this.badgeCount = badgeCount;
81+
updateBootstrapState();
82+
}
83+
84+
@Override public void setBootstrapSize(DefaultBootstrapSize bootstrapSize) {
85+
this.bootstrapSize = bootstrapSize.scaleFactor();
86+
updateBootstrapState();
87+
}
88+
89+
@Override public void setBootstrapSize(float bootstrapSize) {
90+
this.bootstrapSize = bootstrapSize;
91+
updateBootstrapState();
92+
}
93+
94+
public void setAllowZeroValue(boolean allowZeroValue) {
95+
this.allowZeroValue = allowZeroValue;
96+
updateBootstrapState();
97+
}
98+
99+
public int getBadgeCount() {
100+
return badgeCount;
101+
}
102+
103+
public Drawable getBadgeDrawable() {
104+
return badgeDrawable;
105+
}
106+
107+
@Override public float getBootstrapSize() {
108+
return bootstrapSize;
109+
}
110+
111+
public boolean isAllowZeroValue() {
112+
return allowZeroValue;
113+
}
114+
}

AndroidBootstrap/src/main/java/com/beardedhen/androidbootstrap/BootstrapButton.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.beardedhen.androidbootstrap.api.attributes.ViewGroupPosition;
1616
import com.beardedhen.androidbootstrap.api.defaults.ButtonMode;
1717
import com.beardedhen.androidbootstrap.api.defaults.DefaultBootstrapSize;
18+
import com.beardedhen.androidbootstrap.api.view.BadgeContainerView;
1819
import com.beardedhen.androidbootstrap.api.view.BootstrapSizeView;
1920
import com.beardedhen.androidbootstrap.api.view.ButtonModeView;
2021
import com.beardedhen.androidbootstrap.api.view.OutlineableView;
@@ -29,7 +30,7 @@
2930
* allowing the use of different selection modes e.g. Checkbox/Radio group.
3031
*/
3132
public class BootstrapButton extends AwesomeTextView implements BootstrapSizeView,
32-
OutlineableView, RoundableView, ButtonModeView {
33+
OutlineableView, RoundableView, ButtonModeView, BadgeContainerView {
3334

3435
private static final String TAG = "com.beardedhen.androidbootstrap.BootstrapButton";
3536
private static final String KEY_MODE = "com.beardedhen.androidbootstrap.BootstrapButton.MODE";
@@ -50,6 +51,7 @@ public class BootstrapButton extends AwesomeTextView implements BootstrapSizeVie
5051
private float baselineHoriPadding;
5152
private float baselineStrokeWidth;
5253
private float baselineCornerRadius;
54+
private BootstrapBadge bootstrapBadge;
5355

5456
public BootstrapButton(Context context) {
5557
super(context);
@@ -102,6 +104,7 @@ private void initialise(AttributeSet attrs) {
102104
bundle.putInt(KEY_INDEX, parentIndex);
103105
bundle.putFloat(BootstrapSizeView.KEY, bootstrapSize);
104106
bundle.putSerializable(KEY_MODE, buttonMode);
107+
if (bootstrapBadge != null) bundle.putInt(BadgeContainerView.KEY, bootstrapBadge.getBadgeCount());
105108
return bundle;
106109
}
107110

@@ -113,6 +116,7 @@ private void initialise(AttributeSet attrs) {
113116
this.showOutline = bundle.getBoolean(OutlineableView.KEY);
114117
this.parentIndex = bundle.getInt(KEY_INDEX);
115118
this.bootstrapSize = bundle.getFloat(BootstrapSizeView.KEY);
119+
if (bootstrapBadge != null) setBadgeCount(bundle.getInt(BadgeContainerView.KEY));
116120

117121
Serializable m = bundle.getSerializable(KEY_MODE);
118122

@@ -237,6 +241,15 @@ void updateFromParent(BootstrapBrand bootstrapBrand,
237241
updateBootstrapState();
238242
}
239243

244+
@Override public void displayBadgeDrawable() {
245+
setCompoundDrawablesWithIntrinsicBounds(
246+
null,
247+
null,
248+
this.bootstrapBadge.getBadgeDrawable(),
249+
null);
250+
setCompoundDrawablePadding(DimenUtils.dpToPixels(4));
251+
}
252+
240253
/*
241254
* Getters/Setters
242255
*/
@@ -275,10 +288,32 @@ public void setChecked(boolean checked) {
275288
this.buttonMode = buttonMode;
276289
}
277290

291+
@Override public void setBadge(BootstrapBadge badge) {
292+
this.bootstrapBadge = badge;
293+
this.bootstrapBadge.setBootstrapBrand(getBootstrapBrand(), true);
294+
this.bootstrapBadge.setBootstrapSize(getBootstrapSize());
295+
displayBadgeDrawable();
296+
}
297+
298+
@Override public void setBadgeCount(int badgeCount) {
299+
if (bootstrapBadge != null && badgeCount >= 0) {
300+
this.bootstrapBadge.setBadgeCount(badgeCount);
301+
displayBadgeDrawable();
302+
}
303+
}
304+
305+
@Override public int getBadgeCount() {
306+
if (bootstrapBadge != null) return bootstrapBadge.getBadgeCount();
307+
else return 0;
308+
}
278309
@Override public float getBootstrapSize() {
279310
return bootstrapSize;
280311
}
281312

313+
@Override public BootstrapBadge getBootstrapBadge() {
314+
return bootstrapBadge;
315+
}
316+
282317
@Override public void setBootstrapSize(DefaultBootstrapSize bootstrapSize) {
283318
setBootstrapSize(bootstrapSize.scaleFactor());
284319
}
@@ -287,5 +322,4 @@ public void setChecked(boolean checked) {
287322
this.bootstrapSize = bootstrapSize;
288323
updateBootstrapState();
289324
}
290-
291325
}

AndroidBootstrap/src/main/java/com/beardedhen/androidbootstrap/BootstrapDrawableFactory.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
import android.graphics.Color;
88
import android.graphics.Paint;
99
import android.graphics.Path;
10+
import android.graphics.Rect;
1011
import android.graphics.drawable.BitmapDrawable;
1112
import android.graphics.drawable.Drawable;
1213
import android.graphics.drawable.GradientDrawable;
1314
import android.graphics.drawable.LayerDrawable;
1415
import android.graphics.drawable.StateListDrawable;
1516
import android.os.Build;
1617
import android.support.annotation.ColorInt;
18+
import android.text.TextPaint;
1719

1820
import com.beardedhen.androidbootstrap.api.attributes.BootstrapBrand;
1921
import com.beardedhen.androidbootstrap.api.attributes.ViewGroupPosition;
@@ -432,6 +434,50 @@ private static Drawable createCloseCrossIcon(Context context, int width, int hei
432434
return new BitmapDrawable(context.getResources(), canvasBitmap);
433435
}
434436

437+
public static Drawable createBadgeDrawable(Context context, BootstrapBrand brand, int width, int height, int badgeCount, boolean allowZeroValue, boolean insideAnObject) {
438+
if (allowZeroValue || badgeCount > 0) {
439+
Paint badgePaint = new Paint();
440+
Rect canvasBounds = new Rect();
441+
TextPaint badgeTextPaint = new TextPaint();
442+
badgePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
443+
badgeTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
444+
badgeTextPaint.setTextAlign(Paint.Align.CENTER);
445+
badgeTextPaint.setTextSize((float) (height * 0.7));
446+
447+
if (insideAnObject) {
448+
badgePaint.setColor(brand.defaultTextColor(context));
449+
badgeTextPaint.setColor(brand.defaultFill(context));
450+
} else {
451+
badgePaint.setColor(brand.defaultFill(context));
452+
badgeTextPaint.setColor(brand.defaultTextColor(context));
453+
}
454+
455+
int rectLength = (int) badgeTextPaint.measureText(String.valueOf(badgeCount).substring(0, String.valueOf(badgeCount).length() - 1));
456+
457+
Bitmap canvasBitmap = Bitmap.createBitmap(width + rectLength, height, Bitmap.Config.ARGB_8888);
458+
Canvas canvas = new Canvas(canvasBitmap);
459+
canvas.getClipBounds(canvasBounds);
460+
461+
int firstCircleDx = canvasBounds.left + canvasBounds.height() / 2;
462+
int circleDy = canvasBounds.height() / 2;
463+
int circleRadius = canvasBounds.height() / 2;
464+
int secondCircleDx = firstCircleDx + rectLength;
465+
466+
Rect rect = new Rect();
467+
rect.left = firstCircleDx;
468+
rect.top = 0;
469+
rect.right = rect.left + rectLength;
470+
rect.bottom = canvasBounds.height();
471+
472+
canvas.drawCircle(firstCircleDx, circleDy, circleRadius, badgePaint);
473+
canvas.drawRect(rect, badgePaint);
474+
canvas.drawCircle(secondCircleDx, circleDy, circleRadius, badgePaint);
475+
canvas.drawText(String.valueOf(badgeCount), canvasBounds.width() / 2, canvasBounds.height() / 2 - ((badgeTextPaint.descent() + badgeTextPaint.ascent()) / 2), badgeTextPaint);
476+
477+
return new BitmapDrawable(context.getResources(), canvasBitmap);
478+
} else return null;
479+
}
480+
435481
static ColorStateList bootstrapDropDownViewText(Context context) {
436482

437483
int defaultColor = context.getResources().getColor(R.color.bootstrap_gray_dark);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.beardedhen.androidbootstrap.api.view;
2+
3+
import com.beardedhen.androidbootstrap.BootstrapBadge;
4+
5+
/**
6+
* Classes which implement this interface can be used as BootstrapBadge container.
7+
* For example, a Button can be set up with BootstrapBadge.
8+
*/
9+
public interface BadgeContainerView {
10+
11+
String KEY = "com.beardedhen.androidbootstrap.api.view.BadgeContainerView";
12+
13+
/**
14+
* Sets the badge object to be used inside a container.
15+
*
16+
* @param badge BootstrapBadge to setup
17+
*/
18+
void setBadge(BootstrapBadge badge);
19+
20+
/**
21+
* Sets badge count to be displayed to user.
22+
*
23+
* @param count badge count
24+
*/
25+
void setBadgeCount(int count);
26+
27+
/**
28+
* Retrieves BootstrapBadge object from container view.
29+
*
30+
* @return BootstrapBadge if exist or null if not
31+
*/
32+
BootstrapBadge getBootstrapBadge();
33+
34+
/**
35+
* Retrieves current badge count.
36+
*
37+
* @return badge count
38+
*/
39+
int getBadgeCount();
40+
41+
/**
42+
* Method where badge display logic must be implemented
43+
*
44+
*/
45+
void displayBadgeDrawable();
46+
}

AndroidBootstrap/src/main/res/values/attrs.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<attr name="checked" format="boolean"/>
5050
<attr name="dropdownResource" format="reference"/>
5151
<attr name="itemHeight" format="dimension"/>
52+
<attr name="allowZeroValue" format="boolean"/>
5253
<attr name="strongText" format="string"/>
5354
<attr name="messageText" format="string"/>
5455
<attr name="dismissible" format="boolean"/>
@@ -134,5 +135,10 @@
134135
<attr name="dismissible"/>
135136
</declare-styleable>
136137

138+
<declare-styleable name="BootstrapBadge">
139+
<attr name="allowZeroValue"/>
140+
<attr name="bootstrapSize"/>
141+
</declare-styleable>
142+
137143
</resources>
138144

AndroidBootstrap/src/main/res/values/dimens.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,6 @@
6767
<!-- Misc -->
6868
<dimen name="bootstrap_default_corner_radius">3dp</dimen>
6969
<dimen name="bootstrap_progress_bar_height">14dp</dimen>
70+
<dimen name="bootstrap_badge_default_size">16dp</dimen>
7071

7172
</resources>

sample/src/main/AndroidManifest.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@
5858
/>
5959
<activity
6060
android:name=".BootstrapDropDownExample"
61-
android:label="BootstrapDropDown"
61+
android:label="BootstrapDropDownExample"
6262
/>
6363
<activity
64-
android:name=".BootstrapAlertExample"
65-
android:label="BootstrapAlert"
64+
android:name=".BootstrapBadgeExample"
65+
android:label="BootstrapBadgeExample"
6666
/>
6767
</application>
6868

0 commit comments

Comments
 (0)