45 |
46 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/sidebar/src/main/java/com/allenliu/sidebar/SideBar.java:
--------------------------------------------------------------------------------
1 | package com.allenliu.sidebar;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.util.AttributeSet;
8 | import android.view.MotionEvent;
9 |
10 | /**
11 | * Created by Allen Liu on 2019/12/12.
12 | */
13 | public class SideBar extends android.support.v7.widget.AppCompatTextView {
14 | private String[] letters = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I",
15 | "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
16 | "W", "X", "Y", "Z", "#"};
17 | private Paint textPaint;
18 | private Paint bigTextPaint;
19 |
20 | private ISideBarSelectCallBack callBack;
21 | private float eventY;
22 | private float w;
23 | private float sideTextWidth;
24 | /**
25 | * 是否重新测量宽高
26 | */
27 | private boolean isTouching = false;
28 | private float itemH;
29 |
30 | /**
31 | * 振幅
32 | */
33 | private float A = dp(100);
34 | /**
35 | * 波峰与bigText之间的距离
36 | */
37 | private int gapBetweenText = dp(50);
38 |
39 | /**
40 | * 开口数量
41 | */
42 | private int openCount = 13;
43 | /**
44 | * 字体缩放,基于textSize
45 | */
46 | private float fontScale = 1;
47 | private float bigTextSize;
48 |
49 | public SideBar(Context context) {
50 | super(context);
51 | init(null);
52 | }
53 |
54 | public SideBar(Context context, AttributeSet attrs) {
55 | super(context, attrs);
56 | init(attrs);
57 | }
58 |
59 | public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {
60 | super(context, attrs, defStyleAttr);
61 | init(attrs);
62 | }
63 |
64 | /**
65 | * set MaxFontScale
66 | *
67 | * @param fontScale
68 | * @return
69 | */
70 | public SideBar setFontScale(float fontScale) {
71 | this.fontScale = fontScale;
72 | return this;
73 | }
74 |
75 | public void setDataResource(String[] data) {
76 | letters = data;
77 | invalidate();
78 | }
79 |
80 | public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {
81 | this.callBack = callBack;
82 | }
83 |
84 | public SideBar setBigTextSize(float bigTextSize) {
85 | this.bigTextSize = bigTextSize;
86 | bigTextPaint.setTextSize(bigTextSize);
87 | // invalidate();
88 | return this;
89 | }
90 |
91 | public SideBar setA(float a) {
92 | A = a;
93 | // invalidate();
94 | return this;
95 | }
96 |
97 | public SideBar setGapBetweenText(int gapBetweenText) {
98 | this.gapBetweenText = gapBetweenText;
99 | // invalidate();
100 | return this;
101 | }
102 |
103 | public SideBar setOpenCount(int openCount) {
104 | this.openCount = openCount;
105 | // invalidate();
106 | return this;
107 | }
108 |
109 | private void caculateAW(int height) {
110 | itemH = height * 1.0f / letters.length;
111 | /**
112 | * 开口宽度
113 | */
114 | float opendWidth = itemH * openCount;
115 | //角速度 2PI/t 周期
116 | w = (float) (Math.PI * 2.0f / (opendWidth * 2));
117 | }
118 |
119 |
120 | @Override
121 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
122 | int mode = MeasureSpec.getMode(widthMeasureSpec);
123 | int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
124 | caculateAW(MeasureSpec.getSize(heightMeasureSpec));
125 | if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
126 | viewWidth = !isTouching ? (int) (sideTextWidth + getPaddingLeft() + getPaddingRight()) : (int) (A + gapBetweenText + getBigTextWidth() + getPaddingLeft() + getPaddingRight());
127 | }
128 | // CLog.e("width:" + viewWidth + "height:" + MeasureSpec.getSize(heightMeasureSpec));
129 | setMeasuredDimension(viewWidth, MeasureSpec.getSize(heightMeasureSpec));
130 | }
131 |
132 | private void init(AttributeSet attrs) {
133 | // setPadding(dp(10), 0, dp(10), 0);
134 | if (attrs != null) {
135 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);
136 | A = typedArray.getInteger(R.styleable.SideBar_A, dp(100));
137 | fontScale = typedArray.getFloat(R.styleable.SideBar_fontScale, 1);
138 | bigTextSize = typedArray.getFloat(R.styleable.SideBar_bigTextSize, getTextSize() * 3);
139 | gapBetweenText = typedArray.getInteger(R.styleable.SideBar_gapBetweenText, dp(50));
140 | openCount = typedArray.getInteger(R.styleable.SideBar_openCount, 13);
141 | } else {
142 | bigTextSize = getTextSize() * 3;
143 | }
144 | textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
145 | textPaint.setColor(getCurrentTextColor());
146 | textPaint.setTextSize(getTextSize());
147 | textPaint.setTextAlign(Paint.Align.CENTER);
148 |
149 | bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
150 | bigTextPaint.setColor(getCurrentTextColor());
151 | bigTextPaint.setTextSize(bigTextSize);
152 | bigTextPaint.setTextAlign(Paint.Align.CENTER);
153 |
154 |
155 | float sideTextHeight = textPaint.getFontMetrics().descent - textPaint.getFontMetrics().ascent;
156 | sideTextWidth = textPaint.measureText("W");
157 | }
158 |
159 |
160 | private int dp(int v) {
161 | final float scale = getContext().getResources().getDisplayMetrics().density;
162 | return (int) (v * scale + 0.5f);
163 | }
164 |
165 |
166 | @Override
167 | public boolean onTouchEvent(MotionEvent event) {
168 | int startTouchX = (int) (getMeasuredWidth() - A);
169 | switch (event.getAction()) {
170 | case MotionEvent.ACTION_DOWN:
171 | case MotionEvent.ACTION_MOVE:
172 | if (event.getX() > startTouchX) {
173 | eventY = event.getY();
174 | if (!isTouching) {
175 | isTouching = true;
176 | requestLayout();
177 | } else {
178 | invalidate();
179 | }
180 |
181 | } else {
182 | if (isTouching) {
183 | resetDefault();
184 | }
185 | }
186 | return true;
187 | case MotionEvent.ACTION_CANCEL:
188 | case MotionEvent.ACTION_UP:
189 | resetDefault();
190 | return true;
191 |
192 | }
193 | return super.onTouchEvent(event);
194 | }
195 |
196 | private void resetDefault() {
197 | isTouching = false;
198 | eventY = 0;
199 | requestLayout();
200 | }
201 |
202 |
203 | @Override
204 | protected void onDraw(Canvas canvas) {
205 | int singleSideCount = openCount / 2;
206 | int index = isTouching && eventY >= 0 && eventY <= getMeasuredHeight() ? (int) Math.floor((eventY / itemH)) : -(singleSideCount + 1);
207 | // index=Math.min(letters.length,index);
208 | // CLog.e("index:" + index + "eventY:" + eventY);
209 | float sideX = sideTextWidth / 2 + getPaddingRight();
210 | for (int i = 0; i < letters.length; i++) {
211 | //rest textsize
212 | textPaint.setTextSize(getTextSize());
213 | int y = (int) (itemH * (i + 1));
214 | int x;
215 | if (Math.abs(i - index) > singleSideCount) {
216 | x = (int) (getMeasuredWidth() - sideX);
217 | } else {
218 | float percent = eventY / itemH;
219 | int t = (int) (i * itemH - eventY);
220 | double v = A * Math.sin(w * t + Math.PI / 2);
221 |
222 | // //如果算出来小于字体宽度 就取字体宽度
223 | v = Math.max(v, sideX);
224 | x = (int) (getMeasuredWidth() - v);
225 | //根据delta缩放字体
226 | if (v == sideX) {
227 | textPaint.setTextSize(getTextSize());
228 | } else {
229 | float delta = (Math.abs((i - percent)) / singleSideCount);
230 | float textSize = getTextSize() + (1 - delta) * getTextSize() * fontScale;
231 | // textSize=Math.max(textSize,getTextSize());
232 | textPaint.setTextSize(textSize);
233 | }
234 |
235 | }
236 | canvas.drawText(letters[i], x, y, textPaint);
237 |
238 | }
239 | if (index != -(singleSideCount + 1)) {
240 | canvas.drawText(letters[index], getPaddingLeft() + getBigTextWidth() / 2, (int) (itemH * (index + 1)), bigTextPaint);
241 | if (callBack != null) {
242 | callBack.onSelectStr(index, letters[index]);
243 |
244 | }
245 | }
246 |
247 | }
248 |
249 | private float getBigTextWidth() {
250 | return bigTextPaint.measureText("W");
251 | }
252 | }
--------------------------------------------------------------------------------