├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── michaelmuenzer │ │ └── android │ │ └── scrollablennumberpicker │ │ ├── ScrollableNumberPicker.java │ │ └── ScrollableNumberPickerListener.java │ └── res │ ├── color │ └── btn_tint_selector.xml │ ├── drawable │ ├── ic_arrow_down.xml │ ├── ic_arrow_left.xml │ ├── ic_arrow_right.xml │ └── ic_arrow_up.xml │ ├── layout │ └── number_picker.xml │ └── values │ ├── attrs.xml │ ├── boolean.xml │ ├── colors.xml │ ├── dimens.xml │ ├── integers.xml │ └── strings.xml ├── media ├── sample.gif └── sample_400.gif ├── sample-mobile ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── michaelmuenzer │ │ └── android │ │ └── scrollablenumberpicker │ │ └── sample │ │ └── MainActivity.java │ └── res │ ├── color │ └── btn_tint_selector_main.xml │ ├── drawable-hdpi │ ├── ic_fast_forward_empty.png │ ├── ic_fast_forward_full.png │ ├── ic_fast_rewind_empty.png │ └── ic_fast_rewind_full.png │ ├── drawable-mdpi │ ├── ic_fast_forward_empty.png │ ├── ic_fast_forward_full.png │ ├── ic_fast_rewind_empty.png │ └── ic_fast_rewind_full.png │ ├── drawable-xhdpi │ ├── ic_fast_forward_empty.png │ ├── ic_fast_forward_full.png │ ├── ic_fast_rewind_empty.png │ └── ic_fast_rewind_full.png │ ├── drawable-xxhdpi │ ├── ic_fast_forward_empty.png │ ├── ic_fast_forward_full.png │ ├── ic_fast_rewind_empty.png │ └── ic_fast_rewind_full.png │ ├── drawable-xxxhdpi │ ├── ic_fast_forward_empty.png │ ├── ic_fast_forward_full.png │ ├── ic_fast_rewind_empty.png │ └── ic_fast_rewind_full.png │ ├── drawable │ ├── arrow_down_bold_circle_outline.xml │ ├── arrow_up_bold_circle_outline.xml │ ├── btn_left_selector_main.xml │ ├── btn_right_selector_main.xml │ └── number_picker_bg_color.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── sample-tv ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── michaelmuenzer │ │ └── android │ │ └── scrollablenumberpicker │ │ └── sample │ │ └── MainActivity.java │ └── res │ ├── drawable-hdpi │ └── banner.png │ ├── drawable-mdpi │ └── banner.png │ ├── drawable-xhdpi │ └── banner.png │ ├── drawable-xxhdpi │ └── banner.png │ ├── drawable-xxxhdpi │ └── banner.png │ ├── drawable │ ├── number_picker_bg_color.xml │ ├── number_picker_bg_normal.xml │ └── number_picker_selector.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── backup_descriptor.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | /build/* 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Michael Muenzer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ScrollableNumberPicker 2 | ============ 3 | This view provides an user-friendly numerical input interface. It can easily be customized and is built to be used on Android-TV as well. 4 | 5 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-ScrollableNumberPicker-orange.svg?style=flat)](https://android-arsenal.com/details/1/5676) 6 | [![Download](https://api.bintray.com/packages/michaelmuenzer/ScrollableNumberPicker/ScrollableNumberPicker/images/download.svg) ](https://bintray.com/michaelmuenzer/ScrollableNumberPicker/ScrollableNumberPicker/_latestVersion) 7 | [![API](https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=16) 8 | 9 | How does it look like? 10 | -------- 11 | ![alt tag](https://raw.github.com/michaelmuenzer/ScrollableNumberPicker/master/media/sample.gif) 12 | 13 | Getting started 14 | -------- 15 | The library is available on `jcenter()`. Just add these lines in your `build.gradle`: 16 | 17 | ```groovy 18 | dependencies { 19 | compile 'com.michaelmuenzer.android:ScrollableNumberPicker:0.2.2' 20 | } 21 | ``` 22 | 23 | Alternatively you can use [jitpack.io](https://jitpack.io/#michaelmuenzer/ScrollableNumberPicker) 24 | 25 | Usage 26 | -------- 27 | Just include `ScrollableNumberPicker` inside our xml-layout. There are samples available for mobile and tv applications inside this repository. 28 | 29 | ```xml 30 | 34 | ``` 35 | 36 | If you want to change the value by scrolling on the view, you can enable and control speed like this: 37 | ```xml 38 | 43 | ``` 44 | 45 | You can make use of various other custom attributes to define how the increment and decrement interactions should behave: 46 | ```xml 47 | 51 | 52 | 60 | 61 | ``` 62 | 63 | There exist further attributes which let you customize the general appearance of the view: 64 | ```xml 65 | 82 | ``` 83 | 84 | You can essentially make the element look exactly like you want by using the `android:background` attribute: 85 | ```xml 86 | 90 | ``` 91 | 92 | number_picker_bg_color.xml: 93 | ```xml 94 | 95 | 97 | 98 | 99 | 100 | 101 | 102 | 105 | 106 | ``` 107 | 108 | You can use `ScrollableNumberPickerListener` to build further processing logic around the selected number: 109 | 110 | ```Java 111 | numberPicker.setListener(new ScrollableNumberPickerListener() { 112 | @Override 113 | public void onNumberPicked(int value) { 114 | // Do some magic 115 | } 116 | }); 117 | ``` 118 | 119 | If you use it on Android TV, please include the following to allow D-pad support. 120 | 121 | ```xml 122 | 128 | ``` 129 | 130 | ```Java 131 | @Override 132 | public boolean onKeyDown(int keyCode, KeyEvent event) { 133 | return onKey(keyCode, event); 134 | } 135 | 136 | @Override 137 | public boolean onKeyUp(int keyCode, KeyEvent event) { 138 | return onKey(keyCode, event); 139 | } 140 | 141 | private boolean onKey(int keyCode, KeyEvent event) { 142 | if (horizontalNumberPicker.isFocused()) { 143 | return horizontalNumberPicker.handleKeyEvent(keyCode, event); 144 | } 145 | 146 | return false; 147 | } 148 | ``` 149 | 150 | Questions? 151 | -------- 152 | If you have any questions feel free to open a github issue with a 'question' label 153 | 154 | License 155 | -------- 156 | Licensed under the MIT license. See [LICENSE](LICENSE.md). 157 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:2.3.1' 7 | } 8 | } 9 | 10 | allprojects { 11 | repositories { 12 | jcenter() 13 | } 14 | } 15 | 16 | task clean(type: Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Mar 21 21:55:40 CET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | PUBLISH_GROUP_ID = 'com.michaelmuenzer.android' 5 | PUBLISH_ARTIFACT_ID = 'ScrollableNumberPicker' 6 | PUBLISH_VERSION = '0.2.2' 7 | } 8 | 9 | android { 10 | compileSdkVersion 25 11 | buildToolsVersion "25.0.3" 12 | 13 | lintOptions { 14 | disable 'RestrictedApi' 15 | } 16 | 17 | defaultConfig { 18 | minSdkVersion 16 19 | targetSdkVersion 25 20 | versionCode 1 21 | versionName "1.0" 22 | } 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | } 30 | 31 | final ANDROID_SUPPORTLIB_VERSION = '25.3.1' 32 | 33 | dependencies { 34 | compile "com.android.support:support-compat:${ANDROID_SUPPORTLIB_VERSION}" 35 | compile "com.android.support:appcompat-v7:${ANDROID_SUPPORTLIB_VERSION}" 36 | compile "com.android.support:support-annotations:${ANDROID_SUPPORTLIB_VERSION}" 37 | } 38 | 39 | apply from: 'https://raw.githubusercontent.com/blundell/release-android-library/master/android-release-aar.gradle' -------------------------------------------------------------------------------- /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 /Users/michaelmuenzer/Library/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /library/src/main/java/com/michaelmuenzer/android/scrollablennumberpicker/ScrollableNumberPicker.java: -------------------------------------------------------------------------------- 1 | package com.michaelmuenzer.android.scrollablennumberpicker; 2 | 3 | import android.content.Context; 4 | import android.content.res.ColorStateList; 5 | import android.content.res.Resources; 6 | import android.content.res.TypedArray; 7 | import android.graphics.drawable.Drawable; 8 | import android.os.Handler; 9 | import android.support.annotation.DrawableRes; 10 | import android.support.annotation.NonNull; 11 | import android.support.v4.content.ContextCompat; 12 | import android.support.v4.graphics.drawable.DrawableCompat; 13 | import android.support.v4.widget.TextViewCompat; 14 | import android.util.AttributeSet; 15 | import android.util.TypedValue; 16 | import android.view.Gravity; 17 | import android.view.KeyEvent; 18 | import android.view.LayoutInflater; 19 | import android.view.MotionEvent; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.widget.ImageView; 23 | import android.widget.LinearLayout; 24 | import android.widget.TextView; 25 | 26 | import static android.view.KeyEvent.KEYCODE_DPAD_DOWN; 27 | import static android.view.KeyEvent.KEYCODE_DPAD_LEFT; 28 | import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT; 29 | import static android.view.KeyEvent.KEYCODE_DPAD_UP; 30 | 31 | public class ScrollableNumberPicker extends LinearLayout { 32 | private final static int INVALID_RES = -1; 33 | private final static float SLOWING_FACTOR = 1.25f; 34 | private final static int MIN_UPDATE_INTERVAL_MS = 50; 35 | 36 | @DrawableRes 37 | private int downIcon = R.drawable.ic_arrow_down; 38 | 39 | @DrawableRes 40 | private int upIcon = R.drawable.ic_arrow_up; 41 | 42 | @DrawableRes 43 | private int leftIcon = R.drawable.ic_arrow_left; 44 | 45 | @DrawableRes 46 | private int rightIcon = R.drawable.ic_arrow_right; 47 | 48 | private int mValue; 49 | private int mMaxValue; 50 | private int mMinValue; 51 | private int mStepSize; 52 | 53 | private float mValueTextSize; 54 | private int mValueTextColor; 55 | private int mValueTextAppearanceResId; 56 | 57 | private boolean mScrollEnabled; 58 | private int mUpdateIntervalMillis; 59 | private float mButtonTouchScaleFactor; 60 | private int mOrientation; 61 | private ColorStateList mButtonColorStateList; 62 | 63 | private int mValueMarginStart; 64 | private int mValueMarginEnd; 65 | 66 | private ImageView mMinusButton; 67 | private ImageView mPlusButton; 68 | private TextView mValueTextView; 69 | 70 | private int mButtonPaddingLeft; 71 | private int mButtonPaddingRight; 72 | private int mButtonPaddingTop; 73 | private int mButtonPaddingBottom; 74 | 75 | private boolean mAutoIncrement; 76 | private boolean mAutoDecrement; 77 | 78 | private Handler mUpdateIntervalHandler; 79 | 80 | private ScrollableNumberPickerListener mListener; 81 | 82 | public ScrollableNumberPicker(Context context) { 83 | super(context); 84 | init(context, null); 85 | } 86 | 87 | public ScrollableNumberPicker(Context context, AttributeSet attrs) { 88 | super(context, attrs); 89 | init(context, attrs); 90 | } 91 | 92 | public ScrollableNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) { 93 | super(context, attrs, defStyleAttr); 94 | init(context, attrs); 95 | } 96 | 97 | @SuppressWarnings("unused") 98 | public ImageView getButtonMinusView() { 99 | return mMinusButton; 100 | } 101 | 102 | @SuppressWarnings("unused") 103 | public ImageView getButtonPlusView() { 104 | return mPlusButton; 105 | } 106 | 107 | @SuppressWarnings("unused") 108 | public boolean isScrollEnabled() { 109 | return mScrollEnabled; 110 | } 111 | 112 | @SuppressWarnings("unused") 113 | public int getUpdateIntervalMillis() { 114 | return mUpdateIntervalMillis; 115 | } 116 | 117 | @SuppressWarnings("unused") 118 | public float getButtonTouchScaleFactor() { 119 | return mButtonTouchScaleFactor; 120 | } 121 | 122 | @SuppressWarnings("unused") 123 | public int getOrientation() { 124 | return mOrientation; 125 | } 126 | 127 | @SuppressWarnings("unused") 128 | public ColorStateList getButtonColorStateList() { 129 | return mButtonColorStateList; 130 | } 131 | 132 | @SuppressWarnings("unused") 133 | public int getValueMarginStart() { 134 | return mValueMarginStart; 135 | } 136 | 137 | @SuppressWarnings("unused") 138 | public int getValueMarginEnd() { 139 | return mValueMarginEnd; 140 | } 141 | 142 | @SuppressWarnings("unused") 143 | public TextView getValueView() { 144 | return mValueTextView; 145 | } 146 | 147 | @SuppressWarnings("unused") 148 | public int getValue() { 149 | return mValue; 150 | } 151 | 152 | @SuppressWarnings("WeakerAccess") 153 | public void setValue(int value) { 154 | if (value > mMaxValue) { 155 | value = mMaxValue; 156 | } 157 | if (value < mMinValue) { 158 | value = mMinValue; 159 | } 160 | 161 | mValue = value; 162 | setValue(); 163 | } 164 | 165 | private void setValue() { 166 | mValueTextView.setText(String.valueOf(mValue)); 167 | if (mListener != null) { 168 | mListener.onNumberPicked(mValue); 169 | } 170 | } 171 | 172 | @SuppressWarnings("unused") 173 | public int getMaxValue() { 174 | return mMaxValue; 175 | } 176 | 177 | @SuppressWarnings("unused") 178 | public void setMaxValue(int maxValue) { 179 | mMaxValue = maxValue; 180 | if (maxValue < mValue) { 181 | mValue = maxValue; 182 | setValue(); 183 | } 184 | } 185 | 186 | @SuppressWarnings("unused") 187 | public int getMinValue() { 188 | return mMinValue; 189 | } 190 | 191 | @SuppressWarnings("unused") 192 | public void setMinValue(int minValue) { 193 | mMinValue = minValue; 194 | if (minValue > mValue) { 195 | mValue = minValue; 196 | setValue(); 197 | } 198 | } 199 | 200 | @SuppressWarnings("unused") 201 | public int getStepSize() { 202 | return mStepSize; 203 | } 204 | 205 | @SuppressWarnings("unused") 206 | public void setStepSize(int stepSize) { 207 | mStepSize = stepSize; 208 | } 209 | 210 | @SuppressWarnings("unused") 211 | public float getValueTextSize() { 212 | return mValueTextSize; 213 | } 214 | 215 | @SuppressWarnings("unused") 216 | public void setValueTextSize(float valueTextSize) { 217 | mValueTextSize = valueTextSize; 218 | mValueTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mValueTextSize); 219 | } 220 | 221 | @SuppressWarnings("unused") 222 | public int getValueTextColor() { 223 | return mValueTextColor; 224 | } 225 | 226 | @SuppressWarnings("unused") 227 | public void setValueTextColor(int valueTextColor) { 228 | mValueTextColor = valueTextColor; 229 | mValueTextView.setTextColor(mValueTextColor); 230 | } 231 | 232 | @SuppressWarnings("unused") 233 | public void setScrollEnabled(boolean scrollEnabled) { 234 | mScrollEnabled = scrollEnabled; 235 | } 236 | 237 | @SuppressWarnings("unused") 238 | public int getButtonPaddingLeft() { 239 | return mButtonPaddingLeft; 240 | } 241 | 242 | @SuppressWarnings("unused") 243 | public void setButtonPaddingLeft(int buttonPaddingLeft) { 244 | mButtonPaddingLeft = buttonPaddingLeft; 245 | setPlusMinusButtonPaddings(); 246 | } 247 | 248 | @SuppressWarnings("unused") 249 | public int getButtonPaddingRight() { 250 | return mButtonPaddingRight; 251 | } 252 | 253 | @SuppressWarnings("unused") 254 | public void setButtonPaddingRight(int buttonPaddingRight) { 255 | mButtonPaddingRight = buttonPaddingRight; 256 | setPlusMinusButtonPaddings(); 257 | } 258 | 259 | @SuppressWarnings("unused") 260 | public int getButtonPaddingTop() { 261 | return mButtonPaddingTop; 262 | } 263 | 264 | @SuppressWarnings("unused") 265 | public void setButtonPaddingTop(int buttonPaddingTop) { 266 | mButtonPaddingTop = buttonPaddingTop; 267 | setPlusMinusButtonPaddings(); 268 | } 269 | 270 | @SuppressWarnings("unused") 271 | public int getButtonPaddingBottom() { 272 | return mButtonPaddingBottom; 273 | } 274 | 275 | @SuppressWarnings("unused") 276 | public void setButtonPaddingBottom(int buttonPaddingBottom) { 277 | mButtonPaddingBottom = buttonPaddingBottom; 278 | setPlusMinusButtonPaddings(); 279 | } 280 | 281 | @SuppressWarnings("unused") 282 | public long getOnLongPressUpdateInterval() { 283 | return mUpdateIntervalMillis; 284 | } 285 | 286 | @SuppressWarnings("unused") 287 | public void setOnLongPressUpdateInterval(int intervalMillis) { 288 | if (intervalMillis < MIN_UPDATE_INTERVAL_MS) { 289 | intervalMillis = MIN_UPDATE_INTERVAL_MS; 290 | } 291 | 292 | mUpdateIntervalMillis = intervalMillis; 293 | } 294 | 295 | @SuppressWarnings("unused") 296 | public void setListener(ScrollableNumberPickerListener listener) { 297 | mListener = listener; 298 | } 299 | 300 | public boolean handleKeyEvent(int keyCode, KeyEvent event) { 301 | int eventAction = event.getAction(); 302 | if (eventAction == KeyEvent.ACTION_DOWN) { 303 | if (mOrientation == HORIZONTAL) { 304 | if (keyCode == KEYCODE_DPAD_LEFT) { 305 | if (event.getRepeatCount() == 0) { 306 | scaleImageViewDrawable(mMinusButton, mButtonTouchScaleFactor); 307 | } 308 | decrement(); 309 | return true; 310 | } else if (keyCode == KEYCODE_DPAD_RIGHT) { 311 | if (event.getRepeatCount() == 0) { 312 | scaleImageViewDrawable(mPlusButton, mButtonTouchScaleFactor); 313 | } 314 | increment(); 315 | return true; 316 | } 317 | } else { 318 | if (keyCode == KEYCODE_DPAD_UP) { 319 | if (event.getRepeatCount() == 0) { 320 | scaleImageViewDrawable(mPlusButton, mButtonTouchScaleFactor); 321 | } 322 | increment(); 323 | return true; 324 | } else if (keyCode == KEYCODE_DPAD_DOWN) { 325 | if (event.getRepeatCount() == 0) { 326 | scaleImageViewDrawable(mMinusButton, mButtonTouchScaleFactor); 327 | } 328 | decrement(); 329 | return true; 330 | } 331 | } 332 | } else if (eventAction == KeyEvent.ACTION_UP) { 333 | if (mOrientation == HORIZONTAL) { 334 | if (keyCode == KEYCODE_DPAD_LEFT) { 335 | setButtonMinusImage(); 336 | return true; 337 | } else if (keyCode == KEYCODE_DPAD_RIGHT) { 338 | setButtonPlusImage(); 339 | return true; 340 | } 341 | } else { 342 | if (keyCode == KEYCODE_DPAD_UP) { 343 | setButtonPlusImage(); 344 | return true; 345 | } else if (keyCode == KEYCODE_DPAD_DOWN) { 346 | setButtonMinusImage(); 347 | return true; 348 | } 349 | } 350 | } 351 | 352 | return false; 353 | } 354 | 355 | private void init(Context context, AttributeSet attrs) { 356 | if (isInEditMode()) { 357 | return; 358 | } 359 | 360 | LayoutInflater layoutInflater = 361 | (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 362 | layoutInflater.inflate(R.layout.number_picker, this); 363 | 364 | TypedArray typedArray = 365 | context.obtainStyledAttributes(attrs, R.styleable.ScrollableNumberPicker); 366 | Resources res = getResources(); 367 | 368 | downIcon = typedArray.getResourceId(R.styleable.ScrollableNumberPicker_snp_buttonIconDown, downIcon); 369 | upIcon = typedArray.getResourceId(R.styleable.ScrollableNumberPicker_snp_buttonIconUp, upIcon); 370 | leftIcon = typedArray.getResourceId(R.styleable.ScrollableNumberPicker_snp_buttonIconLeft, leftIcon); 371 | rightIcon = typedArray.getResourceId(R.styleable.ScrollableNumberPicker_snp_buttonIconRight, rightIcon); 372 | 373 | mMinValue = typedArray.getInt(R.styleable.ScrollableNumberPicker_snp_minValue, 374 | res.getInteger(R.integer.default_minValue)); 375 | mMaxValue = typedArray.getInt(R.styleable.ScrollableNumberPicker_snp_maxValue, 376 | res.getInteger(R.integer.default_maxValue)); 377 | 378 | mStepSize = typedArray.getInt(R.styleable.ScrollableNumberPicker_snp_stepSize, 379 | res.getInteger(R.integer.default_stepSize)); 380 | 381 | mUpdateIntervalMillis = typedArray.getInt(R.styleable.ScrollableNumberPicker_snp_updateInterval, 382 | res.getInteger(R.integer.default_updateInterval)); 383 | 384 | mOrientation = typedArray.getInt(R.styleable.ScrollableNumberPicker_snp_orientation, 385 | LinearLayout.HORIZONTAL); 386 | 387 | mValue = typedArray.getInt(R.styleable.ScrollableNumberPicker_snp_value, 388 | res.getInteger(R.integer.default_value)); 389 | 390 | mValueTextSize = typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_value_text_size, 391 | INVALID_RES); 392 | mValueTextColor = typedArray.getColor(R.styleable.ScrollableNumberPicker_snp_value_text_color, 393 | 0); 394 | mValueTextAppearanceResId = typedArray.getResourceId(R.styleable.ScrollableNumberPicker_snp_value_text_appearance, INVALID_RES); 395 | 396 | mScrollEnabled = typedArray.getBoolean(R.styleable.ScrollableNumberPicker_snp_scrollEnabled, 397 | res.getBoolean(R.bool.default_scrollEnabled)); 398 | 399 | mButtonColorStateList = ContextCompat.getColorStateList(context, typedArray.getResourceId(R.styleable.ScrollableNumberPicker_snp_buttonBackgroundTintSelector, R.color.btn_tint_selector)); 400 | 401 | mValueMarginStart = (int) typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_valueMarginStart, res.getDimension(R.dimen.default_value_margin_start)); 402 | mValueMarginEnd = (int) typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_valueMarginStart, res.getDimension(R.dimen.default_value_margin_end)); 403 | 404 | mButtonPaddingLeft = (int) typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_buttonPaddingLeft, res.getDimension(R.dimen.default_button_padding_left)); 405 | mButtonPaddingRight = (int) typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_buttonPaddingRight, res.getDimension(R.dimen.default_button_padding_right)); 406 | mButtonPaddingTop = (int) typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_buttonPaddingTop, res.getDimension(R.dimen.default_button_padding_top)); 407 | mButtonPaddingBottom = (int) typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_buttonPaddingBottom, res.getDimension(R.dimen.default_button_padding_bottom)); 408 | 409 | TypedValue outValue = new TypedValue(); 410 | res.getValue(R.dimen.default_button_scale_factor, outValue, true); 411 | float defaultValue = outValue.getFloat(); 412 | mButtonTouchScaleFactor = typedArray.getFloat(R.styleable.ScrollableNumberPicker_snp_buttonTouchScaleFactor, defaultValue); 413 | 414 | typedArray.recycle(); 415 | 416 | initViews(); 417 | 418 | mAutoIncrement = false; 419 | mAutoDecrement = false; 420 | 421 | mUpdateIntervalHandler = new Handler(); 422 | } 423 | 424 | private void initViews() { 425 | setOrientation(mOrientation); 426 | setGravity(Gravity.CENTER); 427 | 428 | initValueView(); 429 | initButtonPlusView(); 430 | initButtonMinusView(); 431 | 432 | if (mScrollEnabled) { 433 | setOnTouchListener(new OnTouchListener() { 434 | private float lastX = 0.0f; 435 | private float lastY = 0.0f; 436 | private final int scrollOffsetPx = getResources().getDimensionPixelSize(R.dimen.default_scroll_offset); 437 | 438 | @Override 439 | public boolean onTouch(View view, MotionEvent motionEvent) { 440 | float currentX = motionEvent.getX(); 441 | float currentY = motionEvent.getY(); 442 | 443 | switch (motionEvent.getAction()) { 444 | case MotionEvent.ACTION_DOWN: 445 | lastX = currentX; 446 | lastY = currentY; 447 | break; 448 | case MotionEvent.ACTION_MOVE: 449 | if (mOrientation == HORIZONTAL) { 450 | float moveDeltaX = currentX - lastX; 451 | if (moveDeltaX > scrollOffsetPx) { 452 | increment(); 453 | break; 454 | } else if ((scrollOffsetPx * -1) > moveDeltaX) { 455 | decrement(); 456 | break; 457 | } 458 | } else { 459 | float moveDeltaY = currentY - lastY; 460 | if (moveDeltaY > scrollOffsetPx) { 461 | decrement(); 462 | break; 463 | } else if ((scrollOffsetPx * -1) > moveDeltaY) { 464 | increment(); 465 | break; 466 | } 467 | } 468 | 469 | lastX = currentX; 470 | lastY = currentY; 471 | break; 472 | case MotionEvent.ACTION_UP: 473 | int numberOfRuns; 474 | int singleRunLength = getResources().getDimensionPixelSize(R.dimen.default_scroll_repeat_length); 475 | if (mOrientation == HORIZONTAL) { 476 | float moveDeltaX = currentX - lastX; 477 | if (moveDeltaX > 0) { 478 | mAutoIncrement = true; 479 | } else { 480 | mAutoDecrement = true; 481 | } 482 | 483 | numberOfRuns = (int) (Math.abs(moveDeltaX) / singleRunLength); 484 | } else { 485 | float moveDeltaY = currentY - lastY; 486 | if (moveDeltaY > 0) { 487 | mAutoDecrement = true; 488 | } else { 489 | mAutoIncrement = true; 490 | } 491 | 492 | numberOfRuns = (int) (Math.abs(moveDeltaY) / singleRunLength); 493 | } 494 | 495 | mUpdateIntervalHandler.post(new RepeatSlowingRunnable(numberOfRuns, mUpdateIntervalMillis)); 496 | break; 497 | default: 498 | return false; 499 | } 500 | 501 | return true; 502 | } 503 | }); 504 | } 505 | } 506 | 507 | private void initValueView() { 508 | mValueTextView = (TextView) findViewById(R.id.text_value); 509 | 510 | if (mValueTextAppearanceResId != INVALID_RES) { 511 | TextViewCompat.setTextAppearance(mValueTextView, mValueTextAppearanceResId); 512 | } 513 | 514 | if (mValueTextColor != 0) { 515 | mValueTextView.setTextColor(mValueTextColor); 516 | } 517 | 518 | if (mValueTextSize != INVALID_RES) { 519 | mValueTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mValueTextSize); 520 | } 521 | 522 | LinearLayout.LayoutParams layoutParams = (LayoutParams) mValueTextView.getLayoutParams(); 523 | if (mOrientation == HORIZONTAL) { 524 | layoutParams.setMargins(mValueMarginStart, 0, mValueMarginEnd, 0); 525 | } else { 526 | layoutParams.setMargins(0, mValueMarginStart, 0, mValueMarginEnd); 527 | } 528 | 529 | mValueTextView.setLayoutParams(layoutParams); 530 | 531 | setValue(); 532 | } 533 | 534 | private void initButtonPlusView() { 535 | setButtonPlusImage(); 536 | 537 | mPlusButton.setOnClickListener(new OnClickListener() { 538 | public void onClick(View v) { 539 | increment(); 540 | } 541 | }); 542 | 543 | mPlusButton.setOnLongClickListener(new OnLongClickListener() { 544 | public boolean onLongClick(View v) { 545 | mAutoIncrement = true; 546 | mUpdateIntervalHandler.post(new RepeatRunnable()); 547 | return false; 548 | } 549 | }); 550 | 551 | mPlusButton.setOnTouchListener(new OnTouchListener() { 552 | public boolean onTouch(View v, MotionEvent event) { 553 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 554 | scaleImageViewDrawable(mPlusButton, mButtonTouchScaleFactor); 555 | } else if (event.getAction() == MotionEvent.ACTION_UP) { 556 | if (mAutoIncrement) { 557 | mAutoIncrement = false; 558 | } 559 | 560 | setButtonPlusImage(); 561 | } 562 | 563 | return false; 564 | } 565 | }); 566 | } 567 | 568 | private void initButtonMinusView() { 569 | setButtonMinusImage(); 570 | 571 | mMinusButton.setOnClickListener(new View.OnClickListener() { 572 | public void onClick(View v) { 573 | decrement(); 574 | } 575 | }); 576 | 577 | mMinusButton.setOnLongClickListener(new View.OnLongClickListener() { 578 | public boolean onLongClick(View v) { 579 | mAutoDecrement = true; 580 | mUpdateIntervalHandler.post(new RepeatRunnable()); 581 | return false; 582 | } 583 | }); 584 | 585 | mMinusButton.setOnTouchListener(new View.OnTouchListener() { 586 | public boolean onTouch(View v, MotionEvent event) { 587 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 588 | scaleImageViewDrawable(mMinusButton, mButtonTouchScaleFactor); 589 | } else if (event.getAction() == MotionEvent.ACTION_UP) { 590 | if (mAutoDecrement) { 591 | mAutoDecrement = false; 592 | } 593 | 594 | setButtonMinusImage(); 595 | } 596 | 597 | return false; 598 | } 599 | }); 600 | } 601 | 602 | private void setButtonPlusImage() { 603 | if (mOrientation == LinearLayout.VERTICAL) { 604 | mPlusButton = (ImageView) findViewById(R.id.button_increase); 605 | mPlusButton.setImageResource(upIcon); 606 | } else if (mOrientation == LinearLayout.HORIZONTAL) { 607 | mPlusButton = (ImageView) findViewById(R.id.button_decrease); 608 | mPlusButton.setImageResource(rightIcon); 609 | } 610 | 611 | tintButton(mPlusButton, mButtonColorStateList); 612 | 613 | setButtonLayoutParams(mPlusButton); 614 | } 615 | 616 | private void setButtonMinusImage() { 617 | if (mOrientation == LinearLayout.VERTICAL) { 618 | mMinusButton = (ImageView) findViewById(R.id.button_decrease); 619 | mMinusButton.setImageResource(downIcon); 620 | } else if (mOrientation == LinearLayout.HORIZONTAL) { 621 | mMinusButton = (ImageView) findViewById(R.id.button_increase); 622 | mMinusButton.setImageResource(leftIcon); 623 | } 624 | 625 | tintButton(mMinusButton, mButtonColorStateList); 626 | 627 | setButtonLayoutParams(mMinusButton); 628 | } 629 | 630 | private void setButtonLayoutParams(ImageView button) { 631 | LayoutParams params = (LayoutParams) button.getLayoutParams(); 632 | params.width = ViewGroup.LayoutParams.WRAP_CONTENT; 633 | params.height = ViewGroup.LayoutParams.WRAP_CONTENT; 634 | params.setMargins(0, 0, 0, 0); 635 | button.setLayoutParams(params); 636 | 637 | setButtonPaddings(button); 638 | } 639 | 640 | private void setPlusMinusButtonPaddings() { 641 | setButtonPaddings(mMinusButton); 642 | setButtonPaddings(mPlusButton); 643 | } 644 | 645 | private void setButtonPaddings(ImageView button) { 646 | button.setPadding(mButtonPaddingLeft, mButtonPaddingTop, mButtonPaddingRight, mButtonPaddingBottom); 647 | } 648 | 649 | private void tintButton(@NonNull ImageView button, ColorStateList colorStateList) { 650 | Drawable drawable = DrawableCompat.wrap(button.getDrawable()); 651 | DrawableCompat.setTintList(drawable, colorStateList); 652 | button.setImageDrawable(drawable); 653 | } 654 | 655 | private void scaleImageViewDrawable(ImageView view, float scaleFactor) { 656 | Drawable drawable = view.getDrawable(); 657 | int currentWidth = drawable.getIntrinsicWidth(); 658 | int currentHeight = drawable.getIntrinsicHeight(); 659 | int newWidth = (int) (currentWidth * scaleFactor); 660 | int newHeight = (int) (currentHeight * scaleFactor); 661 | 662 | if (newWidth < currentWidth && newHeight < currentHeight) { 663 | int marginWidth = (currentWidth - newWidth) / 2; 664 | int marginHeight = (currentHeight - newHeight) / 2; 665 | 666 | //setBounds is not working on FireTV, that's why we use a workaround with margins 667 | LayoutParams params = (LayoutParams) view.getLayoutParams(); 668 | params.width = newWidth; 669 | params.height = newHeight; 670 | params.setMargins(marginWidth, marginHeight, marginWidth, marginHeight); 671 | view.setLayoutParams(params); 672 | } 673 | } 674 | 675 | private void increment() { 676 | if (mValue < mMaxValue) { 677 | setValue(mValue + mStepSize); 678 | } 679 | } 680 | 681 | private void decrement() { 682 | if (mValue > mMinValue) { 683 | setValue(mValue - mStepSize); 684 | } 685 | } 686 | 687 | private class RepeatRunnable implements Runnable { 688 | public void run() { 689 | if (mAutoIncrement) { 690 | increment(); 691 | mUpdateIntervalHandler.postDelayed(new RepeatRunnable(), mUpdateIntervalMillis); 692 | } else if (mAutoDecrement) { 693 | decrement(); 694 | mUpdateIntervalHandler.postDelayed(new RepeatRunnable(), mUpdateIntervalMillis); 695 | } 696 | } 697 | } 698 | 699 | private class RepeatSlowingRunnable implements Runnable { 700 | long mUpdateIntervalMillis = 0; 701 | int mNumberOfLeftRuns = 0; 702 | 703 | RepeatSlowingRunnable(int numberOfLeftRuns, long millis) { 704 | mUpdateIntervalMillis = millis; 705 | mNumberOfLeftRuns = numberOfLeftRuns; 706 | } 707 | 708 | public void run() { 709 | long millisNextRun = (long) (mUpdateIntervalMillis * SLOWING_FACTOR); 710 | 711 | if (mNumberOfLeftRuns > 0) { 712 | int leftRuns = mNumberOfLeftRuns - 1; 713 | 714 | if (mAutoIncrement) { 715 | increment(); 716 | mUpdateIntervalHandler.postDelayed(new RepeatSlowingRunnable(leftRuns, millisNextRun), mUpdateIntervalMillis); 717 | } else if (mAutoDecrement) { 718 | decrement(); 719 | mUpdateIntervalHandler.postDelayed(new RepeatSlowingRunnable(leftRuns, millisNextRun), mUpdateIntervalMillis); 720 | } 721 | } else { 722 | mAutoIncrement = false; 723 | mAutoDecrement = false; 724 | } 725 | } 726 | } 727 | } 728 | -------------------------------------------------------------------------------- /library/src/main/java/com/michaelmuenzer/android/scrollablennumberpicker/ScrollableNumberPickerListener.java: -------------------------------------------------------------------------------- 1 | package com.michaelmuenzer.android.scrollablennumberpicker; 2 | 3 | @SuppressWarnings("unused") 4 | public interface ScrollableNumberPickerListener { 5 | void onNumberPicked(int value); 6 | } 7 | -------------------------------------------------------------------------------- /library/src/main/res/color/btn_tint_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_arrow_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_arrow_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_arrow_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_arrow_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /library/src/main/res/layout/number_picker.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 10 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /library/src/main/res/values/boolean.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffffff 4 | #ff000000 5 | -------------------------------------------------------------------------------- /library/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24dp 4 | 0dp 5 | 0dp 6 | 0dp 7 | 0dp 8 | 0dp 9 | 0dp 10 | 4dp 11 | 25dp 12 | 13 | 1.0 14 | -------------------------------------------------------------------------------- /library/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0 4 | 999 5 | 0 6 | 1 7 | 100 8 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | increase 4 | decrease 5 | -------------------------------------------------------------------------------- /media/sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/media/sample.gif -------------------------------------------------------------------------------- /media/sample_400.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/media/sample_400.gif -------------------------------------------------------------------------------- /sample-mobile/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample-mobile/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "com.michaelmuenzer.android.scrollablenumberpicker.sample" 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | vectorDrawables.useSupportLibrary = true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile 'com.android.support:appcompat-v7:25.3.1' 25 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 26 | compile project(path: ':library') 27 | } 28 | -------------------------------------------------------------------------------- /sample-mobile/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 /Users/michaelmuenzer/Library/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /sample-mobile/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sample-mobile/src/main/java/com/michaelmuenzer/android/scrollablenumberpicker/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.michaelmuenzer.android.scrollablenumberpicker.sample; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.Toast; 6 | 7 | import com.michaelmuenzer.android.scrollablennumberpicker.ScrollableNumberPicker; 8 | import com.michaelmuenzer.android.scrollablennumberpicker.ScrollableNumberPickerListener; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_main); 16 | 17 | final ScrollableNumberPicker verticalNumberPicker = (ScrollableNumberPicker) findViewById(R.id.number_picker_vertical); 18 | verticalNumberPicker.setListener(new ScrollableNumberPickerListener() { 19 | @Override 20 | public void onNumberPicked(int value) { 21 | if(value == verticalNumberPicker.getMaxValue()) { 22 | Toast.makeText(MainActivity.this, getString(R.string.msg_toast_max_value), Toast.LENGTH_LONG).show(); 23 | } 24 | } 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/color/btn_tint_selector_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-hdpi/ic_fast_forward_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-hdpi/ic_fast_forward_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-hdpi/ic_fast_forward_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-hdpi/ic_fast_forward_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-hdpi/ic_fast_rewind_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-hdpi/ic_fast_rewind_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-hdpi/ic_fast_rewind_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-hdpi/ic_fast_rewind_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-mdpi/ic_fast_forward_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-mdpi/ic_fast_forward_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-mdpi/ic_fast_forward_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-mdpi/ic_fast_forward_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-mdpi/ic_fast_rewind_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-mdpi/ic_fast_rewind_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-mdpi/ic_fast_rewind_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-mdpi/ic_fast_rewind_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xhdpi/ic_fast_forward_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xhdpi/ic_fast_forward_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xhdpi/ic_fast_forward_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xhdpi/ic_fast_forward_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xhdpi/ic_fast_rewind_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xhdpi/ic_fast_rewind_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xhdpi/ic_fast_rewind_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xhdpi/ic_fast_rewind_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_forward_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_forward_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_forward_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_forward_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_rewind_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_rewind_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_rewind_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxhdpi/ic_fast_rewind_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_forward_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_forward_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_forward_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_forward_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_rewind_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_rewind_empty.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_rewind_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/drawable-xxxhdpi/ic_fast_rewind_full.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable/arrow_down_bold_circle_outline.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable/arrow_up_bold_circle_outline.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable/btn_left_selector_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable/btn_right_selector_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/drawable/number_picker_bg_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 29 | 30 | 44 | 45 | 46 | 50 | 51 | 69 | 70 | 89 | 90 | 91 | 95 | 96 | 112 | 113 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-mobile/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample-mobile/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #a8cf45 8 | #ffffffff 9 | #ff000000 10 | 11 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ScrollableNumberPicker 3 | 4 | Max value has been reached! 5 | 6 | -------------------------------------------------------------------------------- /sample-mobile/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /sample-tv/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample-tv/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "com.michaelmuenzer.android.tv.scrollablenumberpicker.sample" 8 | minSdkVersion 21 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile 'com.android.support:leanback-v17:25.3.1' 23 | compile project(path: ':library') 24 | } 25 | -------------------------------------------------------------------------------- /sample-tv/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 /Users/michaelmuenzer/Library/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /sample-tv/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sample-tv/src/main/java/com/michaelmuenzer/android/scrollablenumberpicker/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.michaelmuenzer.android.scrollablenumberpicker.sample; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.KeyEvent; 6 | 7 | import com.michaelmuenzer.android.scrollablennumberpicker.ScrollableNumberPicker; 8 | import com.michaelmuenzer.android.scrollablenumberpicker.R; 9 | 10 | public class MainActivity extends Activity { 11 | private ScrollableNumberPicker horizontalNumberPicker; 12 | 13 | private ScrollableNumberPicker verticalNumberPicker; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | 20 | horizontalNumberPicker = (ScrollableNumberPicker) findViewById(R.id.number_picker_horizontal); 21 | verticalNumberPicker = (ScrollableNumberPicker) findViewById(R.id.number_picker_vertical); 22 | } 23 | 24 | @Override 25 | public boolean onKeyDown(int keyCode, KeyEvent event) { 26 | return onKey(keyCode, event); 27 | } 28 | 29 | 30 | @Override 31 | public boolean onKeyUp(int keyCode, KeyEvent event) { 32 | return onKey(keyCode, event); 33 | } 34 | 35 | private boolean onKey(int keyCode, KeyEvent event) { 36 | if (horizontalNumberPicker.isFocused()) { 37 | return horizontalNumberPicker.handleKeyEvent(keyCode, event); 38 | } else if (verticalNumberPicker.isFocused()) { 39 | return verticalNumberPicker.handleKeyEvent(keyCode, event); 40 | } 41 | 42 | return false; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable-hdpi/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/drawable-hdpi/banner.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable-mdpi/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/drawable-mdpi/banner.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable-xhdpi/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/drawable-xhdpi/banner.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable-xxhdpi/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/drawable-xxhdpi/banner.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable-xxxhdpi/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/drawable-xxxhdpi/banner.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable/number_picker_bg_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable/number_picker_bg_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/drawable/number_picker_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 27 | 28 | 46 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmuenzer/ScrollableNumberPicker/db4f423e0192f9850da4ea96a96f34ea3786c9c0/sample-tv/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-tv/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #e12822 4 | #00ffffff 5 | #ffffffff 6 | #ff000000 7 | 8 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ScrollableNumberPicker 3 | 4 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /sample-tv/src/main/res/xml/backup_descriptor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample-mobile', ':sample-tv', ':library' --------------------------------------------------------------------------------