├── .gitignore ├── README.md ├── build.gradle ├── extras ├── mrb_star_border_icon_black_24dp.xml └── mrb_star_border_icon_black_48dp.xml ├── gradle-mvn-push.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── zhanghai │ │ └── android │ │ └── materialratingbar │ │ ├── BaseDrawable.java │ │ ├── ClipDrawableCompat.java │ │ ├── MaterialRatingBar.java │ │ ├── MaterialRatingDrawable.java │ │ ├── TileDrawable.java │ │ ├── TintableDrawable.java │ │ └── internal │ │ ├── DrawableCompat.java │ │ └── ThemeUtils.java │ └── res │ ├── drawable │ ├── mrb_star_border_icon_black_36dp.xml │ └── mrb_star_icon_black_36dp.xml │ └── values │ ├── attrs.xml │ └── styles.xml ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── zhanghai │ │ └── android │ │ └── materialratingbar │ │ └── sample │ │ ├── AboutActivity.java │ │ ├── AppUtils.java │ │ └── MainActivity.java │ ├── launcher_icon-web.png │ └── res │ ├── layout │ ├── about_activity.xml │ └── main_activity.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── launcher_icon.png │ ├── mipmap-mdpi │ └── launcher_icon.png │ ├── mipmap-xhdpi │ └── launcher_icon.png │ ├── mipmap-xxhdpi │ └── launcher_icon.png │ ├── mipmap-xxxhdpi │ └── launcher_icon.png │ ├── values-sw600dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── screenshot ├── google_io.jpg ├── google_io_raw.png ├── google_play_store.jpg ├── google_play_store_raw.png ├── material_icons.png ├── sample_app.jpg └── sample_app_raw.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /.idea/ 3 | /build/ 4 | /captures/ 5 | /local.properties 6 | .DS_Store 7 | *.iml 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MaterialRatingBar 2 | 3 | ![Icon](sample/src/main/launcher_icon-web.png) 4 | 5 | Material Design `RatingBar` with better appearance, compatible with Android 3.0+. 6 | 7 | ## Why MaterialRatingBar? 8 | 9 | - Consistent appearance on Android 3.0+. 10 | - Extends framework `RatingBar`. 11 | - Get the 2dp star border background as in Material Icons and Google apps. 12 | - Correct custom tinting across platforms. 13 | - Able to render correctly when `layout_width` is set to `match_parent`, as in Google Play Store. 14 | - Able to scale correctly when `layout_height` is set to values other than 16dp, 36dp and 48dp. 15 | - Able to display ratings such as `4.3` correctly, which will be filled to `4.5` by framework's incorrect implementation. 16 | - Avoid framework's sunken half star visual glitch. 17 | - Used as a drop-in replacement for framework `RatingBar`. 18 | 19 | ## Preview 20 | 21 | Google Play 22 | 23 | [Sample APK](//github.com/zhanghai/MaterialRatingBar/releases/download/v1.4.0/sample-release.apk) 24 | 25 | ![Sample app](screenshot/sample_app.jpg) 26 | 27 | ## Integration 28 | 29 | Gradle: 30 | 31 | ```gradle 32 | implementation 'me.zhanghai.android.materialratingbar:library:1.4.0' 33 | ``` 34 | 35 | ## Usage 36 | 37 | Simply replace your `RatingBar` with `MaterialRatingBar`, and remember to apply a corresponding style for correct behavior. 38 | 39 | For example, to create a normal `MaterialRatingBar`: 40 | 41 | ```xml 42 | 46 | ``` 47 | 48 | In order to make your `RatingBar` take the correct and consistent size on all versions, you will always need to use one of the styles from this library. The trick inside it is `android:minHeight` and `android:maxHeight` that controls the drawable height. 49 | 50 | You can checkout more small or indicator variants in [styles.xml](library/src/main/res/values/styles.xml). 51 | 52 | You can use `app:mrb_fillBackgroundStars` to control whether background stars are filled, otherwise it defaults to the same value as `android:isIndicator` which is the behavior of Google apps. 53 | 54 | 8 tint-related attributes such as `app:mrb_progressTint` and `app:mrb_progressTintMode` are also supported so that they can control the tinting of rating drawables. The default tint color is `?colorControlActivated`, and the default tint mode is `src_in`. 55 | 56 | An `OnRatingChangeListener` interface is also added to `MaterialRatingBar`, which enables callback while user is dragging, just as the listener in `SeekBar`. 57 | 58 | For a detailed example, you can refer to the [sample app's layout](//github.com/zhanghai/MaterialRatingBar/blob/master/sample/src/main/res/layout/main_activity.xml), where you can find examples such as tinting and wide layout. 59 | 60 | ## Design 61 | 62 | ### Filled star or star border 63 | 64 | The framework's `RatingBar` uses filling stars with grey color as track, however as per the Material Icons site, star border icons are given. 65 | 66 | ![Material Icons](screenshot/material_icons.png) 67 | 68 | And as for the Google Play Store and Google I/O app, they are both using the star borders as track. 69 | 70 | ![Google Play Store](screenshot/google_play_store.jpg) ![Google I/O](screenshot/google_io.jpg) 71 | 72 | With Google's design practice and aesthetic considerations taken into account, I decided to use the star border style. 73 | 74 | ### Star size 75 | 76 | Google Play Store has stars of optical size 24dp, while Google I/O app and framework `Widget.Material.RatingBar.Indicator` have stars of size 36dp (which are of optical size 30dp). (The framework's default size of 64dp is ridiculously large and thus not taken into consideration.) 77 | 78 | Also noticing that the Material Icons site gives icons of 24dp (optical 20dp) and 36dp (optical 30dp), I decided to stick to the 36dp approach which is also visually pleasant. 79 | 80 | ### Star border width 81 | 82 | The ring for radio button in Material Design has a width of 2dp, and with experiments on other border widths, I decided to adopt the 2dp border width. 83 | 84 | The star border icon is drawn with the help of Inkscape, by downloading the star icon SVG from Material Icons, duplicating the outer border path of the star, setting a stroke of 4dp, running stroke to path on it, extracting the inner border path, and finally combining this path and the original outer border path. 85 | 86 | ### Wide layout 87 | 88 | Framework `RatingBar` gives erroneous rendering for `RatingBar` when `layout_width` is set to `match_parent` by tiling the stars without any gap. Since Google Play Store employed the wide design, I implemented it inside this library as well, so that `match_parent` will work properly for `MaterialRatingBar`. 89 | 90 | ### Dragging 91 | 92 | Google Play Store and Google I/O app both used an implementation other than `RatingBar`, which means dragging on the bar across stars won't work (it is the functionality of `AbsSeekBar`). I think this is a handy way of interaction for users, and it enables the setting of 0 star which can be useful if you want to enable users to reset their rating to unrated. 93 | 94 | ## License 95 | 96 | Copyright 2016 Zhang Hai 97 | 98 | Licensed under the Apache License, Version 2.0 (the "License"); 99 | you may not use this file except in compliance with the License. 100 | You may obtain a copy of the License at 101 | 102 | http://www.apache.org/licenses/LICENSE-2.0 103 | 104 | Unless required by applicable law or agreed to in writing, software 105 | distributed under the License is distributed on an "AS IS" BASIS, 106 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 107 | See the License for the specific language governing permissions and 108 | limitations under the License. 109 | -------------------------------------------------------------------------------- /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 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.2' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | version = VERSION_NAME 18 | group = GROUP 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /extras/mrb_star_border_icon_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /extras/mrb_star_border_icon_black_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /gradle-mvn-push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | def isReleaseBuild() { 21 | return VERSION_NAME.contains("SNAPSHOT") == false 22 | } 23 | 24 | def getReleaseRepositoryUrl() { 25 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 26 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 27 | } 28 | 29 | def getSnapshotRepositoryUrl() { 30 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 31 | : "https://oss.sonatype.org/content/repositories/snapshots/" 32 | } 33 | 34 | def getRepositoryUsername() { 35 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 36 | } 37 | 38 | def getRepositoryPassword() { 39 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : 40 | System.console() != null ? System.console().readLine("\nNexus password: ") : "" 41 | } 42 | 43 | afterEvaluate { project -> 44 | uploadArchives { 45 | repositories { 46 | mavenDeployer { 47 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 48 | 49 | pom.groupId = GROUP 50 | pom.artifactId = POM_ARTIFACT_ID 51 | pom.version = VERSION_NAME 52 | 53 | repository(url: getReleaseRepositoryUrl()) { 54 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 55 | } 56 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 57 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 58 | } 59 | 60 | pom.project { 61 | name POM_NAME 62 | packaging POM_PACKAGING 63 | description POM_DESCRIPTION 64 | url POM_URL 65 | 66 | scm { 67 | url POM_SCM_URL 68 | connection POM_SCM_CONNECTION 69 | developerConnection POM_SCM_DEV_CONNECTION 70 | } 71 | 72 | licenses { 73 | license { 74 | name POM_LICENCE_NAME 75 | url POM_LICENCE_URL 76 | distribution POM_LICENCE_DIST 77 | } 78 | } 79 | 80 | developers { 81 | developer { 82 | id POM_DEVELOPER_ID 83 | name POM_DEVELOPER_NAME 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | signing { 92 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 93 | sign configurations.archives 94 | } 95 | 96 | gradle.taskGraph.whenReady { taskGraph -> 97 | if (taskGraph.allTasks.any { it instanceof Sign }) { 98 | allprojects { 99 | ext."signing.password" = System.console() != null ? System.console().readLine("\nPGP private key password: ") : "" 100 | } 101 | } 102 | } 103 | 104 | task androidJavadocs(type: Javadoc) { 105 | source = android.sourceSets.main.java.srcDirs 106 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 107 | classpath += configurations.compile 108 | } 109 | 110 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 111 | classifier = 'javadoc' 112 | from androidJavadocs.destinationDir 113 | } 114 | 115 | task androidSourcesJar(type: Jar) { 116 | classifier = 'sources' 117 | from android.sourceSets.main.java.sourceFiles 118 | } 119 | 120 | artifacts { 121 | archives androidSourcesJar 122 | archives androidJavadocsJar 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | 23 | VERSION_NAME=1.4.0 24 | VERSION_CODE=11 25 | GROUP=me.zhanghai.android.materialratingbar 26 | 27 | POM_DESCRIPTION=A Material Design RatingBar with consistent appearance 28 | POM_URL=https://github.com/zhanghai/MaterialRatingBar 29 | POM_SCM_URL=https://github.com/zhanghai/MaterialRatingBar 30 | POM_SCM_CONNECTION=scm:git@github.com:zhanghai/MaterialRatingBar.git 31 | POM_SCM_DEV_CONNECTION=scm:git@github.com:zhanghai/MaterialRatingBar.git 32 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 33 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 34 | POM_LICENCE_DIST=repo 35 | POM_DEVELOPER_ID=zhanghai 36 | POM_DEVELOPER_NAME=Zhang Hai 37 | 38 | ANDROID_MIN_SDK_VERSION=14 39 | ANDROID_COMPILE_SDK_VERSION=29 40 | ANDROID_BUILD_TOOLS_VERSION=28.0.3 41 | ANDROID_TARGET_SDK_VERSION=29 42 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanghai/MaterialRatingBar/50ec0bff414cbdf7993d3c110c6cd4db0c70a099/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 20 21:05:32 PDT 2019 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-5.4.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 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | apply plugin: 'com.android.library' 7 | 8 | android { 9 | 10 | compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION) 11 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION 12 | 13 | defaultConfig { 14 | minSdkVersion Integer.parseInt(project.ANDROID_MIN_SDK_VERSION) 15 | targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION) 16 | versionCode Integer.parseInt(project.VERSION_CODE) 17 | versionName project.VERSION_NAME 18 | consumerProguardFiles 'proguard-rules.pro' 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | } 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation fileTree(dir: 'libs', include: ['*.jar']) 35 | implementation 'androidx.appcompat:appcompat:1.1.0' 36 | implementation 'androidx.annotation:annotation:1.1.0' 37 | } 38 | 39 | apply from: '../gradle-mvn-push.gradle' 40 | -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=MaterialRatingBar Library 2 | POM_ARTIFACT_ID=library 3 | POM_PACKAGING=aar 4 | -------------------------------------------------------------------------------- /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 /opt/android-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 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/BaseDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar; 7 | 8 | import android.content.res.ColorStateList; 9 | import android.graphics.Canvas; 10 | import android.graphics.Color; 11 | import android.graphics.ColorFilter; 12 | import android.graphics.PixelFormat; 13 | import android.graphics.PorterDuff; 14 | import android.graphics.PorterDuffColorFilter; 15 | import android.graphics.Rect; 16 | import android.graphics.drawable.Drawable; 17 | 18 | import androidx.annotation.ColorInt; 19 | import androidx.annotation.NonNull; 20 | import androidx.annotation.Nullable; 21 | 22 | abstract class BaseDrawable extends Drawable implements TintableDrawable { 23 | 24 | protected int mAlpha = 0xFF; 25 | protected ColorFilter mColorFilter; 26 | protected ColorStateList mTintList; 27 | protected PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_IN; 28 | protected PorterDuffColorFilter mTintFilter; 29 | 30 | private DummyConstantState mConstantState = new DummyConstantState(); 31 | 32 | @Override 33 | public int getAlpha() { 34 | return mAlpha; 35 | } 36 | 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | @Override 41 | public void setAlpha(int alpha) { 42 | if (mAlpha != alpha) { 43 | mAlpha = alpha; 44 | invalidateSelf(); 45 | } 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public ColorFilter getColorFilter() { 53 | return mColorFilter; 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public void setColorFilter(@Nullable ColorFilter colorFilter) { 61 | mColorFilter = colorFilter; 62 | invalidateSelf(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public void setTint(@ColorInt int tintColor) { 70 | setTintList(ColorStateList.valueOf(tintColor)); 71 | } 72 | 73 | /** 74 | * {@inheritDoc} 75 | */ 76 | @Override 77 | public void setTintList(@Nullable ColorStateList tint) { 78 | mTintList = tint; 79 | if (updateTintFilter()) { 80 | invalidateSelf(); 81 | } 82 | } 83 | 84 | /** 85 | * {@inheritDoc} 86 | */ 87 | @Override 88 | public void setTintMode(@NonNull PorterDuff.Mode tintMode) { 89 | mTintMode = tintMode; 90 | if (updateTintFilter()) { 91 | invalidateSelf(); 92 | } 93 | } 94 | 95 | @Override 96 | public boolean isStateful() { 97 | return mTintList != null && mTintList.isStateful(); 98 | } 99 | 100 | @Override 101 | protected boolean onStateChange(int[] state) { 102 | return updateTintFilter(); 103 | } 104 | 105 | private boolean updateTintFilter() { 106 | 107 | if (mTintList == null || mTintMode == null) { 108 | boolean hadTintFilter = mTintFilter != null; 109 | mTintFilter = null; 110 | return hadTintFilter; 111 | } 112 | 113 | int tintColor = mTintList.getColorForState(getState(), Color.TRANSPARENT); 114 | // They made PorterDuffColorFilter.setColor() and setMode() @hide. 115 | mTintFilter = new PorterDuffColorFilter(tintColor, mTintMode); 116 | return true; 117 | } 118 | 119 | /** 120 | * {@inheritDoc} 121 | */ 122 | @Override 123 | public int getOpacity() { 124 | // Be safe. 125 | return PixelFormat.TRANSLUCENT; 126 | } 127 | 128 | /** 129 | * {@inheritDoc} 130 | */ 131 | @Override 132 | public void draw(Canvas canvas) { 133 | 134 | Rect bounds = getBounds(); 135 | if (bounds.width() == 0 || bounds.height() == 0) { 136 | return; 137 | } 138 | 139 | int saveCount = canvas.save(); 140 | canvas.translate(bounds.left, bounds.top); 141 | onDraw(canvas, bounds.width(), bounds.height()); 142 | canvas.restoreToCount(saveCount); 143 | } 144 | 145 | protected ColorFilter getColorFilterForDrawing() { 146 | return mColorFilter != null ? mColorFilter : mTintFilter; 147 | } 148 | 149 | protected abstract void onDraw(Canvas canvas, int width, int height); 150 | 151 | // Workaround LayerDrawable.ChildDrawable which calls getConstantState().newDrawable() 152 | // without checking for null. 153 | // We are never inflated from XML so the protocol of ConstantState does not apply to us. In 154 | // order to make LayerDrawable happy, we return ourselves from DummyConstantState.newDrawable(). 155 | 156 | @Override 157 | public ConstantState getConstantState() { 158 | return mConstantState; 159 | } 160 | 161 | private class DummyConstantState extends ConstantState { 162 | 163 | @Override 164 | public int getChangingConfigurations() { 165 | return 0; 166 | } 167 | 168 | @NonNull 169 | @Override 170 | public Drawable newDrawable() { 171 | return BaseDrawable.this; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/ClipDrawableCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar; 7 | 8 | import android.content.res.ColorStateList; 9 | import android.graphics.PorterDuff; 10 | import android.graphics.drawable.ClipDrawable; 11 | import android.graphics.drawable.Drawable; 12 | import android.util.Log; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | 17 | class ClipDrawableCompat extends ClipDrawable implements TintableDrawable { 18 | 19 | private static final String TAG = ClipDrawableCompat.class.getSimpleName(); 20 | 21 | private Drawable mDrawable; 22 | 23 | private DummyConstantState mConstantState = new DummyConstantState(); 24 | 25 | public ClipDrawableCompat(Drawable drawable, int gravity, int orientation) { 26 | super(drawable, gravity, orientation); 27 | 28 | mDrawable = drawable; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public Drawable getDrawable() { 34 | return mDrawable; 35 | } 36 | 37 | @Override 38 | public void setTint(int tintColor) { 39 | if (mDrawable instanceof TintableDrawable) { 40 | //noinspection RedundantCast 41 | ((TintableDrawable) mDrawable).setTint(tintColor); 42 | } else { 43 | Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + 44 | " Lollipop"); 45 | super.setTint(tintColor); 46 | } 47 | } 48 | 49 | @Override 50 | public void setTintList(ColorStateList tint) { 51 | if (mDrawable instanceof TintableDrawable) { 52 | //noinspection RedundantCast 53 | ((TintableDrawable) mDrawable).setTintList(tint); 54 | } else { 55 | Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + 56 | " Lollipop"); 57 | super.setTintList(tint); 58 | } 59 | } 60 | 61 | @Override 62 | public void setTintMode(PorterDuff.Mode tintMode) { 63 | if (mDrawable instanceof TintableDrawable) { 64 | //noinspection RedundantCast 65 | ((TintableDrawable) mDrawable).setTintMode(tintMode); 66 | } else { 67 | Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + 68 | " Lollipop"); 69 | super.setTintMode(tintMode); 70 | } 71 | } 72 | 73 | // Workaround LayerDrawable.ChildDrawable which calls getConstantState().newDrawable() 74 | // without checking for null. 75 | // We are never inflated from XML so the protocol of ConstantState does not apply to us. In 76 | // order to make LayerDrawable happy, we return ourselves from DummyConstantState.newDrawable(). 77 | 78 | @Override 79 | public ConstantState getConstantState() { 80 | return mConstantState; 81 | } 82 | 83 | private class DummyConstantState extends ConstantState { 84 | 85 | @Override 86 | public int getChangingConfigurations() { 87 | return 0; 88 | } 89 | 90 | @NonNull 91 | @Override 92 | public Drawable newDrawable() { 93 | return ClipDrawableCompat.this; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/MaterialRatingBar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.content.Context; 10 | import android.content.res.ColorStateList; 11 | import android.graphics.PorterDuff; 12 | import android.graphics.drawable.Drawable; 13 | import android.graphics.drawable.LayerDrawable; 14 | import android.os.Build; 15 | import android.util.AttributeSet; 16 | import android.util.Log; 17 | import android.view.View; 18 | import android.widget.RatingBar; 19 | 20 | import androidx.annotation.Nullable; 21 | import androidx.appcompat.widget.TintTypedArray; 22 | import me.zhanghai.android.materialratingbar.internal.DrawableCompat; 23 | 24 | // AppCompatRatingBar will add undesired measuring behavior. 25 | @SuppressLint("AppCompatCustomView") 26 | public class MaterialRatingBar extends RatingBar { 27 | 28 | private static final String TAG = MaterialRatingBar.class.getSimpleName(); 29 | 30 | private TintInfo mProgressTintInfo = new TintInfo(); 31 | 32 | private MaterialRatingDrawable mDrawable; 33 | 34 | private OnRatingChangeListener mOnRatingChangeListener; 35 | private float mLastKnownRating; 36 | 37 | public MaterialRatingBar(Context context) { 38 | super(context); 39 | 40 | init(null, 0); 41 | } 42 | 43 | public MaterialRatingBar(Context context, AttributeSet attrs) { 44 | super(context, attrs); 45 | 46 | init(attrs, 0); 47 | } 48 | 49 | public MaterialRatingBar(Context context, AttributeSet attrs, int defStyleAttr) { 50 | super(context, attrs, defStyleAttr); 51 | 52 | init(attrs, defStyleAttr); 53 | } 54 | 55 | @SuppressWarnings("RestrictedApi") 56 | private void init(AttributeSet attrs, int defStyleAttr) { 57 | 58 | Context context = getContext(); 59 | TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, 60 | R.styleable.MaterialRatingBar, defStyleAttr, 0); 61 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressTint)) { 62 | mProgressTintInfo.mProgressTintList = a.getColorStateList( 63 | R.styleable.MaterialRatingBar_mrb_progressTint); 64 | mProgressTintInfo.mHasProgressTintList = true; 65 | } 66 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressTintMode)) { 67 | mProgressTintInfo.mProgressTintMode = DrawableCompat.parseTintMode(a.getInt( 68 | R.styleable.MaterialRatingBar_mrb_progressTintMode, -1), null); 69 | mProgressTintInfo.mHasProgressTintMode = true; 70 | } 71 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_secondaryProgressTint)) { 72 | mProgressTintInfo.mSecondaryProgressTintList = a.getColorStateList( 73 | R.styleable.MaterialRatingBar_mrb_secondaryProgressTint); 74 | mProgressTintInfo.mHasSecondaryProgressTintList = true; 75 | } 76 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_secondaryProgressTintMode)) { 77 | mProgressTintInfo.mSecondaryProgressTintMode = DrawableCompat.parseTintMode(a.getInt( 78 | R.styleable.MaterialRatingBar_mrb_secondaryProgressTintMode, -1), null); 79 | mProgressTintInfo.mHasSecondaryProgressTintMode = true; 80 | } 81 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressBackgroundTint)) { 82 | mProgressTintInfo.mProgressBackgroundTintList = a.getColorStateList( 83 | R.styleable.MaterialRatingBar_mrb_progressBackgroundTint); 84 | mProgressTintInfo.mHasProgressBackgroundTintList = true; 85 | } 86 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressBackgroundTintMode)) { 87 | mProgressTintInfo.mProgressBackgroundTintMode = DrawableCompat.parseTintMode(a.getInt( 88 | R.styleable.MaterialRatingBar_mrb_progressBackgroundTintMode, -1), null); 89 | mProgressTintInfo.mHasProgressBackgroundTintMode = true; 90 | } 91 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_indeterminateTint)) { 92 | mProgressTintInfo.mIndeterminateTintList = a.getColorStateList( 93 | R.styleable.MaterialRatingBar_mrb_indeterminateTint); 94 | mProgressTintInfo.mHasIndeterminateTintList = true; 95 | } 96 | if (a.hasValue(R.styleable.MaterialRatingBar_mrb_indeterminateTintMode)) { 97 | mProgressTintInfo.mIndeterminateTintMode = DrawableCompat.parseTintMode(a.getInt( 98 | R.styleable.MaterialRatingBar_mrb_indeterminateTintMode, -1), null); 99 | mProgressTintInfo.mHasIndeterminateTintMode = true; 100 | } 101 | boolean fillBackgroundStars = a.getBoolean( 102 | R.styleable.MaterialRatingBar_mrb_fillBackgroundStars, isIndicator()); 103 | a.recycle(); 104 | 105 | mDrawable = new MaterialRatingDrawable(getContext(), fillBackgroundStars); 106 | mDrawable.setStarCount(getNumStars()); 107 | setProgressDrawable(mDrawable); 108 | } 109 | 110 | @Override 111 | public void setNumStars(int numStars) { 112 | super.setNumStars(numStars); 113 | 114 | // mDrawable can be null during super class initialization. 115 | if (mDrawable != null) { 116 | mDrawable.setStarCount(numStars); 117 | } 118 | } 119 | 120 | @Override 121 | protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 122 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 123 | 124 | int height = getMeasuredHeight(); 125 | int width = Math.round(height * mDrawable.getTileRatio() * getNumStars()); 126 | setMeasuredDimension(View.resolveSizeAndState(width, widthMeasureSpec, 0), height); 127 | } 128 | 129 | @Override 130 | public void setProgressDrawable(Drawable d) { 131 | super.setProgressDrawable(d); 132 | 133 | // mProgressTintInfo can be null during super class initialization. 134 | if (mProgressTintInfo != null) { 135 | applyProgressTints(); 136 | } 137 | } 138 | 139 | @Override 140 | public void setIndeterminateDrawable(Drawable d) { 141 | super.setIndeterminateDrawable(d); 142 | 143 | // mProgressTintInfo can be null during super class initialization. 144 | if (mProgressTintInfo != null) { 145 | applyIndeterminateTint(); 146 | } 147 | } 148 | 149 | /** 150 | * @deprecated Use {@link #getSupportProgressTintList()} instead. 151 | */ 152 | @Nullable 153 | @Override 154 | public ColorStateList getProgressTintList() { 155 | // Samsung Android 10 might call this in super class constructor. 156 | if (mProgressTintInfo == null) { 157 | return null; 158 | } 159 | logRatingBarTintWarning(); 160 | return getSupportProgressTintList(); 161 | } 162 | 163 | /** 164 | * @deprecated Use {@link #setSupportProgressTintList(ColorStateList)} instead. 165 | */ 166 | @Override 167 | public void setProgressTintList(@Nullable ColorStateList tint) { 168 | logRatingBarTintWarning(); 169 | setSupportProgressTintList(tint); 170 | } 171 | 172 | /** 173 | * @deprecated Use {@link #getSupportProgressTintMode()} instead. 174 | */ 175 | @Nullable 176 | @Override 177 | public PorterDuff.Mode getProgressTintMode() { 178 | logRatingBarTintWarning(); 179 | return getSupportProgressTintMode(); 180 | } 181 | 182 | /** 183 | * @deprecated Use {@link #setSupportProgressTintMode(PorterDuff.Mode)} instead. 184 | */ 185 | @Override 186 | public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 187 | logRatingBarTintWarning(); 188 | setSupportProgressTintMode(tintMode); 189 | } 190 | 191 | /** 192 | * @deprecated Use {@link #getSupportSecondaryProgressTintList()} instead. 193 | */ 194 | @Nullable 195 | @Override 196 | public ColorStateList getSecondaryProgressTintList() { 197 | logRatingBarTintWarning(); 198 | return getSupportSecondaryProgressTintList(); 199 | } 200 | 201 | /** 202 | * @deprecated Use {@link #setSupportSecondaryProgressTintList(ColorStateList)} instead. 203 | */ 204 | @Override 205 | public void setSecondaryProgressTintList(@Nullable ColorStateList tint) { 206 | logRatingBarTintWarning(); 207 | setSupportSecondaryProgressTintList(tint); 208 | } 209 | 210 | /** 211 | * @deprecated Use {@link #getSupportSecondaryProgressTintMode()} instead. 212 | */ 213 | @Nullable 214 | @Override 215 | public PorterDuff.Mode getSecondaryProgressTintMode() { 216 | logRatingBarTintWarning(); 217 | return getSupportSecondaryProgressTintMode(); 218 | } 219 | 220 | /** 221 | * @deprecated Use {@link #setSupportSecondaryProgressTintMode(PorterDuff.Mode)} instead. 222 | */ 223 | @Override 224 | public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 225 | logRatingBarTintWarning(); 226 | setSupportSecondaryProgressTintMode(tintMode); 227 | } 228 | 229 | /** 230 | * @deprecated Use {@link #getSupportProgressBackgroundTintList()} instead. 231 | */ 232 | @Nullable 233 | @Override 234 | public ColorStateList getProgressBackgroundTintList() { 235 | logRatingBarTintWarning(); 236 | return getSupportProgressBackgroundTintList(); 237 | } 238 | 239 | /** 240 | * @deprecated Use {@link #setSupportProgressBackgroundTintList(ColorStateList)} instead. 241 | */ 242 | @Override 243 | public void setProgressBackgroundTintList(@Nullable ColorStateList tint) { 244 | logRatingBarTintWarning(); 245 | setSupportProgressBackgroundTintList(tint); 246 | } 247 | 248 | /** 249 | * @deprecated Use {@link #getSupportProgressBackgroundTintMode()} instead. 250 | */ 251 | @Nullable 252 | @Override 253 | public PorterDuff.Mode getProgressBackgroundTintMode() { 254 | logRatingBarTintWarning(); 255 | return getSupportProgressBackgroundTintMode(); 256 | } 257 | 258 | /** 259 | * @deprecated Use {@link #setSupportProgressBackgroundTintMode(PorterDuff.Mode)} instead. 260 | */ 261 | @Override 262 | public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 263 | logRatingBarTintWarning(); 264 | setSupportProgressBackgroundTintMode(tintMode); 265 | } 266 | 267 | /** 268 | * @deprecated Use {@link #getSupportIndeterminateTintList()} instead. 269 | */ 270 | @Nullable 271 | @Override 272 | public ColorStateList getIndeterminateTintList() { 273 | logRatingBarTintWarning(); 274 | return getSupportIndeterminateTintList(); 275 | } 276 | 277 | /** 278 | * @deprecated Use {@link #setSupportIndeterminateTintList(ColorStateList)} instead. 279 | */ 280 | @Override 281 | public void setIndeterminateTintList(@Nullable ColorStateList tint) { 282 | logRatingBarTintWarning(); 283 | setSupportIndeterminateTintList(tint); 284 | } 285 | 286 | /** 287 | * @deprecated Use {@link #getSupportIndeterminateTintMode()} instead. 288 | */ 289 | @Nullable 290 | @Override 291 | public PorterDuff.Mode getIndeterminateTintMode() { 292 | logRatingBarTintWarning(); 293 | return getSupportIndeterminateTintMode(); 294 | } 295 | 296 | /** 297 | * @deprecated Use {@link #setSupportIndeterminateTintMode(PorterDuff.Mode)} instead. 298 | */ 299 | @Override 300 | public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { 301 | logRatingBarTintWarning(); 302 | setSupportIndeterminateTintMode(tintMode); 303 | } 304 | 305 | private void logRatingBarTintWarning() { 306 | Log.w(TAG, "Non-support version of tint method called, this is error-prone and will crash" + 307 | " below Lollipop if you are calling it as a method of RatingBar instead of" + 308 | " MaterialRatingBar"); 309 | } 310 | 311 | /** 312 | * @see RatingBar#getProgressTintList() 313 | */ 314 | @Nullable 315 | public ColorStateList getSupportProgressTintList() { 316 | return mProgressTintInfo.mProgressTintList; 317 | } 318 | 319 | /** 320 | * @see RatingBar#setProgressTintList(ColorStateList) 321 | */ 322 | public void setSupportProgressTintList(@Nullable ColorStateList tint) { 323 | mProgressTintInfo.mProgressTintList = tint; 324 | mProgressTintInfo.mHasProgressTintList = true; 325 | 326 | applyPrimaryProgressTint(); 327 | } 328 | 329 | /** 330 | * @see RatingBar#getProgressTintMode() 331 | */ 332 | @Nullable 333 | public PorterDuff.Mode getSupportProgressTintMode() { 334 | return mProgressTintInfo.mProgressTintMode; 335 | } 336 | 337 | /** 338 | * @see RatingBar#setProgressTintMode(PorterDuff.Mode) 339 | */ 340 | public void setSupportProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 341 | mProgressTintInfo.mProgressTintMode = tintMode; 342 | mProgressTintInfo.mHasProgressTintMode = true; 343 | 344 | applyPrimaryProgressTint(); 345 | } 346 | 347 | /** 348 | * @see RatingBar#getSecondaryProgressTintList() 349 | */ 350 | @Nullable 351 | public ColorStateList getSupportSecondaryProgressTintList() { 352 | return mProgressTintInfo.mSecondaryProgressTintList; 353 | } 354 | 355 | /** 356 | * @see RatingBar#setSecondaryProgressTintList(ColorStateList) 357 | */ 358 | public void setSupportSecondaryProgressTintList(@Nullable ColorStateList tint) { 359 | mProgressTintInfo.mSecondaryProgressTintList = tint; 360 | mProgressTintInfo.mHasSecondaryProgressTintList = true; 361 | 362 | applySecondaryProgressTint(); 363 | } 364 | 365 | /** 366 | * @see RatingBar#getSecondaryProgressTintMode() 367 | */ 368 | @Nullable 369 | public PorterDuff.Mode getSupportSecondaryProgressTintMode() { 370 | return mProgressTintInfo.mSecondaryProgressTintMode; 371 | } 372 | 373 | /** 374 | * @see RatingBar#setSecondaryProgressTintMode(PorterDuff.Mode) 375 | */ 376 | public void setSupportSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 377 | mProgressTintInfo.mSecondaryProgressTintMode = tintMode; 378 | mProgressTintInfo.mHasSecondaryProgressTintMode = true; 379 | 380 | applySecondaryProgressTint(); 381 | } 382 | 383 | /** 384 | * @see RatingBar#getProgressBackgroundTintList() 385 | */ 386 | @Nullable 387 | public ColorStateList getSupportProgressBackgroundTintList() { 388 | return mProgressTintInfo.mProgressBackgroundTintList; 389 | } 390 | 391 | /** 392 | * @see RatingBar#setProgressBackgroundTintList(ColorStateList) 393 | */ 394 | public void setSupportProgressBackgroundTintList(@Nullable ColorStateList tint) { 395 | mProgressTintInfo.mProgressBackgroundTintList = tint; 396 | mProgressTintInfo.mHasProgressBackgroundTintList = true; 397 | 398 | applyProgressBackgroundTint(); 399 | } 400 | 401 | /** 402 | * @see RatingBar#getProgressBackgroundTintMode() 403 | */ 404 | @Nullable 405 | public PorterDuff.Mode getSupportProgressBackgroundTintMode() { 406 | return mProgressTintInfo.mProgressBackgroundTintMode; 407 | } 408 | 409 | /** 410 | * @see RatingBar#setProgressBackgroundTintMode(PorterDuff.Mode) 411 | */ 412 | public void setSupportProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 413 | mProgressTintInfo.mProgressBackgroundTintMode = tintMode; 414 | mProgressTintInfo.mHasProgressBackgroundTintMode = true; 415 | 416 | applyProgressBackgroundTint(); 417 | } 418 | 419 | /** 420 | * @see RatingBar#getIndeterminateTintList() 421 | */ 422 | @Nullable 423 | public ColorStateList getSupportIndeterminateTintList() { 424 | return mProgressTintInfo.mIndeterminateTintList; 425 | } 426 | 427 | /** 428 | * @see RatingBar#setIndeterminateTintList(ColorStateList) 429 | */ 430 | public void setSupportIndeterminateTintList(@Nullable ColorStateList tint) { 431 | mProgressTintInfo.mIndeterminateTintList = tint; 432 | mProgressTintInfo.mHasIndeterminateTintList = true; 433 | 434 | applyIndeterminateTint(); 435 | } 436 | 437 | /** 438 | * @see RatingBar#getIndeterminateTintMode() 439 | */ 440 | @Nullable 441 | public PorterDuff.Mode getSupportIndeterminateTintMode() { 442 | return mProgressTintInfo.mIndeterminateTintMode; 443 | } 444 | 445 | /** 446 | * @see RatingBar#setIndeterminateTintMode(PorterDuff.Mode) 447 | */ 448 | public void setSupportIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { 449 | mProgressTintInfo.mIndeterminateTintMode = tintMode; 450 | mProgressTintInfo.mHasIndeterminateTintMode = true; 451 | 452 | applyIndeterminateTint(); 453 | } 454 | 455 | private void applyProgressTints() { 456 | if (getProgressDrawable() == null) { 457 | return; 458 | } 459 | applyPrimaryProgressTint(); 460 | applyProgressBackgroundTint(); 461 | applySecondaryProgressTint(); 462 | } 463 | 464 | private void applyPrimaryProgressTint() { 465 | if (getProgressDrawable() == null) { 466 | return; 467 | } 468 | if (mProgressTintInfo.mHasProgressTintList || mProgressTintInfo.mHasProgressTintMode) { 469 | Drawable target = getTintTargetFromProgressDrawable(android.R.id.progress, true); 470 | if (target != null) { 471 | applyTintForDrawable(target, mProgressTintInfo.mProgressTintList, 472 | mProgressTintInfo.mHasProgressTintList, mProgressTintInfo.mProgressTintMode, 473 | mProgressTintInfo.mHasProgressTintMode); 474 | } 475 | } 476 | } 477 | 478 | private void applySecondaryProgressTint() { 479 | if (getProgressDrawable() == null) { 480 | return; 481 | } 482 | if (mProgressTintInfo.mHasSecondaryProgressTintList 483 | || mProgressTintInfo.mHasSecondaryProgressTintMode) { 484 | Drawable target = getTintTargetFromProgressDrawable(android.R.id.secondaryProgress, 485 | false); 486 | if (target != null) { 487 | applyTintForDrawable(target, mProgressTintInfo.mSecondaryProgressTintList, 488 | mProgressTintInfo.mHasSecondaryProgressTintList, 489 | mProgressTintInfo.mSecondaryProgressTintMode, 490 | mProgressTintInfo.mHasSecondaryProgressTintMode); 491 | } 492 | } 493 | } 494 | 495 | private void applyProgressBackgroundTint() { 496 | if (getProgressDrawable() == null) { 497 | return; 498 | } 499 | if (mProgressTintInfo.mHasProgressBackgroundTintList 500 | || mProgressTintInfo.mHasProgressBackgroundTintMode) { 501 | Drawable target = getTintTargetFromProgressDrawable(android.R.id.background, false); 502 | if (target != null) { 503 | applyTintForDrawable(target, mProgressTintInfo.mProgressBackgroundTintList, 504 | mProgressTintInfo.mHasProgressBackgroundTintList, 505 | mProgressTintInfo.mProgressBackgroundTintMode, 506 | mProgressTintInfo.mHasProgressBackgroundTintMode); 507 | } 508 | } 509 | } 510 | 511 | private Drawable getTintTargetFromProgressDrawable(int layerId, boolean shouldFallback) { 512 | Drawable progressDrawable = getProgressDrawable(); 513 | if (progressDrawable == null) { 514 | return null; 515 | } 516 | progressDrawable.mutate(); 517 | Drawable layerDrawable = null; 518 | if (progressDrawable instanceof LayerDrawable) { 519 | layerDrawable = ((LayerDrawable) progressDrawable).findDrawableByLayerId(layerId); 520 | } 521 | if (layerDrawable == null && shouldFallback) { 522 | layerDrawable = progressDrawable; 523 | } 524 | return layerDrawable; 525 | } 526 | 527 | private void applyIndeterminateTint() { 528 | Drawable indeterminateDrawable = getIndeterminateDrawable(); 529 | if (indeterminateDrawable == null) { 530 | return; 531 | } 532 | if (mProgressTintInfo.mHasIndeterminateTintList 533 | || mProgressTintInfo.mHasIndeterminateTintMode) { 534 | indeterminateDrawable.mutate(); 535 | applyTintForDrawable(indeterminateDrawable, mProgressTintInfo.mIndeterminateTintList, 536 | mProgressTintInfo.mHasIndeterminateTintList, 537 | mProgressTintInfo.mIndeterminateTintMode, 538 | mProgressTintInfo.mHasIndeterminateTintMode); 539 | } 540 | } 541 | 542 | // Progress drawables in this library has already rewritten tint related methods for 543 | // compatibility. 544 | @SuppressLint("NewApi") 545 | private void applyTintForDrawable(Drawable drawable, ColorStateList tintList, 546 | boolean hasTintList, PorterDuff.Mode tintMode, 547 | boolean hasTintMode) { 548 | 549 | if (hasTintList || hasTintMode) { 550 | 551 | if (hasTintList) { 552 | if (drawable instanceof TintableDrawable) { 553 | //noinspection RedundantCast 554 | ((TintableDrawable) drawable).setTintList(tintList); 555 | } else { 556 | logDrawableTintWarning(); 557 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 558 | drawable.setTintList(tintList); 559 | } 560 | } 561 | } 562 | 563 | if (hasTintMode) { 564 | if (drawable instanceof TintableDrawable) { 565 | //noinspection RedundantCast 566 | ((TintableDrawable) drawable).setTintMode(tintMode); 567 | } else { 568 | logDrawableTintWarning(); 569 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 570 | drawable.setTintMode(tintMode); 571 | } 572 | } 573 | } 574 | 575 | // The drawable (or one of its children) may not have been 576 | // stateful before applying the tint, so let's try again. 577 | if (drawable.isStateful()) { 578 | drawable.setState(getDrawableState()); 579 | } 580 | } 581 | } 582 | 583 | private void logDrawableTintWarning() { 584 | Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + 585 | " Lollipop"); 586 | } 587 | 588 | /** 589 | * Get the listener that is listening for rating change events. 590 | * 591 | * @return The listener, may be null. 592 | */ 593 | public OnRatingChangeListener getOnRatingChangeListener() { 594 | return mOnRatingChangeListener; 595 | } 596 | 597 | /** 598 | * Sets the listener to be called when the rating changes. 599 | * 600 | * @param listener The listener. 601 | */ 602 | public void setOnRatingChangeListener(OnRatingChangeListener listener) { 603 | mOnRatingChangeListener = listener; 604 | } 605 | 606 | @Override 607 | public synchronized void setSecondaryProgress(int secondaryProgress) { 608 | super.setSecondaryProgress(secondaryProgress); 609 | 610 | // HACK: Check and call our listener here because this method is always called by 611 | // updateSecondaryProgress() from onProgressRefresh(). 612 | float rating = getRating(); 613 | if (mOnRatingChangeListener != null && rating != mLastKnownRating) { 614 | mOnRatingChangeListener.onRatingChanged(this, rating); 615 | } 616 | mLastKnownRating = rating; 617 | } 618 | 619 | /** 620 | * A callback that notifies clients when the rating has been changed. This includes changes that 621 | * were initiated by the user through a touch gesture or arrow key/trackball as well as changes 622 | * that were initiated programmatically. This callback will be called 623 | * continuously while the user is dragging, different from framework's 624 | * {@link OnRatingBarChangeListener}. 625 | */ 626 | public interface OnRatingChangeListener { 627 | 628 | /** 629 | * Notification that the rating has changed. This will be called 630 | * continuously while the user is dragging, different from framework's 631 | * {@link OnRatingBarChangeListener}. 632 | * 633 | * @param ratingBar The RatingBar whose rating has changed. 634 | * @param rating The current rating. This will be in the range 0..numStars. 635 | */ 636 | void onRatingChanged(MaterialRatingBar ratingBar, float rating); 637 | } 638 | 639 | private static class TintInfo { 640 | 641 | public ColorStateList mProgressTintList; 642 | public PorterDuff.Mode mProgressTintMode; 643 | public boolean mHasProgressTintList; 644 | public boolean mHasProgressTintMode; 645 | 646 | public ColorStateList mSecondaryProgressTintList; 647 | public PorterDuff.Mode mSecondaryProgressTintMode; 648 | public boolean mHasSecondaryProgressTintList; 649 | public boolean mHasSecondaryProgressTintMode; 650 | 651 | public ColorStateList mProgressBackgroundTintList; 652 | public PorterDuff.Mode mProgressBackgroundTintMode; 653 | public boolean mHasProgressBackgroundTintList; 654 | public boolean mHasProgressBackgroundTintMode; 655 | 656 | public ColorStateList mIndeterminateTintList; 657 | public PorterDuff.Mode mIndeterminateTintMode; 658 | public boolean mHasIndeterminateTintList; 659 | public boolean mHasIndeterminateTintMode; 660 | } 661 | } 662 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/MaterialRatingDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.content.Context; 10 | import android.graphics.Color; 11 | import android.graphics.drawable.ColorDrawable; 12 | import android.graphics.drawable.Drawable; 13 | import android.graphics.drawable.LayerDrawable; 14 | import android.view.Gravity; 15 | 16 | import androidx.appcompat.content.res.AppCompatResources; 17 | import me.zhanghai.android.materialratingbar.internal.ThemeUtils; 18 | 19 | public class MaterialRatingDrawable extends LayerDrawable { 20 | 21 | public MaterialRatingDrawable(Context context, boolean fillBackgroundStars) { 22 | super(new Drawable[] { 23 | createLayerDrawableWithTintAttrRes(fillBackgroundStars ? 24 | R.drawable.mrb_star_icon_black_36dp 25 | : R.drawable.mrb_star_border_icon_black_36dp, fillBackgroundStars ? 26 | R.attr.colorControlHighlight : R.attr.colorControlNormal, context), 27 | fillBackgroundStars ? createClippedLayerDrawableWithTintColor( 28 | R.drawable.mrb_star_icon_black_36dp, Color.TRANSPARENT, context) 29 | : createClippedLayerDrawableWithTintAttrRes( 30 | R.drawable.mrb_star_border_icon_black_36dp, R.attr.colorControlActivated, 31 | context), 32 | createClippedLayerDrawableWithTintAttrRes(R.drawable.mrb_star_icon_black_36dp, 33 | R.attr.colorControlActivated, context) 34 | }); 35 | 36 | setId(0, android.R.id.background); 37 | setId(1, android.R.id.secondaryProgress); 38 | setId(2, android.R.id.progress); 39 | } 40 | 41 | private static Drawable createLayerDrawableWithTintColor(int tileRes, int tintColor, 42 | Context context) { 43 | TileDrawable drawable = new TileDrawable(AppCompatResources.getDrawable(context, 44 | tileRes)); 45 | drawable.mutate(); 46 | //noinspection RedundantCast 47 | ((TintableDrawable) drawable).setTint(tintColor); 48 | return drawable; 49 | } 50 | 51 | private static Drawable createLayerDrawableWithTintAttrRes(int tileRes, int tintAttrRes, 52 | Context context) { 53 | int tintColor = ThemeUtils.getColorFromAttrRes(tintAttrRes, context); 54 | return createLayerDrawableWithTintColor(tileRes, tintColor, context); 55 | } 56 | 57 | @SuppressLint("RtlHardcoded") 58 | private static Drawable createClippedLayerDrawableWithTintColor(int tileResId, int tintColor, 59 | Context context) { 60 | return new ClipDrawableCompat(createLayerDrawableWithTintColor(tileResId, tintColor, 61 | context), Gravity.LEFT, ClipDrawableCompat.HORIZONTAL); 62 | } 63 | 64 | @SuppressLint("RtlHardcoded") 65 | private static Drawable createClippedLayerDrawableWithTintAttrRes(int tileResId, 66 | int tintAttrRes, 67 | Context context) { 68 | return new ClipDrawableCompat(createLayerDrawableWithTintAttrRes(tileResId, tintAttrRes, 69 | context), Gravity.LEFT, ClipDrawableCompat.HORIZONTAL); 70 | } 71 | 72 | @SuppressLint("RtlHardcoded") 73 | private static Drawable createClippedTransparentLayerDrawable() { 74 | return new ClipDrawableCompat(new TileDrawable(new ColorDrawable(Color.TRANSPARENT)), 75 | Gravity.LEFT, ClipDrawableCompat.HORIZONTAL); 76 | } 77 | 78 | public float getTileRatio() { 79 | Drawable drawable = getTileDrawableByLayerId(android.R.id.progress).getDrawable(); 80 | return (float) drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight(); 81 | } 82 | 83 | public void setStarCount(int count) { 84 | getTileDrawableByLayerId(android.R.id.background).setTileCount(count); 85 | getTileDrawableByLayerId(android.R.id.secondaryProgress).setTileCount(count); 86 | getTileDrawableByLayerId(android.R.id.progress).setTileCount(count); 87 | } 88 | 89 | @SuppressLint("NewApi") 90 | private TileDrawable getTileDrawableByLayerId(int id) { 91 | Drawable layerDrawable = findDrawableByLayerId(id); 92 | switch (id) { 93 | case android.R.id.background: 94 | return (TileDrawable) layerDrawable; 95 | case android.R.id.secondaryProgress: 96 | case android.R.id.progress: { 97 | ClipDrawableCompat clipDrawable = (ClipDrawableCompat) layerDrawable; 98 | return (TileDrawable) clipDrawable.getDrawable(); 99 | } 100 | default: 101 | // Should never reach here. 102 | throw new RuntimeException(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/TileDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar; 7 | 8 | import android.graphics.Canvas; 9 | import android.graphics.ColorFilter; 10 | import android.graphics.drawable.Drawable; 11 | 12 | import androidx.annotation.NonNull; 13 | 14 | class TileDrawable extends BaseDrawable { 15 | 16 | private Drawable mDrawable; 17 | private int mTileCount = -1; 18 | 19 | public TileDrawable(Drawable drawable) { 20 | mDrawable = drawable; 21 | } 22 | 23 | public Drawable getDrawable() { 24 | return mDrawable; 25 | } 26 | 27 | public int getTileCount() { 28 | return mTileCount; 29 | } 30 | 31 | public void setTileCount(int tileCount) { 32 | mTileCount = tileCount; 33 | invalidateSelf(); 34 | } 35 | 36 | @NonNull 37 | @Override 38 | public Drawable mutate() { 39 | mDrawable = mDrawable.mutate(); 40 | return this; 41 | } 42 | 43 | @Override 44 | protected void onDraw(Canvas canvas, int width, int height) { 45 | 46 | mDrawable.setAlpha(mAlpha); 47 | ColorFilter colorFilter = getColorFilterForDrawing(); 48 | if (colorFilter != null) { 49 | mDrawable.setColorFilter(colorFilter); 50 | } 51 | 52 | int tileHeight = mDrawable.getIntrinsicHeight(); 53 | float scale = (float) height / tileHeight; 54 | canvas.scale(scale, scale); 55 | 56 | float scaledWidth = width / scale; 57 | if (mTileCount < 0) { 58 | int tileWidth = mDrawable.getIntrinsicWidth(); 59 | for (int x = 0; x < scaledWidth; x += tileWidth) { 60 | mDrawable.setBounds(x, 0, x + tileWidth, tileHeight); 61 | mDrawable.draw(canvas); 62 | } 63 | } else { 64 | float tileWidth = scaledWidth / mTileCount; 65 | for (int i = 0; i < mTileCount; ++i) { 66 | int drawableWidth = mDrawable.getIntrinsicWidth(); 67 | float tileCenter = tileWidth * (i + 0.5f); 68 | float drawableWidthHalf = (float) drawableWidth / 2; 69 | mDrawable.setBounds(Math.round(tileCenter - drawableWidthHalf), 0, 70 | Math.round(tileCenter + drawableWidthHalf), tileHeight); 71 | mDrawable.draw(canvas); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/TintableDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar; 7 | 8 | import android.content.res.ColorStateList; 9 | import android.graphics.ColorFilter; 10 | import android.graphics.PorterDuff; 11 | import android.graphics.drawable.Drawable; 12 | 13 | import androidx.annotation.ColorInt; 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | 17 | /** 18 | * A {@code Drawable} that is tintable. 19 | */ 20 | public interface TintableDrawable { 21 | 22 | /** 23 | * Specifies tint color for this drawable. 24 | *

25 | * A Drawable's drawing content will be blended together with its tint 26 | * before it is drawn to the screen. This functions similarly to 27 | * {@link Drawable#setColorFilter(int, PorterDuff.Mode)}. 28 | *

29 | *

30 | * To clear the tint, pass {@code null} to 31 | * {@link #setTintList(ColorStateList)}. 32 | *

33 | *

Note: Setting a color filter via 34 | * {@link Drawable#setColorFilter(ColorFilter)} or 35 | * {@link Drawable#setColorFilter(int, PorterDuff.Mode)} overrides tint. 36 | *

37 | * 38 | * @param tintColor Color to use for tinting this drawable 39 | * @see #setTintList(ColorStateList) 40 | * @see #setTintMode(PorterDuff.Mode) 41 | */ 42 | void setTint(@ColorInt int tintColor); 43 | 44 | /** 45 | * Specifies tint color for this drawable as a color state list. 46 | *

47 | * A Drawable's drawing content will be blended together with its tint 48 | * before it is drawn to the screen. This functions similarly to 49 | * {@link Drawable#setColorFilter(int, PorterDuff.Mode)}. 50 | *

51 | *

Note: Setting a color filter via 52 | * {@link Drawable#setColorFilter(ColorFilter)} or 53 | * {@link Drawable#setColorFilter(int, PorterDuff.Mode)} overrides tint. 54 | *

55 | * 56 | * @param tint Color state list to use for tinting this drawable, or 57 | * {@code null} to clear the tint 58 | * @see #setTint(int) 59 | * @see #setTintMode(PorterDuff.Mode) 60 | */ 61 | void setTintList(@Nullable ColorStateList tint); 62 | 63 | /** 64 | * Specifies a tint blending mode for this drawable. 65 | *

66 | * Defines how this drawable's tint color should be blended into the drawable 67 | * before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#SRC_IN}. 68 | *

69 | *

Note: Setting a color filter via 70 | * {@link Drawable#setColorFilter(ColorFilter)} or 71 | * {@link Drawable#setColorFilter(int, PorterDuff.Mode)} overrides tint. 72 | *

73 | * 74 | * @param tintMode A Porter-Duff blending mode 75 | * @see #setTint(int) 76 | * @see #setTintList(ColorStateList) 77 | */ 78 | void setTintMode(@NonNull PorterDuff.Mode tintMode); 79 | } 80 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/internal/DrawableCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar.internal; 7 | 8 | import android.graphics.PorterDuff; 9 | 10 | public class DrawableCompat { 11 | 12 | /** 13 | * Parses a {@link PorterDuff.Mode} from a tintMode attribute's enum value. 14 | */ 15 | public static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) { 16 | switch (value) { 17 | case 3: return PorterDuff.Mode.SRC_OVER; 18 | case 5: return PorterDuff.Mode.SRC_IN; 19 | case 9: return PorterDuff.Mode.SRC_ATOP; 20 | case 14: return PorterDuff.Mode.MULTIPLY; 21 | case 15: return PorterDuff.Mode.SCREEN; 22 | case 16: return PorterDuff.Mode.ADD; 23 | default: return defaultMode; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/java/me/zhanghai/android/materialratingbar/internal/ThemeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar.internal; 7 | 8 | import android.content.Context; 9 | import android.content.res.TypedArray; 10 | 11 | public class ThemeUtils { 12 | 13 | private ThemeUtils() {} 14 | 15 | public static int getColorFromAttrRes(int attrRes, Context context) { 16 | TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); 17 | try { 18 | return a.getColor(0, 0); 19 | } finally { 20 | a.recycle(); 21 | } 22 | } 23 | 24 | public static float getFloatFromAttrRes(int attrRes, Context context) { 25 | TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); 26 | try { 27 | return a.getFloat(0, 0); 28 | } finally { 29 | a.recycle(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/mrb_star_border_icon_black_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/mrb_star_icon_black_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /library/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 20 | 21 | 25 | 26 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | apply plugin: 'com.android.application' 7 | 8 | android { 9 | 10 | compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION) 11 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION 12 | 13 | defaultConfig { 14 | applicationId "me.zhanghai.android.materialratingbar.sample" 15 | minSdkVersion Integer.parseInt(project.ANDROID_MIN_SDK_VERSION) 16 | targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION) 17 | versionCode Integer.parseInt(project.VERSION_CODE) 18 | versionName project.VERSION_NAME 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | 26 | signingConfigs { 27 | release { 28 | storeFile file('../../github.jks') 29 | storePassword System.console() != null ? System.console().readLine("\nKeystore password: ") : "" 30 | keyAlias 'materialratingbar' 31 | keyPassword System.console() != null ? System.console().readLine("\nKey password: ") : "" 32 | } 33 | } 34 | 35 | buildTypes { 36 | release { 37 | minifyEnabled true 38 | shrinkResources true 39 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 40 | signingConfig signingConfigs.release 41 | } 42 | } 43 | } 44 | 45 | dependencies { 46 | implementation fileTree(dir: 'libs', include: ['*.jar']) 47 | implementation 'androidx.appcompat:appcompat:1.1.0' 48 | implementation 'com.jakewharton:butterknife:10.2.0' 49 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0' 50 | implementation project(':library') 51 | } 52 | -------------------------------------------------------------------------------- /sample/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 /opt/android-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 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 11 | 12 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /sample/src/main/java/me/zhanghai/android/materialratingbar/sample/AboutActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar.sample; 7 | 8 | import android.os.Bundle; 9 | import android.text.method.LinkMovementMethod; 10 | import android.view.MenuItem; 11 | import android.widget.TextView; 12 | 13 | import androidx.appcompat.app.AppCompatActivity; 14 | import butterknife.BindView; 15 | import butterknife.ButterKnife; 16 | 17 | public class AboutActivity extends AppCompatActivity { 18 | 19 | @BindView(R.id.version) 20 | TextView mVersionText; 21 | @BindView(R.id.github) 22 | TextView mGithubText; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | 28 | setContentView(R.layout.about_activity); 29 | ButterKnife.bind(this); 30 | 31 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 32 | 33 | String version = getString(R.string.about_version_format, BuildConfig.VERSION_NAME); 34 | mVersionText.setText(version); 35 | mGithubText.setMovementMethod(LinkMovementMethod.getInstance()); 36 | } 37 | 38 | @Override 39 | public boolean onOptionsItemSelected(MenuItem item) { 40 | switch (item.getItemId()) { 41 | case android.R.id.home: 42 | AppUtils.navigateUp(this); 43 | return true; 44 | default: 45 | return super.onOptionsItemSelected(item); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sample/src/main/java/me/zhanghai/android/materialratingbar/sample/AppUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar.sample; 7 | 8 | import android.app.Activity; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.pm.PackageInfo; 12 | import android.content.pm.PackageManager; 13 | import android.os.Bundle; 14 | 15 | import androidx.core.app.NavUtils; 16 | import androidx.core.app.TaskStackBuilder; 17 | 18 | public class AppUtils { 19 | 20 | private AppUtils() {} 21 | 22 | public static PackageInfo getPackageInfo(Context context) { 23 | try { 24 | return context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 25 | } catch (PackageManager.NameNotFoundException e) { 26 | // Should never happen. 27 | throw new RuntimeException(e); 28 | } 29 | } 30 | 31 | // From http://developer.android.com/training/implementing-navigation/ancestral.html#NavigateUp . 32 | public static void navigateUp(Activity activity, Bundle extras) { 33 | Intent upIntent = NavUtils.getParentActivityIntent(activity); 34 | if (upIntent != null) { 35 | if (extras != null) { 36 | upIntent.putExtras(extras); 37 | } 38 | if (NavUtils.shouldUpRecreateTask(activity, upIntent)) { 39 | // This activity is NOT part of this app's task, so create a new task 40 | // when navigating up, with a synthesized back stack. 41 | TaskStackBuilder.create(activity) 42 | // Add all of this activity's parents to the back stack. 43 | .addNextIntentWithParentStack(upIntent) 44 | // Navigate up to the closest parent. 45 | .startActivities(); 46 | } else { 47 | // This activity is part of this app's task, so simply 48 | // navigate up to the logical parent activity. 49 | // According to http://stackoverflow.com/a/14792752/2420519 50 | //NavUtils.navigateUpTo(activity, upIntent); 51 | upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 52 | activity.startActivity(upIntent); 53 | } 54 | } 55 | activity.finish(); 56 | } 57 | 58 | public static void navigateUp(Activity activity) { 59 | navigateUp(activity, null); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sample/src/main/java/me/zhanghai/android/materialratingbar/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zhang Hai 3 | * All Rights Reserved. 4 | */ 5 | 6 | package me.zhanghai.android.materialratingbar.sample; 7 | 8 | import android.content.Intent; 9 | import android.os.Bundle; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.view.animation.Animation; 13 | import android.view.animation.LinearInterpolator; 14 | import android.view.animation.Transformation; 15 | import android.widget.RatingBar; 16 | 17 | import androidx.appcompat.app.AppCompatActivity; 18 | import butterknife.BindViews; 19 | import butterknife.ButterKnife; 20 | 21 | public class MainActivity extends AppCompatActivity { 22 | 23 | @BindViews({ 24 | R.id.framework_decimal_ratingbar, 25 | R.id.library_decimal_ratingbar, 26 | R.id.library_tinted_decimal_ratingbar 27 | }) 28 | RatingBar[] mDecimalRatingBars; 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | 34 | setContentView(R.layout.main_activity); 35 | ButterKnife.bind(this); 36 | 37 | mDecimalRatingBars[0].startAnimation(new RatingAnimation()); 38 | } 39 | 40 | @Override 41 | public boolean onCreateOptionsMenu(Menu menu) { 42 | getMenuInflater().inflate(R.menu.menu_main, menu); 43 | return true; 44 | } 45 | 46 | @Override 47 | public boolean onOptionsItemSelected(MenuItem item) { 48 | switch (item.getItemId()) { 49 | case R.id.action_about: 50 | startActivity(new Intent(this, AboutActivity.class)); 51 | default: 52 | return super.onOptionsItemSelected(item); 53 | } 54 | } 55 | 56 | private class RatingAnimation extends Animation { 57 | 58 | public RatingAnimation() { 59 | setDuration(mDecimalRatingBars[0].getNumStars() 60 | * 4 * getResources().getInteger(android.R.integer.config_longAnimTime)); 61 | setInterpolator(new LinearInterpolator()); 62 | setRepeatCount(Animation.INFINITE); 63 | } 64 | 65 | @Override 66 | protected void applyTransformation(float interpolatedTime, Transformation t) { 67 | int progress = Math.round(interpolatedTime * mDecimalRatingBars[0].getMax()); 68 | for (RatingBar ratingBar : mDecimalRatingBars) { 69 | ratingBar.setProgress(progress); 70 | } 71 | } 72 | 73 | @Override 74 | public boolean willChangeTransformationMatrix() { 75 | return false; 76 | } 77 | 78 | @Override 79 | public boolean willChangeBounds() { 80 | return false; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /sample/src/main/launcher_icon-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanghai/MaterialRatingBar/50ec0bff414cbdf7993d3c110c6cd4db0c70a099/sample/src/main/launcher_icon-web.png -------------------------------------------------------------------------------- /sample/src/main/res/layout/about_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 15 | 16 | 25 | 26 | 34 | 35 | 42 | 43 | 50 | 51 | 58 | 59 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 15 | 16 | 24 | 25 | 26 | 27 | 32 | 33 | 38 | 39 | 48 | 49 | 54 | 55 | 63 | 64 | 69 | 70 | 80 | 81 | 82 | 83 | 88 | 89 | 94 | 95 | 102 | 103 | 108 | 109 | 115 | 116 | 121 | 122 | 130 | 131 | 132 | 133 | 138 | 139 | 144 | 145 | 153 | 154 | 159 | 160 | 166 | 167 | 172 | 173 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 13 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanghai/MaterialRatingBar/50ec0bff414cbdf7993d3c110c6cd4db0c70a099/sample/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanghai/MaterialRatingBar/50ec0bff414cbdf7993d3c110c6cd4db0c70a099/sample/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanghai/MaterialRatingBar/50ec0bff414cbdf7993d3c110c6cd4db0c70a099/sample/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanghai/MaterialRatingBar/50ec0bff414cbdf7993d3c110c6cd4db0c70a099/sample/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanghai/MaterialRatingBar/50ec0bff414cbdf7993d3c110c6cd4db0c70a099/sample/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /sample/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 24dp 10 | 11 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | #f44336 10 | 11 | -------------------------------------------------------------------------------- /sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 16dp 10 | 16dp 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | MaterialRatingBar Sample 11 | 12 | Normal Layout 13 | Wide Layout 14 | Decimal Step Size 15 | Framework Implementation 16 | Library Implementation 17 | Library Implementation (Tinted) 18 | About 19 | 20 | About 21 | MaterialRatingBar 22 | Version %1$s 23 | Zhang Hai 2016 24 | View on GitHub 25 | 26 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 |