iconArray = null;
53 | /**
54 | * 要显示的标题
55 | */
56 | public static String title = "LemonBubble";
57 | /**
58 | * 帧动画时间间隔,单位ms
59 | * 如果当前为自定义动画模式,那么该时间为自定义动画的单次变换时间
60 | */
61 | public static int frameAnimationTime = 500;
62 | /**
63 | * 图标占比 0 - 1,图标控件的边长占高度的比例
64 | */
65 | public static float proportionOfIcon = 0.675f;
66 | /**
67 | * 间距占比 0 - 1,图标控件和标题控件之间距离占整个控件的比例(如果横向布局那么就相当于宽度,纵向布局相当于高度)
68 | */
69 | public static float proportionOfSpace = 0.1f;
70 | /**
71 | * 内边距占比 0 - 1,整个泡泡控件的内边距,x最终为左右的内边距(左右内边距以宽度算最终的像素值)
72 | */
73 | public static float proportionOfPaddingX = 0.1f;
74 | /**
75 | * 内边距占比 0 - 1,整个泡泡控件的内边距,y最终为上下的内边距(上下边距以高度算最终的像素值)
76 | */
77 | public static float proportionOfPaddingY = 0.1f;
78 | /**
79 | * 位置样式
80 | */
81 | public static LemonBubbleLocationStyle locationStyle = LemonBubbleLocationStyle.CENTER;
82 | /**
83 | * 泡泡控件显示时偏移,当位置样式为上中的时候,偏移值是向下移动,当位置样式为底部时候,偏移值是向上移动
84 | */
85 | public static float proportionOfDeviation = 0f;
86 | /**
87 | * 是否展示蒙版,展示蒙版后,显示泡泡控件时会产生一个蒙版层来拦截所有其他控件的点击事件
88 | */
89 | public static boolean isShowMaskView = true;
90 | /**
91 | * 蒙版颜色
92 | */
93 | public static int maskColor = Color.argb(150, 0, 0, 0);
94 | /**
95 | * 泡泡控件的背景色
96 | */
97 | public static int backgroundColor = Color.argb((int) (255 * 0.8), 255, 255, 255);
98 | /**
99 | * 图标渲染色
100 | */
101 | public static int iconColor = Color.BLACK;
102 | /**
103 | * 标题文字颜色
104 | */
105 | public static int titleColor = Color.BLACK;
106 | /**
107 | * 标题字体大小
108 | */
109 | public static int titleFontSize = 11;
110 | /**
111 | * 蒙版被点击的回调
112 | */
113 | public static LemonBubbleMaskOnTouchContext onMaskTouchContext = null;
114 | /**
115 | * 是否显示状态栏
116 | */
117 | public static boolean showStatusBar = true;
118 | /**
119 | * 状态栏的颜色
120 | */
121 | public static int statusBarColor = Color.BLACK;
122 |
123 | /**
124 | * 是否使用Fragment显示状态检测功能
125 | * 如果开启,则如果在未被显示的Fragment中调用,弹框会自动被忽略
126 | *
127 | * 测试阶段哦,如果发现检测的不准确,麻烦告诉我一声,liuri@lemonsoft.net
128 | */
129 | public static boolean useFragmentDisplayCheck = true;
130 |
131 |
132 | }
133 |
134 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/LemonBubbleInfo.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble;
2 |
3 | import android.app.Fragment;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.view.View;
9 | import android.widget.RelativeLayout;
10 | import android.widget.TextView;
11 |
12 | import net.lemonsoft.lemonbubble.enums.LemonBubbleLayoutStyle;
13 | import net.lemonsoft.lemonbubble.enums.LemonBubbleLocationStyle;
14 | import net.lemonsoft.lemonbubble.interfaces.LemonBubbleMaskOnTouchContext;
15 | import net.lemonsoft.lemonbubble.interfaces.LemonBubblePaintContext;
16 | import net.lemonsoft.lemonbubble.interfaces.LemonBubbleProgressModePaintContext;
17 |
18 | import java.util.List;
19 |
20 | /**
21 | * 柠檬泡泡信息对象,再次对象中详细描述了泡泡控件显示的各种细节
22 | * Created by LiuRi on 2016/12/23.
23 | */
24 |
25 | public class LemonBubbleInfo {
26 |
27 | private LemonBubblePrivateSizeTool _PST = LemonBubblePrivateSizeTool.getPrivateSizeTool();
28 |
29 | private LemonBubblePrivateAnimationTool _PAT = LemonBubblePrivateAnimationTool.defaultPrivateAnimationTool();
30 |
31 | /**
32 | * 泡泡控件的宽
33 | */
34 | private int bubbleWidth = LemonBubbleGlobal.bubbleWidth;
35 | /**
36 | * 泡泡控件的高
37 | */
38 | private int bubbleHeight = LemonBubbleGlobal.bubbleHeight;
39 | /**
40 | * 泡泡控件的圆角半径
41 | */
42 | private int cornerRadius = LemonBubbleGlobal.cornerRadius;
43 | /**
44 | * 图文布局属性
45 | */
46 | private LemonBubbleLayoutStyle layoutStyle = LemonBubbleGlobal.layoutStyle;
47 | /**
48 | * 自定义图标动画
49 | */
50 | private LemonBubblePaintContext iconAnimation = LemonBubbleGlobal.iconAnimation;
51 | /**
52 | * 自定义图标动画是否重复播放
53 | */
54 | private boolean isIconAnimationRepeat = LemonBubbleGlobal.isIconAnimationRepeat;
55 | /**
56 | * 进度被改变的回调
57 | */
58 | private LemonBubbleProgressModePaintContext onProgressChanged = LemonBubbleGlobal.onProgressChanged;
59 | /**
60 | * 图标数组,如果该数组为空或者该对象为null,那么显示自定义动画,如果图标为一张,那么固定显示那个图标,大于一张的时候显示图片帧动画
61 | */
62 | private List iconArray = LemonBubbleGlobal.iconArray;
63 | /**
64 | * 要显示的标题
65 | */
66 | private String title = LemonBubbleGlobal.title;
67 | /**
68 | * 帧动画时间间隔,单位ms
69 | * 如果当前为自定义动画模式,那么该时间为自定义动画的单次变换时间
70 | */
71 | private int frameAnimationTime = LemonBubbleGlobal.frameAnimationTime;
72 | /**
73 | * 图标占比 0 - 1,图标控件的边长占高度的比例
74 | */
75 | private float proportionOfIcon = LemonBubbleGlobal.proportionOfIcon;
76 | /**
77 | * 间距占比 0 - 1,图标控件和标题控件之间距离占整个控件的比例(如果横向布局那么就相当于宽度,纵向布局相当于高度)
78 | */
79 | private float proportionOfSpace = LemonBubbleGlobal.proportionOfSpace;
80 | /**
81 | * 内边距占比 0 - 1,整个泡泡控件的内边距,x最终为左右的内边距(左右内边距以宽度算最终的像素值)
82 | */
83 | private float proportionOfPaddingX = LemonBubbleGlobal.proportionOfPaddingX;
84 | /**
85 | * 内边距占比 0 - 1,整个泡泡控件的内边距,y最终为上下的内边距(上下边距以高度算最终的像素值)
86 | */
87 | private float proportionOfPaddingY = LemonBubbleGlobal.proportionOfPaddingY;
88 | /**
89 | * 位置样式
90 | */
91 | private LemonBubbleLocationStyle locationStyle = LemonBubbleGlobal.locationStyle;
92 | /**
93 | * 泡泡控件显示时偏移,当位置样式为上中的时候,偏移值是向下移动,当位置样式为底部时候,偏移值是向上移动
94 | */
95 | private float proportionOfDeviation = LemonBubbleGlobal.proportionOfDeviation;
96 | /**
97 | * 是否展示蒙版,展示蒙版后,显示泡泡控件时会产生一个蒙版层来拦截所有其他控件的点击事件
98 | */
99 | private boolean isShowMaskView = LemonBubbleGlobal.isShowMaskView;
100 | /**
101 | * 蒙版颜色
102 | */
103 | private int maskColor = LemonBubbleGlobal.maskColor;
104 | /**
105 | * 泡泡控件的背景色
106 | */
107 | private int backgroundColor = LemonBubbleGlobal.backgroundColor;
108 | /**
109 | * 图标渲染色
110 | */
111 | private int iconColor = LemonBubbleGlobal.iconColor;
112 | /**
113 | * 标题文字颜色
114 | */
115 | private int titleColor = LemonBubbleGlobal.titleColor;
116 | /**
117 | * 标题字体大小
118 | */
119 | private int titleFontSize = LemonBubbleGlobal.titleFontSize;
120 | /**
121 | * 蒙版被点击的回调
122 | */
123 | private LemonBubbleMaskOnTouchContext onMaskTouchContext = LemonBubbleGlobal.onMaskTouchContext;
124 | /**
125 | * 是否显示状态栏
126 | */
127 | private boolean showStatusBar = LemonBubbleGlobal.showStatusBar;
128 | /**
129 | * 状态栏的颜色
130 | */
131 | private int statusBarColor = LemonBubbleGlobal.statusBarColor;
132 |
133 | public String getTitle() {
134 | return title;
135 | }
136 |
137 | public LemonBubbleInfo setTitle(String title) {
138 | this.title = title;
139 | return this;
140 | }
141 |
142 | public int getBubbleWidth() {
143 | return bubbleWidth;
144 | }
145 |
146 | public LemonBubbleInfo setBubbleWidth(int bubbleWidth) {
147 | this.bubbleWidth = bubbleWidth;
148 | return this;
149 | }
150 |
151 | public int getBubbleHeight() {
152 | return bubbleHeight;
153 | }
154 |
155 | public LemonBubbleInfo setBubbleHeight(int bubbleHeight) {
156 | this.bubbleHeight = bubbleHeight;
157 | return this;
158 | }
159 |
160 | public LemonBubbleInfo setBubbleSize(int width, int height) {
161 | setBubbleWidth(width);
162 | setBubbleHeight(height);
163 | return this;
164 | }
165 |
166 | public int getCornerRadius() {
167 | return cornerRadius;
168 | }
169 |
170 | public LemonBubbleInfo setCornerRadius(int cornerRadius) {
171 | this.cornerRadius = cornerRadius;
172 | return this;
173 | }
174 |
175 | public LemonBubbleLayoutStyle getLayoutStyle() {
176 | return layoutStyle;
177 | }
178 |
179 | public LemonBubbleInfo setLayoutStyle(LemonBubbleLayoutStyle layoutStyle) {
180 | this.layoutStyle = layoutStyle;
181 | return this;
182 | }
183 |
184 | public LemonBubblePaintContext getIconAnimation() {
185 | return iconAnimation;
186 | }
187 |
188 | public LemonBubbleInfo setIconAnimation(LemonBubblePaintContext iconAnimation) {
189 | this.iconAnimation = iconAnimation;
190 | return this;
191 | }
192 |
193 | public boolean isIconAnimationRepeat() {
194 | return isIconAnimationRepeat;
195 | }
196 |
197 | public LemonBubbleInfo setIconAnimationRepeat(boolean iconAnimationRepeat) {
198 | isIconAnimationRepeat = iconAnimationRepeat;
199 | return this;
200 | }
201 |
202 | public LemonBubbleProgressModePaintContext getOnProgressChanged() {
203 | return onProgressChanged;
204 | }
205 |
206 | public LemonBubbleInfo setOnProgressChanged(LemonBubbleProgressModePaintContext onProgressChanged) {
207 | this.onProgressChanged = onProgressChanged;
208 | return this;
209 | }
210 |
211 | public List getIconArray() {
212 | return iconArray;
213 | }
214 |
215 | public LemonBubbleInfo setIconArray(List iconArray) {
216 | this.iconArray = iconArray;
217 | return this;
218 | }
219 |
220 | public int getFrameAnimationTime() {
221 | return frameAnimationTime;
222 | }
223 |
224 | public LemonBubbleInfo setFrameAnimationTime(int frameAnimationTime) {
225 | this.frameAnimationTime = frameAnimationTime;
226 | return this;
227 | }
228 |
229 | public float getProportionOfIcon() {
230 | return proportionOfIcon;
231 | }
232 |
233 | public LemonBubbleInfo setProportionOfIcon(float proportionOfIcon) {
234 | this.proportionOfIcon = proportionOfIcon;
235 | return this;
236 | }
237 |
238 | public float getProportionOfSpace() {
239 | return proportionOfSpace;
240 | }
241 |
242 | public LemonBubbleInfo setProportionOfSpace(float proportionOfSpace) {
243 | this.proportionOfSpace = proportionOfSpace;
244 | return this;
245 | }
246 |
247 | public float getProportionOfPaddingX() {
248 | return proportionOfPaddingX;
249 | }
250 |
251 | public LemonBubbleInfo setProportionOfPaddingX(float proportionOfPaddingX) {
252 | this.proportionOfPaddingX = proportionOfPaddingX;
253 | return this;
254 | }
255 |
256 | public float getProportionOfPaddingY() {
257 | return proportionOfPaddingY;
258 | }
259 |
260 | public LemonBubbleInfo setProportionOfPaddingY(float proportionOfPaddingY) {
261 | this.proportionOfPaddingY = proportionOfPaddingY;
262 | return this;
263 | }
264 |
265 | public LemonBubbleLocationStyle getLocationStyle() {
266 | return locationStyle;
267 | }
268 |
269 | public LemonBubbleInfo setLocationStyle(LemonBubbleLocationStyle locationStyle) {
270 | this.locationStyle = locationStyle;
271 | return this;
272 | }
273 |
274 | public float getProportionOfDeviation() {
275 | return proportionOfDeviation;
276 | }
277 |
278 | public LemonBubbleInfo setProportionOfDeviation(float proportionOfDeviation) {
279 | this.proportionOfDeviation = proportionOfDeviation;
280 | return this;
281 | }
282 |
283 | public boolean isShowMaskView() {
284 | return isShowMaskView;
285 | }
286 |
287 | public LemonBubbleInfo setShowMaskView(boolean showMaskView) {
288 | isShowMaskView = showMaskView;
289 | return this;
290 | }
291 |
292 | public int getMaskColor() {
293 | return maskColor;
294 | }
295 |
296 | public LemonBubbleInfo setMaskColor(int maskColor) {
297 | this.maskColor = maskColor;
298 | return this;
299 | }
300 |
301 | public int getBackgroundColor() {
302 | return backgroundColor;
303 | }
304 |
305 | public LemonBubbleInfo setBackgroundColor(int backgroundColor) {
306 | this.backgroundColor = backgroundColor;
307 | return this;
308 | }
309 |
310 | public int getIconColor() {
311 | return iconColor;
312 | }
313 |
314 | public LemonBubbleInfo setIconColor(int iconColor) {
315 | this.iconColor = iconColor;
316 | return this;
317 | }
318 |
319 | public int getTitleColor() {
320 | return titleColor;
321 | }
322 |
323 | public LemonBubbleInfo setTitleColor(int titleColor) {
324 | this.titleColor = titleColor;
325 | return this;
326 | }
327 |
328 | public int getTitleFontSize() {
329 | return titleFontSize;
330 | }
331 |
332 | public LemonBubbleInfo setTitleFontSize(int titleFontSize) {
333 | this.titleFontSize = titleFontSize;
334 | return this;
335 | }
336 |
337 | public LemonBubbleMaskOnTouchContext getOnMaskTouchContext() {
338 | return onMaskTouchContext;
339 | }
340 |
341 | public LemonBubbleInfo setOnMaskTouchContext(LemonBubbleMaskOnTouchContext onMaskTouchContext) {
342 | this.onMaskTouchContext = onMaskTouchContext;
343 | return this;
344 | }
345 |
346 | public boolean isShowStatusBar() {
347 | return showStatusBar;
348 | }
349 |
350 | public LemonBubbleInfo setShowStatusBar(boolean showStatusBar) {
351 | this.showStatusBar = showStatusBar;
352 | return this;
353 | }
354 |
355 | public int getStatusBarColor() {
356 | return statusBarColor;
357 | }
358 |
359 | public LemonBubbleInfo setStatusBarColor(int statusBarColor) {
360 | this.statusBarColor = statusBarColor;
361 | return this;
362 | }
363 |
364 | private int _DP(int value) {
365 | return LemonBubblePrivateSizeTool.getPrivateSizeTool().dpToPx(value);
366 | }
367 |
368 | /**
369 | * 根据当前泡泡信息对象中的属性计算并设置泡泡控件中内容面板的位置和大小
370 | *
371 | * @param view 泡泡控件中的内容信息面板控件
372 | */
373 | void calBubbleViewContentPanelFrame(View view) {
374 | int y = 0;
375 | switch (locationStyle) {
376 | case CENTER:// 当控件设置属性为屏幕居中的时候
377 | y = (int) ((_PST.screenHeightDp() - bubbleHeight) / 2.0);// 计算屏幕居中
378 | break;
379 | case BOTTOM:// 当位置属性设置为在屏幕底部的时候
380 | y = _PST.screenHeightDp() - bubbleHeight;
381 | }
382 | y += (locationStyle != LemonBubbleLocationStyle.BOTTOM ? 1 : -1) *
383 | (proportionOfDeviation * _PST.screenHeightDp());// 根据当前的位置属性 是上面还是中间还是下面判断便宜方向并加上偏移的值
384 | // 应用位置
385 | _PAT.setLocation(view, (int) ((_PST.screenWidthDp() - bubbleWidth) / 2.0), y);
386 | // 应用尺寸
387 | _PAT.setSize(view, bubbleWidth, bubbleHeight);
388 | }
389 |
390 | int getLineHeight(TextView textView) {
391 | Paint.FontMetrics fontMetrics = textView.getPaint().getFontMetrics();
392 | return _PST.pxToDp((int) (fontMetrics.descent - fontMetrics.top)) + 2;
393 | }
394 |
395 | /**
396 | * 获取指定的textV的行高
397 | *
398 | * @param textView 要获取的textView的行高
399 | * @return textView的每行的高度
400 | */
401 | int getTitleHeight(TextView textView, int viewWidth) {
402 | int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(_PST.dpToPx(viewWidth), View.MeasureSpec.AT_MOST);
403 | int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
404 | textView.measure(widthMeasureSpec, heightMeasureSpec);
405 | return _PST.pxToDp(textView.getMeasuredHeight());
406 | }
407 |
408 | /**
409 | * 获取textView的文字宽
410 | *
411 | * @param textView 要查询文字宽的标签控件
412 | * @return 获取到的文字宽度数值
413 | */
414 | int getTitleWidth(TextView textView) {
415 | return Math.min(
416 | (int) (bubbleWidth * (1 - proportionOfPaddingX * 2 - proportionOfSpace) - bubbleHeight * proportionOfIcon),
417 | _PST.pxToDp((int) textView.getPaint().measureText(textView.getText().toString()))
418 | );
419 | }
420 |
421 | /**
422 | * 计算泡泡控件中的图标和标题控件的位置和大小,并进行应用赋值
423 | *
424 | * @param paintView 图标和动画显示内容控件
425 | * @param titleView 标题标签控件
426 | */
427 | void calPaintViewAndTitleViewFrame(LemonBubblePaintView paintView, TextView titleView) {
428 | int bubbleContentWidth = (int) (bubbleWidth * (1 - proportionOfPaddingX * 2));
429 | int bubbleContentHeight = (int) (bubbleHeight * (1 - proportionOfPaddingY * 2));
430 | int iconWidth = (int) (layoutStyle == LemonBubbleLayoutStyle.TITLE_ONLY ? 0 : bubbleContentHeight * proportionOfIcon);
431 | int baseX = (int) (bubbleWidth * proportionOfPaddingX);
432 | int baseY = (int) (bubbleHeight * proportionOfPaddingY);
433 | int titleWidth = (int) ((layoutStyle == LemonBubbleLayoutStyle.ICON_TOP_TITLE_BOTTOM ||
434 | layoutStyle == LemonBubbleLayoutStyle.ICON_BOTTOM_TITLE_TOP ||
435 | layoutStyle == LemonBubbleLayoutStyle.TITLE_ONLY) ?
436 | bubbleContentWidth :
437 | bubbleContentWidth * (1 - proportionOfSpace - iconWidth));
438 | int titleHeight = getLineHeight(titleView);
439 | int iconX, titleX, iconY, titleY;
440 | iconX = titleX = baseX;
441 | iconY = titleY = baseY;
442 | titleView.setText(title);// 应用设置的标题文字
443 | titleView.setTextSize(titleFontSize);// 应用预先设置的字体
444 |
445 | switch (layoutStyle) {
446 | case ICON_TOP_TITLE_BOTTOM: {// 图上文下
447 | titleView.setLayoutParams(new RelativeLayout.LayoutParams(_DP(bubbleContentWidth),
448 | RelativeLayout.LayoutParams.WRAP_CONTENT));
449 | titleView.postInvalidate();// 即时刷新
450 | titleHeight = getTitleHeight(titleView, titleWidth);
451 | int contentHeight = (int) (iconWidth + bubbleContentHeight * proportionOfSpace + titleHeight);
452 | iconX = baseX + (bubbleContentWidth - iconWidth) / 2;
453 | iconY = baseY + (bubbleContentHeight - contentHeight) / 2;
454 | titleY = (int) (iconY + iconWidth + bubbleContentHeight * proportionOfSpace);
455 | titleX = (int) ((baseX) + (bubbleContentWidth - titleWidth) / 2.0);
456 | break;
457 | }
458 | case ICON_BOTTOM_TITLE_TOP: {// 图下文上
459 | titleView.setLayoutParams(new RelativeLayout.LayoutParams(_DP(bubbleContentWidth),
460 | RelativeLayout.LayoutParams.WRAP_CONTENT));
461 | titleView.postInvalidate();// 即时刷新
462 | titleHeight = getTitleHeight(titleView, titleWidth);
463 | int contentHeight = (int) (iconWidth + bubbleContentHeight * proportionOfSpace + titleHeight);
464 | titleY = (int) (baseY + (bubbleContentHeight - contentHeight) / 2.0);
465 | titleX = (int) (baseX + (bubbleContentWidth - titleWidth) / 2.0);
466 | iconX = (int) (baseX + (bubbleContentWidth - iconWidth) / 2.0);
467 | iconY = (int) (titleY + titleHeight + bubbleContentHeight * proportionOfSpace);
468 | break;
469 | }
470 | case ICON_LEFT_TITLE_RIGHT: {// 图左文右
471 | titleWidth = getTitleWidth(titleView);
472 | titleView.postInvalidate();// 即时刷新
473 | int contentWidth = (int) (iconWidth + bubbleContentWidth * proportionOfSpace + getTitleWidth(titleView));
474 | iconX = (int) (baseX + (bubbleContentWidth - contentWidth) / 2.0);
475 | iconY = (int) (baseY + (bubbleContentHeight - iconWidth) / 2.0);
476 | titleX = (int) (iconX + iconWidth + bubbleContentWidth * proportionOfSpace);
477 | titleY = (int) (baseY + (bubbleContentHeight - titleHeight) / 2.0);
478 | break;
479 | }
480 | case ICON_RIGHT_TITLE_LEFT: {// 图右文左
481 | titleWidth = getTitleWidth(titleView);
482 | titleView.postInvalidate();// 即时刷新
483 | int contentWidth = (int) (iconWidth + bubbleContentWidth * proportionOfSpace + getTitleWidth(titleView));
484 | titleX = (int) (baseX + (bubbleContentWidth - contentWidth) / 2.0);
485 | titleY = (int) (baseY + (bubbleContentHeight - titleHeight) / 2.0);
486 | iconX = (int) (titleX + getTitleWidth(titleView) + bubbleContentWidth * proportionOfSpace);
487 | iconY = (int) (baseY + (bubbleContentHeight - iconWidth) / 2.0);
488 | break;
489 | }
490 | case ICON_ONLY: {
491 | titleX = titleY = titleWidth = titleHeight = 0;
492 | iconX = (int) (baseX + (bubbleContentWidth - iconWidth) / 2.0);
493 | iconY = (int) (baseY + (bubbleContentHeight - iconWidth) / 2.0);
494 | break;
495 | }
496 | case TITLE_ONLY: {
497 | titleView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
498 | RelativeLayout.LayoutParams.WRAP_CONTENT));
499 | titleView.postInvalidate();// 即时刷新
500 | titleHeight = getTitleHeight(titleView, titleWidth);
501 | iconX = iconY = iconWidth = 0;
502 | titleX = baseX;
503 | titleY = (int) (baseY + (bubbleContentHeight - getTitleHeight(titleView, titleWidth)) / 2.0);
504 | break;
505 | }
506 | }
507 | _PAT.setLocation(paintView, iconX, iconY);
508 | _PAT.setSize(paintView, iconWidth, iconWidth);
509 | _PAT.setLocation(titleView, titleX, titleY);
510 | _PAT.setSize(titleView, titleWidth, titleHeight);
511 | }
512 |
513 | /**
514 | * 展示这个泡泡控件,并且在指定的时间后关闭
515 | *
516 | * @param context 要显示在哪个Activity
517 | * @param autoCloseTime 自动关闭的时间
518 | */
519 | public void show(Context context, int autoCloseTime) {
520 | LemonBubble.showBubbleInfo(context, this, autoCloseTime);
521 | }
522 |
523 | /**
524 | * 展示这个泡泡控件,并且在指定的时间后关闭
525 | *
526 | * @param fragment 要判断是否处于显示状态的fragment
527 | * @param autoCloseTime 自动关闭的时间
528 | */
529 | public void show(Fragment fragment, int autoCloseTime) {
530 | LemonBubble.showBubbleInfo(fragment, this, autoCloseTime);
531 | }
532 |
533 | /**
534 | * 展示这个泡泡控件,并且在指定的时间后关闭
535 | *
536 | * @param fragment 要判断是否处于显示状态的fragment
537 | * @param autoCloseTime 自动关闭的时间
538 | */
539 | public void show(android.support.v4.app.Fragment fragment, int autoCloseTime) {
540 | LemonBubble.showBubbleInfo(fragment, this, autoCloseTime);
541 | }
542 |
543 | /**
544 | * 展示这个跑酷控件
545 | *
546 | * @param context 要显示在哪个Activity
547 | */
548 | public void show(Context context) {
549 | LemonBubble.showBubbleInfo(context, this);
550 | }
551 |
552 | /**
553 | * 展示这个跑酷控件
554 | *
555 | * @param fragment 要判断是否处于显示状态的fragment
556 | */
557 | public void show(Fragment fragment) {
558 | LemonBubble.showBubbleInfo(fragment, this);
559 | }
560 |
561 | /**
562 | * 展示这个跑酷控件
563 | *
564 | * @param fragment 要判断是否处于显示状态的fragment
565 | */
566 | public void show(android.support.v4.app.Fragment fragment) {
567 | LemonBubble.showBubbleInfo(fragment, this);
568 | }
569 |
570 | }
571 |
572 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/LemonBubblePaintView.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.widget.ImageView;
7 |
8 | /**
9 | * 泡泡中的动画与帧图片展示控件
10 | * Created by LiuRi on 2016/12/24.
11 | */
12 |
13 | public class LemonBubblePaintView extends ImageView {
14 |
15 | // 保存泡泡信息对象
16 | private LemonBubbleInfo _bubbleInfo;
17 | // 控制动画播放进度的数值动画器
18 | private ValueAnimator _playProgressValueAnimator;
19 | // 动画播放进度存储变量,0-1之间的浮点数
20 | private float _playProgressValue;
21 |
22 | public LemonBubblePaintView(final Context context) {
23 | super(context);
24 | if (_playProgressValueAnimator != null) // 如果动画执行器变量不是null
25 | _playProgressValueAnimator.end(); // 那么有可能上一次动画执行器的还没执行完,先停止上一次的
26 | else // 如果执行到这里,说明动画执行器对象还为null,
27 | _playProgressValueAnimator = ValueAnimator.ofFloat(0, 1);// 那么创建动画执行器
28 | _playProgressValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
29 | @Override
30 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
31 | _playProgressValue = (float) valueAnimator.getAnimatedValue();// 保存当前的动画进度值
32 | postInvalidate();// 刷新界面(重新调用onDraw方法重绘)
33 | }
34 | });
35 | }
36 |
37 | public void setBubbleInfo(LemonBubbleInfo bubbleInfo) {
38 | if (_playProgressValueAnimator != null)
39 | _playProgressValueAnimator.end();// 为了保险起见,先尝试停止上一次的动画执行器
40 | _bubbleInfo = bubbleInfo;// 保存泡泡信息对象
41 | if (bubbleInfo != null) {// 如果传进来的不是null,那么开始调用动画执行器,开始播放动画,因为防止在非自定义动画模式下显示自定义动画的最后一帧,所以在这里进行一次判断
42 |
43 | // 这里之所以没用Integer.MAX_VALUE,是因为在Android7.0中有时候有问题,如果谁能知道原因麻烦告诉我一下
44 | // ⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️⤵️
45 | _playProgressValueAnimator.setRepeatCount(_bubbleInfo.isIconAnimationRepeat() ? 99999999 : 0);// 根据泡泡信息对象中设置的是否重复来设置重复次数
46 | _playProgressValueAnimator.start();// 开始播放动画
47 | _playProgressValueAnimator.setDuration(bubbleInfo.getFrameAnimationTime());// 设置单次动画的总执行时间
48 | }
49 | }
50 |
51 | @Override
52 | protected void onDraw(Canvas canvas) {
53 | super.onDraw(canvas);
54 | if (_bubbleInfo != null &&
55 | _bubbleInfo.getIconAnimation() != null &&
56 | (_bubbleInfo.getIconArray() == null || _bubbleInfo.getIconArray().size() == 0))// 判断非空指针才进行操作
57 | _bubbleInfo.getIconAnimation().paint(canvas, _playProgressValue);// 调用泡泡信息对象中的预先设置的绘制函数开始绘制
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/LemonBubblePrivateAnimationTool.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.graphics.Color;
5 | import android.graphics.RectF;
6 | import android.graphics.drawable.ColorDrawable;
7 | import android.graphics.drawable.Drawable;
8 | import android.graphics.drawable.ShapeDrawable;
9 | import android.graphics.drawable.shapes.RoundRectShape;
10 | import android.os.Build;
11 | import android.view.View;
12 | import android.widget.RelativeLayout;
13 |
14 | /**
15 | * LemonBubble私有类,该类可以以动画的方式移动控件的位置、大小等外观属性
16 | * 开发者,请你不要在你的项目中尝试调用此类中的方法,你可以在LemonKit中找到更适合你的替代品
17 | * https://github.com/1em0nsOft/LemonKit4Android
18 | * Created by LiuRi on 2016/12/25.
19 | */
20 |
21 | class LemonBubblePrivateAnimationTool {
22 |
23 | // 私有动画工具类实例变量,单例方法准备
24 | private static LemonBubblePrivateAnimationTool _privateAnimationTool;
25 | // 私有尺寸工具类,该变量仅为了精简代码,代码整洁
26 | private static LemonBubblePrivateSizeTool _PST = LemonBubblePrivateSizeTool.getPrivateSizeTool();
27 |
28 | // 私有动画工具类的单例方法
29 | static synchronized LemonBubblePrivateAnimationTool defaultPrivateAnimationTool() {
30 | if (_privateAnimationTool == null)
31 | _privateAnimationTool = new LemonBubblePrivateAnimationTool();
32 | return _privateAnimationTool;
33 | }
34 |
35 | // 根据DP值返回px值,为了简洁易懂代码而写
36 | private int _DP(int value) {
37 | return LemonBubblePrivateSizeTool.getPrivateSizeTool().dpToPx(value);
38 | }
39 |
40 | /**
41 | * 对指定的控件设置尺寸大小
42 | *
43 | * @param view 要设置尺寸的控件
44 | * @param widthDp 宽度,单位dp
45 | * @param heightDp 高度,单位dp
46 | */
47 | void setSize(final View view, final int widthDp, final int heightDp) {
48 | // 获取整个动画的其实空间宽高
49 | final int startWidth = _PST.pxToDp(view.getLayoutParams() == null ? 0 : view.getLayoutParams().width);
50 | final int startHeight = _PST.pxToDp(view.getLayoutParams() == null ? 0 : view.getLayoutParams().height);
51 | // 计算起始宽高和目标宽高之间的差值
52 | final int subWidth = widthDp - startWidth;
53 | final int subHeight = heightDp - startHeight;
54 | // 创建动画进度处理器,从0-1之间设置动画的进度周期
55 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
56 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
57 | @Override
58 | public void onAnimationUpdate(ValueAnimator animation) {
59 | // 根据动画执行进度来计算当前进度下宽和高
60 | int currentWidth = (int) ((float) animation.getAnimatedValue() * subWidth + startWidth);
61 | int currentHeight = (int) ((float) animation.getAnimatedValue() * subHeight + startHeight);
62 | // 设置宽和高
63 | view.setLayoutParams(
64 | new RelativeLayout.LayoutParams(_DP(currentWidth), _DP(currentHeight)));
65 | // 刷新界面
66 | view.postInvalidate();
67 | }
68 | });
69 | // 启动动画执行器
70 | valueAnimator.start();
71 | }
72 |
73 | /**
74 | * 设置控件的位置
75 | *
76 | * @param view 要设置位置的控件对象
77 | * @param x 水平x坐标
78 | * @param y 垂直y坐标
79 | */
80 | void setLocation(final View view, int x, int y) {
81 | // 获取当前控件的初始XY坐标的DP值
82 | final int startX = _PST.pxToDp((int) (view.getX()));
83 | final int startY = _PST.pxToDp((int) (view.getY()));
84 | // 计算起始坐标和结束坐标之间的差值
85 | final int subX = x - startX;
86 | final int subY = y - startY;
87 | // 初始化动画过程处理器
88 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
89 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
90 | @Override
91 | public void onAnimationUpdate(ValueAnimator animation) {
92 | // 根据动画0-1之间的播放进度计算此时的控件XY坐标位置
93 | view.setX(_DP((int) ((float) animation.getAnimatedValue() * subX + startX)));
94 | view.setY(_DP((int) ((float) animation.getAnimatedValue() * subY + startY)));
95 | view.postInvalidate();
96 | }
97 | });
98 | // 开始执行动画
99 | valueAnimator.start();
100 | }
101 |
102 | /**
103 | * 渐变设置透明度
104 | *
105 | * @param view 设置透明度的控件
106 | * @param alpha 透明度的目标值
107 | */
108 | void setAlpha(final View view, float alpha) {
109 | // 初始化动画执行器,让值得起始点为当前控件的透明度,目标值为目标透明度
110 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(view.getAlpha(), alpha);
111 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
112 | @Override
113 | public void onAnimationUpdate(ValueAnimator animation) {
114 | // 动态取出当前动画执行器的动画过程值并设置给控件
115 | view.setAlpha((float) animation.getAnimatedValue());
116 | }
117 | });
118 | // 启动动画执行器
119 | valueAnimator.start();
120 | }
121 |
122 | /**
123 | * 动画改变背景颜色
124 | *
125 | * @param view 要改变背景颜色的控件
126 | * @param color 要改变成的目标背景颜色
127 | */
128 | void setBackgroundColor(final View view, final int cornerRadius, int color) {
129 | int startColor = Color.argb(0, 255, 255, 255);
130 | Drawable drawable = view.getBackground();
131 | if (drawable instanceof ColorDrawable)// 如果是ColorDrawable,那么通过这种方式取出原始颜色
132 | startColor = ((ColorDrawable) drawable).getColor();
133 | if (drawable instanceof ShapeDrawable)// 如果是ShapeDrawable,那么通过这种方式取出原始颜色
134 | startColor = ((ShapeDrawable) drawable).getPaint().getColor();
135 | // 先算出原颜色的ARGB值
136 | final int startA = (startColor & 0xff000000) >>> 24;
137 | final int startR = (startColor & 0x00ff0000) >> 16;
138 | final int startG = (startColor & 0x0000ff00) >> 8;
139 | final int startB = (startColor & 0x000000ff);
140 | // 算出目标颜色的ARGB值
141 | int aimA = (color & 0xff000000) >>> 24;
142 | int aimR = (color & 0x00ff0000) >> 16;
143 | int aimG = (color & 0x0000ff00) >> 8;
144 | int aimB = (color & 0x000000ff);
145 | // 算颜色ARGB的差值
146 | final int subA = aimA - startA;
147 | final int subR = aimR - startR;
148 | final int subG = aimG - startG;
149 | final int subB = aimB - startB;
150 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
151 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
152 | @Override
153 | public void onAnimationUpdate(ValueAnimator animation) {
154 | // 根据进度来计算当前动画进度下ARGB的四种值并应用修改颜色
155 | int color = Color.argb(
156 | (int) (startA + subA * (float) animation.getAnimatedValue()),
157 | (int) (startR + subR * (float) animation.getAnimatedValue()),
158 | (int) (startG + subG * (float) animation.getAnimatedValue()),
159 | (int) (startB + subB * (float) animation.getAnimatedValue()));
160 | if (cornerRadius == 0)// 如果当前不需要圆角,那么直接设置背景颜色即可
161 | view.setBackgroundColor(color);
162 | else// 如果当前空间设置了圆角,那么还得通过构造圆角背景的方法来创建drawable并设置
163 | setCornerRadius(view, cornerRadius, color);
164 | }
165 | });
166 | // 开始动画执行器
167 | valueAnimator.start();
168 | }
169 |
170 | // 设置空间的圆角
171 | void setCornerRadius(View view, int radius, int color) {
172 | radius = _DP(radius);
173 | int borderWidth = 0;// 加边框后会出现空心圆角矩形的效果,所以设置为0
174 | float[] outerRadius = new float[8];
175 | float[] innerRadius = new float[8];
176 | for (int i = 0; i < 8; i++) {
177 | outerRadius[i] = radius + borderWidth;
178 | innerRadius[i] = radius;
179 | }
180 | ShapeDrawable shapeDrawable = // 创建图形drawable
181 | new ShapeDrawable(
182 | // 创建圆角矩形
183 | new RoundRectShape(outerRadius,
184 | new RectF(borderWidth, borderWidth, borderWidth, borderWidth),
185 | innerRadius));
186 | shapeDrawable.getPaint().setColor(color);// 使用指定的颜色绘制,即背景颜色
187 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
188 | // 高版本SDK使用新的API
189 | view.setBackground(shapeDrawable);
190 | } else {
191 | view.setBackgroundDrawable(shapeDrawable);
192 | }
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/LemonBubblePrivateSizeTool.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.view.WindowManager;
6 |
7 | /**
8 | * LemonBubble内私有使用的尺寸工具类
9 | * 开发者,请你不要在你的项目中尝试调用此类中的方法,你可以在LemonKit中找到更适合你的替代品
10 | * https://github.com/1em0nsOft/LemonKit4Android
11 | * Created by LiuRi on 2016/12/23.
12 | */
13 |
14 | class LemonBubblePrivateSizeTool {
15 |
16 | private float _density;
17 | private DisplayMetrics _metrics;
18 |
19 | private static LemonBubblePrivateSizeTool _privateSizeTool;
20 |
21 | static synchronized LemonBubblePrivateSizeTool getPrivateSizeTool() {
22 | if (_privateSizeTool == null)
23 | _privateSizeTool = new LemonBubblePrivateSizeTool();
24 | return _privateSizeTool;
25 | }
26 |
27 | void setContext(Context context) {
28 | _density = context.getResources().getDisplayMetrics().density;
29 | _metrics = new DisplayMetrics();
30 | ((WindowManager) (context.getSystemService(Context.WINDOW_SERVICE))).getDefaultDisplay().getMetrics(_metrics);
31 | }
32 |
33 | /**
34 | * 换算dp到px
35 | *
36 | * @param dpValue dp的数值
37 | * @return 对应的px数值
38 | */
39 | int dpToPx(int dpValue) {
40 | return (int) (_density * dpValue + 0.5f);
41 | }
42 |
43 | /**
44 | * 换算px到dp
45 | *
46 | * @param pxValue px的数值
47 | * @return 对应的dp数值
48 | */
49 | int pxToDp(int pxValue) {
50 | return (int) (pxValue / _density + 0.5f);
51 | }
52 |
53 | /**
54 | * 获取屏幕的宽,单位dp
55 | *
56 | * @return 屏幕宽度dp值
57 | */
58 | int screenWidthDp() {
59 | return pxToDp(_metrics.widthPixels);
60 | }
61 |
62 | /**
63 | * 获取屏幕的高,单位dp
64 | *
65 | * @return 屏幕高度的dp值
66 | */
67 | int screenHeightDp() {
68 | return pxToDp(_metrics.heightPixels);
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/LemonBubbleView.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.app.Dialog;
5 | import android.app.Fragment;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.graphics.Color;
9 | import android.graphics.ImageFormat;
10 | import android.os.Build;
11 | import android.os.Handler;
12 | import android.view.Gravity;
13 | import android.view.KeyEvent;
14 | import android.view.View;
15 | import android.view.Window;
16 | import android.view.WindowManager;
17 | import android.view.animation.LinearInterpolator;
18 | import android.webkit.WebView;
19 | import android.widget.RelativeLayout;
20 | import android.widget.TextView;
21 |
22 | import net.lemonsoft.lemonbubble.interfaces.LemonBubbleLifeCycleDelegate;
23 |
24 | /**
25 | * 柠檬泡泡控件
26 | * Created by LiuRi on 2016/12/23.
27 | */
28 |
29 | public class LemonBubbleView {
30 |
31 | // 泡泡控件的实体控件容器
32 | private Dialog _container;
33 | // 整个dialog全屏的布局容器
34 | private RelativeLayout _rootLayout;
35 | // 对context对象进行存储的变量
36 | private Context _context;
37 | // 对LemonBubblePrivateSizeTool的名称缩短变量,此变量仅为了让名称变短,代码整洁
38 | private LemonBubblePrivateSizeTool _PST = LemonBubblePrivateSizeTool.getPrivateSizeTool();
39 | // 对LemonBubblePrivateAnimationTool的名称缩短变量,此变量仅为了让名称变短,代码整洁
40 | private LemonBubblePrivateAnimationTool _PAT = LemonBubblePrivateAnimationTool.defaultPrivateAnimationTool();
41 |
42 | // 当前正在显示的泡泡控件的信息对象
43 | private LemonBubbleInfo _currentBubbleInfo;
44 | // 背景灰色半透明蒙版
45 | private View _backMaskView;
46 | // 包含弹出框真正内容的小布局面板
47 | private RelativeLayout _contentPanel;
48 | // 动画和帧图片显示的控件
49 | private LemonBubblePaintView _paintView;
50 | // 标题显示标签控件
51 | private TextView _titleView;
52 | // 当前是否被显示状态
53 | private boolean _isShow;
54 | // 记录连环动画当前播放的帧索引的变量
55 | private int _frameAnimationPlayIndex;
56 | // 帧动画播放指针切换动画器
57 | private ValueAnimator _framePlayIndexAnimator;
58 | /**
59 | * 生命周期代理,可以通过生命周期代理来处理一些提示框显示或消失等节点的特殊事件
60 | */
61 | private LemonBubbleLifeCycleDelegate lifeCycleDelegate;
62 |
63 | // 是否已经初始化过了,避免重新创建控件
64 | private boolean haveInit = false;
65 |
66 | // 用于存储单例对象的变量
67 | private static LemonBubbleView _defaultBubbleViewObject;
68 |
69 | public boolean isShow() {
70 | return _isShow;
71 | }
72 |
73 | public synchronized void setIsShow(boolean isShow) {
74 | this._isShow = isShow;
75 | }
76 |
77 | /**
78 | * 获取单例泡泡控件对象
79 | *
80 | * @return 单例泡泡控件实例对象
81 | */
82 | public static synchronized LemonBubbleView defaultBubbleView(Context context) {
83 | if (_defaultBubbleViewObject == null)
84 | _defaultBubbleViewObject = new LemonBubbleView();
85 | return _defaultBubbleViewObject;
86 | }
87 |
88 | /**
89 | * 获取单例泡泡控件对象 - 调用此方法前提是通过setDefaultContext方法设置了默认的context对象
90 | *
91 | * @return 单例泡泡控件实例对象
92 | */
93 | public static synchronized LemonBubbleView defaultBubbleView() {
94 | if (_defaultBubbleViewObject == null) {
95 | _defaultBubbleViewObject = new LemonBubbleView();
96 | }
97 | return _defaultBubbleViewObject;
98 | }
99 |
100 | /**
101 | * 自动初始化
102 | *
103 | * @param context 上下文对象
104 | */
105 | private void autoInit(Context context) {
106 | _context = context;
107 | _PST.setContext(context);// 初始化尺寸工具类
108 | if (!haveInit) {
109 | initContainerAndRootLayout();// 初始化容器和根视图
110 | initCommonView();// 初始化公共的控件
111 | haveInit = true;
112 | }
113 | }
114 |
115 | /**
116 | * 初始化容器与根视图布局
117 | */
118 | private void initContainerAndRootLayout() {
119 | _container = new Dialog(// 判断是否有状态栏
120 | _context,
121 | _currentBubbleInfo.isShowStatusBar() ?
122 | android.R.style.Theme_NoTitleBar :
123 | android.R.style.Theme_NoTitleBar_Fullscreen
124 | ) {
125 | @Override
126 | public void dismiss() {
127 | super.dismiss();
128 | if (lifeCycleDelegate != null)
129 | lifeCycleDelegate.alreadyHide(LemonBubbleView.this, _currentBubbleInfo);
130 | }
131 | };// 创建对话框对象并设置无标题栏主题
132 | if (_currentBubbleInfo.isShowStatusBar()) {
133 | Window window = _container.getWindow();// 设置
134 | if (window != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
135 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
136 | window.setStatusBarColor(_currentBubbleInfo.getStatusBarColor());
137 | }
138 | }
139 | _rootLayout = new RelativeLayout(_context);// 实例化根布局对象
140 | Window window = _container.getWindow();
141 | if (window == null) {// 检测是否成功获取window对象
142 | // 如果为null那么不再继续进行,防止空指针异常
143 | new Exception("Get lemon bubble dialog's window error!").printStackTrace();
144 | return;
145 | }
146 | window.getDecorView().setPadding(0, 0, 0, 0);// 去掉系统默认的与屏幕边缘的内边距
147 | window.setBackgroundDrawableResource(android.R.color.transparent);// 设置背景透明
148 | window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);// 设置窗口全屏
149 | window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);// 防止状态栏更新导致界面卡顿
150 | _container.setContentView(_rootLayout);// 把根视图与对话框相关联
151 | _container.setCanceledOnTouchOutside(false);// 设置背景点击关闭为true
152 | _container.setOnKeyListener(new DialogInterface.OnKeyListener() {// 禁止返回按钮返回
153 | public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
154 | if ((keyCode == KeyEvent.KEYCODE_HOME || keyCode == KeyEvent.KEYCODE_BACK) && event.getRepeatCount() == 0) {
155 | return true;
156 | } else {
157 | return false;
158 | }
159 | }
160 | });
161 | }
162 |
163 | /**
164 | * 初始化公共的控件
165 | */
166 | private void initCommonView() {
167 | // 实例化灰色半透明蒙版控件
168 | _backMaskView = new View(_context);
169 | _backMaskView.setOnClickListener(new View.OnClickListener() {
170 | @Override
171 | public void onClick(View v) {
172 | if (_currentBubbleInfo.getOnMaskTouchContext() != null)
173 | _currentBubbleInfo.getOnMaskTouchContext().onTouch(_currentBubbleInfo, LemonBubbleView.this);
174 | }
175 | });
176 | // 设置全屏宽
177 | _backMaskView.setLayoutParams(new RelativeLayout.LayoutParams(_PST.dpToPx(_PST.screenWidthDp()), _PST.dpToPx(_PST.screenHeightDp())));
178 | _rootLayout.setAlpha(0);// 设置全透明,也就是默认不可见,后期通过动画改变来显示
179 |
180 | // 实例化内容面板控件
181 | _contentPanel = new RelativeLayout(_context);
182 | _contentPanel.setX(_PST.dpToPx((int) (_PST.screenWidthDp() / 2.0)));
183 | _contentPanel.setY(_PST.dpToPx((int) (_PST.screenHeightDp() / 2.0)));
184 |
185 | // 实例化绘图动画和帧图片显示的控件
186 | _paintView = new LemonBubblePaintView(_context);
187 |
188 | // 实例化标题显示标签控件
189 | _titleView = new TextView(_context);
190 | _titleView.setX(0);
191 | _titleView.setY(0);
192 | _titleView.setGravity(Gravity.CENTER);
193 |
194 | // 把所有控件添加到根视图上
195 | _rootLayout.addView(_backMaskView);// 半透明灰色背景
196 | _rootLayout.addView(_contentPanel);// 主内容面板
197 | _contentPanel.addView(_paintView);// 动画和帧图标显示控件放置到内容面板上
198 | _contentPanel.addView(_titleView);// 标题显示标签控件放置到内容面板上
199 |
200 | }
201 |
202 | /**
203 | * 根据泡泡信息对象初始化内容面板
204 | *
205 | * @param info 泡泡信息对象
206 | */
207 | private void initContentPanel(final LemonBubbleInfo info) {
208 | if (_framePlayIndexAnimator != null)
209 | _framePlayIndexAnimator.end();
210 | _paintView.setImageBitmap(null);
211 | _paintView.setBubbleInfo(null);
212 | if (info.getIconArray() == null || info.getIconArray().size() == 0) {
213 | // 显示自定义动画
214 | _paintView.setBubbleInfo(info);
215 | } else if (info.getIconArray().size() == 1) {
216 | // 显示单张图片
217 | _paintView.setImageBitmap(info.getIconArray().get(0));
218 | } else {
219 | // 逐帧连环动画
220 | _framePlayIndexAnimator = ValueAnimator.ofInt(0, info.getIconArray().size());// 设置逐帧动画播放器的帧数范围为0 到为设置的图片数组中的元素数量 - 1
221 | _framePlayIndexAnimator.setDuration(info.getIconArray().size() * info.getFrameAnimationTime());// 设置逐帧动画播放时间为动画帧数 * 每帧的时间间隔
222 | _framePlayIndexAnimator.setRepeatCount(Integer.MAX_VALUE);// 设置重复次数为最大整数,为了不手动停止就一直循环
223 | _framePlayIndexAnimator.setInterpolator(new LinearInterpolator());
224 | _framePlayIndexAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
225 | @Override
226 | public void onAnimationUpdate(ValueAnimator animation) {
227 | // 逐帧修改图片,启动连环动画的效果
228 | if (((int) animation.getAnimatedValue()) < info.getIconArray().size())
229 | _paintView.setImageBitmap(info.getIconArray().get((Integer) animation.getAnimatedValue()));
230 | }
231 | });
232 | // 启动动画执行器
233 | _framePlayIndexAnimator.start();
234 | }
235 | // 设置根视图的透明度为1,不透明
236 | _PAT.setAlpha(_rootLayout, 1);
237 | // 动画改变到内容面板的背景颜色到预设值
238 | _PAT.setBackgroundColor(_contentPanel, info.getCornerRadius(), info.getBackgroundColor());
239 | // 设置内容面板的透明度为1,不透明
240 | _PAT.setAlpha(_contentPanel, 1);
241 | _titleView.setTextColor(_currentBubbleInfo.getTitleColor());
242 | // 设置蒙版色
243 | _PAT.setBackgroundColor(_backMaskView, 0, info.getMaskColor());
244 | // 调用泡泡控件信息对象中的方法来计算面板和图标标题等控件的位置和大小,并动画移动
245 | info.calBubbleViewContentPanelFrame(_contentPanel);
246 | info.calPaintViewAndTitleViewFrame(_paintView, _titleView);
247 | }
248 |
249 | /**
250 | * 判断制定的fragment当前是否被显示中
251 | *
252 | * @param fragment 要判断是否被显示的fragment
253 | * @return 是否显示的布尔值
254 | */
255 | private boolean isFragmentShowing(android.support.v4.app.Fragment fragment) {
256 | if (!LemonBubbleGlobal.useFragmentDisplayCheck)// 没有开启Fragment显示检测
257 | return true;
258 | if (!fragment.getUserVisibleHint())// ViewPager嵌套时还没有触发显示
259 | return false;
260 | if (fragment.isHidden())// 当前fragment被隐藏了
261 | return false;
262 | if (fragment.getActivity() == null)
263 | return false;
264 | return true;
265 | }
266 |
267 | /**
268 | * 判断制定的fragment当前是否被显示中
269 | *
270 | * @param fragment 要判断是否被显示的fragment
271 | * @return 是否显示的布尔值
272 | */
273 | private boolean isFragmentShowing(Fragment fragment) {
274 | if (!LemonBubbleGlobal.useFragmentDisplayCheck)// 没有开启Fragment显示检测
275 | return true;
276 | if (!fragment.getUserVisibleHint())// ViewPager嵌套时还没有触发显示
277 | return false;
278 | if (fragment.isHidden())// 当前fragment被隐藏了
279 | return false;
280 | if (fragment.getActivity() == null)
281 | return false;
282 | return true;
283 | }
284 |
285 | /**
286 | * 检测指定的fragment是否处于显示状态,如果是的话那么展示泡泡控件
287 | *
288 | * @param fragment 要判断是否显示的fragment
289 | * @param bubbleInfo 泡泡信息对象
290 | */
291 | public void showBubbleInfo(Fragment fragment, LemonBubbleInfo bubbleInfo) {
292 | if (isFragmentShowing(fragment))
293 | showBubbleInfo(fragment.getActivity(), bubbleInfo);
294 | }
295 |
296 | /**
297 | * 检测指定的fragment是否处于显示状态,如果是的话那么展示泡泡控件
298 | *
299 | * @param fragment 要判断是否显示的fragment
300 | * @param bubbleInfo 泡泡信息对象
301 | */
302 | public void showBubbleInfo(android.support.v4.app.Fragment fragment, LemonBubbleInfo bubbleInfo) {
303 | if (isFragmentShowing(fragment))
304 | showBubbleInfo(fragment.getActivity(), bubbleInfo);
305 | }
306 |
307 | /**
308 | * 检测指定的fragment是否处于显示状态,如果是的话那么展示泡泡控件,并在指定的时间后关闭
309 | *
310 | * @param fragment 要判断是否显示的fragment
311 | * @param bubbleInfo 泡泡信息对象
312 | * @param autoCloseTime 自动关闭的时间
313 | */
314 | public void showBubbleInfo(Fragment fragment, LemonBubbleInfo bubbleInfo, int autoCloseTime) {
315 | if (isFragmentShowing(fragment))
316 | showBubbleInfo(fragment.getActivity(), bubbleInfo, autoCloseTime);
317 | }
318 |
319 | /**
320 | * 检测指定的fragment是否处于显示状态,如果是的话那么展示泡泡控件,并在指定的时间后关闭
321 | *
322 | * @param fragment 要判断是否显示的fragment
323 | * @param bubbleInfo 泡泡信息对象
324 | * @param autoCloseTime 自动关闭的时间
325 | */
326 | public void showBubbleInfo(android.support.v4.app.Fragment fragment, LemonBubbleInfo bubbleInfo, int autoCloseTime) {
327 | if (isFragmentShowing(fragment))
328 | showBubbleInfo(fragment.getActivity(), bubbleInfo, autoCloseTime);
329 | }
330 |
331 | /**
332 | * 展示泡泡控件
333 | *
334 | * @param context 上下文对象
335 | * @param bubbleInfo 泡泡信息描述对象
336 | */
337 | public void showBubbleInfo(Context context, LemonBubbleInfo bubbleInfo) {
338 | if (lifeCycleDelegate != null)
339 | lifeCycleDelegate.willShow(this, bubbleInfo);
340 | if (_context != null && !_context.equals(context))
341 | haveInit = false;
342 | _currentBubbleInfo = bubbleInfo;// 现将泡泡信息对象保存起来
343 | autoInit(context);
344 | if (!isShow()) {// 如果已经显示,就不进行再弹出新的层
345 | _container.show();
346 | }
347 | initContentPanel(bubbleInfo);// 根据泡泡信息对象对正文内容面板进行初始化
348 | new Handler().postDelayed(new Runnable() {
349 | @Override
350 | public void run() {
351 | if (lifeCycleDelegate != null)
352 | lifeCycleDelegate.alreadyShow(LemonBubbleView.this, _currentBubbleInfo);
353 | }
354 | }, 300);// 300是动画播放duration的默认时间
355 | }
356 |
357 | /**
358 | * 展示泡泡控件并在指定的时间后关闭
359 | *
360 | * @param context 上下文对象
361 | * @param bubbleInfo 泡泡信息描述对象
362 | * @param autoCloseTime 自动关闭的时间
363 | */
364 | public void showBubbleInfo(final Context context, final LemonBubbleInfo bubbleInfo, int autoCloseTime) {
365 | showBubbleInfo(context, bubbleInfo);
366 | new Handler().postDelayed(new Runnable() {
367 | @Override
368 | public void run() {
369 | if (_currentBubbleInfo.hashCode() == bubbleInfo.hashCode())// 当前正在显示的泡泡信息对象没有改变
370 | hide();
371 | }
372 | }, autoCloseTime);// 延迟关闭
373 | }
374 |
375 | /**
376 | * 隐藏当前正在显示的泡泡控件
377 | */
378 | public void hide() {
379 | if (lifeCycleDelegate != null)
380 | lifeCycleDelegate.willHide(this, _currentBubbleInfo);
381 | _PAT.setAlpha(_rootLayout, 0);// 动画设置根视图不透明
382 | _PAT.setAlpha(_contentPanel, 0);// 动画设置内容面板不透明
383 | _PAT.setSize(_contentPanel, 0, 0);// 动画设置面板的大小为0,0
384 | _PAT.setSize(_paintView, 0, 0);// 动画设置图标动画控件的大小为0,0
385 | _PAT.setSize(_titleView, 0, 0);// 动画设置标题控件的大小为0,0
386 | _PAT.setLocation(_paintView, 0, 0);// 动画设置图标动画控件的坐标为0,0,可以让动画看起来更像是整体缩小
387 | _PAT.setLocation(_titleView, 0, 0);// 动画设置标题控件的坐标为0,0,可以让动画看起来更像是整体缩小
388 | // 把内容面板缩小至屏幕中间
389 | _PAT.setLocation(_contentPanel, _PST.screenWidthDp() / 2, _PST.screenHeightDp() / 2);
390 | setIsShow(false);// 设置当前的状态为不显示状态
391 | new Handler().postDelayed(new Runnable() {
392 | @Override
393 | public void run() {
394 | _container.dismiss();
395 | haveInit = false;// 让其每次彻底关闭后在开启都重新创建对象,防止部分手机按返回键后再次弹出时候闪退
396 | // 如果哪位大神有更好的办法请联系我 liuri@lemonsoft.net
397 | }
398 | }, 300);// 待所有动画处理完毕后关闭根Dialog
399 | }
400 |
401 | /**
402 | * 强制关闭当前正在显示的泡泡控件
403 | */
404 | public void forceHide() {
405 | try {
406 | _container.dismiss();
407 | } catch (NullPointerException e) {
408 | System.err.println("未创建LemonBubble时调用了forceHide(),异常已经捕捉。");
409 | }
410 | this.haveInit = false;
411 | }
412 |
413 | public LemonBubbleLifeCycleDelegate getLifeCycleDelegate() {
414 | return lifeCycleDelegate;
415 | }
416 |
417 | public void setLifeCycleDelegate(LemonBubbleLifeCycleDelegate lifeCycleDelegate) {
418 | this.lifeCycleDelegate = lifeCycleDelegate;
419 | }
420 | }
421 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/enums/LemonBubbleLayoutStyle.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble.enums;
2 |
3 | /**
4 | * 泡泡控件中的布局样式枚举
5 | * Created by LiuRi on 2016/12/24.
6 | */
7 |
8 | public enum LemonBubbleLayoutStyle {
9 |
10 | /**
11 | * 图标在上,标题文字在下
12 | */
13 | ICON_TOP_TITLE_BOTTOM(0),
14 | /**
15 | * 图标在下,标题文字在上
16 | */
17 | ICON_BOTTOM_TITLE_TOP(3),
18 | /**
19 | * 图标在左,标题文字在右
20 | */
21 | ICON_LEFT_TITLE_RIGHT(1),
22 | /**
23 | * 图标在右,标题文字在左
24 | */
25 | ICON_RIGHT_TITLE_LEFT(4),
26 | /**
27 | * 只显示图标
28 | */
29 | ICON_ONLY(2),
30 | /**
31 | * 只显示标题文字
32 | */
33 | TITLE_ONLY(5);
34 |
35 | private int value;
36 |
37 | public int getValue() {
38 | return value;
39 | }
40 |
41 | LemonBubbleLayoutStyle(int value) {
42 | this.value = value;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/enums/LemonBubbleLocationStyle.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble.enums;
2 |
3 | /**
4 | * 泡泡控件的位置的枚举
5 | * Created by LiuRi on 2016/12/24.
6 | */
7 |
8 | public enum LemonBubbleLocationStyle {
9 |
10 | /**
11 | * 位于屏幕的顶部
12 | */
13 | TOP(0),
14 | /**
15 | * 位于屏幕的中间
16 | */
17 | CENTER(0),
18 | /**
19 | * 位于屏幕的底部
20 | */
21 | BOTTOM(1);
22 |
23 | private int value;
24 |
25 | public int getValue() {
26 | return value;
27 | }
28 |
29 | LemonBubbleLocationStyle(int value) {
30 | this.value = value;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/interfaces/LemonBubbleLifeCycleDelegate.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble.interfaces;
2 |
3 | import net.lemonsoft.lemonbubble.LemonBubbleInfo;
4 | import net.lemonsoft.lemonbubble.LemonBubbleView;
5 |
6 | /**
7 | * LemonBubble的生命周期支持
8 | * Created by LiuRi on 2017/2/21.
9 | */
10 |
11 | public interface LemonBubbleLifeCycleDelegate {
12 |
13 | /**
14 | * LemonBubble将要被显示
15 | */
16 | void willShow(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo);
17 |
18 | /**
19 | * LemonBubble已经被显示完毕
20 | */
21 | void alreadyShow(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo);
22 |
23 | /**
24 | * LemonBubble即将被关闭
25 | */
26 | void willHide(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo);
27 |
28 | /**
29 | * LemonBubble已经被关闭
30 | */
31 | void alreadyHide(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo);
32 |
33 | abstract class Adapter implements LemonBubbleLifeCycleDelegate {
34 | @Override
35 | public void willShow(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo) {
36 |
37 | }
38 |
39 | @Override
40 | public void alreadyShow(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo) {
41 |
42 | }
43 |
44 | @Override
45 | public void willHide(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo) {
46 |
47 | }
48 |
49 | @Override
50 | public void alreadyHide(LemonBubbleView bubbleView, LemonBubbleInfo bubbleInfo) {
51 |
52 | }
53 |
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/interfaces/LemonBubbleMaskOnTouchContext.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble.interfaces;
2 |
3 | import net.lemonsoft.lemonbubble.LemonBubbleInfo;
4 | import net.lemonsoft.lemonbubble.LemonBubbleView;
5 |
6 | /**
7 | * 柠檬泡泡控件的蒙版被触摸的回调上下文
8 | * Created by LiuRi on 2017/1/9.
9 | */
10 |
11 | public interface LemonBubbleMaskOnTouchContext {
12 |
13 | void onTouch(LemonBubbleInfo bubbleInfo, LemonBubbleView bubbleView);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/interfaces/LemonBubblePaintContext.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble.interfaces;
2 |
3 | import android.graphics.Canvas;
4 |
5 | /**
6 | * 正式绘制动画的接口
7 | */
8 | public interface LemonBubblePaintContext {
9 | /**
10 | * 绘制方法
11 | *
12 | * @param canvas 要绘制图形的画布
13 | * @param playProgress 当前动画播放的进度
14 | */
15 | void paint(Canvas canvas, float playProgress);
16 | }
17 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/java/net/lemonsoft/lemonbubble/interfaces/LemonBubbleProgressModePaintContext.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble.interfaces;
2 |
3 | import android.graphics.Canvas;
4 |
5 | /**
6 | * 当泡泡控件显示为进度条模式的时候来需要调用的接口,通过此接口中传入的进度值等信息进行绘制进度条样式
7 | * Created by LiuRi on 2016/12/24.
8 | */
9 |
10 | public interface LemonBubbleProgressModePaintContext {
11 | /**
12 | * 绘制方法
13 | *
14 | * @param canvas 要绘制图形的画布
15 | * @param playProgress 当前动画播放的进度
16 | * @param currentProgress 当前应该显示的进度条进度
17 | */
18 | void paint(Canvas canvas, float playProgress, float currentProgress);
19 | }
20 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LemonBubble
3 |
4 |
--------------------------------------------------------------------------------
/LemonBubble/lemonbubble/src/test/java/net/lemonsoft/lemonbubble/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package net.lemonsoft.lemonbubble;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/LemonBubble/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':lemonbubble-samples', ':lemonbubble'
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LemonBubble4Android
2 | > 作者:1em0nsOft - LiuRi
3 | >
4 | > 版本号:1.0.13
5 | >
6 | > 简介:这是一个完全Made in China的炫酷弹出指示层Android版本(-_-#意思就是还有iOS的),他能让你快速的自定义任何样式的弹出框。
7 | >
8 | > **意见建议反馈QQ群:370157608 (还寻思啥呢,赶紧加啊!)**
9 |
10 | > 最新更新记录:
11 | >
12 | > 修复了逐帧动画的卡顿bug
13 | >
14 | > 对旋转动画的锯齿问题进行了修复
15 |
16 | - 废话不多说,先看看图,来~
17 |
18 | 
19 |
20 | - 感觉怎么样呢?^_^ 光看图感觉到时还挺不错的,那怎么集成到项目中呢?来来,使用Gradle,首先在你的Project build.gradle文件中(allprojects ->repositories节点)加入如下代码:
21 |
22 | ```
23 | allprojects {
24 | repositories {
25 | jcenter()
26 | // 加入下面这行
27 | maven { url 'https://jitpack.io' }
28 | }
29 | }
30 | ```
31 |
32 | 然后在你的Module(xxx e.g:app) build.gradle中(dependencies节点)加入如下代码:
33 |
34 | ```
35 | dependencies {
36 | // ... 你的其他依赖
37 | // 然后加入下面这行
38 | compile 'com.github.1em0nsOft:LemonBubble4Android:1.0.13'
39 | }
40 | ```
41 |
42 | 最后重新build一下就可以啦。
43 |
44 | 接下来,我们验证一下我们是否集成成功,随便找一个Activity,在onCreate方法里面我们加上如下一行代码:
45 |
46 | ```
47 | LemonBubble.showRight(this, "集成成功!", 2000);
48 | ```
49 |
50 | 运行一下,可以看到如下界面,说明我们集成成功咯!
51 |
52 | 
53 |
54 | LemonBubble默认自带了三种泡泡样式,带有一个绿色的对号的成功泡泡,带有一个红色X错号的错误泡泡,带有蓝色无限旋转的等待控件,你可以使用如下三种方式调用他们:
55 |
56 | ```
57 | LemonBubble.showRight(this, "成功啦!", 2000);
58 | LemonBubble.showError(this, "出错啦", 2000);
59 | LemonBubble.showRoundProgress(this, "等待中...");
60 | ```
61 |
62 | 上面三个方法中,showRight和showError可以通过传入的第三个参数来控制泡泡显示的时间,单位ms。当你弹出了一个泡泡控件之后你也可以随时使用`LemonBubble.hide()`进行关闭当前正在显示的泡泡控件。
63 |
64 | 如果你想自定义样式的话,你只需要新建一个LemonBubbleInfo对象,然后对其进行修改属性即可,你也可以分别通过
65 |
66 | ```
67 | LemonBubble.getRightBubbleInfo()
68 | LemonBubble.getErrorBubbleInfo()
69 | LemonBubble.getRoundProgressBubbleInfo()
70 | ```
71 |
72 | 三个方法来获取我们预先为您写好的包含正确、错误、等待信息的LemonBubbleInfo对象,然后通过修改其属性的方式来快速自定义自己的泡泡控件,比如,我们现在通过如下代码自定义泡泡信息对象:
73 |
74 | ```
75 | // 获取默认的正确信息的泡泡信息对象
76 | LemonBubbleInfo myInfo = LemonBubble.getRightBubbleInfo();
77 | // 设置图标在左侧,标题在右侧
78 | myInfo.setLayoutStyle(LemonBubbleLayoutStyle.ICON_LEFT_TITLE_RIGHT);
79 | // 设置泡泡控件在底部
80 | myInfo.setLocationStyle(LemonBubbleLocationStyle.BOTTOM);
81 | // 设置泡泡控件的动画图标颜色为蓝色
82 | myInfo.setIconColor(Color.BLUE);
83 | // 设置泡泡控件的尺寸,单位dp
84 | myInfo.setBubbleSize(200, 80);
85 | // 设置泡泡控件的偏移比例为整个屏幕的0.01,
86 | myInfo.setProportionOfDeviation(0.01f);
87 | // 设置泡泡控件的标题
88 | myInfo.setTitle("自定义泡泡控件");
89 | // 展示自定义的泡泡控件,并显示2s后关闭
90 | LemonBubble.showBubbleInfo(this, myInfo, 2000);
91 | ```
92 |
93 | 一顿乱改,我们运行一下程序,发现泡泡控件已经按我们修改的样式显示出来啦:
94 |
95 | 
96 |
97 | 怎么样,是不是很简单?快来体验一下吧~
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Resource/LemonBubble.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LemonITCN/LemonBubble4Android/3fcf39961fba3ee263baba77e780b94708ba0e96/Resource/LemonBubble.gif
--------------------------------------------------------------------------------
/Resource/LemonBubble4Android.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LemonITCN/LemonBubble4Android/3fcf39961fba3ee263baba77e780b94708ba0e96/Resource/LemonBubble4Android.gif
--------------------------------------------------------------------------------
/Resource/example-run01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LemonITCN/LemonBubble4Android/3fcf39961fba3ee263baba77e780b94708ba0e96/Resource/example-run01.jpg
--------------------------------------------------------------------------------
/Resource/example-run02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LemonITCN/LemonBubble4Android/3fcf39961fba3ee263baba77e780b94708ba0e96/Resource/example-run02.jpg
--------------------------------------------------------------------------------
/Resource/lemonbubble.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LemonITCN/LemonBubble4Android/3fcf39961fba3ee263baba77e780b94708ba0e96/Resource/lemonbubble.mp4
--------------------------------------------------------------------------------