Skip to content

Commit 2d2aeb9

Browse files
Merge pull request #152 from redwerk/bootstrap_alerts
Bootstrap Alert implementation
2 parents 2249f5c + ca7d457 commit 2d2aeb9

14 files changed

Lines changed: 413 additions & 6 deletions

File tree

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package com.beardedhen.androidbootstrap;
2+
3+
import android.content.Context;
4+
import android.content.res.TypedArray;
5+
import android.graphics.Rect;
6+
import android.graphics.drawable.Drawable;
7+
import android.os.Build;
8+
import android.text.Html;
9+
import android.util.AttributeSet;
10+
import android.view.Gravity;
11+
import android.view.TouchDelegate;
12+
import android.view.View;
13+
import android.view.animation.AccelerateInterpolator;
14+
import android.view.animation.AlphaAnimation;
15+
import android.view.animation.Animation;
16+
import android.widget.ImageView;
17+
import android.widget.RelativeLayout;
18+
import android.widget.TextView;
19+
20+
import com.beardedhen.androidbootstrap.api.attributes.BootstrapBrand;
21+
import com.beardedhen.androidbootstrap.api.defaults.DefaultBootstrapBrand;
22+
import com.beardedhen.androidbootstrap.utils.DimenUtils;
23+
24+
import java.util.concurrent.atomic.AtomicInteger;
25+
26+
public class BootstrapAlert extends RelativeLayout {
27+
28+
private ImageView closeButton;
29+
private TextView alertText;
30+
31+
private BootstrapBrand bootstrapBrand;
32+
33+
private String strongText;
34+
private String messageText;
35+
36+
private float baselineFontSize;
37+
private float baselinePadding;
38+
39+
private AlphaAnimation fadeInAnimation;
40+
private AlphaAnimation fadeOutAnimation;
41+
42+
private boolean dismissible;
43+
private boolean hidden;
44+
45+
private OnDismissListener onDismissListener;
46+
47+
private static final AtomicInteger nextGeneratedId = new AtomicInteger(1);
48+
49+
public BootstrapAlert(Context context) {
50+
super(context);
51+
initialise(null);
52+
}
53+
54+
public BootstrapAlert(Context context, AttributeSet attrs) {
55+
super(context, attrs);
56+
initialise(attrs);
57+
}
58+
59+
public BootstrapAlert(Context context, AttributeSet attrs, int defStyleAttr) {
60+
super(context, attrs, defStyleAttr);
61+
initialise(attrs);
62+
}
63+
64+
private void initialise(AttributeSet attrs) {
65+
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BootstrapAlert);
66+
67+
try {
68+
int typeOrdinal = a.getInt(R.styleable.BootstrapAlert_bootstrapBrand, -1);
69+
70+
this.bootstrapBrand = DefaultBootstrapBrand.fromAttributeValue(typeOrdinal);
71+
72+
strongText = a.getString(R.styleable.BootstrapAlert_strongText);
73+
messageText = a.getString(R.styleable.BootstrapAlert_messageText);
74+
dismissible = a.getBoolean(R.styleable.BootstrapAlert_dismissible, false);
75+
if (strongText == null) {
76+
strongText = "";
77+
}
78+
if (messageText == null) {
79+
messageText = "";
80+
}
81+
} finally {
82+
a.recycle();
83+
}
84+
85+
baselineFontSize = DimenUtils.pixelsFromSpResource(getContext(), R.dimen.bootstrap_button_default_font_size);
86+
baselinePadding = DimenUtils.pixelsFromDpResource(getContext(), R.dimen.bootstrap_alert_paddings);
87+
88+
updateBootstrapState();
89+
}
90+
91+
private void updateBootstrapState() {
92+
alertText = new TextView(getContext());
93+
closeButton = new ImageView(getContext());
94+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
95+
alertText.setId(generateViewUniqueId());
96+
closeButton.setId(generateViewUniqueId());
97+
} else {
98+
alertText.setId(View.generateViewId());
99+
closeButton.setId(View.generateViewId());
100+
}
101+
fadeInAnimation = new AlphaAnimation(0.0f, 1.0f);
102+
fadeInAnimation.setDuration(300);
103+
fadeInAnimation.setInterpolator(new AccelerateInterpolator());
104+
fadeOutAnimation = new AlphaAnimation(1.0f, 0.0f);
105+
fadeOutAnimation.setDuration(300);
106+
fadeOutAnimation.setInterpolator(new AccelerateInterpolator());
107+
108+
fadeInAnimation.setAnimationListener(new Animation.AnimationListener() {
109+
@Override public void onAnimationStart(Animation animation) {setVisibility(VISIBLE);}
110+
@Override public void onAnimationEnd(Animation animation) {}
111+
@Override public void onAnimationRepeat(Animation animation) {}
112+
});
113+
114+
fadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {
115+
@Override public void onAnimationStart(Animation animation) {}
116+
@Override public void onAnimationEnd(Animation animation) {
117+
setVisibility(GONE);
118+
if (onDismissListener != null) onDismissListener.onDismiss();
119+
}
120+
@Override public void onAnimationRepeat(Animation animation) {}
121+
});
122+
123+
LayoutParams textParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
124+
LayoutParams closeParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
125+
textParams.addRule(RelativeLayout.LEFT_OF, closeButton.getId());
126+
closeParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
127+
128+
alertText.setLayoutParams(textParams);
129+
alertText.setTextSize(baselineFontSize);
130+
alertText.setGravity(Gravity.LEFT);
131+
alertText.setTextColor(BootstrapDrawableFactory.bootstrapButtonText(
132+
getContext(),
133+
true,
134+
bootstrapBrand));
135+
alertText.setText(Html.fromHtml(String.format("<b>%s</b>%s", strongText, (strongText.length() > 0 ? "&nbsp;" + messageText : messageText))));
136+
137+
closeButton.setLayoutParams(closeParams);
138+
closeButton.setBackgroundDrawable(BootstrapDrawableFactory.bootstrapAlertCloseIcon(
139+
getContext(),
140+
(int) baselineFontSize,
141+
(int) baselineFontSize,
142+
DimenUtils.dpToPixels(6)));
143+
144+
Drawable bg = BootstrapDrawableFactory.bootstrapAlert(
145+
getContext(),
146+
bootstrapBrand);
147+
148+
if (Build.VERSION.SDK_INT >= 16) {
149+
setBackground(bg);
150+
} else {
151+
setBackgroundDrawable(bg);
152+
}
153+
addView(alertText);
154+
if (dismissible) {
155+
addView(closeButton);
156+
((View) closeButton.getParent()).post(new Runnable() {
157+
@Override
158+
public void run() {
159+
Rect bounds = new Rect();
160+
closeButton.getHitRect(bounds);
161+
bounds.top -= DimenUtils.dpToPixels(6);
162+
bounds.bottom += DimenUtils.dpToPixels(6);
163+
bounds.left -= DimenUtils.dpToPixels(6);
164+
bounds.right += DimenUtils.dpToPixels(6);
165+
TouchDelegate touchDelegate = new TouchDelegate(bounds, closeButton);
166+
if (View.class.isInstance(closeButton.getParent())) {
167+
((View) closeButton.getParent()).setTouchDelegate(touchDelegate);
168+
}
169+
}
170+
});
171+
closeButton.setOnClickListener(
172+
new OnClickListener() {
173+
@Override public void onClick(View v) {
174+
hide();
175+
}
176+
}
177+
);
178+
}
179+
180+
int vert = (int) (baselinePadding * 1.5);
181+
int hori = (int) (baselinePadding * 1.5);
182+
setPadding(hori, vert, hori, vert);
183+
}
184+
185+
public void hide() {
186+
hidden = true;
187+
startAnimation(fadeOutAnimation);
188+
}
189+
190+
public void show() {
191+
hidden = false;
192+
startAnimation(fadeInAnimation);
193+
}
194+
195+
public boolean isHidden() {
196+
return hidden;
197+
}
198+
199+
public void setOnDismissListener(OnDismissListener onDismissListener) {
200+
this.onDismissListener = onDismissListener;
201+
}
202+
203+
private int generateViewUniqueId() {
204+
for (;;) {
205+
final int result = nextGeneratedId.get();
206+
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
207+
int newValue = result + 1;
208+
if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
209+
if (nextGeneratedId.compareAndSet(result, newValue)) {
210+
return result;
211+
}
212+
}
213+
}
214+
215+
public interface OnDismissListener {
216+
void onDismiss();
217+
}
218+
}

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,19 @@ static Drawable bootstrapThumbnail(Context context,
177177
return drawable;
178178
}
179179

180+
static Drawable bootstrapAlert(Context context,
181+
BootstrapBrand bootstrapBrand) {
182+
183+
GradientDrawable disabledGd = new GradientDrawable();
184+
185+
int strokeWidth = context.getResources().getDimensionPixelSize(R.dimen.bootstrap_alert_stroke_width);
186+
187+
disabledGd.setColor(ColorUtils.increaseOpacity(context, bootstrapBrand.getColor(), 40));
188+
disabledGd.setCornerRadius(6);
189+
disabledGd.setStroke(strokeWidth, ColorUtils.increaseOpacity(context, bootstrapBrand.getColor(), 70));
190+
return disabledGd;
191+
}
192+
180193
/**
181194
* Generates a colorstatelist for a bootstrap button
182195
*
@@ -340,6 +353,27 @@ static StateListDrawable bootstrapDropDownArrow(Context context, int width, int
340353
return stateListDrawable;
341354
}
342355

356+
static StateListDrawable bootstrapAlertCloseIcon(Context context, int width, int height, int inset) {
357+
358+
StateListDrawable stateListDrawable = new StateListDrawable();
359+
360+
int defaultColor = ColorUtils.resolveColor(R.color.bootstrap_alert_cross_default, context);
361+
int activeColor = ColorUtils.resolveColor(R.color.bootstrap_gray, context);
362+
int disabledColor = ColorUtils.resolveColor(R.color.bootstrap_alert_cross_default, context);
363+
364+
if (Build.VERSION.SDK_INT >= 14) {
365+
stateListDrawable.addState(new int[]{android.R.attr.state_hovered}, createCloseCrossIcon(context, width, height, activeColor, inset));
366+
}
367+
368+
stateListDrawable.addState(new int[]{android.R.attr.state_activated}, createCloseCrossIcon(context, width, height, activeColor, inset));
369+
stateListDrawable.addState(new int[]{android.R.attr.state_focused}, createCloseCrossIcon(context, width, height, activeColor, inset));
370+
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, createCloseCrossIcon(context, width, height, activeColor, inset));
371+
stateListDrawable.addState(new int[]{android.R.attr.state_selected}, createCloseCrossIcon(context, width, height, activeColor, inset));
372+
stateListDrawable.addState(new int[]{-android.R.attr.state_enabled}, createCloseCrossIcon(context, width, height, disabledColor, inset));
373+
stateListDrawable.addState(new int[]{}, createCloseCrossIcon(context, width, height, defaultColor, inset));
374+
return stateListDrawable;
375+
}
376+
343377
/**
344378
* Creates arrow icon that depends on ExpandDirection
345379
*
@@ -379,6 +413,25 @@ private static Drawable createArrowIcon(Context context, int width, int height,
379413
return new BitmapDrawable(context.getResources(), canvasBitmap);
380414
}
381415

416+
private static Drawable createCloseCrossIcon(Context context, int width, int height, int color, int inset) {
417+
Bitmap canvasBitmap = Bitmap.createBitmap(width + inset * 2, height + inset * 2, Bitmap.Config.ARGB_8888);
418+
Canvas canvas = new Canvas(canvasBitmap);
419+
Paint paint = new Paint();
420+
paint.setStyle(Paint.Style.FILL_AND_STROKE);
421+
paint.setStrokeWidth(3);
422+
paint.setColor(color);
423+
paint.setAntiAlias(true);
424+
Path path = new Path();
425+
path.setFillType(Path.FillType.EVEN_ODD);
426+
path.moveTo(inset, inset);
427+
path.lineTo(width + inset, height + inset);
428+
path.moveTo(width + inset, inset);
429+
path.lineTo(inset, height + inset);
430+
path.close();
431+
canvas.drawPath(path, paint);
432+
return new BitmapDrawable(context.getResources(), canvasBitmap);
433+
}
434+
382435
static ColorStateList bootstrapDropDownViewText(Context context) {
383436

384437
int defaultColor = context.getResources().getColor(R.color.bootstrap_gray_dark);

AndroidBootstrap/src/main/java/com/beardedhen/androidbootstrap/api/attributes/BootstrapBrand.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,5 @@ public interface BootstrapBrand extends Serializable {
8585
*/
8686
@ColorInt int disabledTextColor(Context context);
8787

88+
@ColorInt int getColor();
8889
}

AndroidBootstrap/src/main/java/com/beardedhen/androidbootstrap/api/defaults/DefaultBootstrapBrand.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,9 @@ public static DefaultBootstrapBrand fromAttributeValue(int attrValue) {
9898
return resolveColor(textColor, context);
9999
}
100100

101+
@ColorInt public int getColor() {
102+
return color;
103+
}
104+
105+
101106
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
<attr name="checked" format="boolean"/>
5050
<attr name="dropdownResource" format="reference"/>
5151
<attr name="itemHeight" format="dimension"/>
52+
<attr name="strongText" format="string"/>
53+
<attr name="messageText" format="string"/>
54+
<attr name="dismissible" format="boolean"/>
5255

5356
<!-- View attributes -->
5457

@@ -124,5 +127,12 @@
124127
<attr name="bootstrapSize"/>
125128
</declare-styleable>
126129

130+
<declare-styleable name="BootstrapAlert">
131+
<attr name="bootstrapBrand"/>
132+
<attr name="strongText"/>
133+
<attr name="messageText"/>
134+
<attr name="dismissible"/>
135+
</declare-styleable>
136+
127137
</resources>
128138

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@
3030
<color name="bootstrap_well_background">#F5F5F5</color>
3131
<color name="bootstrap_dropdown_divider">#e7e7e7</color>
3232
<color name="bootstrap_well_border_color">#E7E7E7</color>
33+
<color name="bootstrap_alert_cross_default">#64818a91</color>
3334

3435
</resources>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
<dimen name="bootstrap_edit_text_corner_radius">6dp</dimen>
1515
<dimen name="bootstrap_edit_text_edge_width">1.5dp</dimen>
1616

17+
<!--Default alert size -->
18+
<dimen name="bootstrap_alert_paddings">8dp</dimen>
19+
<dimen name="bootstrap_alert_stroke_width">1dp</dimen>
20+
<dimen name="bootstrap_alert_default_font_size">14sp</dimen>
21+
1722
<!-- Thumbnails -->
1823
<dimen name="bthumbnail_default_border">8dp</dimen>
1924
<dimen name="bthumbnail_rounded_corner">8dp</dimen>

sample/src/main/AndroidManifest.xml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@
5454
/>
5555
<activity
5656
android:name=".BootstrapWellExample"
57-
android:label="BootstrapEditTextExample"
57+
android:label="BootstrapWell"
5858
/>
5959
<activity
6060
android:name=".BootstrapDropDownExample"
61-
android:label="BootstrapDropDownExample"
62-
/>
61+
android:label="BootstrapDropDown"
62+
/>
63+
<activity
64+
android:name=".BootstrapAlertExample"
65+
android:label="BootstrapAlert"
66+
/>
6367
</application>
6468

6569
</manifest>

0 commit comments

Comments
 (0)