├── .gitattributes ├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── BadgedView.iml ├── BadgedView ├── .gitattributes ├── .gitignore ├── BadgedView-BadgedView.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── github │ │ └── chaossss │ │ └── widget │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── chaossss │ │ │ └── widget │ │ │ ├── badge │ │ │ ├── BaseBadge.java │ │ │ ├── CircleBadge.java │ │ │ ├── RecBadge.java │ │ │ └── TriBadge.java │ │ │ └── view │ │ │ ├── BadgedView.java │ │ │ ├── CircleBadgedView.java │ │ │ ├── RecBadgedView.java │ │ │ └── TriBadgedView.java │ └── res │ │ └── values │ │ ├── attrs_badged_view.xml │ │ ├── attrs_circlebadged_view.xml │ │ ├── attrs_recbadged_view.xml │ │ ├── attrs_tribadged_view.xml │ │ ├── colors.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── github │ └── chaossss │ └── widget │ └── ExampleUnitTest.java ├── README.md ├── README_ENGLISH.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── github │ │ └── chaossss │ │ └── badgedview │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── chaossss │ │ │ └── badgedview │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ ├── gem.png │ │ ├── hebe.png │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ ├── colors.xml │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── github │ └── chaossss │ └── badgedview │ └── ExampleUnitTest.java ├── build.gradle ├── example.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | BadgedView -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BadgedView.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BadgedView/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /BadgedView/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /BadgedView/BadgedView-BadgedView.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /BadgedView/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | 5 | version = "1.1.0" 6 | 7 | android { 8 | compileSdkVersion 23 9 | buildToolsVersion "23.0.2" 10 | 11 | defaultConfig { 12 | minSdkVersion 12 13 | targetSdkVersion 23 14 | versionCode 1 15 | versionName "1.1.0" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(dir: 'libs', include: ['*.jar']) 27 | testCompile 'junit:junit:4.12' 28 | compile 'com.android.support:appcompat-v7:23.1.0' 29 | } 30 | 31 | def siteUrl = 'https://github.com/chaossss/BadgedView' 32 | def gitUrl = 'https://github.com/chaossss/BadgedView' 33 | group = "com.github.chaossss" 34 | 35 | install { 36 | repositories.mavenInstaller { 37 | // This generates POM.xml with proper parameters 38 | pom { 39 | project { 40 | packaging 'aar' 41 | // Add your description here 42 | name 'BadgedView allows you show a badge into any View' 43 | url siteUrl 44 | // Set your license 45 | licenses { 46 | license { 47 | name 'The Apache Software License, Version 2.0' 48 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 49 | } 50 | } 51 | developers { 52 | developer { 53 | id 'chaossss' 54 | name 'chaossss' 55 | email '437700255@qq.com' 56 | } 57 | } 58 | scm { 59 | connection gitUrl 60 | developerConnection gitUrl 61 | url siteUrl 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | task sourcesJar(type: Jar) { 69 | from android.sourceSets.main.java.srcDirs 70 | classifier = 'sources' 71 | } 72 | task javadoc(type: Javadoc) { 73 | source = android.sourceSets.main.java.srcDirs 74 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 75 | } 76 | task javadocJar(type: Jar, dependsOn: javadoc) { 77 | classifier = 'javadoc' 78 | from javadoc.destinationDir 79 | } 80 | artifacts { 81 | archives javadocJar 82 | archives sourcesJar 83 | } 84 | 85 | Properties properties = new Properties() 86 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 87 | bintray { 88 | user = properties.getProperty("bintray.user") 89 | key = properties.getProperty("bintray.apikey") 90 | configurations = ['archives'] 91 | pkg { 92 | repo = "maven" 93 | name = "BadgedView" 94 | websiteUrl = siteUrl 95 | vcsUrl = gitUrl 96 | licenses = ["Apache-2.0"] 97 | publish = true 98 | } 99 | } -------------------------------------------------------------------------------- /BadgedView/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\ADT\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 | -------------------------------------------------------------------------------- /BadgedView/src/androidTest/java/com/github/chaossss/widget/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /BadgedView/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/badge/BaseBadge.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.badge; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.ColorFilter; 7 | import android.graphics.Paint; 8 | import android.graphics.Rect; 9 | import android.graphics.Typeface; 10 | import android.graphics.drawable.Drawable; 11 | 12 | /** 13 | * Class defines what attr Badge owns, and how badge finish its drawing 14 | * Created by chaos on 2015/11/26. 15 | */ 16 | public class BaseBadge extends Drawable { 17 | protected static final String TYPEFACE = "sans-serif-black"; 18 | protected static final int TYPEFACE_STYLE = Typeface.NORMAL; 19 | 20 | protected int badgePadding; 21 | protected int badgeCornerRadius; 22 | 23 | protected int textSize; 24 | protected int badgeColor; 25 | protected int badgeTextColor; 26 | 27 | protected String badgeText; 28 | 29 | protected Paint paint; 30 | protected Bitmap badge; 31 | protected Context context; 32 | 33 | public BaseBadge(Context context, String badgeText, int badgeColor, int badgeTextColor) { 34 | this.badgeText = badgeText; 35 | 36 | this.badgeColor = badgeColor; 37 | this.badgeTextColor = badgeTextColor; 38 | 39 | paint = new Paint(); 40 | this.context = context; 41 | } 42 | 43 | public void setText(String text) { 44 | this.badgeText = text; 45 | } 46 | 47 | public void setCornerRadius(int corner_radius) { 48 | this.badgeCornerRadius = corner_radius; 49 | } 50 | 51 | public void setTextSize(int textSize) { 52 | this.textSize = textSize; 53 | } 54 | 55 | public void setPadding(int padding) { 56 | this.badgePadding = padding; 57 | } 58 | 59 | @Override 60 | public void draw(Canvas canvas) { 61 | } 62 | 63 | public void initBadge(){ 64 | 65 | } 66 | 67 | public void reset() { 68 | if(badge != null) { 69 | badge.recycle(); 70 | badge = null; 71 | } 72 | } 73 | 74 | @Override 75 | public void setAlpha(int alpha) { 76 | } 77 | 78 | @Override 79 | public void setColorFilter(ColorFilter colorFilter) { 80 | } 81 | 82 | @Override 83 | public int getOpacity() { 84 | return 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/badge/CircleBadge.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.badge; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.graphics.Typeface; 9 | import android.text.TextPaint; 10 | import android.util.DisplayMetrics; 11 | 12 | /** 13 | * Created by chaos on 2016/1/6. 14 | */ 15 | public class CircleBadge extends BaseBadge { 16 | private static final double MAGIC = 1.732; 17 | 18 | private int radius; 19 | private int center; 20 | 21 | public CircleBadge(Context context, String badgeText, int badgeColor, int badgeTextColor) { 22 | super(context, badgeText, badgeColor, badgeTextColor); 23 | } 24 | 25 | @Override 26 | public void initBadge() { 27 | Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 28 | backgroundPaint.setColor(badgeColor); 29 | backgroundPaint.setStyle(Paint.Style.FILL); 30 | 31 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 32 | float scaledDensity = dm.scaledDensity; 33 | 34 | TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint 35 | .SUBPIXEL_TEXT_FLAG); 36 | textPaint.setTypeface(Typeface.create(TYPEFACE, TYPEFACE_STYLE)); 37 | textPaint.setTextSize(textSize * scaledDensity); 38 | textPaint.setTextAlign(Paint.Align.CENTER); 39 | textPaint.setColor(badgeTextColor); 40 | 41 | Rect textBounds = new Rect(); 42 | textPaint.getTextBounds(badgeText, 0, badgeText.length(), textBounds); 43 | 44 | if(textBounds.height() > textBounds.width()){ 45 | radius = (int)(textBounds.height() / 2 * MAGIC); 46 | } else { 47 | radius = (int)(textBounds.width() / 2 * MAGIC); 48 | } 49 | 50 | badge = Bitmap.createBitmap(radius * 2, radius * 2, Bitmap.Config.ARGB_8888); 51 | badge.setHasAlpha(true); 52 | 53 | Canvas canvas = new Canvas(badge); 54 | canvas.drawCircle(radius, radius, radius, backgroundPaint); 55 | canvas.drawText(badgeText, radius, radius + textBounds.height() / 2, textPaint); 56 | } 57 | 58 | @Override 59 | public void draw(Canvas canvas) { 60 | if(badge == null) { 61 | initBadge(); 62 | } 63 | canvas.drawBitmap(badge, getBounds().left, getBounds().top, paint); 64 | } 65 | 66 | @Override 67 | public int getIntrinsicWidth() { 68 | return radius * 2; 69 | } 70 | 71 | @Override 72 | public int getIntrinsicHeight() { 73 | return radius * 2; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/badge/RecBadge.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.badge; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.PorterDuff; 8 | import android.graphics.PorterDuffXfermode; 9 | import android.graphics.Rect; 10 | import android.graphics.Typeface; 11 | import android.os.Build; 12 | import android.text.TextPaint; 13 | import android.util.DisplayMetrics; 14 | 15 | /** 16 | * Badge that draws a rectangle 17 | * Created by chaos on 2015/11/26. 18 | */ 19 | public class RecBadge extends BaseBadge { 20 | private int width; 21 | private int height; 22 | 23 | public RecBadge(Context context, String badgeText, int badgeColor, int badgeTextColor) { 24 | super(context, badgeText, badgeColor, badgeTextColor); 25 | } 26 | 27 | @Override 28 | public void initBadge(){ 29 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 30 | float density = dm.density; 31 | float scaledDensity = dm.scaledDensity; 32 | 33 | TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint 34 | .SUBPIXEL_TEXT_FLAG); 35 | textPaint.setTypeface(Typeface.create(TYPEFACE, TYPEFACE_STYLE)); 36 | textPaint.setTextSize(textSize * scaledDensity); 37 | textPaint.setColor(badgeTextColor); 38 | 39 | float padding = badgePadding * density; 40 | float cornerRadius = badgeCornerRadius * density; 41 | 42 | Rect textBounds = new Rect(); 43 | textPaint.getTextBounds(badgeText, 0, badgeText.length(), textBounds); 44 | height = (int) (padding + textBounds.height() + padding); 45 | width = (int) (padding + textBounds.width() + padding); 46 | 47 | badge = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 48 | badge.setHasAlpha(true); 49 | 50 | Canvas canvas = new Canvas(badge); 51 | Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 52 | backgroundPaint.setColor(badgeColor); 53 | 54 | if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) { 55 | canvas.drawRoundRect(0, 0, width, height, cornerRadius, cornerRadius, backgroundPaint); 56 | }else { 57 | // TODO: 11/21/15 support cornerRadius for api < 21 58 | canvas.drawRect(0, 0, width, height, backgroundPaint); 59 | } 60 | 61 | // punch out the word ,leaving transparency 62 | textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 63 | canvas.drawText(badgeText, padding, height - padding, textPaint); 64 | } 65 | 66 | @Override 67 | public void draw(Canvas canvas) { 68 | if(badge == null) { 69 | initBadge(); 70 | } 71 | canvas.drawBitmap(badge, getBounds().left, getBounds().top, paint); 72 | } 73 | 74 | @Override 75 | public int getIntrinsicWidth() { 76 | return width; 77 | } 78 | 79 | @Override 80 | public int getIntrinsicHeight() { 81 | return height; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/badge/TriBadge.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.badge; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.Rect; 9 | import android.graphics.Typeface; 10 | import android.text.TextPaint; 11 | import android.util.DisplayMetrics; 12 | 13 | /** 14 | * Badge that draws a triangle 15 | * Created by chaos on 2015/11/27. 16 | */ 17 | public class TriBadge extends BaseBadge { 18 | private static final double SQUARE2 = 1.414; 19 | private static final double SQUARE5 = 2.236; 20 | 21 | public static final int TOPLEFT = 0x00; 22 | public static final int TOPRIGHT = 0x01; 23 | public static final int BOTTMLEFT = 0x10; 24 | public static final int BOTTOMRIGHT = 0x11; 25 | 26 | private int triLength; 27 | private int rootViewWidth; 28 | private int rootViewHeight; 29 | private int tribadgeGravity; 30 | 31 | public TriBadge(Context context, String badgeText, int badgeColor, int badgeTextColor) { 32 | super(context, badgeText, badgeColor, badgeTextColor); 33 | } 34 | 35 | public void setTribadgeGravity(int tribadgeGravity) { 36 | this.tribadgeGravity = tribadgeGravity; 37 | } 38 | 39 | public void setRootViewParam(int rootViewWidth, int rootViewHeight){ 40 | this.rootViewWidth = rootViewWidth; 41 | this.rootViewHeight = rootViewHeight; 42 | } 43 | 44 | @Override 45 | public void initBadge() { 46 | Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 47 | backgroundPaint.setColor(badgeColor); 48 | backgroundPaint.setStyle(Paint.Style.FILL); 49 | 50 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 51 | float scaledDensity = dm.scaledDensity; 52 | 53 | TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint 54 | .SUBPIXEL_TEXT_FLAG); 55 | textPaint.setTypeface(Typeface.create(TYPEFACE, TYPEFACE_STYLE)); 56 | textPaint.setTextSize(textSize * scaledDensity); 57 | textPaint.setColor(badgeTextColor); 58 | 59 | badge = Bitmap.createBitmap(rootViewWidth, rootViewHeight, Bitmap.Config.ARGB_8888); 60 | badge.setHasAlpha(true); 61 | 62 | Canvas canvas = new Canvas(badge); 63 | 64 | Path path = new Path(); 65 | Rect textBounds = new Rect(); 66 | textPaint.getTextBounds(badgeText, 0, badgeText.length(), textBounds); 67 | 68 | //Calculates triangle badges's length by the input badgeText's length 69 | triLength = (int)((textBounds.height() / 4 * 7 + textBounds.width() / 2) * SQUARE2); 70 | 71 | drawTribadgePath(canvas, path, backgroundPaint); 72 | 73 | float textYOffset = textBounds.height() / 8 * 11; 74 | //Keep the offset distance 75 | if(tribadgeGravity >> 1 == 0){ 76 | textYOffset = -textYOffset / 2; 77 | } 78 | float textXOffset = (float)(triLength * SQUARE2 - textBounds.width()) / 2; 79 | 80 | canvas.drawTextOnPath(badgeText, path, textXOffset, textYOffset, textPaint); 81 | } 82 | 83 | private void drawTribadgePath(Canvas canvas, Path path, Paint backgroundPaint){ 84 | switch (tribadgeGravity){ 85 | case TOPLEFT: 86 | path.moveTo(0, triLength); 87 | path.lineTo(triLength, 0); 88 | path.lineTo(0, 0); 89 | path.close(); 90 | break; 91 | 92 | case TOPRIGHT: 93 | path.moveTo(rootViewWidth - triLength, 0); 94 | path.lineTo(rootViewWidth, triLength); 95 | path.lineTo(rootViewWidth, 0); 96 | path.close(); 97 | break; 98 | 99 | case BOTTMLEFT: 100 | path.moveTo(0, rootViewHeight - triLength); 101 | path.lineTo(triLength, rootViewHeight); 102 | path.lineTo(0, rootViewHeight); 103 | path.close(); 104 | break; 105 | 106 | case BOTTOMRIGHT: 107 | path.moveTo(rootViewWidth - triLength, rootViewHeight); 108 | path.lineTo(rootViewWidth, rootViewHeight - triLength); 109 | path.lineTo(rootViewWidth, rootViewHeight); 110 | path.close(); 111 | break; 112 | } 113 | 114 | canvas.drawPath(path, backgroundPaint); 115 | } 116 | 117 | @Override 118 | public void draw(Canvas canvas) { 119 | if(badge == null) { 120 | initBadge(); 121 | } 122 | canvas.drawBitmap(badge, 0, 0, paint); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/view/BadgedView.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.os.Build; 7 | import android.support.v4.content.ContextCompat; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.view.ViewOutlineProvider; 13 | 14 | import com.github.chaossss.widget.R; 15 | import com.github.chaossss.widget.badge.BaseBadge; 16 | 17 | /** 18 | * An extension to {@link ViewGroup} which has a Badge. 19 | * Created by chaos on 2015/11/27. 20 | */ 21 | public class BadgedView extends ViewGroup { 22 | protected BaseBadge badge; 23 | 24 | protected boolean drawBadge = false; 25 | 26 | protected int badgeColor; 27 | protected int widthRatio; 28 | protected int heightRatio; 29 | protected int badgePadding; 30 | 31 | protected int badgeTextSize; 32 | protected int badgeTextColor; 33 | protected int badgeCornerRadius; 34 | 35 | protected String badgeText; 36 | 37 | public BadgedView(Context context, AttributeSet attrs) { 38 | super(context, attrs); 39 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BadgedImageView, 0, 0); 40 | 41 | badgePadding = a.getDimensionPixelSize(R.styleable.BadgedImageView_badgePadding, 4); 42 | badgeTextSize = a.getDimensionPixelSize(R.styleable.BadgedImageView_badgeTextSize, 12); 43 | badgeCornerRadius = a.getDimensionPixelSize(R.styleable.BadgedImageView_badgeCornerRadius, 2); 44 | 45 | badgeText = a.getString(R.styleable.BadgedImageView_badgeText); 46 | 47 | badgeColor = a.getColor(R.styleable.BadgedImageView_badgeColor, ContextCompat.getColor(context, R.color.default_badge_color)); 48 | badgeTextColor = a.getColor(R.styleable.BadgedImageView_badgeTextColor, ContextCompat.getColor(context, R.color.default_badge_text_color)); 49 | 50 | widthRatio = a.getInt(R.styleable.BadgedImageView_badgeWidthRatio, 1); 51 | heightRatio = a.getInt(R.styleable.BadgedImageView_badgeHeightRatio, 1); 52 | 53 | initBadge(context); 54 | 55 | a.recycle(); 56 | setWillNotDraw(false); 57 | if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) 58 | setOutlineProvider(ViewOutlineProvider.BOUNDS); 59 | } 60 | 61 | public void initBadge(Context context) { 62 | badge = new BaseBadge(context, badgeText, badgeColor, badgeTextColor); 63 | badge.setPadding(badgePadding); 64 | badge.setTextSize(badgeTextSize); 65 | badge.setCornerRadius(badgeCornerRadius); 66 | } 67 | 68 | @Override 69 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 70 | int width = getScaleWidth(widthMeasureSpec); 71 | int height = getScaleHeight(heightMeasureSpec); 72 | super.onMeasure(width, height); 73 | measureChildren(width, height); 74 | } 75 | 76 | @Override 77 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 78 | for(int i = 0; i < getChildCount(); i++) { 79 | View child = getChildAt(i); 80 | LayoutParams lp = child.getLayoutParams(); 81 | child.layout(lp.width, lp.height, lp.width + child.getMeasuredWidth(), lp.height + child.getMeasuredHeight()); 82 | } 83 | } 84 | 85 | private int getScaleWidth(int widthMeasureSpec){ 86 | return !isScaleWidth() ? widthMeasureSpec : MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec) * widthRatio / heightRatio, 87 | MeasureSpec.EXACTLY); 88 | } 89 | 90 | private int getScaleHeight(int heightMeasureSpec){ 91 | return isScaleWidth() ? heightMeasureSpec : MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) * heightRatio / widthRatio, 92 | MeasureSpec.EXACTLY); 93 | } 94 | 95 | private boolean isScaleWidth() { 96 | return widthRatio / heightRatio < 1; 97 | } 98 | 99 | public void showBadge(boolean show) { 100 | drawBadge = show; 101 | invalidate(); 102 | } 103 | 104 | public void setBadgeText(String newText) { 105 | badge.setText(newText); 106 | badge.reset(); 107 | invalidate(); 108 | } 109 | 110 | public boolean isBadgeVisible() { 111 | return drawBadge; 112 | } 113 | 114 | @Override 115 | public void draw(Canvas canvas) { 116 | super.draw(canvas); 117 | if (drawBadge) { 118 | badge.draw(canvas); 119 | } 120 | } 121 | 122 | @Override 123 | public boolean onInterceptTouchEvent(MotionEvent ev) { 124 | return true; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/view/CircleBadgedView.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.util.AttributeSet; 8 | import android.view.Gravity; 9 | 10 | import com.github.chaossss.widget.R; 11 | import com.github.chaossss.widget.badge.CircleBadge; 12 | 13 | /** 14 | * Created by chaos on 2016/1/6. 15 | */ 16 | public class CircleBadgedView extends BadgedView { 17 | private int circlebadgeGravity; 18 | private boolean badgeBoundsSet = false; 19 | 20 | public CircleBadgedView(Context context, AttributeSet attrs) { 21 | super(context, attrs); 22 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecBadgedView, 0, 0); 23 | circlebadgeGravity = a.getInt(R.styleable.CircleBadgedView_circlebadgeGravity, Gravity.TOP | Gravity 24 | .RIGHT); 25 | a.recycle(); 26 | } 27 | 28 | @Override 29 | public void initBadge(Context context) { 30 | badge = new CircleBadge(context, badgeText, badgeColor, badgeTextColor); 31 | badge.setTextSize(badgeTextSize); 32 | } 33 | 34 | @Override 35 | public void draw(Canvas canvas) { 36 | super.draw(canvas); 37 | if (drawBadge) { 38 | badge.draw(canvas); 39 | 40 | if (!badgeBoundsSet) { 41 | layoutBadge(); 42 | } 43 | } 44 | } 45 | 46 | private void layoutBadge() { 47 | Rect badgeBounds = badge.getBounds(); 48 | 49 | Gravity.apply(circlebadgeGravity, 50 | badge.getIntrinsicWidth(), 51 | badge.getIntrinsicHeight(), 52 | new Rect(0, 0, getWidth(), getHeight()), 53 | badgeBounds); 54 | badge.setBounds(badgeBounds); 55 | badgeBoundsSet = true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/view/RecBadgedView.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.util.AttributeSet; 8 | import android.view.Gravity; 9 | 10 | import com.github.chaossss.widget.R; 11 | import com.github.chaossss.widget.badge.RecBadge; 12 | 13 | /** 14 | * An extension to {@link BadgedView} which has a Rectangle Badge. 15 | * Created by chaos on 2015/11/25. 16 | */ 17 | 18 | public class RecBadgedView extends BadgedView { 19 | private int recbadgeGravity; 20 | private boolean badgeBoundsSet = false; 21 | 22 | public RecBadgedView(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | 25 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecBadgedView, 0, 0); 26 | recbadgeGravity = a.getInt(R.styleable.RecBadgedView_recbadgeGravity, Gravity.TOP | Gravity 27 | .LEFT); 28 | a.recycle(); 29 | } 30 | 31 | @Override 32 | public void initBadge(Context context) { 33 | badge = new RecBadge(context, badgeText, badgeColor, badgeTextColor); 34 | badge.setPadding(badgePadding); 35 | badge.setTextSize(badgeTextSize); 36 | badge.setCornerRadius(badgeCornerRadius); 37 | } 38 | 39 | @Override 40 | public void draw(Canvas canvas) { 41 | super.draw(canvas); 42 | if (drawBadge) { 43 | badge.draw(canvas); 44 | 45 | if (!badgeBoundsSet) { 46 | layoutBadge(); 47 | } 48 | } 49 | } 50 | 51 | private void layoutBadge() { 52 | Rect badgeBounds = badge.getBounds(); 53 | 54 | Gravity.apply(recbadgeGravity, 55 | badge.getIntrinsicWidth(), 56 | badge.getIntrinsicHeight(), 57 | new Rect(0, 0, getWidth(), getHeight()), 58 | badgePadding, 59 | badgePadding, 60 | badgeBounds); 61 | badge.setBounds(badgeBounds); 62 | badgeBoundsSet = true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /BadgedView/src/main/java/com/github/chaossss/widget/view/TriBadgedView.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.util.AttributeSet; 7 | 8 | import com.github.chaossss.widget.R; 9 | import com.github.chaossss.widget.badge.TriBadge; 10 | 11 | /** 12 | * An extension to {@link BadgedView} which has a Triangle Badge. 13 | * Created by chaos on 2015/11/27. 14 | */ 15 | public class TriBadgedView extends BadgedView { 16 | private int tribadgeGravity; 17 | 18 | public TriBadgedView(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TriBadgedView, 0, 0); 21 | tribadgeGravity = a.getInt(R.styleable.TriBadgedView_TribadgeGravity, TriBadge.BOTTOMRIGHT); 22 | a.recycle(); 23 | } 24 | 25 | @Override 26 | public void initBadge(Context context) { 27 | badge = new TriBadge(context, badgeText, badgeColor, badgeTextColor); 28 | badge.setPadding(badgePadding); 29 | badge.setTextSize(badgeTextSize); 30 | badge.setCornerRadius(badgeCornerRadius); 31 | } 32 | 33 | @Override 34 | public void draw(Canvas canvas) { 35 | ((TriBadge)badge).setTribadgeGravity(tribadgeGravity); 36 | ((TriBadge)badge).setRootViewParam(getWidth(), getHeight()); 37 | super.draw(canvas); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /BadgedView/src/main/res/values/attrs_badged_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BadgedView/src/main/res/values/attrs_circlebadged_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BadgedView/src/main/res/values/attrs_recbadged_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BadgedView/src/main/res/values/attrs_tribadged_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /BadgedView/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF4081 4 | #50000000 5 | 6 | -------------------------------------------------------------------------------- /BadgedView/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BadgeView 3 | 4 | -------------------------------------------------------------------------------- /BadgedView/src/test/java/com/github/chaossss/widget/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.github.chaossss.widget; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BadgedView 2 | 3 | [English Version](README_ENGLISH.md) 4 | 5 | BadgedView 能够为任意一个 View 添加矩形或三角形标签。 6 | 7 | BadgedImageView 就是 [Plaid](https://github.com/nickbutcher/plaid) 和 [虎扑体育](http://mobile.hupu.com/?_r=globalNav) 用来给图片添加标签的控件。原作者的实现里,只能为 ImageView 添加 Badge,我看了看他的代码([开源](https://github.com/yesidlazaro/BadgedImageview))发现这个控件其实蛮简单的,想法挺好,但是代码写的不是特别好。而且我觉得应该能将这个控件应用到任意一个 View 上边,所以我根据我的想法实现了 BadgedView。 8 | 9 | 效果图: 10 | 11 | ![](example.png) 12 | 13 | #Feature 14 | 15 | ## Implemented 16 | 17 | - 设置标签内的文字 18 | - 设置标签背景颜色 19 | - 自定义标签形状、文字 20 | - 将标签应用于任意 View 21 | - 可在标签所在布局设置长宽比例 22 | - 设置矩形标签或三角形标签在视图中的位置 23 | - 为任意 View 添加矩形、三角形、圆形标签 24 | 25 | ## Processing 26 | 27 | - 添加其他样式的标签 28 | - 不需要实现子类就能为每一个 View 添加任意多的标签 29 | 30 | #Usage 31 | 32 | ##Dependency 33 | 34 | ###Min SDK:2.3.3 35 | 36 | ###Add Dependency 37 | 38 | 在项目的 build.gradle 里添加: 39 | 40 | ``` 41 | dependencies { 42 | ... 43 | compile 'com.github.chaossss:BadgedView:1.1.1' 44 | } 45 | ``` 46 | 47 | ##Attr 48 | 49 | | Attr | usage | 50 | |---------|--------| 51 | | app:badgeText | 设置标签中显示的文字 | 52 | | app:badgeTextSize | 设置标签中显示的文字大小 | 53 | | app:badgeTextColor | 设置标签中显示的文字颜色 | 54 | | app:badgePadding | 设置标签中文字和标签边界的间距 | 55 | | app:badgeWidthRatio | 设置标签布局的长宽比 | 56 | | app:badgeHeightRatio | 设置标签布局的长宽比 | 57 | | app:badgeCornerRadius | 设置矩形标签圆角的半径 | 58 | | app:recbadgeGravity(RecBadgedView) | 设置矩形标签的位置 | 59 | | app:tribadgeGravity(TriBadgedView) | 设置三角标签的位置 | 60 | 61 | ##API 62 | 63 | | API | usage | 64 | |---------|--------| 65 | | showBadge(boolean show) | 显示标签 | 66 | | setBadgeText(String newText) | 设置标签显示文字 | 67 | 68 | ##Customize BadgedView 69 | 70 | 1. 继承 BadgedView,父类 BadgedView 已经有了标签常用的属性 71 | 2. 在子类添加你所需要的特定的属性域 72 | 3. 重写 initBadge(Context context) 和 draw(Canvas canvas) 方法以实现你的需求 73 | 74 | 如果需要自定义标签: 75 | 76 | 4. 继承 BaseBadge 77 | 5. 在子类添加你所需要的特定的属性域 78 | 6. 重写 initBadge(),在其中对 badge 完成你所需要的标签的绘制工作 79 | 80 | ##Activity 81 | 82 | ```java 83 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 84 | private RecBadgedView anyView; 85 | private RecBadgedView scaleView; 86 | private RecBadgedView regularView; 87 | 88 | private TriBadgedView triView; 89 | 90 | @Override 91 | protected void onCreate(Bundle savedInstanceState) { 92 | super.onCreate(savedInstanceState); 93 | setContentView(R.layout.activity_main); 94 | 95 | anyView = (RecBadgedView) findViewById(R.id.badge_any_view); 96 | scaleView = (RecBadgedView) findViewById(R.id.badge_scale_view); 97 | regularView = (RecBadgedView) findViewById(R.id.badge_regular_view); 98 | 99 | triView = (TriBadgedView) findViewById(R.id.badge_tri_view); 100 | 101 | anyView.setBadgeText("I'm badge"); 102 | anyView.setOnClickListener(this); 103 | anyView.showBadge(true); 104 | 105 | scaleView.showBadge(true); 106 | regularView.showBadge(true); 107 | 108 | triView.showBadge(true); 109 | } 110 | 111 | @Override 112 | public void onClick(View v) { 113 | if(anyView.isBadgeVisible()){ 114 | anyView.showBadge(false); 115 | } else { 116 | anyView.showBadge(true); 117 | } 118 | } 119 | } 120 | ``` 121 | 122 | ##XML 123 | 124 | ```xml 125 | 126 | 131 | 135 | 145 | 150 | 151 | 152 | 160 | 165 | 166 | 167 | 174 |