├── .gitignore ├── .idea └── vcs.xml ├── README.md ├── build.gradle ├── demo.gif ├── demo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── chendong │ │ └── gank │ │ └── superbadge │ │ ├── App.java │ │ └── MainActivity.java │ └── res │ ├── layout │ ├── activity_main.xml │ └── fragment_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── chendong │ │ └── gank │ │ └── library │ │ ├── BadgeManger.java │ │ ├── StringSerializable.java │ │ ├── SuperBadgeDater.java │ │ └── SuperBadgeHelper.java │ └── res │ └── values │ ├── attrs.xml │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperBadge - 超级角标 2 | Message red dot count solution - 消息红点计数解决方案 3 | 4 | * #####代码解析请跳转至我的简书,[《Android消息计数最终解决方案》](http://www.jianshu.com/p/0ab751842192). 5 | 6 | * #####[Demo下载](http://ac-nuiddhh1.clouddn.com/ad242ff781c7274bf711.apk). 7 | 8 | 9 | ![demoImg](demo.gif) 10 | 11 | 12 | ###Add dependencies 13 | ------------------------------ 14 | Add it in your root build.gradle at the end of repositories: 15 | 16 | allprojects { 17 | repositories { 18 | ... 19 | maven { url 'https://jitpack.io' } 20 | } 21 | } 22 | Step 2. Add the dependency 23 | 24 | dependencies { 25 | compile 'com.github.chendongde310:SuperBadge:latest.release' 26 | } 27 | 28 | 29 | ### How to use 30 | ------------------------------------ 31 | 32 | * step1 初始化控件 33 | 34 | /** 35 | * 36 | * @param context Activity 37 | * @param view 绑定角标view 38 | * @param tag 用于绑定的唯一标记 39 | * @param num 角标数字 40 | * @param style 显示样式 (如使用小图标,设置样式为 STYLE_SMALL ) 41 | * @return SuperBadgeHelper 42 | */ 43 | SuperBadgeHelper.init(Activity context, View view, String tag, int num,int style) 44 | 45 | * step2 绑定上级控件 46 | 47 | /** 48 | * 根据父级控件tag绑定父级控件 49 | * @param tag 父级控件的Tag 50 | */ 51 | SuperBadgeHelper.bindView(String tag) 52 | //or 53 | SuperBadgeHelper.bindView(mSuperBadgeHelper) 54 | 55 | 56 | 57 | * step3 设置已读 58 | 59 | /** 60 | * 读取所有消息,清空数字为0 61 | */ 62 | SuperBadgeHelper.read() 63 | 64 | 65 | * other method 66 | 67 | //增加数字 - 必须为根节点控件 68 | addNum(int i) 69 | //减少数字 - 必须为根节点控件 70 | lessNum(int i) 71 | //是否显示角标 72 | setShowBadge(boolean b) 73 | //设置角标半径 74 | setDipRadius(int dipRadius) 75 | //设置角标颜色 76 | setBadgeColor(int badgeColor) 77 | //设置样式 (STYLE_DEFAULT、STYLE_GONE、STYLE_SMALL) 78 | setBadgeStyle(int style) 79 | //为0时是否隐藏badge(默认隐藏) 80 | setHideOnNull(boolean mHideOnNull) 81 | 82 | #TO DO 83 | 84 | * 提供自定义绘制方法 85 | * (已完成)小红点(不显示具体数字,半径小的红点提示) 86 | * read()方法 异步(网络 、数据库读写)请求失败后的处理 87 | * 增加将节点标记为未读状态功能(+1s) 88 | * 桌面(APP图标)角标显示 89 | 90 | -------------------------------------------------------------------------------- /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 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.2' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 10 | 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | jcenter() 17 | maven { url 'https://jitpack.io' } 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo.gif -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion '23.0.1' 6 | defaultConfig { 7 | applicationId "com.chendong.gank.superbadge" 8 | minSdkVersion 17 9 | targetSdkVersion 23 10 | versionCode 2 11 | versionName "0.1" 12 | 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | //compile project(':library') 25 | compile 'com.android.support:appcompat-v7:23.4.0' 26 | compile 'com.android.support:support-v13:23.4.0' 27 | compile 'com.android.support:support-v4:23.4.0' 28 | compile 'com.android.support:recyclerview-v7:23.4.0' 29 | compile 'com.orhanobut:logger:1.10' 30 | compile project(':library') 31 | } 32 | -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android_Studio\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/src/main/java/com/chendong/gank/superbadge/App.java: -------------------------------------------------------------------------------- 1 | package com.chendong.gank.superbadge; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | /** 7 | * 作者:陈东 — www.renwey.com 8 | * 日期:2016/12/5 - 9:47 9 | * 注释: 10 | */ 11 | public class App extends Activity { 12 | 13 | @Override 14 | protected void onRestoreInstanceState(Bundle savedInstanceState) { 15 | 16 | super.onRestoreInstanceState(savedInstanceState); 17 | } 18 | 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | 23 | super.onCreate(savedInstanceState); 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/src/main/java/com/chendong/gank/superbadge/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.chendong.gank.superbadge; 2 | 3 | 4 | import android.app.Activity; 5 | import android.app.Fragment; 6 | import android.app.FragmentManager; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.support.annotation.Nullable; 10 | import android.support.v13.app.FragmentPagerAdapter; 11 | import android.support.v4.view.ViewPager; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.view.WindowManager; 16 | import android.widget.TextView; 17 | 18 | import com.chendong.gank.library.SuperBadgeHelper; 19 | 20 | import static com.chendong.gank.library.SuperBadgeHelper.init; 21 | 22 | /** 23 | * 作者:chendong - github.com/chendongde310 24 | * 日期:2016/12/2 - 11:25 25 | * 注释: 26 | * 更新说明: 27 | */ 28 | public class MainActivity extends Activity { 29 | 30 | private SectionsPagerAdapter mSectionsPagerAdapter; 31 | 32 | private ViewPager mViewPager; 33 | private TextView text1; 34 | private TextView text2; 35 | private TextView text3; 36 | 37 | private PlaceholderFragment Fragment1; 38 | private PlaceholderFragment Fragment2; 39 | private PlaceholderFragment Fragment3; 40 | 41 | 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | setContentView(R.layout.activity_main); 47 | 48 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 49 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 50 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 51 | } 52 | 53 | this.text3 = (TextView) findViewById(R.id.text3); 54 | this.text2 = (TextView) findViewById(R.id.text2); 55 | this.text1 = (TextView) findViewById(R.id.text1); 56 | 57 | 58 | setOnClick(text1, 0); 59 | setOnClick(text2, 1); 60 | setOnClick(text3, 2); 61 | 62 | 63 | Fragment1 = PlaceholderFragment.newInstance(1, init(this, text1, text1.getText().toString()).getTag()); 64 | Fragment2 = PlaceholderFragment.newInstance(2, init(this, text2, text2.getText().toString()).getTag()); 65 | Fragment3 = PlaceholderFragment.newInstance(3, init(this, text3, text3.getText().toString()).getTag()); 66 | 67 | 68 | mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager()); 69 | mViewPager = (ViewPager) findViewById(R.id.container); 70 | mViewPager.setAdapter(mSectionsPagerAdapter); 71 | mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 72 | @Override 73 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 74 | 75 | } 76 | 77 | 78 | @Override 79 | public void onPageSelected(int position) { 80 | setColor(position); 81 | switch (position){ 82 | case 0: 83 | text1.setBackgroundColor(getResources().getColor(R.color.main_menu_tab)); 84 | break; 85 | case 1: 86 | text2.setBackgroundColor(getResources().getColor(R.color.main_menu_tab)); 87 | break; 88 | case 2: 89 | text3.setBackgroundColor(getResources().getColor(R.color.main_menu_tab)); 90 | break; 91 | } 92 | } 93 | 94 | @Override 95 | public void onPageScrollStateChanged(int state) { 96 | 97 | } 98 | }); 99 | mViewPager.setCurrentItem(1); 100 | } 101 | 102 | 103 | private void setOnClick(final View view, final int i) { 104 | 105 | view.setOnClickListener(new View.OnClickListener() { 106 | @Override 107 | public void onClick(View v) { 108 | setColor(i); 109 | view.setBackgroundColor(getResources().getColor(R.color.main_menu_tab)); 110 | } 111 | }); 112 | } 113 | 114 | private void setColor(int i){ 115 | mViewPager.setCurrentItem(i); 116 | text1.setBackgroundColor(getResources().getColor(R.color.white)); 117 | text2.setBackgroundColor(getResources().getColor(R.color.white)); 118 | text3.setBackgroundColor(getResources().getColor(R.color.white)); 119 | } 120 | 121 | public static class PlaceholderFragment extends Fragment { 122 | 123 | private static final String ARG_SECTION_NUMBER = "section_number"; 124 | private static final String SUPER_BADGE_TAG = "super_badge_tag"; 125 | 126 | private TextView textView; 127 | private SuperBadgeHelper tvsb; 128 | private Thread thread; 129 | private TextView rootbadge1; 130 | private TextView rootbadge2; 131 | private TextView rootbadge3; 132 | private SuperBadgeHelper rb1; 133 | private SuperBadgeHelper rb2; 134 | private SuperBadgeHelper rb3; 135 | 136 | public PlaceholderFragment() { 137 | } 138 | 139 | 140 | public static PlaceholderFragment newInstance(int sectionNumber, String tag) { 141 | PlaceholderFragment fragment = new PlaceholderFragment(); 142 | Bundle args = new Bundle(); 143 | args.putInt(ARG_SECTION_NUMBER, sectionNumber); 144 | args.putString(SUPER_BADGE_TAG, tag); 145 | fragment.setArguments(args); 146 | return fragment; 147 | } 148 | 149 | 150 | @Override 151 | public void onCreate(@Nullable Bundle savedInstanceState) { 152 | super.onCreate(savedInstanceState); 153 | 154 | 155 | } 156 | 157 | @Override 158 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 159 | Bundle savedInstanceState) { 160 | 161 | String sectionNumber = String.valueOf(getArguments().getInt(ARG_SECTION_NUMBER)); 162 | String tag = getArguments().getString(SUPER_BADGE_TAG); 163 | View rootView = inflater.inflate(R.layout.fragment_main, container, false); 164 | this.rootbadge3 = (TextView) rootView.findViewById(R.id.root_badge_3); 165 | this.rootbadge2 = (TextView) rootView.findViewById(R.id.root_badge_2); 166 | this.rootbadge1 = (TextView) rootView.findViewById(R.id.root_badge_1); 167 | 168 | rootbadge1.setText(String.format("%s根节点1", tag)); 169 | rootbadge2.setText(String.format("%s根节点2", tag)); 170 | rootbadge3.setText(String.format("%s根节点3", tag)); 171 | 172 | 173 | textView = (TextView) rootView.findViewById(R.id.section_label); 174 | textView.setText("读取全部" + tag + "节点"); 175 | tvsb = init(getActivity(), textView, "textView" + sectionNumber, 0); 176 | tvsb.bindView(getArguments().getString(SUPER_BADGE_TAG)); 177 | 178 | 179 | rb1 = SuperBadgeHelper.init(getActivity(), rootbadge1, "R.id.root_badge_1" + sectionNumber, 3,SuperBadgeHelper.STYLE_SMALL); 180 | rb2 = SuperBadgeHelper.init(getActivity(), rootbadge2, "R.id.root_badge_2" + sectionNumber, 1,SuperBadgeHelper.STYLE_GONE); 181 | rb3 = SuperBadgeHelper.init(getActivity(), rootbadge3, "R.id.root_badge_3" + sectionNumber, 7,SuperBadgeHelper.STYLE_DEFAULT); 182 | rb1.bindView(tvsb); 183 | rb2.bindView(tvsb); 184 | rb3.bindView(tvsb); 185 | 186 | 187 | 188 | // rb1.setBadgeColor(Color.parseColor("#d3321b")); 189 | // rb2.setBadgeColor(Color.parseColor("#d3321b")); 190 | // rb3.setBadgeColor(Color.parseColor("#d3321b")); 191 | 192 | 193 | setReadOnClick(textView, tvsb); 194 | setReadOnClick(rootbadge1, rb1); 195 | setReadOnClick(rootbadge2, rb2); 196 | setReadOnClick(rootbadge3, rb3); 197 | 198 | 199 | //模拟消息添加 200 | if (thread == null) { 201 | thread = new Thread(new Runnable() { 202 | @Override 203 | public void run() { 204 | while (getActivity() != null) { 205 | getActivity().runOnUiThread(new Runnable() { 206 | @Override 207 | public void run() { 208 | rb1.addNum((int) (Math.random() * 5)); 209 | rb2.addNum((int) (Math.random() * 5)); 210 | rb3.addNum((int) (Math.random() * 5)); 211 | } 212 | }); 213 | try { 214 | Thread.sleep((int) (Math.random() * 3000 + 10000)); 215 | } catch (InterruptedException e) { 216 | e.printStackTrace(); 217 | } 218 | } 219 | } 220 | }); 221 | thread.start(); 222 | } 223 | 224 | 225 | return rootView; 226 | } 227 | 228 | 229 | private void setReadOnClick(View view, final SuperBadgeHelper superBadge) { 230 | 231 | view.setOnClickListener(new View.OnClickListener() { 232 | @Override 233 | public void onClick(View v) { 234 | superBadge.read(); 235 | } 236 | }); 237 | } 238 | } 239 | 240 | 241 | public class SectionsPagerAdapter extends FragmentPagerAdapter { 242 | 243 | 244 | public SectionsPagerAdapter(FragmentManager fm) { 245 | super(fm); 246 | } 247 | 248 | @Override 249 | public Fragment getItem(int position) { 250 | 251 | switch (position) { 252 | case 0: 253 | return Fragment1; 254 | case 1: 255 | return Fragment2; 256 | case 2: 257 | return Fragment3; 258 | default: 259 | return new Fragment(); 260 | } 261 | } 262 | 263 | @Override 264 | public int getCount() { 265 | return 3; 266 | } 267 | 268 | 269 | 270 | @Override 271 | public CharSequence getPageTitle(int position) { 272 | return "页面" + position; 273 | } 274 | } 275 | 276 | 277 | 278 | 279 | 280 | } 281 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 13 | 20 | 21 | 30 | 31 | 41 | 42 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | 25 | 26 | 36 | 37 | 38 | 47 | 48 | 57 | 58 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #039cd9 4 | #039cd9 5 | #FF4081 6 | 7 | #DDDDDD 8 | #a4ffffff 9 | #039cd9 10 | 11 | -------------------------------------------------------------------------------- /demo/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SuperBadge 3 | MainActivity 4 | Settings 5 | 页面%1$d 6 | 7 | Hello sb 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendongde310/SuperBadge/87040eea3d3c682eef190405d850f59322a8603e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 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-3.2.1-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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group = 'com.github.chendongde310' 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.1" 7 | 8 | defaultConfig { 9 | minSdkVersion 11 10 | targetSdkVersion 23 11 | versionCode 3 12 | versionName "0.2.1" 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android_Studio\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/src/main/java/com/chendong/gank/library/BadgeManger.java: -------------------------------------------------------------------------------- 1 | package com.chendong.gank.library; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.Typeface; 6 | import android.graphics.drawable.ShapeDrawable; 7 | import android.graphics.drawable.shapes.RoundRectShape; 8 | import android.util.AttributeSet; 9 | import android.util.Log; 10 | import android.util.TypedValue; 11 | import android.view.Gravity; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.FrameLayout; 15 | import android.widget.FrameLayout.LayoutParams; 16 | import android.widget.TabWidget; 17 | import android.widget.TextView; 18 | 19 | import java.io.Serializable; 20 | 21 | /** 22 | * 作者:chendongde310 23 | * Github:www.github.com/chendongde310 24 | * 日期:2016/12/5 - 15:39 25 | * 注释:https://github.com/stefanjauker/BadgeView 26 | * 更新内容: 27 | */ 28 | public class BadgeManger extends TextView implements Serializable { 29 | 30 | public static final int STYLE_DEFAULT = 1; 31 | public static final int STYLE_GONE = 0; 32 | public static final int STYLE_SMALL = 2; 33 | private boolean mHideOnNull = true; 34 | private int style; 35 | 36 | public BadgeManger(Context context) { 37 | this(context, null); 38 | } 39 | 40 | public BadgeManger(Context context, AttributeSet attrs) { 41 | this(context, attrs, android.R.attr.textViewStyle); 42 | } 43 | 44 | public BadgeManger(Context context, AttributeSet attrs, int defStyle) { 45 | super(context, attrs, defStyle); 46 | 47 | init(); 48 | } 49 | 50 | private void init() { 51 | if (!(getLayoutParams() instanceof LayoutParams)) { 52 | LayoutParams layoutParams = 53 | new LayoutParams( 54 | ViewGroup.LayoutParams.WRAP_CONTENT, 55 | ViewGroup.LayoutParams.WRAP_CONTENT, 56 | Gravity.RIGHT | Gravity.TOP); 57 | setLayoutParams(layoutParams); 58 | } 59 | 60 | // set default font 61 | setTextColor(Color.WHITE); 62 | setTypeface(Typeface.DEFAULT_BOLD); 63 | setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); 64 | setPadding(dip2Px(5), dip2Px(1), dip2Px(5), dip2Px(1)); 65 | 66 | // set default background 67 | setBackground(9, Color.parseColor("#d3321b")); 68 | 69 | setGravity(Gravity.CENTER); 70 | 71 | // default values 72 | setHideOnNull(true); 73 | setBadgeCount(0); 74 | } 75 | 76 | public void setBackground(int dipRadius, int badgeColor) { 77 | int radius = dip2Px(dipRadius); 78 | float[] radiusArray = new float[]{radius, radius, radius, radius, radius, radius, radius, radius}; 79 | 80 | RoundRectShape roundRect = new RoundRectShape(radiusArray, null, null); 81 | ShapeDrawable bgDrawable = new ShapeDrawable(roundRect); 82 | bgDrawable.getPaint().setColor(badgeColor); 83 | setBackgroundDrawable(bgDrawable); 84 | } 85 | 86 | public void setBadgeStyle(int style) { 87 | this.style = style; 88 | switch (style) { 89 | case STYLE_DEFAULT: 90 | setVisibility(View.VISIBLE); 91 | init(); 92 | break; 93 | case STYLE_SMALL: 94 | setVisibility(View.VISIBLE); 95 | setSmallBadge(); 96 | break; 97 | case STYLE_GONE: 98 | setVisibility(View.GONE); 99 | break; 100 | } 101 | 102 | } 103 | 104 | private void setSmallBadge() { 105 | setWidth(dip2Px(7.5f)); 106 | setHeight(dip2Px(7.5f)); 107 | } 108 | 109 | 110 | public boolean isHideOnNull() { 111 | return mHideOnNull; 112 | } 113 | 114 | 115 | public void setHideOnNull(boolean hideOnNull) { 116 | mHideOnNull = hideOnNull; 117 | setText(getText()); 118 | } 119 | 120 | 121 | @Override 122 | public void setText(CharSequence text, TextView.BufferType type) { 123 | if (isHideOnNull() && (text == null || text.toString().equalsIgnoreCase("0"))) { 124 | setVisibility(View.GONE); 125 | } else { 126 | setVisibility(View.VISIBLE); 127 | } 128 | if (style == STYLE_GONE) { 129 | setVisibility(View.GONE); 130 | } 131 | super.setText(text, type); 132 | } 133 | 134 | 135 | public void setBadgeCount(int count) { 136 | 137 | if (style == STYLE_SMALL && count != 0) { 138 | setText(String.valueOf("")); 139 | } else { 140 | setText(String.valueOf(count)); 141 | } 142 | } 143 | 144 | public int getBadgeGravity() { 145 | LayoutParams params = (LayoutParams) getLayoutParams(); 146 | return params.gravity; 147 | } 148 | 149 | public void setBadgeGravity(int gravity) { 150 | LayoutParams params = (LayoutParams) getLayoutParams(); 151 | params.gravity = gravity; 152 | setLayoutParams(params); 153 | } 154 | 155 | public void setBadgeMargin(int leftDipMargin, int topDipMargin, int rightDipMargin, int bottomDipMargin) { 156 | LayoutParams params = (LayoutParams) getLayoutParams(); 157 | params.leftMargin = dip2Px(leftDipMargin); 158 | params.topMargin = dip2Px(topDipMargin); 159 | params.rightMargin = dip2Px(rightDipMargin); 160 | params.bottomMargin = dip2Px(bottomDipMargin); 161 | setLayoutParams(params); 162 | } 163 | 164 | public int[] getBadgeMargin() { 165 | LayoutParams params = (LayoutParams) getLayoutParams(); 166 | return new int[]{params.leftMargin, params.topMargin, params.rightMargin, params.bottomMargin}; 167 | } 168 | 169 | public void setBadgeMargin(int dipMargin) { 170 | setBadgeMargin(dipMargin, dipMargin, dipMargin, dipMargin); 171 | } 172 | 173 | 174 | public void setTargetView(TabWidget target, int tabIndex) { 175 | View tabView = target.getChildTabViewAt(tabIndex); 176 | setTargetView(tabView); 177 | } 178 | 179 | 180 | public void setTargetView(View target) { 181 | if (getParent() != null) { 182 | ((ViewGroup) getParent()).removeView(this); 183 | } 184 | 185 | if (target == null) { 186 | return; 187 | } 188 | 189 | 190 | if (target.getParent() instanceof FrameLayout) { 191 | ((FrameLayout) target.getParent()).addView(this); 192 | 193 | } else if (target.getParent() instanceof ViewGroup) { 194 | 195 | ViewGroup parentContainer = (ViewGroup) target.getParent(); 196 | int groupIndex = parentContainer.indexOfChild(target); 197 | parentContainer.removeView(target); 198 | 199 | FrameLayout badgeContainer = new FrameLayout(getContext()); 200 | ViewGroup.LayoutParams parentLayoutParams = target.getLayoutParams(); 201 | 202 | badgeContainer.setLayoutParams(parentLayoutParams); 203 | target.setLayoutParams(new ViewGroup.LayoutParams( 204 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 205 | 206 | parentContainer.addView(badgeContainer, groupIndex, parentLayoutParams); 207 | badgeContainer.addView(target); 208 | 209 | badgeContainer.addView(this); 210 | } else if (target.getParent() == null) { 211 | Log.e(getClass().getSimpleName(), "ParentView is needed"); 212 | } 213 | 214 | } 215 | 216 | 217 | private int dip2Px(float dip) { 218 | return (int) (dip * getContext().getResources().getDisplayMetrics().density + 0.5f); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /library/src/main/java/com/chendong/gank/library/StringSerializable.java: -------------------------------------------------------------------------------- 1 | package com.chendong.gank.library; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.text.TextUtils; 6 | import android.util.Log; 7 | 8 | import java.io.ByteArrayInputStream; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.IOException; 11 | import java.io.ObjectInputStream; 12 | import java.io.ObjectOutputStream; 13 | import java.io.StreamCorruptedException; 14 | 15 | /** 16 | * 17 | * 作者:chendongde310 18 | * Github:www.github.com/chendongde310 19 | * 日期:2016/12/5 - 16:20 20 | * 注释: 21 | * 更新内容: 22 | */ 23 | public class StringSerializable { 24 | 25 | 26 | private static final String FILENAME= "SuperBadgeDater"; 27 | 28 | /** 29 | * desc:保存对象 30 | 31 | * @param context 32 | * @param key 33 | * @param obj 要保存的对象,只能保存实现了serializable的对象 34 | * modified: 35 | */ 36 | static void saveSuperBadgeHelper(Context context, String key , SuperBadgeHelper obj){ 37 | try { 38 | // 保存对象 39 | SharedPreferences.Editor sharedata = context.getSharedPreferences(FILENAME, 0).edit(); 40 | //先将序列化结果写到byte缓存中,其实就分配一个内存空间 41 | ByteArrayOutputStream bos=new ByteArrayOutputStream(); 42 | ObjectOutputStream os=new ObjectOutputStream(bos); 43 | //将对象序列化写入byte缓存 44 | os.writeObject(obj); 45 | //将序列化的数据转为16进制保存 46 | String bytesToHexString = bytesToHexString(bos.toByteArray()); 47 | //保存该16进制数组 48 | sharedata.putString(key, bytesToHexString); 49 | sharedata.commit(); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | Log.e("", "保存SuperBadgeHelper失败"); 53 | } 54 | } 55 | /** 56 | * desc:将数组转为16进制 57 | * @param bArray 58 | * @return 59 | * modified: 60 | */ 61 | private static String bytesToHexString(byte[] bArray) { 62 | if(bArray == null){ 63 | return null; 64 | } 65 | if(bArray.length == 0){ 66 | return ""; 67 | } 68 | StringBuffer sb = new StringBuffer(bArray.length); 69 | String sTemp; 70 | for (int i = 0; i < bArray.length; i++) { 71 | sTemp = Integer.toHexString(0xFF & bArray[i]); 72 | if (sTemp.length() < 2) 73 | sb.append(0); 74 | sb.append(sTemp.toUpperCase()); 75 | } 76 | return sb.toString(); 77 | } 78 | /** 79 | * desc:获取保存的SuperBadgeHelper对象 80 | * @param context 81 | * @param key 82 | * @return 83 | * modified: 84 | */ 85 | static SuperBadgeHelper readSuperBadgeHelper(Context context,String key ){ 86 | try { 87 | SharedPreferences sharedata = context.getSharedPreferences(FILENAME, 0); 88 | if (sharedata.contains(key)) { 89 | String string = sharedata.getString(key, ""); 90 | if(TextUtils.isEmpty(string)){ 91 | return null; 92 | }else{ 93 | //将16进制的数据转为数组,准备反序列化 94 | byte[] stringToBytes = StringToBytes(string); 95 | ByteArrayInputStream bis=new ByteArrayInputStream(stringToBytes); 96 | ObjectInputStream is=new ObjectInputStream(bis); 97 | //返回反序列化得到的对象 98 | SuperBadgeHelper readSuperBadgeHelper = (SuperBadgeHelper) is.readObject(); 99 | return readSuperBadgeHelper; 100 | } 101 | } 102 | } catch (StreamCorruptedException e) { 103 | // TODO Auto-generated catch block 104 | e.printStackTrace(); 105 | } catch (IOException e) { 106 | // TODO Auto-generated catch block 107 | e.printStackTrace(); 108 | } catch (ClassNotFoundException e) { 109 | // TODO Auto-generated catch block 110 | e.printStackTrace(); 111 | } 112 | //所有异常返回null 113 | return null; 114 | 115 | } 116 | /** 117 | * desc:将16进制的数据转为数组 118 | * 119 | * @param data 120 | * @return 121 | * modified: 122 | */ 123 | private static byte[] StringToBytes(String data){ 124 | String hexString=data.toUpperCase().trim(); 125 | if (hexString.length()%2!=0) { 126 | return null; 127 | } 128 | byte[] retData=new byte[hexString.length()/2]; 129 | for(int i=0;i= '0' && hex_char1 <='9') 135 | int_ch1 = (hex_char1-48)*16; //// 0 的Ascll - 48 136 | else if(hex_char1 >= 'A' && hex_char1 <='F') 137 | int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65 138 | else 139 | return null; 140 | i++; 141 | char hex_char2 = hexString.charAt(i); ///两位16进制数中的第二位(低位) 142 | int int_ch2; 143 | if(hex_char2 >= '0' && hex_char2 <='9') 144 | int_ch2 = (hex_char2-48); //// 0 的Ascll - 48 145 | else if(hex_char2 >= 'A' && hex_char2 <='F') 146 | int_ch2 = hex_char2-55; //// A 的Ascll - 65 147 | else 148 | return null; 149 | int_ch = int_ch1+int_ch2; 150 | retData[i/2]=(byte) int_ch;//将转化后的数放入Byte里 151 | } 152 | return retData; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /library/src/main/java/com/chendong/gank/library/SuperBadgeDater.java: -------------------------------------------------------------------------------- 1 | package com.chendong.gank.library; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * 9 | * 作者:chendongde310 10 | * Github:www.github.com/chendongde310 11 | * 日期:2016/12/5 - 15:37 12 | * 注释:角标数据管理 13 | * 更新内容: 14 | */ 15 | 16 | public class SuperBadgeDater implements Serializable { 17 | 18 | private static final SuperBadgeDater Instance = new SuperBadgeDater(); 19 | 20 | Map map = new HashMap<>(); 21 | 22 | private SuperBadgeDater() { 23 | 24 | } 25 | public static SuperBadgeDater getInstance() { 26 | return Instance; 27 | } 28 | 29 | public void addBadge(SuperBadgeHelper superBadge) { 30 | map.put(superBadge.getTag(), superBadge); 31 | } 32 | public SuperBadgeHelper getBadge(String tag) { 33 | return map.get(tag); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /library/src/main/java/com/chendong/gank/library/SuperBadgeHelper.java: -------------------------------------------------------------------------------- 1 | package com.chendong.gank.library; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.view.View; 6 | 7 | import java.io.Serializable; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 作者:陈东 — www.renwey.com 13 | * 日期:2016/12/1 - 18:01 14 | * 注释: 15 | */ 16 | 17 | /** 18 | * 作者:chendongde310 19 | * Github:www.github.com/chendongde310 20 | * 日期:2016/12/5 - 15:38 21 | * 注释:角标助手,处理全局消息计数模型 22 | * 更新内容: 23 | * 1.添加小红点 24 | */ 25 | public class SuperBadgeHelper implements Serializable, Cloneable { 26 | 27 | public static final int STYLE_DEFAULT = 1; 28 | public static final int STYLE_GONE = 0; 29 | public static final int STYLE_SMALL = 2; 30 | 31 | private String tag; //标签 32 | private View view; //寄生控件 33 | private int num; //计数 34 | private List paterBadge = new ArrayList<>();//关联的上级节点 35 | private List childBadge = new ArrayList<>();//关联的下级节点 36 | private Activity context; //控件所在页面 37 | private BadgeManger badge;//红点管理器 38 | private int style; // 数字样式 39 | private OnNumCallback onNumCallback; 40 | 41 | /** 42 | * @param context 当前Avtivity 43 | * @param view 绑定角标view 44 | * @param tag 用于绑定的唯一标记 45 | * @param num 角标数字 46 | * @param style 是否显示数字 47 | * @return SuperBadgeHelper 48 | */ 49 | private SuperBadgeHelper(Activity context, View view, String tag, int num, int style) { 50 | if (SuperBadgeDater.getInstance().getBadge(tag) != null) { 51 | throw new IllegalArgumentException(tag + "标记已经被其他控件注册"); 52 | } 53 | if (context == null) { 54 | throw new NullPointerException("context not is null "); 55 | } 56 | if (num < 0) { 57 | throw new IllegalArgumentException("初始化角标数字不能小于0"); 58 | } 59 | if (tag == null) { 60 | throw new IllegalArgumentException("tag 不能为空"); 61 | } 62 | 63 | this.tag = tag; 64 | this.view = view; 65 | this.num = num; 66 | this.context = context; 67 | this.style = style; 68 | 69 | badge = new BadgeManger(context); 70 | badge.setTargetView(view); 71 | badge.setBadgeStyle(style); 72 | paterAddNum(num); 73 | } 74 | 75 | public static SuperBadgeHelper init(Activity context, View view, String tag) { 76 | return init(context, view, tag, 0); 77 | } 78 | 79 | public static SuperBadgeHelper init(Activity context, View view, String tag, int num) { 80 | return init(context, view, tag, num, STYLE_DEFAULT); 81 | } 82 | 83 | 84 | /** 85 | * @param context 当前Avtivity 86 | * @param view 绑定角标view 87 | * @param tag 用于绑定的唯一标记 88 | * @param num 角标数字 89 | * @param style 显示样式 90 | * @return SuperBadgeHelper 91 | */ 92 | public static SuperBadgeHelper init(Activity context, View view, String tag, int num, int style) { 93 | SuperBadgeHelper superBadge = SuperBadgeDater.getInstance().getBadge(tag); 94 | if (superBadge != null) { 95 | superBadge.setView(view); 96 | superBadge.setContext(context); 97 | superBadge.setBadgeStyle(style); 98 | superBadge.getBadge().setTargetView(view); 99 | if (superBadge.getStyle()) { 100 | superBadge.getBadge().setBadgeCount(superBadge.getNum()); 101 | } 102 | return superBadge; 103 | } else { 104 | return new SuperBadgeHelper(context, view, tag, num, style); 105 | } 106 | } 107 | 108 | 109 | public static SuperBadgeHelper getSBHelper(String tag) { 110 | SuperBadgeHelper superBadge = SuperBadgeDater.getInstance().getBadge(tag); 111 | if (superBadge == null) { 112 | throw new NullPointerException("没有找到标记为[" + tag + "]的控件"); 113 | } 114 | return superBadge; 115 | } 116 | 117 | 118 | @Deprecated 119 | public void setOnNumCallback(OnNumCallback onNumCallback) { 120 | this.onNumCallback = onNumCallback; 121 | } 122 | 123 | /** 124 | * 设置角标半径 125 | * 126 | * @param dipRadius 半径 127 | */ 128 | public void setDipRadius(int dipRadius) { 129 | badge.setBackground(dipRadius, Color.parseColor("#d3321b")); 130 | } 131 | 132 | /** 133 | * 设置角标颜色 134 | * 135 | * @param badgeColor 颜色 136 | */ 137 | public void setBadgeColor(int badgeColor) { 138 | badge.setBackground(9, badgeColor); 139 | } 140 | 141 | 142 | public void setBackground(int dipRadius, int badgeColor) { 143 | badge.setBackground(dipRadius, badgeColor); 144 | 145 | } 146 | 147 | /** 148 | * @return 149 | */ 150 | public boolean getStyle() { 151 | return style != STYLE_GONE; 152 | } 153 | 154 | 155 | public BadgeManger getBadge() { 156 | return badge; 157 | } 158 | 159 | /** 160 | * 控件添加数字 161 | * 162 | * @param i 添加数字大小 163 | */ 164 | public void addNum(int i) { 165 | if (childBadge.size() >= 1) { 166 | throw new IllegalArgumentException("该控件不是根节点数据控件(该控件包含下级控件),无法完成添加操作"); 167 | } 168 | paterAddNum(i); 169 | } 170 | 171 | 172 | private void paterAddNum(int i) { 173 | if (i < 0) { 174 | // throw new IllegalArgumentException("添加数字不能小于0"); 175 | } else { 176 | this.num = this.num + i; 177 | badge.setBadgeCount(num); 178 | SuperBadgeDater.getInstance().addBadge(this); 179 | //传递变化到上级控件 180 | for (SuperBadgeHelper bean : paterBadge) { 181 | if (bean != null) { 182 | bean.paterAddNum(i); 183 | } 184 | } 185 | } 186 | } 187 | 188 | 189 | public Activity getContext() { 190 | return context; 191 | } 192 | 193 | private void setContext(Activity context) { 194 | this.context = context; 195 | } 196 | 197 | 198 | public String getTag() { 199 | return tag; 200 | } 201 | 202 | private void setTag(String tag) { 203 | this.tag = tag; 204 | } 205 | 206 | public View getView() { 207 | return view; 208 | } 209 | 210 | private void setView(View view) { 211 | this.view = view; 212 | } 213 | 214 | public int getNum() { 215 | return num; 216 | } 217 | 218 | /** 219 | * 读取所有消息,减去所有数字 220 | */ 221 | public void read() { 222 | chlidLessNum(getNum()); 223 | } 224 | 225 | /** 226 | * @param i 减少数字 227 | */ 228 | private void chlidLessNum(int i) { 229 | if (i > 0) { 230 | 231 | badge.setBadgeCount(getNum() - i); 232 | 233 | this.num = num - i; 234 | SuperBadgeDater.getInstance().addBadge(this); 235 | changeBadge(i); 236 | } 237 | } 238 | 239 | /** 240 | * 减少 241 | * 242 | * @param i 减少数字 243 | */ 244 | public void lessNum(int i) { 245 | if (childBadge.size() >= 1) { 246 | throw new IllegalArgumentException("该控件不是根节点数据控件(包含下级控件),无法完成减少操作"); 247 | } 248 | chlidLessNum(i); 249 | } 250 | 251 | 252 | /** 253 | * 根据父级控件标签将他绑定到本级控件 254 | * 255 | * @param tag 父级控件的Tag 256 | */ 257 | public void bindView(String tag) { 258 | 259 | for (SuperBadgeHelper pater : paterBadge) { 260 | if (pater.getTag().equals(tag)) { 261 | // throw new IllegalArgumentException("不能重复添加相同控件"); 262 | return; 263 | } 264 | } 265 | SuperBadgeHelper paterBadgeHelper = SuperBadgeDater.getInstance().getBadge(tag); 266 | if (paterBadgeHelper != null) { 267 | paterBadge.add(paterBadgeHelper); //添加本级父控件 268 | paterBadgeHelper.addChild(this);//添加到父级子控件 269 | } else { 270 | throw new NullPointerException("没有找到标记为[" + tag + "]的控件"); 271 | } 272 | } 273 | 274 | 275 | public void bindView(SuperBadgeHelper pater) { 276 | bindView(pater.getTag()); 277 | } 278 | 279 | 280 | private void addChild(SuperBadgeHelper superBadgeHelper) { 281 | if (superBadgeHelper != null) { 282 | childBadge.add(superBadgeHelper); 283 | paterAddNum(superBadgeHelper.getNum()); 284 | } 285 | } 286 | 287 | 288 | /** 289 | * 传递关联的View 290 | * 291 | * @param num 减少的数字 292 | */ 293 | private void changeBadge(int num) { 294 | 295 | if (num > 0) { 296 | //传递变化到上级控件 297 | for (SuperBadgeHelper bean : paterBadge) { 298 | if (bean != null && bean.getNum() != 0) { 299 | bean.chlidLessNum(num); 300 | } 301 | } 302 | } 303 | 304 | //清空下级控件数字 305 | if (getNum() == 0) { 306 | if (childBadge.size() > 0) { 307 | for (SuperBadgeHelper bean : childBadge) { 308 | if (bean != null) { 309 | bean.read(); 310 | } 311 | } 312 | 313 | } 314 | } 315 | } 316 | 317 | /** 318 | * 角标样式 319 | * 320 | * @param style 321 | */ 322 | public void setBadgeStyle(int style) { 323 | this.style = style; 324 | badge.setBadgeStyle(style); 325 | } 326 | 327 | /** 328 | * 为0时是否显示 329 | * @param mHideOnNull 330 | */ 331 | public void setHideOnNull(boolean mHideOnNull) { 332 | badge.setHideOnNull(mHideOnNull); 333 | } 334 | 335 | 336 | public interface OnNumCallback { 337 | int lodingNum();//加载数字方法 338 | } 339 | 340 | 341 | public void setBadgeGravity(int gravity) { 342 | badge.setBadgeGravity(gravity); 343 | } 344 | 345 | 346 | public void setBadgeMargin(int dipMargin) { 347 | badge.setBadgeMargin(dipMargin); 348 | } 349 | 350 | public void setBadgeMargin(int leftDipMargin, int topDipMargin, int rightDipMargin, int bottomDipMargin) { 351 | badge.setBadgeMargin( leftDipMargin, topDipMargin, rightDipMargin, bottomDipMargin); 352 | } 353 | 354 | 355 | 356 | 357 | } 358 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Library 3 | 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':demo', ':library' 2 | --------------------------------------------------------------------------------