├── .gitignore ├── CHANGELOG.md ├── README-en.md ├── README.md ├── annotation ├── build.gradle └── src │ └── main │ └── java │ └── cn │ └── bingoogolapple │ └── badgeview │ └── annotation │ └── BGABadge.java ├── api ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── bingoogolapple │ │ └── badgeview │ │ ├── BGABadgeViewHelper.java │ │ ├── BGABadgeViewUtil.java │ │ ├── BGABadgeable.java │ │ ├── BGADragBadgeView.java │ │ ├── BGADragDismissDelegate.java │ │ └── BGAExplosionAnimator.java │ └── res │ └── values │ └── attrs.xml ├── build.gradle ├── compiler ├── build.gradle └── src │ └── main │ └── java │ └── cn │ └── bingoogolapple │ └── badgeview │ └── compiler │ └── BGABadgeProcessor.java ├── demo ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── bingoogolapple │ │ └── badgeview │ │ └── demo │ │ ├── activity │ │ └── MainActivity.java │ │ ├── adapter │ │ └── MessageAdapter.java │ │ ├── model │ │ └── MessageModel.java │ │ └── util │ │ ├── BGABadgeInit.java │ │ └── ToastUtil.java │ └── res │ ├── color │ └── selector_maintab.xml │ ├── drawable │ ├── layer_maintab_bg.xml │ ├── selector_item.xml │ ├── selector_tab_discover.xml │ ├── selector_tab_home.xml │ ├── selector_tab_me.xml │ ├── selector_tab_message.xml │ ├── selector_tab_plus.xml │ ├── selector_tab_plusbg.xml │ └── shape_toast.xml │ ├── layout │ ├── activity_main.xml │ ├── item_message.xml │ └── view_toast.xml │ ├── mipmap-hdpi │ ├── avatar_vip.png │ ├── avator.png │ ├── chat_white.png │ ├── ic_launcher.png │ ├── tab_discover_checked.png │ ├── tab_discover_normal.png │ ├── tab_home_checked.png │ ├── tab_home_normal.png │ ├── tab_me_checked.png │ ├── tab_me_normal.png │ ├── tab_message_checked.png │ ├── tab_message_normal.png │ ├── tab_plus_normal.png │ └── tab_plus_pressed.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── toast.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── styles.xml │ └── styles_common.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | config.json 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Eclipse project files 31 | .classpath 32 | .project 33 | .settings/ 34 | 35 | # Intellij project files 36 | *.iml 37 | *.ipr 38 | *.iws 39 | .idea/ 40 | 41 | # Mac system files 42 | .DS_Store 43 | 44 | *.keystore 45 | 46 | repo/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 1.1.9、1.2.0 *(2021-04-05)* 5 | ---------------------------- 6 | 7 | - 从 JCenter 迁移到 JitPack 8 | 9 | Version 1.1.8 *(2018-04-09)* 10 | ---------------------------- 11 | 12 | - fix #49 13 | - badge_dragable 变为 badge_draggable 14 | - BadgeViewHelper 的 setDragable 方法变为 setDraggable 15 | 16 | Version 1.1.7 *(2018-01-24)* 17 | ---------------------------- 18 | 19 | - 优化 BGABadge 注解参数为 Class 20 | ```Java 21 | /** 22 | * 作者:王浩 邮件:bingoogolapple@gmail.com 23 | * 创建时间:2018/1/14 24 | * 描述:初始化 BGABadgeView-Android 25 | * 1.在项目任意一个类上面添加 BGABadge 注解 26 | * 2.需要哪些类具有徽章功能,就把那些类的 class 作为 BGABadge 注解的参数 27 | * 3.再 AS 中执行 Build => Rebuild Project 28 | * 4.经过前面三个步骤后就可以通过「cn.bingoogolapple.badgeview.BGABadge原始类名」来使用徽章控件了 29 | */ 30 | @BGABadge({ 31 | View.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeView,不想用这个类的话就删了这一行 32 | ImageView.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeImageView,不想用这个类的话就删了这一行 33 | TextView.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingTextView,不想用这个类的话就删了这一行 34 | RadioButton.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeRadioButton,不想用这个类的话就删了这一行 35 | LinearLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeLinearLayout,不想用这个类的话就删了这一行 36 | FrameLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFrameLayout,不想用这个类的话就删了这一行 37 | RelativeLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeRelativeLayout,不想用这个类的话就删了这一行 38 | FloatingActionButton.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingActionButton,不想用这个类的话就删了这一行 39 | }) 40 | ``` 41 | 42 | Version 1.1.6 *(2018-01-19)* 43 | ---------------------------- 44 | 45 | - 优化 apt 生成的代码 46 | 47 | Version 1.1.5 *(2018-01-16)* 48 | ---------------------------- 49 | 50 | - 移除 Library 中的 BGABadgeXxxxView,改为 annotationProcessor 的形式 51 | 52 | [ ![Download](https://api.bintray.com/packages/bingoogolapple/maven/bga-badgeview-api/images/download.svg) ](https://bintray.com/bingoogolapple/maven/bga-badgeview-api/_latestVersion) bga-badgeview-api 后面的「latestVersion」指的是左边这个 Download 徽章后面的「数字」,请自行替换。 53 | 54 | ```groovy 55 | dependencies { 56 | implementation 'cn.bingoogolapple:bga-badgeview-api:latestVersion' 57 | annotationProcessor "cn.bingoogolapple:bga-badgeview-compiler:latestVersion" 58 | } 59 | ``` 60 | 61 | ```Java 62 | /** 63 | * 作者:王浩 邮件:bingoogolapple@gmail.com 64 | * 创建时间:2018/1/14 65 | * 描述:初始化 BGABadgeView-Android 66 | * 1.在项目任意一个类上面添加 BGABadge 注解 67 | * 2.需要哪些类具有徽章功能,就把那些类的全限定名作为 BGABadge 注解的参数 68 | * 3.再 AS 中执行 Build => Rebuild Project 69 | * 4.经过前面三个步骤后就可以通过「cn.bingoogolapple.badgeview.BGABadge原始类名」来使用徽章控件了 70 | */ 71 | @BGABadge({ 72 | "android.view.View", // 对应 cn.bingoogolapple.badgeview.BGABadgeView 73 | "android.widget.ImageView", // 对应 cn.bingoogolapple.badgeview.BGABadgeImageView 74 | "android.widget.TextView", // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingTextView 75 | "android.widget.RadioButton", // 对应 cn.bingoogolapple.badgeview.BGABadgeRadioButton 76 | "android.widget.LinearLayout", // 对应 cn.bingoogolapple.badgeview.BGABadgeLinearLayout 77 | "android.widget.FrameLayout", // 对应 cn.bingoogolapple.badgeview.BGABadgeFrameLayout 78 | "android.widget.RelativeLayout", // 对应 cn.bingoogolapple.badgeview.BGABadgeRelativeLayout 79 | "android.support.design.widget.FloatingActionButton", // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingActionButton 80 | }) 81 | public class BGABadgeInit { 82 | } 83 | ``` 84 | 85 | Version 1.1.4 *(2018-01-14)* 86 | ---------------------------- 87 | 88 | - 修改拖拽效果 Window 类型 TYPE_APPLICATION_PANEL 89 | 90 | Version 1.1.3 *(2016-07-22)* 91 | ---------------------------- 92 | 93 | - 取消对 nineoldandroids 的依赖,minSdkVersion 从 9 改到 14。如果你想兼容到 API 14 以下,请使用 v1.1.2 94 | 95 | Version 1.1.2 *(2016-06-03)* 96 | ---------------------------- 97 | 98 | - 增加「badge_dragExtra」触发开始拖拽徽章事件的扩展触摸距离 99 | 100 | Version 1.1.1 *(2016-03-29)* 101 | ---------------------------- 102 | 103 | - 增加徽章描边,badge_borderWidth 徽章描边宽度,badge_borderColor 徽章描边颜色 104 | - 修改 badge_is_resume_travel 为 badge_isResumeTravel 拖拽徽章超出轨迹范围后,再次放回到轨迹范围时,是否恢复轨迹 105 | 106 | Version 1.1.0 *(2016-03-21)* 107 | ---------------------------- 108 | 109 | - fix #14 110 | 111 | Version 1.0.9 *(2016-03-14)* 112 | ---------------------------- 113 | 114 | - 增加getBadgeViewHelper方法,通过Java代码方式配置自定义属性,以便后期支持React Native 115 | 116 | Version 1.0.8 *(2016-03-04)* 117 | ---------------------------- 118 | 119 | - 解决某些手机获取不到状态栏导致绘制高度偏移问题 fix #5 120 | - 增加自定义属性badge_is_resume_travel: 拖拽徽章超出轨迹范围后,再次放回到轨迹范围时,是否恢复轨迹 fix #5 121 | - 取消在NavigationBar区域的拖拽 122 | 123 | Version 1.0.7 *(2016-03-03)* 124 | ---------------------------- 125 | 126 | - 添加isShowBadge方法获取徽章的显示状态 fix #6 127 | 128 | Version 1.0.6 *(2015-12-06)* 129 | ---------------------------- 130 | 131 | - 添加拖拽黏性效果 132 | 133 | Version 1.0.5 *(2015-12-05)* 134 | ---------------------------- 135 | 136 | - 优化拖拽删除时的爆照效果 137 | 138 | Version 1.0.4 *(2015-12-05)* 139 | ---------------------------- 140 | 141 | - 添加拖拽删除时的爆照效果 142 | 143 | Version 1.0.3 *(2015-12-04)* 144 | ---------------------------- 145 | 146 | - 处理RadioButton不能监听点击事件 147 | - 移除BGABadgeCheckedTextView 148 | - 移除AppCompat里相关控件 149 | - 解决6.0系统拖拽拖拽徽章的权限问题 150 | 151 | Version 1.0.2 *(2015-7-16)* 152 | ---------------------------- 153 | 154 | - 解决圆形徽章显示全问题 155 | 156 | Version 1.0.1 *(2015-7-12)* 157 | ---------------------------- 158 | 159 | - 增加拖拽删除功能 160 | - 将showCriclePointBadge改为showCirclePointBadge 161 | 162 | Version 1.0.0 *(2015-7-7)* 163 | ---------------------------- 164 | 165 | Initial release. -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | :heartpulse:BGABadgeView-Android:heartpulse: 2 | ============ 3 | 4 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-BGABadgeView-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/2106) 5 | [![License](https://img.shields.io/badge/license-Apache%202-green.svg)](https://www.apache.org/licenses/LICENSE-2.0) 6 | [![Download](https://jitpack.io/v/bingoogolapple/BGABadgeView-Android.svg)](https://jitpack.io/#bingoogolapple/BGABadgeView-Android) 7 | 8 | ### [中文文档](https://github.com/bingoogolapple/BGABadgeView-Android/blob/master/README.md) 9 | 10 | ### [react-native-bga-badge-view](https://github.com/bingoogolapple/react-native-bga-badge-view) 11 | 12 | **The demo shows:** 13 | * The latest message number of weibo at the bottom tab 14 | * The VIP badge located in lower-right Corner of User’s Photo in the weibo user list 15 | * The new message in the weixin message page 16 | * The subscribe message in the weixin message page 17 | * User’s round corner photo made by RoundedBitmapDrawable from v4 package 18 | * Delete badge by drag and drop 19 | 20 | ### The realization of explosion effect refers to [ExplosionField](https://github.com/tyrantgit/ExplosionField) with the modify of only retaining one View,and only refresh the field around of badge 21 | 22 | ### Screenshots 23 | ![badgeview](https://cloud.githubusercontent.com/assets/8949716/17483429/8f5ab3aa-5db8-11e6-808c-6033f5d5c4ec.gif) 24 | 25 | ### Gradle Dependencies 26 | 27 | [![Download](https://jitpack.io/v/bingoogolapple/BGABadgeView-Android.svg)](https://jitpack.io/#bingoogolapple/BGABadgeView-Android) 28 | 29 | ```groovy 30 | dependencies { 31 | implementation 'com.github.bingoogolapple.BGABadgeView-Android:api:latestVersion' 32 | annotationProcessor 'com.github.bingoogolapple.BGABadgeView-Android:compiler:latestVersion' 33 | } 34 | ``` 35 | 36 | ### Initialize BGABadge 37 | 38 | Add BGABadge annotation to any class, such as newly created class BGABadgeInit 39 | 40 | ```Java 41 | @BGABadge({ 42 | View.class, // ===> cn.bingoogolapple.badgeview.BGABadgeView,If you do not need, please delete this line 43 | ImageView.class, // ===> cn.bingoogolapple.badgeview.BGABadgeImageView,If you do not need, please delete this line 44 | TextView.class, // ===> cn.bingoogolapple.badgeview.BGABadgeFloatingTextView,If you do not need, please delete this line 45 | RadioButton.class, // ===> cn.bingoogolapple.badgeview.BGABadgeRadioButton,If you do not need, please delete this line 46 | LinearLayout.class, // ===> cn.bingoogolapple.badgeview.BGABadgeLinearLayout,If you do not need, please delete this line 47 | RelativeLayout.class, // ===> cn.bingoogolapple.badgeview.BGABadgeRelativeLayout,If you do not need, please delete this line 48 | FloatingActionButton.class, // ===> cn.bingoogolapple.badgeview.BGABadgeFloatingActionButton,If you do not need, please delete this line 49 | ... 50 | ... 51 | ... 52 | }) 53 | public class BGABadgeInit { 54 | } 55 | ``` 56 | 57 | 58 | ### Supported badge style for now 59 | 60 | Class name | usage scenario 61 | :----------- | :----------- 62 | BGABadgeRadioButton | Bottom navigation of weibo home page 63 | BGABadgeImageView | User’s photo of weibo user list 64 | BGABadgeTextView | You can alternatively use BGABadgeCheckedTextView 65 | BGABadgeLinearLayout | Message number in the right of item in the list 66 | BGABadgeRelativeLayout | Message number in the right of item in the list 67 | BGABadgeFrameLayout | Message number in the right of item in the list 68 | 69 | ### Interface/api explain 70 | 71 | ```java 72 | /** 73 | * show circle badge 74 | */ 75 | void showCirclePointBadge(); 76 | 77 | /** 78 | * show text badge 79 | * 80 | * @param badgeText 81 | */ 82 | void showTextBadge(String badgeText); 83 | 84 | /** 85 | * hide badge 86 | */ 87 | void hiddenBadge(); 88 | 89 | /** 90 | * show image badge 91 | * 92 | * @param bitmap 93 | */ 94 | void showDrawableBadge(Bitmap bitmap); 95 | 96 | /** 97 | * set the delegate to delete the badge by drag and drop 98 | * 99 | * @param delegate 100 | */ 101 | void setDragDismissDelegage(BGADragDismissDelegate delegate); 102 | 103 | /** 104 | * Whether show the badge or not 105 | * 106 | * @return 107 | */ 108 | boolean isShowBadge(); 109 | 110 | /** 111 | * Can it be dragged 112 | * 113 | * @return 114 | */ 115 | boolean isDraggable(); 116 | 117 | /** 118 | * Whether being dragged 119 | * 120 | * @return 121 | */ 122 | boolean isDragging(); 123 | ``` 124 | 125 | ### The customed attribution 126 | 127 | The customed attribution | Explain | Default value 128 | :----------- | :----------- | :----------- 129 | badge_bgColor | Badge background | Color.RED 130 | badge_textColor | Text color of badge | Color.WHITE 131 | badge_textSize | Text font size of badge | 10sp 132 | badge_verticalMargin | The distance in pixels from the top edge of badge’s background to the top edge of the host or from the bottom edge of badge’s bottom to the host’s bottom | 4dp 133 | badge_horizontalMargin | The distance in pixels from the edge of badge’s background to the left or right edge of the host | 4dp 134 | badge_padding | Badge’s padding | 4dp 135 | badge_gravity | Badge’s gravity, it’s layout direction | For BGABadgeImageView and BGABadgeRadioButton, upper right.For others, the default is the right side of the vertical center 136 | badge_draggable | The badge can be drag or not | false 137 | badge_isResumeTravel | When the badge is dragged out of path, if it can resume the path in case of put back | false 138 | badge_borderWidth | Border width of badge | 0dp 139 | badge_borderColor | Border color of badge | Color.WHITE 140 | badge_dragExtra | Extra distance of trigger drag event | 4dp 141 | 142 | ### About me 143 | 144 | | Personal homepage | email | QQ for the BGA funs | 145 | | ------------- | ------------ | ------------ | 146 | | bingoogolapple.cn | bingoogolapple@gmail.com | ![BGA_CODE_CLUB](http://7xk9dj.com1.z0.glb.clouddn.com/BGA_CODE_CLUB.png?imageView2/2/w/200) | 147 | 148 | ## Donate 149 | 150 | It takes much time and energy to maintain and improve this project. It BGA helps you, you may want to buy me a coffee :). 151 | 152 |

153 | 154 |

155 | 156 | ## License 157 | 158 | Copyright 2015 bingoogolapple 159 | 160 | Licensed under the Apache License, Version 2.0 (the "License"); 161 | you may not use this file except in compliance with the License. 162 | You may obtain a copy of the License at 163 | 164 | http://www.apache.org/licenses/LICENSE-2.0 165 | 166 | Unless required by applicable law or agreed to in writing, software 167 | distributed under the License is distributed on an "AS IS" BASIS, 168 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 169 | See the License for the specific language governing permissions and 170 | limitations under the License. 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | :heartpulse:BGABadgeView-Android:heartpulse: 2 | ============ 3 | 4 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-BGABadgeView-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/2106) 5 | [![License](https://img.shields.io/badge/license-Apache%202-green.svg)](https://www.apache.org/licenses/LICENSE-2.0) 6 | [![Download](https://jitpack.io/v/bingoogolapple/BGABadgeView-Android.svg)](https://jitpack.io/#bingoogolapple/BGABadgeView-Android) 7 | 8 | ### [English Document](https://github.com/bingoogolapple/BGABadgeView-Android/blob/master/README-en.md) 9 | 10 | ### [react-native-bga-badge-view](https://github.com/bingoogolapple/react-native-bga-badge-view) 11 | 12 | **demo中演示了:** 13 | * 微博底部tab显示最新微博条数 14 | * 微博列表用户头像显示显示右下角vip徽章 15 | * 微信消息界面用户新消息 16 | * 微信消息界面订阅号新消息 17 | * 使用v4包中的RoundedBitmapDrawable制作圆角头像 18 | * 拖拽删除徽章 19 | 20 | ### 爆炸效果参考的 [ExplosionField](https://github.com/tyrantgit/ExplosionField) 改成了只有一个View的情况,只刷新徽章附近的区域 21 | 22 | ### 效果图 23 | ![badgeview](https://cloud.githubusercontent.com/assets/8949716/17483429/8f5ab3aa-5db8-11e6-808c-6033f5d5c4ec.gif) 24 | 25 | ### [点击下载 Apk](http://fir.im/BGABadgeViewDemo) 或扫描下方二维码安装体验 26 | ![Demo](http://7xk9dj.com1.z0.glb.clouddn.com/badge/BGABadgeViewDemoQRCode.png) 27 | 28 | ### 添加 Gradle 依赖 29 | 30 | * 把 `maven { url 'https://jitpack.io' }` 加入到 repositories 中 31 | * 添加如下依赖,末尾的「latestVersion」指的是徽章[![Download](https://jitpack.io/v/bingoogolapple/BGABadgeView-Android.svg)](https://jitpack.io/#bingoogolapple/BGABadgeView-Android)里的版本名称,请自行替换。 32 | 33 | ```groovy 34 | dependencies { 35 | implementation 'com.github.bingoogolapple.BGABadgeView-Android:api:latestVersion' 36 | annotationProcessor 'com.github.bingoogolapple.BGABadgeView-Android:compiler:latestVersion' 37 | } 38 | ``` 39 | 40 | ### 初始化徽章控件 41 | 42 | 1. 在项目任意一个类上面添加 BGABadge 注解,例如新建一个类 BGABadgeInit 专门用于初始化徽章控件 43 | 2. 需要哪些类具有徽章功能,就把那些类的 Class 作为 BGABadge 注解的参数「下面的代码块给出了例子,不需要的可以删掉对应的行」 44 | ```Java 45 | @BGABadge({ 46 | View.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeView,不想用这个类的话就删了这一行 47 | ImageView.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeImageView,不想用这个类的话就删了这一行 48 | TextView.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingTextView,不想用这个类的话就删了这一行 49 | RadioButton.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeRadioButton,不想用这个类的话就删了这一行 50 | LinearLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeLinearLayout,不想用这个类的话就删了这一行 51 | FrameLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFrameLayout,不想用这个类的话就删了这一行 52 | RelativeLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeRelativeLayout,不想用这个类的话就删了这一行 53 | FloatingActionButton.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingActionButton,不想用这个类的话就删了这一行 54 | ... 55 | ... 56 | ... 57 | }) 58 | public class BGABadgeInit { 59 | } 60 | ``` 61 | 3. 再 AS 中执行 Build => Rebuild Project 62 | 4. 经过前面三个步骤后就可以通过「cn.bingoogolapple.badgeview.BGABadge原始类名」来使用徽章控件了 63 | 64 | ### 接口说明 65 | 66 | ```java 67 | /** 68 | * 显示圆点徽章 69 | */ 70 | void showCirclePointBadge(); 71 | 72 | /** 73 | * 显示文字徽章 74 | * 75 | * @param badgeText 76 | */ 77 | void showTextBadge(String badgeText); 78 | 79 | /** 80 | * 隐藏徽章 81 | */ 82 | void hiddenBadge(); 83 | 84 | /** 85 | * 显示图像徽章 86 | * 87 | * @param bitmap 88 | */ 89 | void showDrawableBadge(Bitmap bitmap); 90 | 91 | /** 92 | * 设置拖动删除徽章的代理 93 | * 94 | * @param delegate 95 | */ 96 | void setDragDismissDelegage(BGADragDismissDelegate delegate); 97 | 98 | /** 99 | * 是否显示徽章 100 | * 101 | * @return 102 | */ 103 | boolean isShowBadge(); 104 | 105 | /** 106 | * 是否可拖动 107 | * 108 | * @return 109 | */ 110 | boolean isDraggable(); 111 | 112 | /** 113 | * 是否正在拖动 114 | * 115 | * @return 116 | */ 117 | boolean isDragging(); 118 | ``` 119 | 120 | ### 自定义属性说明 121 | 122 | 属性名 | 说明 | 默认值 123 | :----------- | :----------- | :----------- 124 | badge_bgColor | 徽章背景色 | Color.RED 125 | badge_textColor | 徽章文本的颜色 | Color.WHITE 126 | badge_textSize | 徽章文本字体大小 | 10sp 127 | badge_verticalMargin | 徽章背景与宿主控件上下边缘间距离 | 4dp 128 | badge_horizontalMargin | 徽章背景与宿主控件左右边缘间距离 | 4dp 129 | badge_padding | 徽章文本边缘与徽章背景边缘间的距离 | 4dp 130 | badge_gravity | 徽章在宿主控件中的位置 | BGABadgeImageView和BGABadgeRadioButton是右上方,其他控件是右边垂直居中 131 | badge_draggable | 是否开启拖拽删除徽章 | false 132 | badge_isResumeTravel | 拖拽徽章超出轨迹范围后,再次放回到轨迹范围时,是否恢复轨迹 | false 133 | badge_borderWidth | 徽章描边宽度 | 0dp 134 | badge_borderColor | 徽章描边颜色 | Color.WHITE 135 | badge_dragExtra | 触发开始拖拽徽章事件的扩展触摸距离 | 4dp 136 | 137 | ## 作者联系方式 138 | 139 | | 个人主页 | 邮箱 | 140 | | ------------- | ------------ | 141 | | bingoogolapple.cn | bingoogolapple@gmail.com | 142 | 143 | | 个人微信号 | 微信群 | 公众号 | 144 | | ------------ | ------------ | ------------ | 145 | | 个人微信号 | 微信群 | 公众号 | 146 | 147 | | 个人 QQ 号 | QQ 群 | 148 | | ------------ | ------------ | 149 | | 个人 QQ 号 | QQ 群 | 150 | 151 | ## 打赏支持作者 152 | 153 | 如果您觉得 BGA 系列开源库或工具软件帮您节省了大量的开发时间,可以扫描下方的二维码打赏支持。您的支持将鼓励我继续创作,打赏后还可以加我微信免费开通一年 [上帝小助手浏览器扩展/插件开发平台](https://github.com/bingoogolapple/bga-god-assistant-config) 的会员服务 154 | 155 | | 微信 | QQ | 支付宝 | 156 | | ------------- | ------------- | ------------- | 157 | | 微信 | QQ | 支付宝 | 158 | 159 | ## 作者项目推荐 160 | 161 | * 欢迎您使用我开发的第一个独立开发软件产品 [上帝小助手浏览器扩展/插件开发平台](https://github.com/bingoogolapple/bga-god-assistant-config) 162 | 163 | ## License 164 | 165 | Copyright 2015 bingoogolapple 166 | 167 | Licensed under the Apache License, Version 2.0 (the "License"); 168 | you may not use this file except in compliance with the License. 169 | You may obtain a copy of the License at 170 | 171 | http://www.apache.org/licenses/LICENSE-2.0 172 | 173 | Unless required by applicable law or agreed to in writing, software 174 | distributed under the License is distributed on an "AS IS" BASIS, 175 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 176 | See the License for the specific language governing permissions and 177 | limitations under the License. 178 | -------------------------------------------------------------------------------- /annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'maven' 3 | 4 | sourceCompatibility = JavaVersion.VERSION_1_7 5 | targetCompatibility = JavaVersion.VERSION_1_7 6 | 7 | dependencies { 8 | compileOnly 'com.google.android:android:4.1.1.4' 9 | } 10 | -------------------------------------------------------------------------------- /annotation/src/main/java/cn/bingoogolapple/badgeview/annotation/BGABadge.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview.annotation; 18 | 19 | import android.view.View; 20 | 21 | import java.lang.annotation.ElementType; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | /** 27 | * 作者:王浩 邮件:bingoogolapple@gmail.com 28 | * 创建时间:2018/1/14 29 | * 描述: 30 | */ 31 | @Retention(RetentionPolicy.CLASS) 32 | @Target(ElementType.TYPE) 33 | public @interface BGABadge { 34 | Class[] value() default {}; 35 | } 36 | -------------------------------------------------------------------------------- /api/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | android { 5 | compileSdkVersion ANDROID_BUILD_SDK_VERSION as int 6 | 7 | defaultConfig { 8 | minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION as int 9 | targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int 10 | } 11 | 12 | android { 13 | lintOptions { 14 | abortOnError false 15 | } 16 | } 17 | } 18 | 19 | dependencies { 20 | compileOnly 'com.android.support:appcompat-v7:27.0.2' 21 | api project(':annotation') 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/src/main/java/cn/bingoogolapple/badgeview/BGABadgeViewHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview; 18 | 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.graphics.Bitmap; 22 | import android.graphics.Canvas; 23 | import android.graphics.Color; 24 | import android.graphics.Paint; 25 | import android.graphics.Rect; 26 | import android.graphics.RectF; 27 | import android.text.TextUtils; 28 | import android.util.AttributeSet; 29 | import android.view.MotionEvent; 30 | import android.view.View; 31 | 32 | /** 33 | * 作者:王浩 邮件:bingoogolapple@gmail.com 34 | * 创建时间:15/7/6 下午10:13 35 | * 描述: 36 | */ 37 | public class BGABadgeViewHelper { 38 | private Bitmap mBitmap; 39 | private BGABadgeable mBadgeable; 40 | private Paint mBadgePaint; 41 | /** 42 | * 徽章背景色 43 | */ 44 | private int mBadgeBgColor; 45 | /** 46 | * 徽章文本的颜色 47 | */ 48 | private int mBadgeTextColor; 49 | /** 50 | * 徽章文本字体大小 51 | */ 52 | private int mBadgeTextSize; 53 | /** 54 | * 徽章背景与宿主控件上下边缘间距离 55 | */ 56 | private int mBadgeVerticalMargin; 57 | /** 58 | * 徽章背景与宿主控件左右边缘间距离 59 | */ 60 | private int mBadgeHorizontalMargin; 61 | /*** 62 | * 徽章文本边缘与徽章背景边缘间的距离 63 | */ 64 | private int mBadgePadding; 65 | /** 66 | * 徽章文本 67 | */ 68 | private String mBadgeText; 69 | /** 70 | * 徽章文本所占区域大小 71 | */ 72 | private Rect mBadgeNumberRect; 73 | /** 74 | * 是否显示Badge 75 | */ 76 | private boolean mIsShowBadge; 77 | /** 78 | * 徽章在宿主控件中的位置 79 | */ 80 | private BadgeGravity mBadgeGravity; 81 | /** 82 | * 整个徽章所占区域 83 | */ 84 | private RectF mBadgeRectF; 85 | /** 86 | * 是否可拖动 87 | */ 88 | private boolean mDraggable; 89 | /** 90 | * 拖拽徽章超出轨迹范围后,再次放回到轨迹范围时,是否恢复轨迹 91 | */ 92 | private boolean mIsResumeTravel; 93 | /*** 94 | * 徽章描边宽度 95 | */ 96 | private int mBadgeBorderWidth; 97 | /*** 98 | * 徽章描边颜色 99 | */ 100 | private int mBadgeBorderColor; 101 | /** 102 | * 触发开始拖拽徽章事件的扩展触摸距离 103 | */ 104 | private int mDragExtra; 105 | /** 106 | * 整个徽章加上其触发开始拖拽区域所占区域 107 | */ 108 | private RectF mBadgeDragExtraRectF; 109 | /** 110 | * 拖动时的徽章控件 111 | */ 112 | private BGADragBadgeView mDropBadgeView; 113 | /** 114 | * 是否正在拖动 115 | */ 116 | private boolean mIsDragging; 117 | /** 118 | * 拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的代理 119 | */ 120 | private BGADragDismissDelegate mDelegate; 121 | private boolean mIsShowDrawable = false; 122 | 123 | public BGABadgeViewHelper(BGABadgeable badgeable, Context context, AttributeSet attrs, BadgeGravity defaultBadgeGravity) { 124 | mBadgeable = badgeable; 125 | initDefaultAttrs(context, defaultBadgeGravity); 126 | initCustomAttrs(context, attrs); 127 | afterInitDefaultAndCustomAttrs(); 128 | mDropBadgeView = new BGADragBadgeView(context, this); 129 | } 130 | 131 | private void initDefaultAttrs(Context context, BadgeGravity defaultBadgeGravity) { 132 | mBadgeNumberRect = new Rect(); 133 | mBadgeRectF = new RectF(); 134 | mBadgeBgColor = Color.RED; 135 | mBadgeTextColor = Color.WHITE; 136 | mBadgeTextSize = BGABadgeViewUtil.sp2px(context, 10); 137 | 138 | mBadgePaint = new Paint(); 139 | mBadgePaint.setAntiAlias(true); 140 | mBadgePaint.setStyle(Paint.Style.FILL); 141 | // 设置mBadgeText居中,保证mBadgeText长度为1时,文本也能居中 142 | mBadgePaint.setTextAlign(Paint.Align.CENTER); 143 | 144 | mBadgePadding = BGABadgeViewUtil.dp2px(context, 4); 145 | mBadgeVerticalMargin = BGABadgeViewUtil.dp2px(context, 4); 146 | mBadgeHorizontalMargin = BGABadgeViewUtil.dp2px(context, 4); 147 | 148 | mBadgeGravity = defaultBadgeGravity; 149 | mIsShowBadge = false; 150 | 151 | mBadgeText = null; 152 | 153 | mBitmap = null; 154 | 155 | mIsDragging = false; 156 | 157 | mDraggable = false; 158 | 159 | mBadgeBorderColor = Color.WHITE; 160 | 161 | mDragExtra = BGABadgeViewUtil.dp2px(context, 4); 162 | mBadgeDragExtraRectF = new RectF(); 163 | } 164 | 165 | private void initCustomAttrs(Context context, AttributeSet attrs) { 166 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BGABadgeView); 167 | final int N = typedArray.getIndexCount(); 168 | for (int i = 0; i < N; i++) { 169 | initCustomAttr(typedArray.getIndex(i), typedArray); 170 | } 171 | typedArray.recycle(); 172 | } 173 | 174 | private void initCustomAttr(int attr, TypedArray typedArray) { 175 | if (attr == R.styleable.BGABadgeView_badge_bgColor) { 176 | mBadgeBgColor = typedArray.getColor(attr, mBadgeBgColor); 177 | } else if (attr == R.styleable.BGABadgeView_badge_textColor) { 178 | mBadgeTextColor = typedArray.getColor(attr, mBadgeTextColor); 179 | } else if (attr == R.styleable.BGABadgeView_badge_textSize) { 180 | mBadgeTextSize = typedArray.getDimensionPixelSize(attr, mBadgeTextSize); 181 | } else if (attr == R.styleable.BGABadgeView_badge_verticalMargin) { 182 | mBadgeVerticalMargin = typedArray.getDimensionPixelSize(attr, mBadgeVerticalMargin); 183 | } else if (attr == R.styleable.BGABadgeView_badge_horizontalMargin) { 184 | mBadgeHorizontalMargin = typedArray.getDimensionPixelSize(attr, mBadgeHorizontalMargin); 185 | } else if (attr == R.styleable.BGABadgeView_badge_padding) { 186 | mBadgePadding = typedArray.getDimensionPixelSize(attr, mBadgePadding); 187 | } else if (attr == R.styleable.BGABadgeView_badge_gravity) { 188 | int ordinal = typedArray.getInt(attr, mBadgeGravity.ordinal()); 189 | mBadgeGravity = BadgeGravity.values()[ordinal]; 190 | } else if (attr == R.styleable.BGABadgeView_badge_draggable) { 191 | mDraggable = typedArray.getBoolean(attr, mDraggable); 192 | } else if (attr == R.styleable.BGABadgeView_badge_isResumeTravel) { 193 | mIsResumeTravel = typedArray.getBoolean(attr, mIsResumeTravel); 194 | } else if (attr == R.styleable.BGABadgeView_badge_borderWidth) { 195 | mBadgeBorderWidth = typedArray.getDimensionPixelSize(attr, mBadgeBorderWidth); 196 | } else if (attr == R.styleable.BGABadgeView_badge_borderColor) { 197 | mBadgeBorderColor = typedArray.getColor(attr, mBadgeBorderColor); 198 | } else if (attr == R.styleable.BGABadgeView_badge_dragExtra) { 199 | mDragExtra = typedArray.getDimensionPixelSize(attr, mDragExtra); 200 | } 201 | } 202 | 203 | private void afterInitDefaultAndCustomAttrs() { 204 | mBadgePaint.setTextSize(mBadgeTextSize); 205 | } 206 | 207 | public void setBadgeBgColorInt(int badgeBgColor) { 208 | mBadgeBgColor = badgeBgColor; 209 | mBadgeable.postInvalidate(); 210 | } 211 | 212 | public void setBadgeTextColorInt(int badgeTextColor) { 213 | mBadgeTextColor = badgeTextColor; 214 | mBadgeable.postInvalidate(); 215 | } 216 | 217 | public void setBadgeTextSizeSp(int badgetextSize) { 218 | if (badgetextSize >= 0) { 219 | mBadgeTextSize = BGABadgeViewUtil.sp2px(mBadgeable.getContext(), badgetextSize); 220 | mBadgePaint.setTextSize(mBadgeTextSize); 221 | mBadgeable.postInvalidate(); 222 | } 223 | } 224 | 225 | public void setBadgeVerticalMarginDp(int badgeVerticalMargin) { 226 | if (badgeVerticalMargin >= 0) { 227 | mBadgeVerticalMargin = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgeVerticalMargin); 228 | mBadgeable.postInvalidate(); 229 | } 230 | } 231 | 232 | public void setBadgeHorizontalMarginDp(int badgeHorizontalMargin) { 233 | if (badgeHorizontalMargin >= 0) { 234 | mBadgeHorizontalMargin = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgeHorizontalMargin); 235 | mBadgeable.postInvalidate(); 236 | } 237 | } 238 | 239 | public void setBadgePaddingDp(int badgePadding) { 240 | if (badgePadding >= 0) { 241 | mBadgePadding = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgePadding); 242 | mBadgeable.postInvalidate(); 243 | } 244 | } 245 | 246 | public void setBadgeGravity(BadgeGravity badgeGravity) { 247 | if (badgeGravity != null) { 248 | mBadgeGravity = badgeGravity; 249 | mBadgeable.postInvalidate(); 250 | } 251 | } 252 | 253 | public void setDraggable(boolean draggable) { 254 | mDraggable = draggable; 255 | mBadgeable.postInvalidate(); 256 | } 257 | 258 | public void setIsResumeTravel(boolean isResumeTravel) { 259 | mIsResumeTravel = isResumeTravel; 260 | mBadgeable.postInvalidate(); 261 | } 262 | 263 | public void setBadgeBorderWidthDp(int badgeBorderWidthDp) { 264 | if (badgeBorderWidthDp >= 0) { 265 | mBadgeBorderWidth = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgeBorderWidthDp); 266 | mBadgeable.postInvalidate(); 267 | } 268 | } 269 | 270 | public void setBadgeBorderColorInt(int badgeBorderColor) { 271 | mBadgeBorderColor = badgeBorderColor; 272 | mBadgeable.postInvalidate(); 273 | } 274 | 275 | public boolean onTouchEvent(MotionEvent event) { 276 | switch (event.getAction()) { 277 | case MotionEvent.ACTION_DOWN: 278 | mBadgeDragExtraRectF.left = mBadgeRectF.left - mDragExtra; 279 | mBadgeDragExtraRectF.top = mBadgeRectF.top - mDragExtra; 280 | mBadgeDragExtraRectF.right = mBadgeRectF.right + mDragExtra; 281 | mBadgeDragExtraRectF.bottom = mBadgeRectF.bottom + mDragExtra; 282 | 283 | if ((mBadgeBorderWidth == 0 || mIsShowDrawable) && mDraggable && mIsShowBadge && mBadgeDragExtraRectF.contains(event.getX(), event.getY())) { 284 | mIsDragging = true; 285 | mBadgeable.getParent().requestDisallowInterceptTouchEvent(true); 286 | 287 | Rect badgeableRect = new Rect(); 288 | mBadgeable.getGlobalVisibleRect(badgeableRect); 289 | mDropBadgeView.setStickCenter(badgeableRect.left + mBadgeRectF.left + mBadgeRectF.width() / 2, badgeableRect.top + mBadgeRectF.top + mBadgeRectF.height() / 2); 290 | 291 | mDropBadgeView.onTouchEvent(event); 292 | mBadgeable.postInvalidate(); 293 | return true; 294 | } 295 | break; 296 | case MotionEvent.ACTION_MOVE: 297 | if (mIsDragging) { 298 | mDropBadgeView.onTouchEvent(event); 299 | return true; 300 | } 301 | break; 302 | case MotionEvent.ACTION_UP: 303 | case MotionEvent.ACTION_CANCEL: 304 | if (mIsDragging) { 305 | mDropBadgeView.onTouchEvent(event); 306 | mIsDragging = false; 307 | return true; 308 | } 309 | break; 310 | default: 311 | break; 312 | } 313 | return mBadgeable.callSuperOnTouchEvent(event); 314 | } 315 | 316 | public void endDragWithDismiss() { 317 | hiddenBadge(); 318 | if (mDelegate != null) { 319 | mDelegate.onDismiss(mBadgeable); 320 | } 321 | } 322 | 323 | public void endDragWithoutDismiss() { 324 | mBadgeable.postInvalidate(); 325 | } 326 | 327 | public void drawBadge(Canvas canvas) { 328 | if (mIsShowBadge && !mIsDragging) { 329 | if (mIsShowDrawable) { 330 | drawDrawableBadge(canvas); 331 | } else { 332 | drawTextBadge(canvas); 333 | } 334 | } 335 | } 336 | 337 | /** 338 | * 绘制图像徽章 339 | * 340 | * @param canvas 341 | */ 342 | private void drawDrawableBadge(Canvas canvas) { 343 | mBadgeRectF.left = mBadgeable.getWidth() - mBadgeHorizontalMargin - mBitmap.getWidth(); 344 | mBadgeRectF.top = mBadgeVerticalMargin; 345 | switch (mBadgeGravity) { 346 | case RightTop: 347 | mBadgeRectF.top = mBadgeVerticalMargin; 348 | break; 349 | case RightCenter: 350 | mBadgeRectF.top = (mBadgeable.getHeight() - mBitmap.getHeight()) / 2; 351 | break; 352 | case RightBottom: 353 | mBadgeRectF.top = mBadgeable.getHeight() - mBitmap.getHeight() - mBadgeVerticalMargin; 354 | break; 355 | default: 356 | break; 357 | } 358 | canvas.drawBitmap(mBitmap, mBadgeRectF.left, mBadgeRectF.top, mBadgePaint); 359 | mBadgeRectF.right = mBadgeRectF.left + mBitmap.getWidth(); 360 | mBadgeRectF.bottom = mBadgeRectF.top + mBitmap.getHeight(); 361 | } 362 | 363 | /** 364 | * 绘制文字徽章 365 | * 366 | * @param canvas 367 | */ 368 | private void drawTextBadge(Canvas canvas) { 369 | String badgeText = ""; 370 | if (!TextUtils.isEmpty(mBadgeText)) { 371 | badgeText = mBadgeText; 372 | } 373 | // 获取文本宽所占宽高 374 | mBadgePaint.getTextBounds(badgeText, 0, badgeText.length(), mBadgeNumberRect); 375 | // 计算徽章背景的宽高 376 | int badgeHeight = mBadgeNumberRect.height() + mBadgePadding * 2; 377 | int badgeWidth; 378 | // 当mBadgeText的长度为1或0时,计算出来的高度会比宽度大,此时设置宽度等于高度 379 | if (badgeText.length() == 1 || badgeText.length() == 0) { 380 | badgeWidth = badgeHeight; 381 | } else { 382 | badgeWidth = mBadgeNumberRect.width() + mBadgePadding * 2; 383 | } 384 | 385 | // 计算徽章背景上下的值 386 | mBadgeRectF.top = mBadgeVerticalMargin; 387 | mBadgeRectF.bottom = mBadgeable.getHeight() - mBadgeVerticalMargin; 388 | switch (mBadgeGravity) { 389 | case RightTop: 390 | mBadgeRectF.bottom = mBadgeRectF.top + badgeHeight; 391 | break; 392 | case RightCenter: 393 | mBadgeRectF.top = (mBadgeable.getHeight() - badgeHeight) / 2; 394 | mBadgeRectF.bottom = mBadgeRectF.top + badgeHeight; 395 | break; 396 | case RightBottom: 397 | mBadgeRectF.top = mBadgeRectF.bottom - badgeHeight; 398 | break; 399 | default: 400 | break; 401 | } 402 | 403 | // 计算徽章背景左右的值 404 | mBadgeRectF.right = mBadgeable.getWidth() - mBadgeHorizontalMargin; 405 | mBadgeRectF.left = mBadgeRectF.right - badgeWidth; 406 | 407 | if (mBadgeBorderWidth > 0) { 408 | // 设置徽章边框景色 409 | mBadgePaint.setColor(mBadgeBorderColor); 410 | // 绘制徽章边框背景 411 | canvas.drawRoundRect(mBadgeRectF, badgeHeight / 2, badgeHeight / 2, mBadgePaint); 412 | 413 | // 设置徽章背景色 414 | mBadgePaint.setColor(mBadgeBgColor); 415 | // 绘制徽章背景 416 | canvas.drawRoundRect(new RectF(mBadgeRectF.left + mBadgeBorderWidth, mBadgeRectF.top + mBadgeBorderWidth, mBadgeRectF.right - mBadgeBorderWidth, mBadgeRectF.bottom - mBadgeBorderWidth), (badgeHeight - 2 * mBadgeBorderWidth) / 2, (badgeHeight - 2 * mBadgeBorderWidth) / 2, mBadgePaint); 417 | } else { 418 | // 设置徽章背景色 419 | mBadgePaint.setColor(mBadgeBgColor); 420 | // 绘制徽章背景 421 | canvas.drawRoundRect(mBadgeRectF, badgeHeight / 2, badgeHeight / 2, mBadgePaint); 422 | } 423 | 424 | 425 | if (!TextUtils.isEmpty(mBadgeText)) { 426 | // 设置徽章文本颜色 427 | mBadgePaint.setColor(mBadgeTextColor); 428 | // initDefaultAttrs方法中设置了mBadgeText居中,此处的x为徽章背景的中心点y 429 | float x = mBadgeRectF.left + badgeWidth / 2; 430 | // 注意:绘制文本时的y是指文本底部,而不是文本的中间 431 | float y = mBadgeRectF.bottom - mBadgePadding; 432 | // 绘制徽章文本 433 | canvas.drawText(badgeText, x, y, mBadgePaint); 434 | } 435 | } 436 | 437 | public void showCirclePointBadge() { 438 | showTextBadge(null); 439 | } 440 | 441 | public void showTextBadge(String badgeText) { 442 | mIsShowDrawable = false; 443 | mBadgeText = badgeText; 444 | mIsShowBadge = true; 445 | mBadgeable.postInvalidate(); 446 | } 447 | 448 | public void hiddenBadge() { 449 | mIsShowBadge = false; 450 | mBadgeable.postInvalidate(); 451 | } 452 | 453 | public boolean isShowBadge() { 454 | return mIsShowBadge; 455 | } 456 | 457 | public void showDrawable(Bitmap bitmap) { 458 | mBitmap = bitmap; 459 | mIsShowDrawable = true; 460 | mIsShowBadge = true; 461 | mBadgeable.postInvalidate(); 462 | } 463 | 464 | public boolean isShowDrawable() { 465 | return mIsShowDrawable; 466 | } 467 | 468 | public RectF getBadgeRectF() { 469 | return mBadgeRectF; 470 | } 471 | 472 | public int getBadgePadding() { 473 | return mBadgePadding; 474 | } 475 | 476 | public String getBadgeText() { 477 | return mBadgeText; 478 | } 479 | 480 | public int getBadgeBgColor() { 481 | return mBadgeBgColor; 482 | } 483 | 484 | public int getBadgeTextColor() { 485 | return mBadgeTextColor; 486 | } 487 | 488 | public int getBadgeTextSize() { 489 | return mBadgeTextSize; 490 | } 491 | 492 | public Bitmap getBitmap() { 493 | return mBitmap; 494 | } 495 | 496 | public void setDragDismissDelegate(BGADragDismissDelegate delegate) { 497 | mDelegate = delegate; 498 | } 499 | 500 | public View getRootView() { 501 | return mBadgeable.getRootView(); 502 | } 503 | 504 | public boolean isResumeTravel() { 505 | return mIsResumeTravel; 506 | } 507 | 508 | public boolean isDragging() { 509 | return mIsDragging; 510 | } 511 | 512 | public boolean isDraggable() { 513 | return mDraggable; 514 | } 515 | 516 | public enum BadgeGravity { 517 | RightTop, 518 | RightCenter, 519 | RightBottom 520 | } 521 | } -------------------------------------------------------------------------------- /api/src/main/java/cn/bingoogolapple/badgeview/BGABadgeViewUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview; 18 | 19 | import android.content.Context; 20 | import android.graphics.Bitmap; 21 | import android.graphics.PointF; 22 | import android.graphics.Rect; 23 | import android.util.TypedValue; 24 | 25 | /** 26 | * 作者:王浩 邮件:bingoogolapple@gmail.com 27 | * 创建时间:15/12/5 上午12:27 28 | * 描述: 29 | */ 30 | public class BGABadgeViewUtil { 31 | 32 | private BGABadgeViewUtil() { 33 | } 34 | 35 | public static int dp2px(Context context, float dpValue) { 36 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics()); 37 | } 38 | 39 | public static int sp2px(Context context, float spValue) { 40 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics()); 41 | } 42 | 43 | public static Bitmap createBitmapSafely(BGADragBadgeView dragBadgeView, Rect rect, int retryCount) { 44 | try { 45 | dragBadgeView.setDrawingCacheEnabled(true); 46 | // 只裁剪徽章区域,不然会很卡 47 | return Bitmap.createBitmap(dragBadgeView.getDrawingCache(), rect.left < 0 ? 0 : rect.left, rect.top < 0 ? 0 : rect.top, rect.width(), rect.height()); 48 | } catch (OutOfMemoryError e) { 49 | if (retryCount > 0) { 50 | System.gc(); 51 | return createBitmapSafely(dragBadgeView, rect, retryCount - 1); 52 | } 53 | return null; 54 | } 55 | } 56 | 57 | public static float getDistanceBetween2Points(PointF p0, PointF p1) { 58 | float distance = (float) Math.sqrt(Math.pow(p0.y - p1.y, 2) + Math.pow(p0.x - p1.x, 2)); 59 | return distance; 60 | } 61 | 62 | public static PointF getMiddlePoint(PointF p1, PointF p2) { 63 | return new PointF((p1.x + p2.x) / 2.0f, (p1.y + p2.y) / 2.0f); 64 | } 65 | 66 | public static PointF getPointByPercent(PointF p1, PointF p2, float percent) { 67 | return new PointF(evaluate(percent, p1.x, p2.x), evaluate(percent, p1.y, p2.y)); 68 | } 69 | 70 | // 从FloatEvaluator中拷贝过来,这样就不用每次都new FloatEvaluator了 71 | public static Float evaluate(float fraction, Number startValue, Number endValue) { 72 | float startFloat = startValue.floatValue(); 73 | return startFloat + fraction * (endValue.floatValue() - startFloat); 74 | } 75 | 76 | public static PointF[] getIntersectionPoints(PointF pMiddle, float radius, Double lineK) { 77 | PointF[] points = new PointF[2]; 78 | 79 | float radian, xOffset = 0, yOffset = 0; 80 | if (lineK != null) { 81 | radian = (float) Math.atan(lineK); 82 | xOffset = (float) (Math.sin(radian) * radius); 83 | yOffset = (float) (Math.cos(radian) * radius); 84 | } else { 85 | xOffset = radius; 86 | yOffset = 0; 87 | } 88 | points[0] = new PointF(pMiddle.x + xOffset, pMiddle.y - yOffset); 89 | points[1] = new PointF(pMiddle.x - xOffset, pMiddle.y + yOffset); 90 | 91 | return points; 92 | } 93 | } -------------------------------------------------------------------------------- /api/src/main/java/cn/bingoogolapple/badgeview/BGABadgeable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview; 18 | 19 | import android.content.Context; 20 | import android.graphics.Bitmap; 21 | import android.graphics.Rect; 22 | import android.view.MotionEvent; 23 | import android.view.View; 24 | import android.view.ViewParent; 25 | 26 | /** 27 | * 作者:王浩 邮件:bingoogolapple@gmail.com 28 | * 创建时间:15/7/6 下午10:42 29 | * 描述: 30 | */ 31 | public interface BGABadgeable { 32 | /** 33 | * 显示圆点徽章 34 | */ 35 | void showCirclePointBadge(); 36 | 37 | /** 38 | * 显示文字徽章 39 | * 40 | * @param badgeText 41 | */ 42 | void showTextBadge(String badgeText); 43 | 44 | /** 45 | * 隐藏徽章 46 | */ 47 | void hiddenBadge(); 48 | 49 | /** 50 | * 显示图像徽章 51 | * 52 | * @param bitmap 53 | */ 54 | void showDrawableBadge(Bitmap bitmap); 55 | 56 | /** 57 | * 调用父类的onTouchEvent方法 58 | * 59 | * @param event 60 | * @return 61 | */ 62 | boolean callSuperOnTouchEvent(MotionEvent event); 63 | 64 | /** 65 | * 拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的代理 66 | * 67 | * @param delegate 68 | */ 69 | void setDragDismissDelegate(BGADragDismissDelegate delegate); 70 | 71 | /** 72 | * 是否显示徽章 73 | * 74 | * @return 75 | */ 76 | boolean isShowBadge(); 77 | 78 | /** 79 | * 是否可拖动 80 | * 81 | * @return 82 | */ 83 | boolean isDraggable(); 84 | 85 | /** 86 | * 是否正在拖动 87 | * 88 | * @return 89 | */ 90 | boolean isDragging(); 91 | 92 | BGABadgeViewHelper getBadgeViewHelper(); 93 | 94 | int getWidth(); 95 | 96 | int getHeight(); 97 | 98 | void postInvalidate(); 99 | 100 | ViewParent getParent(); 101 | 102 | int getId(); 103 | 104 | boolean getGlobalVisibleRect(Rect r); 105 | 106 | Context getContext(); 107 | 108 | View getRootView(); 109 | } -------------------------------------------------------------------------------- /api/src/main/java/cn/bingoogolapple/badgeview/BGADragBadgeView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview; 18 | 19 | import android.animation.Animator; 20 | import android.animation.AnimatorListenerAdapter; 21 | import android.animation.ValueAnimator; 22 | import android.content.Context; 23 | import android.graphics.Bitmap; 24 | import android.graphics.Canvas; 25 | import android.graphics.Color; 26 | import android.graphics.Paint; 27 | import android.graphics.Path; 28 | import android.graphics.PixelFormat; 29 | import android.graphics.PointF; 30 | import android.graphics.Rect; 31 | import android.graphics.RectF; 32 | import android.view.Gravity; 33 | import android.view.MotionEvent; 34 | import android.view.View; 35 | import android.view.WindowManager; 36 | import android.view.animation.OvershootInterpolator; 37 | 38 | import java.lang.ref.WeakReference; 39 | 40 | 41 | /** 42 | * 作者:王浩 邮件:bingoogolapple@gmail.com 43 | * 创建时间:15/7/12 下午3:23 44 | * 描述: 45 | */ 46 | class BGADragBadgeView extends View { 47 | private static final String TAG = BGADragBadgeView.class.getSimpleName(); 48 | private BGABadgeViewHelper mBadgeViewHelper; 49 | private Paint mBadgePaint; 50 | private WindowManager mWindowManager; 51 | private WindowManager.LayoutParams mLayoutParams; 52 | private int mStartX; 53 | private int mStartY; 54 | private BGAExplosionAnimator mExplosionAnimator; 55 | private SetExplosionAnimatorNullTask mSetExplosionAnimatorNullTask; 56 | 57 | /** 58 | * 针圆切线的切点 59 | */ 60 | private PointF[] mStickPoints = new PointF[]{ 61 | new PointF(0, 0), 62 | new PointF(0, 0) 63 | }; 64 | /** 65 | * 拖拽圆切线的切点 66 | */ 67 | private PointF[] mDragPoints = new PointF[]{ 68 | new PointF(0, 0), 69 | new PointF(0, 0) 70 | }; 71 | /** 72 | * 控制点 73 | */ 74 | private PointF mControlPoint = new PointF(0, 0); 75 | /** 76 | * 拖拽圆中心点 77 | */ 78 | private PointF mDragCenter = new PointF(0, 0); 79 | /** 80 | * 拖拽圆半径 81 | */ 82 | private float mDragRadius; 83 | 84 | /** 85 | * 针圆中心点 86 | */ 87 | private PointF mStickCenter; 88 | /** 89 | * 针圆半径 90 | */ 91 | private float mStickRadius; 92 | /** 93 | * 拖拽圆最大半径 94 | */ 95 | private int mMaxDragRadius; 96 | /** 97 | * 拖拽圆半径和针圆半径的差值 98 | */ 99 | private int mDragStickRadiusDifference; 100 | /** 101 | * 拖动mDismissThreshold距离后抬起手指徽章消失 102 | */ 103 | private int mDismissThreshold; 104 | 105 | private boolean mDismissAble; 106 | private boolean mIsDragDisappear; 107 | 108 | public BGADragBadgeView(Context context, BGABadgeViewHelper badgeViewHelper) { 109 | super(context); 110 | mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 111 | mBadgeViewHelper = badgeViewHelper; 112 | initBadgePaint(); 113 | initLayoutParams(); 114 | initStick(); 115 | 116 | mSetExplosionAnimatorNullTask = new SetExplosionAnimatorNullTask(this); 117 | } 118 | 119 | private void initBadgePaint() { 120 | mBadgePaint = new Paint(); 121 | mBadgePaint.setAntiAlias(true); 122 | mBadgePaint.setStyle(Paint.Style.FILL); 123 | // 设置mBadgeText居中,保证mBadgeText长度为1时,文本也能居中 124 | mBadgePaint.setTextAlign(Paint.Align.CENTER); 125 | mBadgePaint.setTextSize(mBadgeViewHelper.getBadgeTextSize()); 126 | } 127 | 128 | private void initLayoutParams() { 129 | mLayoutParams = new WindowManager.LayoutParams(); 130 | mLayoutParams.gravity = Gravity.LEFT + Gravity.TOP; 131 | mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 132 | mLayoutParams.format = PixelFormat.TRANSLUCENT; 133 | mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 134 | mLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; 135 | mLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; 136 | } 137 | 138 | private void initStick() { 139 | mMaxDragRadius = BGABadgeViewUtil.dp2px(getContext(), 10); 140 | mDragStickRadiusDifference = BGABadgeViewUtil.dp2px(getContext(), 1); 141 | } 142 | 143 | @Override 144 | protected void onDraw(Canvas canvas) { 145 | try { 146 | if (mExplosionAnimator == null) { 147 | if (mBadgeViewHelper.isShowDrawable()) { 148 | if (mBadgeViewHelper.getBadgeBgColor() == Color.RED) { 149 | mBadgePaint.setColor(mBadgeViewHelper.getBitmap().getPixel(mBadgeViewHelper.getBitmap().getWidth() / 2, 150 | mBadgeViewHelper.getBitmap().getHeight() / 2)); 151 | } else { 152 | mBadgePaint.setColor(mBadgeViewHelper.getBadgeBgColor()); 153 | } 154 | drawStick(canvas); 155 | drawDrawableBadge(canvas); 156 | } else { 157 | mBadgePaint.setColor(mBadgeViewHelper.getBadgeBgColor()); 158 | drawStick(canvas); 159 | drawTextBadge(canvas); 160 | } 161 | } else { 162 | mExplosionAnimator.draw(canvas); 163 | } 164 | } catch (Exception e) { 165 | // 确保自己能被移除 166 | removeSelfWithException(); 167 | } 168 | } 169 | 170 | private void drawDrawableBadge(Canvas canvas) { 171 | canvas.drawBitmap(mBadgeViewHelper.getBitmap(), mStartX, mStartY, mBadgePaint); 172 | } 173 | 174 | private void drawTextBadge(Canvas canvas) { 175 | // 设置徽章背景色 176 | mBadgePaint.setColor(mBadgeViewHelper.getBadgeBgColor()); 177 | // 绘制徽章背景 178 | canvas.drawRoundRect( 179 | new RectF(mStartX, mStartY, mStartX + mBadgeViewHelper.getBadgeRectF().width(), mStartY + mBadgeViewHelper.getBadgeRectF().height()), 180 | mBadgeViewHelper.getBadgeRectF().height() / 2, mBadgeViewHelper.getBadgeRectF().height() / 2, mBadgePaint); 181 | 182 | // 设置徽章文本颜色 183 | mBadgePaint.setColor(mBadgeViewHelper.getBadgeTextColor()); 184 | float x = mStartX + mBadgeViewHelper.getBadgeRectF().width() / 2; 185 | // 注意:绘制文本时的y是指文本底部,而不是文本的中间 186 | float y = mStartY + mBadgeViewHelper.getBadgeRectF().height() - mBadgeViewHelper.getBadgePadding(); 187 | // 绘制徽章文本 188 | String badgeText = mBadgeViewHelper.getBadgeText() == null ? "" : mBadgeViewHelper.getBadgeText(); 189 | canvas.drawText(badgeText, x, y, mBadgePaint); 190 | } 191 | 192 | private void drawStick(Canvas canvas) { 193 | float currentStickRadius = getCurrentStickRadius(); 194 | 195 | // 2. 获取直线与圆的交点 196 | float yOffset = mStickCenter.y - mDragCenter.y; 197 | float xOffset = mStickCenter.x - mDragCenter.x; 198 | Double lineK = null; 199 | if (xOffset != 0) { 200 | lineK = (double) (yOffset / xOffset); 201 | } 202 | // 通过几何图形工具获取交点坐标 203 | mDragPoints = BGABadgeViewUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK); 204 | mStickPoints = BGABadgeViewUtil.getIntersectionPoints(mStickCenter, currentStickRadius, lineK); 205 | 206 | // 3. 获取控制点坐标 207 | mControlPoint = BGABadgeViewUtil.getMiddlePoint(mDragCenter, mStickCenter); 208 | 209 | if (!mIsDragDisappear) { 210 | if (!mDismissAble) { 211 | 212 | // 3. 画连接部分 213 | Path path = new Path(); 214 | // 跳到点1 215 | path.moveTo(mStickPoints[0].x, mStickPoints[0].y); 216 | // 画曲线1 -> 2 217 | path.quadTo(mControlPoint.x, mControlPoint.y, mDragPoints[0].x, mDragPoints[0].y); 218 | // 画直线2 -> 3 219 | path.lineTo(mDragPoints[1].x, mDragPoints[1].y); 220 | // 画曲线3 -> 4 221 | path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints[1].x, mStickPoints[1].y); 222 | path.close(); 223 | canvas.drawPath(path, mBadgePaint); 224 | 225 | // 2. 画固定圆 226 | canvas.drawCircle(mStickCenter.x, mStickCenter.y, currentStickRadius, mBadgePaint); 227 | } 228 | 229 | // 1. 画拖拽圆 230 | canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, mBadgePaint); 231 | } 232 | } 233 | 234 | /** 235 | * 获取针圆实时半径 236 | */ 237 | private float getCurrentStickRadius() { 238 | /** 239 | * distance 0 -> mDismissThreshold 240 | * percent 0.0f -> 1.0f 241 | * currentStickRadius mStickRadius * 100% -> mStickRadius * 20% 242 | */ 243 | float distance = BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter); 244 | distance = Math.min(distance, mDismissThreshold); 245 | float percent = distance / mDismissThreshold; 246 | return BGABadgeViewUtil.evaluate(percent, mStickRadius, mStickRadius * 0.2f); 247 | } 248 | 249 | public void setStickCenter(float x, float y) { 250 | mStickCenter = new PointF(x, y); 251 | } 252 | 253 | @Override 254 | public boolean onTouchEvent(MotionEvent event) { 255 | try { 256 | switch (event.getAction()) { 257 | case MotionEvent.ACTION_DOWN: 258 | handleActionDown(event); 259 | break; 260 | case MotionEvent.ACTION_MOVE: 261 | handleActionMove(event); 262 | break; 263 | case MotionEvent.ACTION_UP: 264 | case MotionEvent.ACTION_CANCEL: 265 | handleActionUp(event); 266 | break; 267 | } 268 | } catch (Exception e) { 269 | // 确保自己能被移除 270 | removeSelfWithException(); 271 | } 272 | return true; 273 | } 274 | 275 | private void handleActionDown(MotionEvent event) { 276 | if (mExplosionAnimator == null && getParent() == null) { 277 | mDragRadius = Math.min(mBadgeViewHelper.getBadgeRectF().width() / 2, mMaxDragRadius); 278 | mStickRadius = mDragRadius - mDragStickRadiusDifference; 279 | mDismissThreshold = (int) (mStickRadius * 10); 280 | 281 | mDismissAble = false; 282 | mIsDragDisappear = false; 283 | 284 | mWindowManager.addView(this, mLayoutParams); 285 | 286 | updateDragPosition(event.getRawX(), event.getRawY()); 287 | } 288 | } 289 | 290 | private void handleActionMove(MotionEvent event) { 291 | if (mExplosionAnimator == null && getParent() != null) { 292 | updateDragPosition(event.getRawX(), event.getRawY()); 293 | 294 | // 处理断开事件 295 | if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) { 296 | mDismissAble = true; 297 | postInvalidate(); 298 | } else if (mBadgeViewHelper.isResumeTravel()) { 299 | mDismissAble = false; 300 | postInvalidate(); 301 | } 302 | } 303 | } 304 | 305 | private void handleActionUp(MotionEvent event) { 306 | handleActionMove(event); 307 | 308 | if (mDismissAble) { 309 | // 拖拽点超出过范围 310 | if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) { 311 | // 现在也超出范围,消失 312 | try { 313 | mIsDragDisappear = true; 314 | startDismissAnim(getNewStartX(event.getRawX()), getNewStartY(event.getRawY())); 315 | } catch (Exception e) { 316 | removeSelf(); 317 | mBadgeViewHelper.endDragWithDismiss(); 318 | } 319 | } else { 320 | // 现在没有超出范围,放回去 321 | removeSelf(); 322 | mBadgeViewHelper.endDragWithoutDismiss(); 323 | } 324 | } else { 325 | // 拖拽点没超出过范围,弹回去 326 | try { 327 | startSpringAnim(); 328 | } catch (Exception e) { 329 | removeSelf(); 330 | mBadgeViewHelper.endDragWithoutDismiss(); 331 | } 332 | } 333 | } 334 | 335 | private void startSpringAnim() { 336 | final PointF startReleaseDragCenter = new PointF(mDragCenter.x, mDragCenter.y); 337 | ValueAnimator springAnim = ValueAnimator.ofFloat(1.0f); 338 | springAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 339 | 340 | @Override 341 | public void onAnimationUpdate(ValueAnimator mAnim) { 342 | // 0.0 -> 1.0f 343 | float percent = mAnim.getAnimatedFraction(); 344 | PointF p = BGABadgeViewUtil.getPointByPercent(startReleaseDragCenter, mStickCenter, percent); 345 | updateDragPosition(p.x, p.y); 346 | } 347 | }); 348 | springAnim.addListener(new AnimatorListenerAdapter() { 349 | @Override 350 | public void onAnimationEnd(Animator animation) { 351 | removeSelf(); 352 | mBadgeViewHelper.endDragWithoutDismiss(); 353 | } 354 | 355 | @Override 356 | public void onAnimationCancel(Animator animation) { 357 | removeSelf(); 358 | mBadgeViewHelper.endDragWithoutDismiss(); 359 | } 360 | }); 361 | 362 | springAnim.setInterpolator(new OvershootInterpolator(4)); 363 | springAnim.setRepeatCount(1); 364 | springAnim.setRepeatMode(ValueAnimator.INFINITE); 365 | springAnim.setDuration(BGAExplosionAnimator.ANIM_DURATION / 2); 366 | springAnim.start(); 367 | } 368 | 369 | private void startDismissAnim(int newX, int newY) { 370 | int badgeWidth = (int) mBadgeViewHelper.getBadgeRectF().width(); 371 | int badgeHeight = (int) mBadgeViewHelper.getBadgeRectF().height(); 372 | Rect rect = new Rect(newX - badgeWidth / 2, newY - badgeHeight / 2, newX + badgeWidth / 2, newY + badgeHeight / 2); 373 | 374 | Bitmap badgeBitmap = BGABadgeViewUtil.createBitmapSafely(this, rect, 1); 375 | if (badgeBitmap == null) { 376 | removeSelf(); 377 | mBadgeViewHelper.endDragWithDismiss(); 378 | return; 379 | } 380 | 381 | if (mExplosionAnimator != null) { 382 | removeSelf(); 383 | mBadgeViewHelper.endDragWithDismiss(); 384 | return; 385 | } 386 | 387 | mExplosionAnimator = new BGAExplosionAnimator(this, rect, badgeBitmap); 388 | mExplosionAnimator.addListener(new AnimatorListenerAdapter() { 389 | @Override 390 | public void onAnimationEnd(Animator animation) { 391 | removeSelf(); 392 | mBadgeViewHelper.endDragWithDismiss(); 393 | } 394 | 395 | @Override 396 | public void onAnimationCancel(Animator animation) { 397 | removeSelf(); 398 | mBadgeViewHelper.endDragWithDismiss(); 399 | } 400 | }); 401 | mExplosionAnimator.start(); 402 | } 403 | 404 | private void removeSelf() { 405 | if (getParent() != null) { 406 | mWindowManager.removeView(this); 407 | } 408 | mDismissAble = false; 409 | mIsDragDisappear = false; 410 | 411 | // 处理有时候爆炸效果结束后出现一瞬间的拖拽效果 412 | postDelayed(mSetExplosionAnimatorNullTask, 60); 413 | } 414 | 415 | /** 416 | * 修改拖拽位置 417 | */ 418 | private void updateDragPosition(float rawX, float rawY) { 419 | mStartX = getNewStartX(rawX); 420 | mStartY = getNewStartY(rawY); 421 | 422 | mDragCenter.set(rawX, rawY); 423 | postInvalidate(); 424 | } 425 | 426 | private int getNewStartX(float rawX) { 427 | int badgeWidth = (int) mBadgeViewHelper.getBadgeRectF().width(); 428 | int newX = (int) rawX - badgeWidth / 2; 429 | if (newX < 0) { 430 | newX = 0; 431 | } 432 | if (newX > mWindowManager.getDefaultDisplay().getWidth() - badgeWidth) { 433 | newX = mWindowManager.getDefaultDisplay().getWidth() - badgeWidth; 434 | } 435 | return newX; 436 | } 437 | 438 | private int getNewStartY(float rawY) { 439 | int badgeHeight = (int) mBadgeViewHelper.getBadgeRectF().height(); 440 | int maxNewY = getHeight() - badgeHeight; 441 | int newStartY = (int) rawY - badgeHeight / 2; 442 | return Math.min(Math.max(0, newStartY), maxNewY); 443 | } 444 | 445 | private void removeSelfWithException() { 446 | removeSelf(); 447 | if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) { 448 | mBadgeViewHelper.endDragWithDismiss(); 449 | } else { 450 | mBadgeViewHelper.endDragWithoutDismiss(); 451 | } 452 | } 453 | 454 | private static class SetExplosionAnimatorNullTask implements Runnable { 455 | private final WeakReference mDragBadgeView; 456 | 457 | public SetExplosionAnimatorNullTask(BGADragBadgeView dragBadgeView) { 458 | mDragBadgeView = new WeakReference<>(dragBadgeView); 459 | } 460 | 461 | @Override 462 | public void run() { 463 | BGADragBadgeView dragBadgeView = mDragBadgeView.get(); 464 | if (dragBadgeView != null) { 465 | dragBadgeView.mExplosionAnimator = null; 466 | } 467 | } 468 | } 469 | } -------------------------------------------------------------------------------- /api/src/main/java/cn/bingoogolapple/badgeview/BGADragDismissDelegate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview; 18 | 19 | /** 20 | * 作者:王浩 邮件:bingoogolapple@gmail.com 21 | * 创建时间:15/7/12 下午6:33 22 | * 描述:拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的代理 23 | */ 24 | public interface BGADragDismissDelegate { 25 | 26 | /** 27 | * 拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的回调方法 28 | * 29 | * @param badgeable 30 | */ 31 | void onDismiss(BGABadgeable badgeable); 32 | } -------------------------------------------------------------------------------- /api/src/main/java/cn/bingoogolapple/badgeview/BGAExplosionAnimator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview; 18 | 19 | import android.animation.ValueAnimator; 20 | import android.graphics.Bitmap; 21 | import android.graphics.Canvas; 22 | import android.graphics.Color; 23 | import android.graphics.Paint; 24 | import android.graphics.Rect; 25 | import android.view.animation.AccelerateInterpolator; 26 | import android.view.animation.Interpolator; 27 | 28 | import java.util.Random; 29 | 30 | /** 31 | * 作者:王浩 邮件:bingoogolapple@gmail.com 32 | * 创建时间:15/12/5 上午12:21 33 | * 描述:参考https://github.com/tyrantgit/ExplosionField改成了只有一个View的情况,只刷新徽章附近的区域 34 | */ 35 | public class BGAExplosionAnimator extends ValueAnimator { 36 | public static final int ANIM_DURATION = 300; 37 | private static final Interpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(0.6f); 38 | private static final float END_VALUE = 1.4f; 39 | private static final int REFRESH_RATIO = 3; 40 | private static float X; 41 | private static float Y; 42 | private static float V; 43 | private static float W; 44 | 45 | private Particle[] mParticles; 46 | private Paint mPaint; 47 | private BGADragBadgeView mDragBadgeView; 48 | private Rect mRect; 49 | private Rect mInvalidateRect; 50 | 51 | public BGAExplosionAnimator(BGADragBadgeView dragBadgeView, Rect rect, Bitmap bitmap) { 52 | setFloatValues(0.0f, END_VALUE); 53 | setDuration(ANIM_DURATION); 54 | setInterpolator(DEFAULT_INTERPOLATOR); 55 | 56 | X = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 5); 57 | Y = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 20); 58 | V = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 2); 59 | W = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 1); 60 | 61 | mPaint = new Paint(); 62 | mDragBadgeView = dragBadgeView; 63 | mRect = rect; 64 | mInvalidateRect = new Rect(mRect.left - mRect.width() * REFRESH_RATIO, mRect.top - mRect.height() * REFRESH_RATIO, mRect.right + mRect.width() * REFRESH_RATIO, mRect.bottom + mRect.height() * REFRESH_RATIO); 65 | 66 | int partLen = 15; 67 | mParticles = new Particle[partLen * partLen]; 68 | Random random = new Random(System.currentTimeMillis()); 69 | int w = bitmap.getWidth() / (partLen + 2); 70 | int h = bitmap.getHeight() / (partLen + 2); 71 | for (int i = 0; i < partLen; i++) { 72 | for (int j = 0; j < partLen; j++) { 73 | mParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random); 74 | } 75 | } 76 | } 77 | 78 | private Particle generateParticle(int color, Random random) { 79 | Particle particle = new Particle(); 80 | particle.color = color; 81 | particle.radius = V; 82 | if (random.nextFloat() < 0.2f) { 83 | particle.baseRadius = V + ((X - V) * random.nextFloat()); 84 | } else { 85 | particle.baseRadius = W + ((V - W) * random.nextFloat()); 86 | } 87 | float nextFloat = random.nextFloat(); 88 | particle.top = mRect.height() * ((0.18f * random.nextFloat()) + 0.2f); 89 | particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat()); 90 | particle.bottom = (mRect.height() * (random.nextFloat() - 0.5f)) * 1.8f; 91 | float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f; 92 | particle.bottom = f; 93 | particle.mag = 4.0f * particle.top / particle.bottom; 94 | particle.neg = (-particle.mag) / particle.bottom; 95 | f = mRect.centerX() + (Y * (random.nextFloat() - 0.5f)); 96 | particle.baseCx = f + mRect.width() / 2; 97 | particle.cx = particle.baseCx; 98 | f = mRect.centerY() + (Y * (random.nextFloat() - 0.5f)); 99 | particle.baseCy = f; 100 | particle.cy = f; 101 | particle.life = END_VALUE / 10 * random.nextFloat(); 102 | particle.overflow = 0.4f * random.nextFloat(); 103 | particle.alpha = 1f; 104 | return particle; 105 | } 106 | 107 | public void draw(Canvas canvas) { 108 | if (!isStarted()) { 109 | return; 110 | } 111 | for (Particle particle : mParticles) { 112 | particle.advance((float) getAnimatedValue()); 113 | if (particle.alpha > 0f) { 114 | mPaint.setColor(particle.color); 115 | mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha)); 116 | canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint); 117 | } 118 | } 119 | postInvalidate(); 120 | } 121 | 122 | @Override 123 | public void start() { 124 | super.start(); 125 | postInvalidate(); 126 | } 127 | 128 | /** 129 | * 只刷新徽章附近的区域 130 | */ 131 | private void postInvalidate() { 132 | mDragBadgeView.postInvalidate(mInvalidateRect.left, mInvalidateRect.top, mInvalidateRect.right, mInvalidateRect.bottom); 133 | } 134 | 135 | private class Particle { 136 | float alpha; 137 | int color; 138 | float cx; 139 | float cy; 140 | float radius; 141 | float baseCx; 142 | float baseCy; 143 | float baseRadius; 144 | float top; 145 | float bottom; 146 | float mag; 147 | float neg; 148 | float life; 149 | float overflow; 150 | 151 | public void advance(float factor) { 152 | float f = 0f; 153 | float normalization = factor / END_VALUE; 154 | if (normalization < life || normalization > 1f - overflow) { 155 | alpha = 0f; 156 | return; 157 | } 158 | normalization = (normalization - life) / (1f - life - overflow); 159 | float f2 = normalization * END_VALUE; 160 | if (normalization >= 0.7f) { 161 | f = (normalization - 0.7f) / 0.3f; 162 | } 163 | alpha = 1f - f; 164 | f = bottom * f2; 165 | cx = baseCx + f; 166 | cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag; 167 | radius = V + (baseRadius - V) * f2; 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /api/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.1.2' 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | maven { url 'https://jitpack.io' } 17 | jcenter() 18 | google() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'maven' 3 | 4 | dependencies { 5 | implementation project(':annotation') 6 | 7 | compileOnly 'com.google.auto.service:auto-service:1.0-rc4' 8 | implementation 'com.google.auto:auto-common:0.10' 9 | implementation 'com.squareup:javapoet:1.9.0' 10 | } 11 | 12 | sourceCompatibility = JavaVersion.VERSION_1_7 13 | targetCompatibility = JavaVersion.VERSION_1_7 14 | -------------------------------------------------------------------------------- /compiler/src/main/java/cn/bingoogolapple/badgeview/compiler/BGABadgeProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 bingoogolapple 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.bingoogolapple.badgeview.compiler; 18 | 19 | import com.google.auto.service.AutoService; 20 | import com.squareup.javapoet.ClassName; 21 | import com.squareup.javapoet.JavaFile; 22 | import com.squareup.javapoet.MethodSpec; 23 | import com.squareup.javapoet.TypeName; 24 | import com.squareup.javapoet.TypeSpec; 25 | 26 | import java.io.IOException; 27 | import java.util.HashSet; 28 | import java.util.LinkedHashSet; 29 | import java.util.List; 30 | import java.util.Set; 31 | 32 | import javax.annotation.processing.AbstractProcessor; 33 | import javax.annotation.processing.Filer; 34 | import javax.annotation.processing.Messager; 35 | import javax.annotation.processing.ProcessingEnvironment; 36 | import javax.annotation.processing.Processor; 37 | import javax.annotation.processing.RoundEnvironment; 38 | import javax.lang.model.SourceVersion; 39 | import javax.lang.model.element.Element; 40 | import javax.lang.model.element.ElementKind; 41 | import javax.lang.model.element.Modifier; 42 | import javax.lang.model.element.TypeElement; 43 | import javax.lang.model.type.DeclaredType; 44 | import javax.lang.model.type.MirroredTypesException; 45 | import javax.lang.model.type.TypeMirror; 46 | import javax.lang.model.util.Elements; 47 | import javax.tools.Diagnostic; 48 | 49 | import cn.bingoogolapple.badgeview.annotation.BGABadge; 50 | 51 | /** 52 | * 作者:王浩 邮件:bingoogolapple@gmail.com 53 | * 创建时间:2018/1/14 54 | * 描述: 55 | */ 56 | @SuppressWarnings("unused") 57 | @AutoService(Processor.class) 58 | public class BGABadgeProcessor extends AbstractProcessor { 59 | private static final String CLASS_JAVA_DOC = "Generated by BGABadgeView-Android. Do not edit it!\n"; 60 | private static final String PACKAGE_NAME = "cn.bingoogolapple.badgeview"; 61 | private static final String CLASS_PREFIX = "BGABadge"; 62 | private Filer mFileUtils; 63 | private Elements mElementUtils; 64 | private Messager mMessager; 65 | 66 | @Override 67 | public synchronized void init(ProcessingEnvironment processingEnvironment) { 68 | super.init(processingEnvironment); 69 | mFileUtils = processingEnv.getFiler(); 70 | mElementUtils = processingEnv.getElementUtils(); 71 | mMessager = processingEnv.getMessager(); 72 | } 73 | 74 | @Override 75 | public SourceVersion getSupportedSourceVersion() { 76 | return SourceVersion.latestSupported(); 77 | } 78 | 79 | /** 80 | * 告知 Processor 哪些注解需要处理 81 | * 82 | * @return 返回一个 Set 集合,集合内容为自定义注解的包名+类名 83 | */ 84 | @Override 85 | public Set getSupportedAnnotationTypes() { 86 | final Set annotationTypes = new LinkedHashSet<>(); 87 | annotationTypes.add(BGABadge.class.getCanonicalName()); 88 | return annotationTypes; 89 | } 90 | 91 | /** 92 | * 所有的注解处理都是从这个方法开始的,当 APT 找到所有需要处理的注解后,会回调这个方法。当没有属于该 Processor 处理的注解被使用时,不会回调该方法 93 | * 94 | * @param set 所有的由该 Processor 处理,并待处理的 Annotations「属于该 Processor 处理的注解,但并未被使用,不存在与这个集合里」 95 | * @param roundEnvironment 表示当前或是之前的运行环境,可以通过该对象查找到注解 96 | * @return 表示这组 Annotation 是否被这个 Processor 消费,如果消费「返回 true」后续子的 Processor 不会再对这组 Annotation 进行处理 97 | */ 98 | @Override 99 | public boolean process(Set set, RoundEnvironment roundEnvironment) { 100 | Set elements = roundEnvironment.getElementsAnnotatedWith(BGABadge.class); 101 | if (elements == null || elements.isEmpty()) { 102 | return true; 103 | } 104 | mMessager.printMessage(Diagnostic.Kind.NOTE, 105 | "====================================== BGABadgeProcessor process START ======================================"); 106 | Set viewClassSet = new HashSet<>(); 107 | parseParams(elements, viewClassSet); 108 | try { 109 | generate(viewClassSet); 110 | } catch (IllegalAccessException e) { 111 | e.printStackTrace(); 112 | } catch (IOException e) { 113 | mMessager.printMessage(Diagnostic.Kind.ERROR, "Exception occurred when generating class file."); 114 | e.printStackTrace(); 115 | } 116 | mMessager.printMessage(Diagnostic.Kind.NOTE, 117 | "====================================== BGABadgeProcessor process END ======================================"); 118 | return true; 119 | } 120 | 121 | private void parseParams(Set elements, Set viewClassSet) { 122 | for (Element element : elements) { 123 | checkAnnotationValid(element, BGABadge.class); 124 | TypeElement classElement = (TypeElement) element; 125 | // 获取该注解的值 126 | BGABadge badgeAnnotation = classElement.getAnnotation(BGABadge.class); 127 | try { 128 | badgeAnnotation.value(); 129 | } catch (MirroredTypesException e) { 130 | List typeMirrors = e.getTypeMirrors(); 131 | for (TypeMirror typeMirror : typeMirrors) { 132 | DeclaredType classTypeMirror = (DeclaredType) typeMirror; 133 | TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement(); 134 | String qualifiedName = classTypeElement.getQualifiedName().toString(); 135 | 136 | viewClassSet.add(qualifiedName); 137 | } 138 | } 139 | } 140 | } 141 | 142 | private void generate(Set viewClassSet) throws IllegalAccessException, IOException { 143 | mMessager.printMessage(Diagnostic.Kind.NOTE, "生成 " + viewClassSet.size() + " 个"); 144 | for (String clazz : viewClassSet) { 145 | int lastDotIndex = clazz.lastIndexOf("."); 146 | String superPackageName = clazz.substring(0, lastDotIndex); 147 | String superClassName = clazz.substring(lastDotIndex + 1); 148 | String className = CLASS_PREFIX + superClassName; 149 | 150 | mMessager.printMessage(Diagnostic.Kind.NOTE, clazz + " ====> " + className); 151 | 152 | TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(className) 153 | .addJavadoc(CLASS_JAVA_DOC) 154 | .addModifiers(Modifier.PUBLIC) 155 | .superclass(ClassName.get(superPackageName, superClassName)) 156 | .addSuperinterface(ClassName.get(PACKAGE_NAME, "BGABadgeable")) 157 | .addField(ClassName.get(PACKAGE_NAME, "BGABadgeViewHelper"), "mBadgeViewHelper", Modifier.PRIVATE); 158 | 159 | generateMethod(typeBuilder, clazz); 160 | 161 | JavaFile javaFile = JavaFile.builder(PACKAGE_NAME, typeBuilder.build()).build(); 162 | javaFile.writeTo(mFileUtils); 163 | } 164 | } 165 | 166 | private void generateMethod(TypeSpec.Builder typeBuilder, String clazz) { 167 | constructor(typeBuilder, clazz); 168 | onTouchEvent(typeBuilder); 169 | callSuperOnTouchEvent(typeBuilder); 170 | if (isAssignable(clazz, "android.view.ViewGroup")) { 171 | dispatchDraw(typeBuilder); 172 | } else { 173 | onDraw(typeBuilder); 174 | } 175 | showCirclePointBadge(typeBuilder); 176 | showTextBadge(typeBuilder); 177 | hiddenBadge(typeBuilder); 178 | showDrawableBadge(typeBuilder); 179 | setDragDismissDelegate(typeBuilder); 180 | isShowBadge(typeBuilder); 181 | isDraggable(typeBuilder); 182 | isDragging(typeBuilder); 183 | getBadgeViewHelper(typeBuilder); 184 | } 185 | 186 | private void constructor(TypeSpec.Builder typeBuilder, String clazz) { 187 | TypeName contextType = ClassName.get("android.content", "Context"); 188 | TypeName attributeSetType = ClassName.get("android.util", "AttributeSet"); 189 | MethodSpec constructorOne = MethodSpec.constructorBuilder() 190 | .addModifiers(Modifier.PUBLIC) 191 | .addParameter(contextType, "context") 192 | .addStatement("this(context, null)") 193 | .build(); 194 | MethodSpec constructorTwo = MethodSpec.constructorBuilder() 195 | .addModifiers(Modifier.PUBLIC) 196 | .addParameter(contextType, "context") 197 | .addParameter(attributeSetType, "attrs") 198 | .addStatement("this(context, attrs, 0)") 199 | .build(); 200 | MethodSpec.Builder constructorThreeBuilder = MethodSpec.constructorBuilder() 201 | .addModifiers(Modifier.PUBLIC) 202 | .addParameter(contextType, "context") 203 | .addParameter(attributeSetType, "attrs") 204 | .addParameter(int.class, "defStyleAttr") 205 | .addStatement("super(context, attrs, defStyleAttr)"); 206 | if (isAssignable(clazz, "android.widget.ImageView") || isAssignable(clazz, "android.widget.RadioButton")) { 207 | constructorThreeBuilder.addStatement("mBadgeViewHelper = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightTop)"); 208 | } else { 209 | constructorThreeBuilder.addStatement( 210 | "mBadgeViewHelper = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightCenter)"); 211 | } 212 | 213 | typeBuilder.addMethod(constructorOne) 214 | .addMethod(constructorTwo) 215 | .addMethod(constructorThreeBuilder.build()); 216 | } 217 | 218 | private void onTouchEvent(TypeSpec.Builder typeBuilder) { 219 | MethodSpec methodSpec = MethodSpec.methodBuilder("onTouchEvent") 220 | .addAnnotation(Override.class) 221 | .addModifiers(Modifier.PUBLIC) 222 | .addParameter(ClassName.get("android.view", "MotionEvent"), "event") 223 | .addStatement("return mBadgeViewHelper.onTouchEvent(event)") 224 | .returns(boolean.class) 225 | .build(); 226 | typeBuilder.addMethod(methodSpec); 227 | } 228 | 229 | private void callSuperOnTouchEvent(TypeSpec.Builder typeBuilder) { 230 | MethodSpec methodSpec = MethodSpec.methodBuilder("callSuperOnTouchEvent") 231 | .addAnnotation(Override.class) 232 | .addModifiers(Modifier.PUBLIC) 233 | .addParameter(ClassName.get("android.view", "MotionEvent"), "event") 234 | .addStatement("return super.onTouchEvent(event)") 235 | .returns(boolean.class) 236 | .build(); 237 | typeBuilder.addMethod(methodSpec); 238 | } 239 | 240 | private void onDraw(TypeSpec.Builder typeBuilder) { 241 | MethodSpec methodSpec = MethodSpec.methodBuilder("onDraw") 242 | .addAnnotation(Override.class) 243 | .addModifiers(Modifier.PUBLIC) 244 | .addParameter(ClassName.get("android.graphics", "Canvas"), "canvas") 245 | .addStatement("super.onDraw(canvas)") 246 | .addStatement("mBadgeViewHelper.drawBadge(canvas)") 247 | .build(); 248 | typeBuilder.addMethod(methodSpec); 249 | } 250 | 251 | private void dispatchDraw(TypeSpec.Builder typeBuilder) { 252 | MethodSpec methodSpec = MethodSpec.methodBuilder("dispatchDraw") 253 | .addAnnotation(Override.class) 254 | .addModifiers(Modifier.PUBLIC) 255 | .addParameter(ClassName.get("android.graphics", "Canvas"), "canvas") 256 | .addStatement("super.dispatchDraw(canvas)") 257 | .addStatement("mBadgeViewHelper.drawBadge(canvas)") 258 | .build(); 259 | typeBuilder.addMethod(methodSpec); 260 | } 261 | 262 | private void showCirclePointBadge(TypeSpec.Builder typeBuilder) { 263 | MethodSpec methodSpec = MethodSpec.methodBuilder("showCirclePointBadge") 264 | .addAnnotation(Override.class) 265 | .addModifiers(Modifier.PUBLIC) 266 | .addStatement("mBadgeViewHelper.showCirclePointBadge()") 267 | .build(); 268 | typeBuilder.addMethod(methodSpec); 269 | } 270 | 271 | private void showTextBadge(TypeSpec.Builder typeBuilder) { 272 | MethodSpec methodSpec = MethodSpec.methodBuilder("showTextBadge") 273 | .addAnnotation(Override.class) 274 | .addModifiers(Modifier.PUBLIC) 275 | .addParameter(String.class, "badgeText") 276 | .addStatement("mBadgeViewHelper.showTextBadge(badgeText)") 277 | .build(); 278 | typeBuilder.addMethod(methodSpec); 279 | } 280 | 281 | private void hiddenBadge(TypeSpec.Builder typeBuilder) { 282 | MethodSpec methodSpec = MethodSpec.methodBuilder("hiddenBadge") 283 | .addAnnotation(Override.class) 284 | .addModifiers(Modifier.PUBLIC) 285 | .addStatement("mBadgeViewHelper.hiddenBadge()") 286 | .build(); 287 | typeBuilder.addMethod(methodSpec); 288 | } 289 | 290 | private void showDrawableBadge(TypeSpec.Builder typeBuilder) { 291 | MethodSpec methodSpec = MethodSpec.methodBuilder("showDrawableBadge") 292 | .addAnnotation(Override.class) 293 | .addModifiers(Modifier.PUBLIC) 294 | .addParameter(ClassName.get("android.graphics", "Bitmap"), "bitmap") 295 | .addStatement("mBadgeViewHelper.showDrawable(bitmap)") 296 | .build(); 297 | typeBuilder.addMethod(methodSpec); 298 | } 299 | 300 | private void setDragDismissDelegate(TypeSpec.Builder typeBuilder) { 301 | MethodSpec methodSpec = MethodSpec.methodBuilder("setDragDismissDelegate") 302 | .addAnnotation(Override.class) 303 | .addModifiers(Modifier.PUBLIC) 304 | .addParameter(ClassName.get(PACKAGE_NAME, "BGADragDismissDelegate"), "delegate") 305 | .addStatement("mBadgeViewHelper.setDragDismissDelegate(delegate)") 306 | .build(); 307 | typeBuilder.addMethod(methodSpec); 308 | } 309 | 310 | private void isShowBadge(TypeSpec.Builder typeBuilder) { 311 | MethodSpec methodSpec = MethodSpec.methodBuilder("isShowBadge") 312 | .addAnnotation(Override.class) 313 | .addModifiers(Modifier.PUBLIC) 314 | .addStatement("return mBadgeViewHelper.isShowBadge()") 315 | .returns(boolean.class) 316 | .build(); 317 | typeBuilder.addMethod(methodSpec); 318 | } 319 | 320 | private void isDraggable(TypeSpec.Builder typeBuilder) { 321 | MethodSpec methodSpec = MethodSpec.methodBuilder("isDraggable") 322 | .addAnnotation(Override.class) 323 | .addModifiers(Modifier.PUBLIC) 324 | .addStatement("return mBadgeViewHelper.isDraggable()") 325 | .returns(boolean.class) 326 | .build(); 327 | typeBuilder.addMethod(methodSpec); 328 | } 329 | 330 | private void isDragging(TypeSpec.Builder typeBuilder) { 331 | MethodSpec methodSpec = MethodSpec.methodBuilder("isDragging") 332 | .addAnnotation(Override.class) 333 | .addModifiers(Modifier.PUBLIC) 334 | .addStatement("return mBadgeViewHelper.isDragging()") 335 | .returns(boolean.class) 336 | .build(); 337 | typeBuilder.addMethod(methodSpec); 338 | } 339 | 340 | private void getBadgeViewHelper(TypeSpec.Builder typeBuilder) { 341 | MethodSpec methodSpec = MethodSpec.methodBuilder("getBadgeViewHelper") 342 | .addAnnotation(Override.class) 343 | .addModifiers(Modifier.PUBLIC) 344 | .addStatement("return mBadgeViewHelper") 345 | .returns(ClassName.get(PACKAGE_NAME, "BGABadgeViewHelper")) 346 | .build(); 347 | typeBuilder.addMethod(methodSpec); 348 | } 349 | 350 | private boolean checkAnnotationValid(Element annotatedElement, Class clazz) { 351 | if (annotatedElement.getKind() != ElementKind.CLASS) { 352 | error(annotatedElement, "%s must be declared on class.", clazz.getSimpleName()); 353 | return false; 354 | } 355 | 356 | if (annotatedElement.getModifiers().contains(Modifier.PRIVATE)) { 357 | error(annotatedElement, "%s must can not be private.", ((TypeElement) annotatedElement).getQualifiedName()); 358 | return false; 359 | } 360 | return true; 361 | } 362 | 363 | private void error(Element element, String message, Object... args) { 364 | if (args.length > 0) { 365 | message = String.format(message, args); 366 | } 367 | mMessager.printMessage(Diagnostic.Kind.ERROR, message, element); 368 | } 369 | 370 | private boolean isAssignable(String childClazz, String superClazz) { 371 | return processingEnv.getTypeUtils().isAssignable( 372 | processingEnv.getElementUtils().getTypeElement(childClazz).asType(), 373 | processingEnv.getElementUtils().getTypeElement(superClazz).asType()); 374 | 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion ANDROID_BUILD_SDK_VERSION as int 5 | 6 | defaultConfig { 7 | minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION as int 8 | targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int 9 | versionCode VERSION_CODE as int 10 | versionName VERSION_NAME 11 | } 12 | 13 | buildTypes { 14 | release { 15 | minifyEnabled true 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | 23 | implementation project(':api') 24 | annotationProcessor project(':compiler') 25 | 26 | // -------------------- 以下一个库是必须依赖的 ---------------------------- 27 | // implementation 'com.github.bingoogolapple.BGABadgeView-Android:api:1.2.0' 28 | // annotationProcessor "com.github.bingoogolapple.BGABadgeView-Android:compiler:1.2.0" 29 | // -------------------- 以上一个库是必须依赖的 ---------------------------- 30 | 31 | // 以下几个库是演示案例所使用到的 32 | implementation 'com.android.support:design:27.0.2' 33 | implementation 'com.android.support:recyclerview-v7:27.0.2' 34 | implementation 'cn.bingoogolapple:bga-baseadapter:1.2.9' 35 | } -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/proguard-rules.pro -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/bingoogolapple/badgeview/demo/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package cn.bingoogolapple.badgeview.demo.activity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.graphics.Color; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.support.v4.graphics.drawable.RoundedBitmapDrawable; 9 | import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.widget.LinearLayoutManager; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.RadioGroup; 16 | 17 | import cn.bingoogolapple.badgeview.BGABadgeFloatingActionButton; 18 | import cn.bingoogolapple.badgeview.BGABadgeFrameLayout; 19 | import cn.bingoogolapple.badgeview.BGABadgeImageView; 20 | import cn.bingoogolapple.badgeview.BGABadgeLinearLayout; 21 | import cn.bingoogolapple.badgeview.BGABadgeRadioButton; 22 | import cn.bingoogolapple.badgeview.BGABadgeRelativeLayout; 23 | import cn.bingoogolapple.badgeview.BGABadgeTextView; 24 | import cn.bingoogolapple.badgeview.BGABadgeView; 25 | import cn.bingoogolapple.badgeview.BGABadgeable; 26 | import cn.bingoogolapple.badgeview.BGADragDismissDelegate; 27 | import cn.bingoogolapple.badgeview.demo.R; 28 | import cn.bingoogolapple.badgeview.demo.adapter.MessageAdapter; 29 | import cn.bingoogolapple.badgeview.demo.model.MessageModel; 30 | import cn.bingoogolapple.badgeview.demo.util.ToastUtil; 31 | import cn.bingoogolapple.baseadapter.BGAOnRVItemClickListener; 32 | 33 | public class MainActivity extends AppCompatActivity { 34 | private BGABadgeView mTestBv; 35 | 36 | private BGABadgeTextView mTestBtv; 37 | 38 | private BGABadgeImageView mNormalBiv; 39 | private BGABadgeImageView mRoundedBiv; 40 | private BGABadgeImageView mCircleBiv; 41 | 42 | private BGABadgeLinearLayout mTestBll; 43 | private BGABadgeRelativeLayout mTestBrl; 44 | private BGABadgeFrameLayout mTestBfl; 45 | 46 | private RecyclerView mRecyclerView; 47 | private MessageAdapter mMessageAdapter; 48 | 49 | 50 | private RadioGroup mTabRg; 51 | private BGABadgeRadioButton mHomeBrb; 52 | private BGABadgeRadioButton mMessageBrb; 53 | private BGABadgeRadioButton mDiscoverBrb; 54 | private BGABadgeRadioButton mMeBrb; 55 | 56 | private BGABadgeFloatingActionButton mChatBfab; 57 | 58 | @Override 59 | protected void onCreate(Bundle savedInstanceState) { 60 | super.onCreate(savedInstanceState); 61 | setContentView(R.layout.activity_main); 62 | ToastUtil.init(this); 63 | 64 | initView(); 65 | testBadgeView(); 66 | testRadioButton(); 67 | testRecyclerView(); 68 | } 69 | 70 | private void initView() { 71 | mTestBv = findViewById(R.id.bv_main_test); 72 | 73 | mTestBtv = findViewById(R.id.btv_main_test); 74 | 75 | mNormalBiv = findViewById(R.id.biv_main_normal); 76 | mRoundedBiv = findViewById(R.id.biv_main_rounded); 77 | mCircleBiv = findViewById(R.id.biv_main_circle); 78 | 79 | mTestBll = findViewById(R.id.bll_main_test); 80 | mTestBrl = findViewById(R.id.brl_main_test); 81 | mTestBfl = findViewById(R.id.bfl_main_test); 82 | 83 | mRecyclerView = findViewById(R.id.recyclerview); 84 | 85 | mTabRg = findViewById(R.id.rg_main_tab); 86 | mHomeBrb = findViewById(R.id.brb_main_home); 87 | mMessageBrb = findViewById(R.id.brb_main_message); 88 | mDiscoverBrb = findViewById(R.id.brb_main_discover); 89 | mMeBrb = findViewById(R.id.brb_main_me); 90 | 91 | mChatBfab = findViewById(R.id.bfab_main_chat); 92 | } 93 | 94 | private void testBadgeView() { 95 | mTestBv.showTextBadge("9"); 96 | mTestBv.getBadgeViewHelper().setBadgeTextSizeSp(15); 97 | mTestBv.getBadgeViewHelper().setBadgePaddingDp(8); 98 | mTestBv.getBadgeViewHelper().setBadgeTextColorInt(Color.parseColor("#FF0000")); 99 | mTestBv.getBadgeViewHelper().setBadgeBgColorInt(Color.parseColor("#00FF00")); 100 | mTestBv.getBadgeViewHelper().setDraggable(true); 101 | mTestBv.getBadgeViewHelper().setBadgePaddingDp(7); 102 | mTestBv.getBadgeViewHelper().setBadgeBorderWidthDp(2); 103 | mTestBv.getBadgeViewHelper().setBadgeBorderColorInt(Color.parseColor("#0000FF")); 104 | 105 | mTestBtv.showCirclePointBadge(); 106 | 107 | mNormalBiv.showCirclePointBadge(); 108 | 109 | Bitmap avatarBadgeBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.avatar_vip); 110 | 111 | RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(getResources(), 112 | BitmapFactory.decodeResource(getResources(), R.mipmap.avator)); 113 | roundedDrawable.getPaint().setAntiAlias(true); 114 | roundedDrawable.setCornerRadius(30); 115 | mRoundedBiv.setImageDrawable(roundedDrawable); 116 | mRoundedBiv.showDrawableBadge(avatarBadgeBitmap); 117 | 118 | Bitmap avatarBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.avator); 119 | RoundedBitmapDrawable circleDrawable = RoundedBitmapDrawableFactory.create(getResources(), avatarBitmap); 120 | circleDrawable.getPaint().setAntiAlias(true); 121 | circleDrawable.setCornerRadius(Math.max(avatarBitmap.getWidth(), avatarBitmap.getHeight()) / 2.0f); 122 | mCircleBiv.setImageDrawable(circleDrawable); 123 | mCircleBiv.showDrawableBadge(avatarBadgeBitmap); 124 | 125 | mTestBll.showDrawableBadge(avatarBadgeBitmap); 126 | mTestBrl.showTextBadge("LoveAndroid"); 127 | mTestBfl.showTextBadge("8"); 128 | 129 | mChatBfab.showTextBadge("8"); 130 | mChatBfab.setDragDismissDelegate(new BGADragDismissDelegate() { 131 | @Override 132 | public void onDismiss(BGABadgeable badgeable) { 133 | ToastUtil.show("清空聊天消息"); 134 | } 135 | }); 136 | 137 | new Handler().postDelayed(new Runnable() { 138 | @Override 139 | public void run() { 140 | mRoundedBiv.hiddenBadge(); 141 | } 142 | }, 3000); 143 | 144 | new Handler().postDelayed(new Runnable() { 145 | @Override 146 | public void run() { 147 | mRoundedBiv.showCirclePointBadge(); 148 | } 149 | }, 6000); 150 | 151 | new Handler().postDelayed(new Runnable() { 152 | @Override 153 | public void run() { 154 | mRoundedBiv.showDrawableBadge(BitmapFactory.decodeResource(getResources(), R.mipmap.avatar_vip)); 155 | } 156 | }, 9000); 157 | } 158 | 159 | private void testRadioButton() { 160 | mHomeBrb.showTextBadge("10"); 161 | mMessageBrb.showTextBadge("1"); 162 | mDiscoverBrb.showTextBadge("..."); 163 | mMeBrb.showDrawableBadge(BitmapFactory.decodeResource(getResources(), R.mipmap.avatar_vip)); 164 | new Handler().postDelayed(new Runnable() { 165 | @Override 166 | public void run() { 167 | mHomeBrb.showTextBadge("1"); 168 | } 169 | }, 5000); 170 | 171 | mHomeBrb.setDragDismissDelegate(new BGADragDismissDelegate() { 172 | @Override 173 | public void onDismiss(BGABadgeable badgeable) { 174 | ToastUtil.show("消息单选按钮徽章拖动消失"); 175 | } 176 | }); 177 | 178 | mTabRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 179 | @Override 180 | public void onCheckedChanged(RadioGroup group, int checkedId) { 181 | switch (checkedId) { 182 | case R.id.brb_main_home: 183 | ToastUtil.show("首页"); 184 | break; 185 | case R.id.brb_main_message: 186 | ToastUtil.show("消息"); 187 | break; 188 | case R.id.brb_main_discover: 189 | ToastUtil.show("发现"); 190 | break; 191 | case R.id.brb_main_me: 192 | ToastUtil.show("我"); 193 | break; 194 | } 195 | } 196 | }); 197 | } 198 | 199 | private void testRecyclerView() { 200 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); 201 | mMessageAdapter = new MessageAdapter(mRecyclerView); 202 | mMessageAdapter.setOnRVItemClickListener(new BGAOnRVItemClickListener() { 203 | @Override 204 | public void onRVItemClick(ViewGroup viewGroup, View view, int position) { 205 | ToastUtil.show("点击了条目 " + mMessageAdapter.getItem(position).title); 206 | } 207 | }); 208 | mRecyclerView.setAdapter(mMessageAdapter); 209 | mMessageAdapter.setData(MessageModel.getTestData()); 210 | } 211 | } -------------------------------------------------------------------------------- /demo/src/main/java/cn/bingoogolapple/badgeview/demo/adapter/MessageAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.bingoogolapple.badgeview.demo.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | 5 | import cn.bingoogolapple.baseadapter.BGARecyclerViewAdapter; 6 | import cn.bingoogolapple.baseadapter.BGAViewHolderHelper; 7 | import cn.bingoogolapple.badgeview.BGABadgeLinearLayout; 8 | import cn.bingoogolapple.badgeview.BGABadgeable; 9 | import cn.bingoogolapple.badgeview.BGADragDismissDelegate; 10 | import cn.bingoogolapple.badgeview.demo.R; 11 | import cn.bingoogolapple.badgeview.demo.model.MessageModel; 12 | import cn.bingoogolapple.badgeview.demo.util.ToastUtil; 13 | 14 | /** 15 | * 作者:王浩 邮件:bingoogolapple@gmail.com 16 | * 创建时间:15/7/12 上午11:39 17 | * 描述: 18 | */ 19 | public class MessageAdapter extends BGARecyclerViewAdapter { 20 | public MessageAdapter(RecyclerView recyclerView) { 21 | super(recyclerView, R.layout.item_message); 22 | } 23 | 24 | @Override 25 | protected void fillData(BGAViewHolderHelper holderHelper, final int position, final MessageModel message) { 26 | holderHelper.setText(R.id.tv_item_message_title, message.title); 27 | holderHelper.setText(R.id.tv_item_message_detail, message.detail); 28 | 29 | BGABadgeLinearLayout rootView = (BGABadgeLinearLayout) holderHelper.getConvertView(); 30 | if (message.newMsgCount > 0) { 31 | rootView.showTextBadge("" + message.newMsgCount); 32 | rootView.setDragDismissDelegate(new BGADragDismissDelegate() { 33 | @Override 34 | public void onDismiss(BGABadgeable badgeable) { 35 | message.newMsgCount = 0; 36 | ToastUtil.show(message.title + "的徽章消失"); 37 | } 38 | }); 39 | } else { 40 | rootView.hiddenBadge(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /demo/src/main/java/cn/bingoogolapple/badgeview/demo/model/MessageModel.java: -------------------------------------------------------------------------------- 1 | package cn.bingoogolapple.badgeview.demo.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | 7 | /** 8 | * 作者:王浩 邮件:bingoogolapple@gmail.com 9 | * 创建时间:15/7/12 上午11:33 10 | * 描述: 11 | */ 12 | public class MessageModel { 13 | public String title; 14 | public String detail; 15 | public int newMsgCount; 16 | 17 | public MessageModel(String title, String detail, int newMsgCount) { 18 | this.title = title; 19 | this.detail = detail; 20 | this.newMsgCount = newMsgCount; 21 | } 22 | 23 | public static List getTestData() { 24 | List messages = new ArrayList<>(); 25 | Random random = new Random(); 26 | for (int i = 0; i < 30; i++) { 27 | messages.add(new MessageModel("title" + i, "detail" + i, random.nextInt(150))); 28 | } 29 | return messages; 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /demo/src/main/java/cn/bingoogolapple/badgeview/demo/util/BGABadgeInit.java: -------------------------------------------------------------------------------- 1 | package cn.bingoogolapple.badgeview.demo.util; 2 | 3 | import android.support.design.widget.FloatingActionButton; 4 | import android.view.View; 5 | import android.widget.FrameLayout; 6 | import android.widget.ImageView; 7 | import android.widget.LinearLayout; 8 | import android.widget.RadioButton; 9 | import android.widget.RelativeLayout; 10 | import android.widget.TextView; 11 | 12 | import cn.bingoogolapple.badgeview.annotation.BGABadge; 13 | 14 | /** 15 | * 作者:王浩 邮件:bingoogolapple@gmail.com 16 | * 创建时间:2018/1/14 17 | * 描述:初始化 BGABadgeView-Android 18 | * 1.在项目任意一个类上面添加 BGABadge 注解 19 | * 2.需要哪些类具有徽章功能,就把那些类的 class 作为 BGABadge 注解的参数 20 | * 3.再 AS 中执行 Build => Rebuild Project 21 | * 4.经过前面三个步骤后就可以通过「cn.bingoogolapple.badgeview.BGABadge原始类名」来使用徽章控件了 22 | */ 23 | @BGABadge({ 24 | View.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeView,不想用这个类的话就删了这一行 25 | ImageView.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeImageView,不想用这个类的话就删了这一行 26 | TextView.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingTextView,不想用这个类的话就删了这一行 27 | RadioButton.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeRadioButton,不想用这个类的话就删了这一行 28 | LinearLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeLinearLayout,不想用这个类的话就删了这一行 29 | FrameLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFrameLayout,不想用这个类的话就删了这一行 30 | RelativeLayout.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeRelativeLayout,不想用这个类的话就删了这一行 31 | FloatingActionButton.class, // 对应 cn.bingoogolapple.badgeview.BGABadgeFloatingActionButton,不想用这个类的话就删了这一行 32 | }) 33 | public class BGABadgeInit { 34 | } 35 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/bingoogolapple/badgeview/demo/util/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package cn.bingoogolapple.badgeview.demo.util; 2 | 3 | import android.content.Context; 4 | import android.view.Gravity; 5 | import android.view.LayoutInflater; 6 | import android.widget.TextView; 7 | import android.widget.Toast; 8 | 9 | import cn.bingoogolapple.badgeview.BGABadgeViewUtil; 10 | import cn.bingoogolapple.badgeview.demo.R; 11 | 12 | /** 13 | * 作者:王浩 邮件:bingoogolapple@gmail.com 14 | * 创建时间:15/12/5 上午11:50 15 | * 描述: 16 | */ 17 | public class ToastUtil { 18 | private static Toast sToast; 19 | private static TextView sMsgTv; 20 | 21 | private ToastUtil() { 22 | } 23 | 24 | public static void init(Context context) { 25 | sToast = new Toast(context.getApplicationContext()); 26 | sToast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, BGABadgeViewUtil.dp2px(context, 70)); 27 | sMsgTv = (TextView) LayoutInflater.from(context).inflate(R.layout.view_toast, null); 28 | sToast.setView(sMsgTv); 29 | sToast.setDuration(Toast.LENGTH_SHORT); 30 | } 31 | 32 | public static void show(CharSequence text) { 33 | sMsgTv.setText(text); 34 | sToast.show(); 35 | } 36 | } -------------------------------------------------------------------------------- /demo/src/main/res/color/selector_maintab.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/layer_maintab_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/selector_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/selector_tab_discover.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/selector_tab_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/selector_tab_me.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/selector_tab_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/selector_tab_plus.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/selector_tab_plusbg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/shape_toast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 20 | 21 | 22 | 31 | 32 | 38 | 39 | 44 | 45 | 55 | 56 | 64 | 65 | 68 | 69 | 70 | 71 | 77 | 78 | 81 | 82 | 86 | 87 | 88 | 89 | 96 | 97 | 100 | 101 | 105 | 106 | 107 | 108 | 113 | 114 | 115 | 120 | 121 | 130 | 131 | 140 | 141 | 146 | 147 | 157 | 158 | 166 | 167 | 168 | 169 | 183 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/item_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 26 | 27 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/view_toast.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/avatar_vip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/avatar_vip.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/avator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/avator.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/chat_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/chat_white.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_discover_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_discover_checked.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_discover_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_discover_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_home_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_home_checked.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_home_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_home_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_me_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_me_checked.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_me_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_me_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_message_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_message_checked.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_message_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_message_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_plus_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_plus_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/tab_plus_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-hdpi/tab_plus_pressed.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/toast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-xhdpi/toast.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #f57257 4 | #f46444 5 | #f46444 6 | 7 | #cccc 8 | -------------------------------------------------------------------------------- /demo/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BGABadgeViewDemo 3 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles_common.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | 23 | 24 | 29 | 30 | 35 | 36 | 41 | 42 | 47 | 48 | 51 | 52 | 55 | 56 | 59 | 60 | 63 | 64 | 67 | 68 | 71 | 72 | 75 | 76 | 79 | 80 | 83 | 84 | 87 | 88 | 91 | 92 | 95 | 96 | 99 | 100 | 103 | 104 | 107 | 108 | 111 | 112 | 116 | 117 | 121 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | PUBLISH_AAR_GITHUB_REPOSITORIES_NAME=BGABadgeView-Android 2 | 3 | ANDROID_BUILD_MIN_SDK_VERSION=14 4 | ANDROID_BUILD_TARGET_SDK_VERSION=27 5 | ANDROID_BUILD_SDK_VERSION=27 6 | 7 | VERSION_NAME=1.1.9 8 | VERSION_CODE=119 9 | 10 | #org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 11 | #org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingoogolapple/BGABadgeView-Android/d69ee719c0c418c35e7936b5e6621c0193198748/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Oct 07 10:51:24 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':annotation', ':api', ':compiler', ':demo' --------------------------------------------------------------------------------