├── .gitignore
├── .idea
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── readme.md
├── seekbarlibrary
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── zlm
│ │ └── libs
│ │ └── widget
│ │ ├── CustomSeekBar.java
│ │ └── MusicSeekBar.java
│ └── res
│ └── values
│ ├── dimens.xml
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.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 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.0.0'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | maven { url 'https://jitpack.io' }
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/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/zhangliangming/SeekBar/acf35a81a9c058912db7e8a07b134f1b82a5332b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Feb 14 14:33:11 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.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 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # 简介 #
2 | 该SeekBar主要完成了普通的进度条(可修改进度条的颜色,游标颜色,二级进度条颜色等等),最后再在普通进度条的基础上实现了歌曲使用的进度条,可弹出窗口显示时间和歌词。该功能主要是在我的乐乐音乐播放器里面使用,其项目地址如下:
3 | https://github.com/zhangliangming/HappyPlayer5.git 。
4 |
5 | # 日志 #
6 | ## v3.5 ##
7 | 1. minSdkVersion 修改为19
8 |
9 | ## v3.0 ##
10 | 1. 添加混淆
11 | 2. 修改包名
12 | 3. 修复
13 | 4. 不混淆接口
14 |
15 | ## v2.1 ##
16 | 1. 修复onStopTrackingTouch
17 |
18 | ## v1.6 ##
19 | 1. 添加TrackingTouchSleepTime
20 |
21 | ## v1.5 ##
22 | 1. 修复bug
23 |
24 | ## v1.4 ##
25 | 1. view修改为SeekBar
26 |
27 | ## v1.3 ##
28 | 1. 修复滑动越界后,出现进度为负数的问题
29 | ## v1.2 ##
30 | 1. 修复弹出窗口报max == 0的问题
31 | ## v1.1 ##
32 | 1. 实现初始功能
33 |
34 |
35 | # 截图 #
36 |
37 | 
38 |
39 | # Gradle #
40 | 1.root build.gradle
41 |
42 | `allprojects {
43 | repositories {
44 | ...
45 | maven { url 'https://jitpack.io' }
46 | }
47 | }`
48 |
49 | 2.app build.gradle
50 |
51 | `dependencies {
52 | compile 'com.github.zhangliangming:SeekBar:v3.5'
53 | }`
54 |
55 | # 混淆注意 #
56 | -keep class com.zlm.libs.widget.** { *; }
57 |
58 | # 调用Demo #
59 | 链接: https://pan.baidu.com/s/1gg7jzWZ 密码: y3qd
60 | # 调用用法 #
61 | 
62 |
63 | 
64 |
65 | 
66 |
67 | # API #
68 | - setBackgroundPaintColor:设置背景颜色
69 | - setProgressColor:设置进度颜色
70 | - setSecondProgressColor:设置第二进度颜色
71 | - setThumbColor:设置游标颜色
72 | - setTimePopupWindowViewColor:设置时间弹窗颜色
73 | - setTimeAndLrcPopupWindowViewColor:设置时间和歌词弹窗颜色
74 | - OnMusicListener.getTimeText:获取时间标签,如果需要弹出窗口时,不能返回null。
75 | - OnMusicListener.getLrcText:如果需要弹出歌词窗口时,不能返回null。
76 |
77 |
78 | # 捐赠 #
79 | 如果该项目对您有所帮助,欢迎您的赞赏
80 |
81 | - 微信
82 |
83 | 
84 |
85 | - 支付宝
86 |
87 | 
88 |
--------------------------------------------------------------------------------
/seekbarlibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/seekbarlibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | group = 'com.github.zhangliangming'
4 |
5 | android {
6 | compileSdkVersion 26
7 |
8 |
9 |
10 | defaultConfig {
11 | minSdkVersion 19
12 | targetSdkVersion 26
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
17 |
18 | }
19 |
20 | buildTypes {
21 | release {
22 | zipAlignEnabled true
23 | minifyEnabled true
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | }
29 |
30 |
31 | dependencies {
32 | implementation 'com.github.zhangliangming:Register:v1.0'
33 | }
34 |
--------------------------------------------------------------------------------
/seekbarlibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -keep class com.zlm.libs.widget.**
24 | -keepclassmembers class com.zlm.libs.widget.** {
25 | public *;
26 | }
27 | -keep class com.zlm.libs.register.** { *; }
--------------------------------------------------------------------------------
/seekbarlibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/seekbarlibrary/src/main/java/com/zlm/libs/widget/CustomSeekBar.java:
--------------------------------------------------------------------------------
1 | package com.zlm.libs.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.RectF;
8 | import android.graphics.drawable.BitmapDrawable;
9 | import android.os.Handler;
10 | import android.util.AttributeSet;
11 | import android.widget.SeekBar;
12 |
13 | import com.zlm.libs.register.RegisterHelper;
14 |
15 |
16 | /**
17 | * @Description: 自定义普通进度条
18 | * @Param:
19 | * @Return:
20 | * @Author: zhangliangming
21 | * @Date: 2018-02-14
22 | * @Throws:
23 | */
24 | public class CustomSeekBar extends SeekBar {
25 | /**
26 | * 背景画笔
27 | */
28 | private Paint mBackgroundPaint;
29 |
30 | /**
31 | * 进度画笔
32 | */
33 | private Paint mProgressPaint;
34 |
35 | /**
36 | * 第二进度画笔
37 | */
38 | private Paint mSecondProgressPaint;
39 |
40 | /**
41 | * 游标画笔
42 | */
43 | private Paint mThumbPaint;
44 |
45 | /**
46 | * 默认
47 | */
48 | private final int TRACKTOUCH_NONE = -1;
49 | /**
50 | * 开始拖动
51 | */
52 | private final int TRACKTOUCH_START = 0;
53 | private int mTrackTouch = TRACKTOUCH_NONE;
54 |
55 | private OnChangeListener mOnChangeListener;
56 |
57 | //TrackingTouch
58 | private boolean isTrackingTouch = false;
59 | private int mTrackingTouchSleepTime = 0;
60 | private Handler mHandler = new Handler();
61 | private Runnable mRunnable = new Runnable() {
62 | @Override
63 | public void run() {
64 | setTrackTouch(TRACKTOUCH_NONE);
65 | }
66 | };
67 |
68 | public CustomSeekBar(Context context) {
69 | super(context);
70 | init(context);
71 | }
72 |
73 | public CustomSeekBar(Context context, AttributeSet attrs) {
74 | super(context, attrs);
75 | init(context);
76 | }
77 |
78 | /**
79 | * 初始化
80 | */
81 | private void init(Context context) {
82 |
83 | RegisterHelper.verify();
84 |
85 | setBackgroundColor(Color.TRANSPARENT);
86 | //
87 | mBackgroundPaint = new Paint();
88 | mBackgroundPaint.setDither(true);
89 | mBackgroundPaint.setAntiAlias(true);
90 | mBackgroundPaint.setColor(Color.parseColor("#e5e5e5"));
91 |
92 | //
93 | mProgressPaint = new Paint();
94 | mProgressPaint.setDither(true);
95 | mProgressPaint.setAntiAlias(true);
96 | mProgressPaint.setColor(Color.parseColor("#0288d1"));
97 |
98 | //
99 | mSecondProgressPaint = new Paint();
100 | mSecondProgressPaint.setDither(true);
101 | mSecondProgressPaint.setAntiAlias(true);
102 | mSecondProgressPaint.setColor(Color.parseColor("#b8b8b8"));
103 |
104 | //
105 | mThumbPaint = new Paint();
106 | mThumbPaint.setDither(true);
107 | mThumbPaint.setAntiAlias(true);
108 | mThumbPaint.setColor(Color.parseColor("#0288d1"));
109 |
110 | //
111 | setThumb(new BitmapDrawable());
112 | setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
113 | @Override
114 | public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
115 | if (mTrackTouch == TRACKTOUCH_START) {
116 | if (mOnChangeListener != null) {
117 | mOnChangeListener.onProgressChanged(CustomSeekBar.this);
118 | }
119 | }
120 | }
121 |
122 | @Override
123 | public void onStartTrackingTouch(SeekBar seekBar) {
124 | isTrackingTouch = true;
125 | mHandler.removeCallbacks(mRunnable);
126 | if (mTrackTouch == TRACKTOUCH_NONE) {
127 | setTrackTouch(TRACKTOUCH_START);
128 | if (mOnChangeListener != null) {
129 | mOnChangeListener.onTrackingTouchStart(CustomSeekBar.this);
130 | }
131 | }
132 | }
133 |
134 | @Override
135 | public void onStopTrackingTouch(SeekBar seekBar) {
136 | isTrackingTouch = false;
137 | if (mTrackTouch == TRACKTOUCH_START) {
138 | if (mOnChangeListener != null) {
139 | mOnChangeListener.onTrackingTouchFinish(CustomSeekBar.this);
140 | }
141 | mHandler.postDelayed(mRunnable, mTrackingTouchSleepTime);
142 | }
143 | }
144 | });
145 | }
146 |
147 | @Override
148 | protected synchronized void onDraw(Canvas canvas) {
149 | int rSize = getHeight() / 4;
150 | if (isTrackingTouch) {
151 | rSize = getHeight() / 3;
152 | }
153 | int height = getHeight() / 4 / 3;
154 | int leftPadding = rSize;
155 |
156 | if (getProgress() > 0) {
157 | leftPadding = 0;
158 | }
159 |
160 | RectF backgroundRect = new RectF(leftPadding, getHeight() / 2 - height, getWidth(),
161 | getHeight() / 2 + height);
162 | canvas.drawRoundRect(backgroundRect, rSize, rSize, mBackgroundPaint);
163 |
164 |
165 | if (getMax() != 0) {
166 | int secondRight = (int) ((float) getSecondaryProgress() / getMax() * getWidth());
167 | RectF secondProgressRect = new RectF(leftPadding, getHeight() / 2 - height,
168 | secondRight, getHeight()
169 | / 2 + height);
170 | canvas.drawRoundRect(secondProgressRect, rSize, rSize, mSecondProgressPaint);
171 |
172 | int progressRight = (int) ((float) getProgress() / getMax() * getWidth());
173 | RectF progressRect = new RectF(leftPadding, getHeight() / 2 - height,
174 | progressRight, getHeight() / 2
175 | + height);
176 | canvas.drawRoundRect(progressRect, rSize, rSize, mProgressPaint);
177 |
178 |
179 | int cx = (int) ((float) getProgress() / getMax() * getWidth());
180 | if ((cx + rSize) > getWidth()) {
181 | cx = getWidth() - rSize;
182 | } else {
183 | cx = Math.max(cx, rSize);
184 | }
185 | int cy = getHeight() / 2;
186 | canvas.drawCircle(cx, cy, rSize, mThumbPaint);
187 | }
188 | }
189 |
190 | @Override
191 | public synchronized void setProgress(int progress) {
192 | if (mTrackTouch == TRACKTOUCH_NONE && getMax() != 0) {
193 | super.setProgress(progress);
194 | }
195 | postInvalidate();
196 | }
197 |
198 | @Override
199 | public synchronized void setSecondaryProgress(int secondaryProgress) {
200 | super.setSecondaryProgress(secondaryProgress);
201 | postInvalidate();
202 | }
203 |
204 | @Override
205 | public synchronized void setMax(int max) {
206 | super.setMax(max);
207 | postInvalidate();
208 | }
209 |
210 | private synchronized void setTrackTouch(int trackTouch) {
211 | this.mTrackTouch = trackTouch;
212 | }
213 |
214 | /**
215 | * 设置背景颜色
216 | *
217 | * @param backgroundColor
218 | */
219 | public void setBackgroundPaintColor(int backgroundColor) {
220 | mBackgroundPaint.setColor(backgroundColor);
221 | postInvalidate();
222 | }
223 |
224 | /**
225 | * 设置进度颜色
226 | *
227 | * @param progressColor
228 | */
229 | public void setProgressColor(int progressColor) {
230 | mProgressPaint.setColor(progressColor);
231 | postInvalidate();
232 | }
233 |
234 | /**
235 | * 设置第二进度颜色
236 | *
237 | * @param secondProgressColor
238 | */
239 | public void setSecondProgressColor(int secondProgressColor) {
240 | mSecondProgressPaint.setColor(secondProgressColor);
241 | postInvalidate();
242 | }
243 |
244 | /**
245 | * 设置游标颜色
246 | *
247 | * @param thumbColor
248 | */
249 | public void setThumbColor(int thumbColor) {
250 | mThumbPaint.setColor(thumbColor);
251 | postInvalidate();
252 | }
253 |
254 | public void setOnChangeListener(OnChangeListener onChangeListener) {
255 | this.mOnChangeListener = onChangeListener;
256 | }
257 |
258 | public void setTrackingTouchSleepTime(int mTrackingTouchSleepTime) {
259 | this.mTrackingTouchSleepTime = mTrackingTouchSleepTime;
260 | }
261 |
262 | public interface OnChangeListener {
263 | /**
264 | * 进度改变
265 | *
266 | * @param seekBar
267 | */
268 | public void onProgressChanged(CustomSeekBar seekBar);
269 |
270 | /**
271 | * 开始拖动
272 | *
273 | * @param seekBar
274 | */
275 | public void onTrackingTouchStart(CustomSeekBar seekBar);
276 |
277 | /**
278 | * 拖动结束
279 | *
280 | * @param seekBar
281 | */
282 | public void onTrackingTouchFinish(CustomSeekBar seekBar);
283 |
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/seekbarlibrary/src/main/java/com/zlm/libs/widget/MusicSeekBar.java:
--------------------------------------------------------------------------------
1 | package com.zlm.libs.widget;
2 |
3 |
4 | import android.content.Context;
5 | import android.graphics.Color;
6 | import android.graphics.drawable.GradientDrawable;
7 | import android.os.Handler;
8 | import android.os.Message;
9 | import android.text.TextUtils;
10 | import android.util.AttributeSet;
11 | import android.view.Display;
12 | import android.view.Gravity;
13 | import android.view.WindowManager;
14 | import android.widget.LinearLayout;
15 | import android.widget.PopupWindow;
16 | import android.widget.TextView;
17 |
18 | import com.zlm.libs.seekbarlibrary.R;
19 |
20 |
21 | /**
22 | * @Description: 自定义音乐进度条
23 | * @Param:
24 | * @Return:
25 | * @Author: zhangliangming
26 | * @Date: 2018-02-14
27 | * @Throws:
28 | */
29 |
30 | public class MusicSeekBar extends CustomSeekBar {
31 | /**
32 | * 滑动事件监听
33 | */
34 | private OnChangeListener mOnChangeListener;
35 | /**
36 | * 音乐滑动事件监听
37 | */
38 | private OnMusicListener mOnMusicListener;
39 |
40 | /**
41 | * 时间窗口
42 | */
43 | private PopupWindow mTimePopupWindow;
44 | private LinearLayout mTimePopupWindowView;
45 | private int mTimePopupWindowViewColor = parserColor("#0288d1", 180);
46 |
47 | /**
48 | * 时间和歌词窗口
49 | */
50 | private PopupWindow mTimeAndLrcPopupWindow;
51 | private LinearLayout mTimeAndLrcPopupWindowView;
52 | private int mTimeAndLrcPopupWindowViewColor = parserColor("#0288d1", 180);
53 |
54 | /**
55 | * 时间标签view
56 | */
57 | private TextView mTimeTextView;
58 |
59 | /**
60 | * 歌词标签view
61 | */
62 | private TextView mLrcTextView;
63 | private Context mContext;
64 |
65 | private final int SHOWTIMEANDLRCVIEW = 0;
66 | private final int SHOWTIMEVIEW = 1;
67 | private final int HIDEVIEW = 2;
68 | private final int UPDATEVIEW = 3;
69 |
70 | /**
71 | *
72 | */
73 | private Handler mHandler = new Handler() {
74 |
75 | @Override
76 | public void handleMessage(Message msg) {
77 |
78 | switch (msg.what) {
79 | case SHOWTIMEANDLRCVIEW:
80 |
81 | showTimeAndLrcDialog();
82 |
83 | break;
84 | case SHOWTIMEVIEW:
85 |
86 | showTimeDialog();
87 |
88 | break;
89 | case HIDEVIEW:
90 |
91 | hideDialog();
92 |
93 | break;
94 | case UPDATEVIEW:
95 |
96 | upDateDialog();
97 |
98 | break;
99 | default:
100 | break;
101 | }
102 |
103 | }
104 | };
105 |
106 |
107 | public MusicSeekBar(Context context) {
108 | super(context);
109 | init(context);
110 | }
111 |
112 | public MusicSeekBar(Context context, AttributeSet attrs) {
113 | super(context, attrs);
114 | init(context);
115 | }
116 |
117 | /**
118 | * 初始化
119 | */
120 | private void init(Context context) {
121 | this.mContext = context;
122 | mOnChangeListener = new OnChangeListener() {
123 | @Override
124 | public void onProgressChanged(CustomSeekBar seekBar) {
125 |
126 | String timeText = null;
127 | String lrcText = null;
128 | if (mOnMusicListener != null) {
129 | timeText = mOnMusicListener.getTimeText();
130 | lrcText = mOnMusicListener.getLrcText();
131 | }
132 | if (timeText != null && !timeText.equals("") && lrcText != null && !lrcText.equals("")) {
133 | mHandler.sendEmptyMessage(SHOWTIMEANDLRCVIEW);
134 | } else if (timeText != null && !timeText.equals("")) {
135 | mHandler.sendEmptyMessage(SHOWTIMEVIEW);
136 | }
137 | mHandler.sendEmptyMessage(UPDATEVIEW);
138 |
139 | if (mOnMusicListener != null) {
140 | mOnMusicListener.onProgressChanged(MusicSeekBar.this);
141 | }
142 | }
143 |
144 | @Override
145 | public void onTrackingTouchStart(CustomSeekBar seekBar) {
146 | if (mOnMusicListener != null) {
147 | mOnMusicListener.onTrackingTouchStart(MusicSeekBar.this);
148 | }
149 | }
150 |
151 | @Override
152 | public void onTrackingTouchFinish(CustomSeekBar seekBar) {
153 | mHandler.sendEmptyMessageDelayed(HIDEVIEW, 200);
154 | if (mOnMusicListener != null) {
155 | mOnMusicListener.onTrackingTouchFinish(MusicSeekBar.this);
156 | }
157 | }
158 | };
159 | setOnChangeListener(mOnChangeListener);
160 | }
161 |
162 | /**
163 | * 显示时间标签窗口
164 | */
165 | private void showTimeDialog() {
166 | if (mTimeAndLrcPopupWindow != null && mTimeAndLrcPopupWindow.isShowing()) {
167 | mTimeAndLrcPopupWindow.dismiss();
168 | }
169 |
170 | //
171 | int popHeight = (int) mContext.getResources().getDimension(R.dimen.pop_height);
172 | //
173 | if (mTimePopupWindow == null) {
174 |
175 | mTimeTextView = new TextView(mContext);
176 |
177 | int timeWidth = (int) (mTimeTextView.getTextSize()) * "00:00".length();
178 | //
179 | LinearLayout.LayoutParams popLayout = new LinearLayout.LayoutParams(timeWidth, popHeight);
180 | mTimePopupWindowView = new LinearLayout(mContext);
181 | mTimePopupWindowView.setLayoutParams(popLayout);
182 |
183 |
184 | //
185 |
186 | LinearLayout.LayoutParams timeLayout = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
187 |
188 | mTimeTextView.setLayoutParams(timeLayout);
189 | mTimeTextView.setTextColor(Color.WHITE);
190 | mTimeTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER | Gravity.CENTER_HORIZONTAL);
191 | mTimeTextView.setSingleLine(true);
192 | mTimeTextView.setEllipsize(TextUtils.TruncateAt.END);
193 | mTimePopupWindowView.addView(mTimeTextView);
194 |
195 | mTimePopupWindow = new PopupWindow(mTimePopupWindowView, timeWidth,
196 | popHeight, true);
197 |
198 | }
199 | //////////////////////////
200 |
201 | int strokeWidth = 1; // 3dp 边框宽度
202 | float[] roundRadius = {15, 15, 15, 15, 15, 15, 15, 15}; // 圆角半径
203 | int strokeColor = Color.TRANSPARENT;
204 |
205 | GradientDrawable gd = new GradientDrawable();// 创建drawable
206 | gd.setColor(mTimePopupWindowViewColor);
207 | gd.setCornerRadii(roundRadius);
208 | gd.setStroke(strokeWidth, strokeColor);
209 |
210 | ///////////////////////////////
211 | mTimePopupWindowView.setBackgroundDrawable(gd);
212 |
213 | if (mTimePopupWindow != null && !mTimePopupWindow.isShowing()) {
214 | int[] location = new int[2];
215 | this.getLocationOnScreen(location);
216 |
217 |
218 | int leftX = (int) (location[0] + getWidth() * (float)getProgress() / getMax());
219 | //判断是否越界
220 | if ((leftX + mTimePopupWindow.getWidth()) > (location[0] + getWidth())) {
221 | leftX = (location[0] + getWidth()) - mTimePopupWindow.getWidth();
222 | } else if (leftX < location[0]) {
223 | leftX = location[0];
224 | }
225 |
226 | mTimePopupWindow.showAtLocation(this, Gravity.NO_GRAVITY, leftX, location[1]
227 | - mTimePopupWindow.getHeight() * 3 / 2);
228 | }
229 | }
230 |
231 | /**
232 | * 显示时间标签和歌词标签窗口
233 | */
234 | private void showTimeAndLrcDialog() {
235 | if (mTimePopupWindow != null && mTimePopupWindow.isShowing()) {
236 | mTimePopupWindow.dismiss();
237 | }
238 | //
239 | WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
240 | Display display = wm.getDefaultDisplay();
241 | int screenWidth = display.getWidth();
242 | //
243 | int popHeight = (int) mContext.getResources().getDimension(R.dimen.pop_height);
244 | int popPadding = (int) mContext.getResources().getDimension(R.dimen.pop_Padding);
245 | //
246 | if (mTimeAndLrcPopupWindow == null) {
247 |
248 | //
249 | LinearLayout.LayoutParams popLayout = new LinearLayout.LayoutParams(screenWidth - popPadding * 2, popHeight);
250 | mTimeAndLrcPopupWindowView = new LinearLayout(mContext);
251 | mTimeAndLrcPopupWindowView.setLayoutParams(popLayout);
252 |
253 |
254 | //
255 | mTimeTextView = new TextView(mContext);
256 | LinearLayout.LayoutParams timeLayout = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
257 | timeLayout.leftMargin = popPadding;
258 | mTimeTextView.setLayoutParams(timeLayout);
259 | mTimeTextView.setTextColor(Color.WHITE);
260 | mTimeTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER | Gravity.CENTER_HORIZONTAL);
261 | mTimeTextView.setSingleLine(true);
262 | mTimeTextView.setEllipsize(TextUtils.TruncateAt.END);
263 | mTimeAndLrcPopupWindowView.addView(mTimeTextView);
264 |
265 | //
266 | mLrcTextView = new TextView(mContext);
267 | LinearLayout.LayoutParams lrcLayout = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
268 | mLrcTextView.setLayoutParams(lrcLayout);
269 | mLrcTextView.setTextColor(Color.WHITE);
270 | mLrcTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER | Gravity.CENTER_HORIZONTAL);
271 | mLrcTextView.setSingleLine(true);
272 | mLrcTextView.setEllipsize(TextUtils.TruncateAt.END);
273 | mTimeAndLrcPopupWindowView.addView(mLrcTextView);
274 |
275 | mTimeAndLrcPopupWindow = new PopupWindow(mTimeAndLrcPopupWindowView, screenWidth - popPadding * 2,
276 | popHeight, true);
277 |
278 | }
279 | //////////////////////////
280 |
281 | int strokeWidth = 1; // 3dp 边框宽度
282 | float[] roundRadius = {15, 15, 15, 15, 15, 15, 15, 15}; // 圆角半径
283 | int strokeColor = Color.TRANSPARENT;
284 |
285 | GradientDrawable gd = new GradientDrawable();// 创建drawable
286 | gd.setColor(mTimeAndLrcPopupWindowViewColor);
287 | gd.setCornerRadii(roundRadius);
288 | gd.setStroke(strokeWidth, strokeColor);
289 |
290 | ///////////////////////////////
291 | mTimeAndLrcPopupWindowView.setBackgroundDrawable(gd);
292 |
293 | if (mTimeAndLrcPopupWindow != null && !mTimeAndLrcPopupWindow.isShowing()) {
294 | int[] location = new int[2];
295 | this.getLocationOnScreen(location);
296 |
297 | mTimeAndLrcPopupWindow.showAtLocation(this, Gravity.NO_GRAVITY, popPadding, location[1]
298 | - mTimeAndLrcPopupWindow.getHeight() * 3 / 2);
299 | }
300 |
301 | }
302 |
303 | /**
304 | * 隐藏窗口
305 | */
306 | private void hideDialog() {
307 | if (mTimeAndLrcPopupWindow != null && mTimeAndLrcPopupWindow.isShowing()) {
308 | mTimeAndLrcPopupWindow.dismiss();
309 | }
310 |
311 | if (mTimePopupWindow != null && mTimePopupWindow.isShowing()) {
312 | mTimePopupWindow.dismiss();
313 | }
314 | }
315 |
316 | /**
317 | * 更新窗口
318 | */
319 | private void upDateDialog() {
320 |
321 | if ((mTimeAndLrcPopupWindow != null && mTimeAndLrcPopupWindow.isShowing()) || (mTimePopupWindow != null && mTimePopupWindow.isShowing())) {
322 |
323 | String timeText = null;
324 | String lrcText = null;
325 | if (mOnMusicListener != null) {
326 | timeText = mOnMusicListener.getTimeText();
327 | lrcText = mOnMusicListener.getLrcText();
328 | }
329 | if (timeText == null) {
330 | return;
331 | }
332 |
333 | mTimeTextView.setText(timeText);
334 |
335 |
336 | //如果时间窗口正在显示
337 | if (mTimePopupWindow != null && mTimePopupWindow.isShowing()) {
338 | int[] location = new int[2];
339 | this.getLocationOnScreen(location);
340 |
341 | int leftX = (int) (location[0] + getWidth() * (float)getProgress() / getMax() - mTimePopupWindow.getWidth() / 2);
342 |
343 | //判断是否越界
344 | if ((leftX + mTimePopupWindow.getWidth()) > (location[0] + getWidth())) {
345 | leftX = (location[0] + getWidth()) - mTimePopupWindow.getWidth();
346 | } else if (leftX < location[0]) {
347 | leftX = location[0];
348 | }
349 |
350 | //更新弹出窗口的位置
351 | mTimePopupWindow.update(leftX, location[1]
352 | - mTimePopupWindow.getHeight() * 3 / 2, -1, -1);
353 | }
354 |
355 | if (lrcText == null) {
356 |
357 | return;
358 | }
359 |
360 | mLrcTextView.setText(lrcText);
361 | }
362 | }
363 |
364 |
365 | public void setOnMusicListener(OnMusicListener onMusicListener) {
366 | this.mOnMusicListener = onMusicListener;
367 | }
368 |
369 | /**
370 | * 解析颜色
371 | *
372 | * @param colorStr #ffffff 颜色字符串
373 | * @param alpha 0-255 透明度
374 | * @return
375 | */
376 | private int parserColor(String colorStr, int alpha) {
377 | int color = Color.parseColor(colorStr);
378 | int red = (color & 0xff0000) >> 16;
379 | int green = (color & 0x00ff00) >> 8;
380 | int blue = (color & 0x0000ff);
381 | return Color.argb(alpha, red, green, blue);
382 | }
383 |
384 | public void setTimePopupWindowViewColor(int mTimePopupWindowViewColor) {
385 | this.mTimePopupWindowViewColor = mTimePopupWindowViewColor;
386 | }
387 |
388 | public void setTimeAndLrcPopupWindowViewColor(int mTimeAndLrcPopupWindowViewColor) {
389 | this.mTimeAndLrcPopupWindowViewColor = mTimeAndLrcPopupWindowViewColor;
390 | }
391 |
392 | /**
393 | * 音乐进度条监听事件
394 | */
395 | public interface OnMusicListener {
396 | /**
397 | * 获取时间标签
398 | *
399 | * @return
400 | */
401 | public String getTimeText();
402 |
403 | /**
404 | * 获取歌词标签
405 | *
406 | * @return
407 | */
408 | public String getLrcText();
409 |
410 | /**
411 | * 进度改变
412 | *
413 | * @param seekBar
414 | */
415 | public void onProgressChanged(MusicSeekBar seekBar);
416 |
417 | /**
418 | * 开始拖动
419 | *
420 | * @param seekBar
421 | */
422 | public void onTrackingTouchStart(MusicSeekBar seekBar);
423 |
424 |
425 | /**
426 | * 拖动结束
427 | *
428 | * @param seekBar
429 | */
430 | public void onTrackingTouchFinish(MusicSeekBar seekBar);
431 | }
432 | }
433 |
--------------------------------------------------------------------------------
/seekbarlibrary/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 40dp
5 | 10dp
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/seekbarlibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Seekbar Library
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':seekbarlibrary'
2 |
--------------------------------------------------------------------------------