├── .gitattributes ├── .gitignore ├── ModifiedStepsView ├── .gitignore ├── .idea │ ├── .name │ ├── compiler.xml │ ├── copyright │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── ModifiedStepsView.iml ├── app │ ├── .gitignore │ ├── app.iml │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── lenovo │ │ │ └── modifiedstepsview │ │ │ └── ApplicationTest.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── lenovo │ │ │ └── modifiedstepsview │ │ │ ├── MainActivity.java │ │ │ └── WeakHandler.java │ │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gif │ └── device-2015-08-18-135551.png ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library │ ├── .gitignore │ ├── build.gradle │ ├── library.iml │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── lenovo │ │ │ └── library │ │ │ └── ApplicationTest.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── lenovo │ │ │ └── library │ │ │ ├── StepsView.java │ │ │ └── StepsViewIndicator.java │ │ └── res │ │ ├── drawable-xxhdpi │ │ ├── load_toast_check.png │ │ └── load_toast_error.png │ │ ├── layout │ │ └── widget_steps_view.xml │ │ └── values │ │ ├── steps_view_attrs.xml │ │ └── strings.xml └── settings.gradle └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /ModifiedStepsView/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/.name: -------------------------------------------------------------------------------- 1 | ModifiedStepsView -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 16 | 28 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | C:\Users\lenovo\AppData\Roaming\Subversion 57 | 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /ModifiedStepsView/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ModifiedStepsView/ModifiedStepsView.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.example.lenovo.modifiedstepsview" 9 | minSdkVersion 15 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:22.2.1' 25 | compile project(':library') 26 | } 27 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\developtools\android sdk windows/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 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/androidTest/java/com/example/lenovo/modifiedstepsview/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.lenovo.modifiedstepsview; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/java/com/example/lenovo/modifiedstepsview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.lenovo.modifiedstepsview; 2 | 3 | import android.graphics.Color; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | 11 | import com.example.lenovo.library.StepsView; 12 | 13 | public class MainActivity extends AppCompatActivity { 14 | 15 | private boolean isStop = false; 16 | 17 | private StepsView stepsView; 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_main); 22 | 23 | getWindow().setBackgroundDrawable(null); //删除默认的背景,防止OVERDRAW 24 | 25 | stepsView = (StepsView) findViewById(R.id.steps_view); 26 | 27 | final String[] steps = {"step1", "step2", "step3", "step4"}; 28 | stepsView.setLabels(steps) 29 | .setColorIndicator(Color.GRAY) 30 | .setBarColor(Color.GREEN) 31 | .setLabelColor(Color.BLACK); 32 | 33 | new Thread() { 34 | public void run() { 35 | for (int i = 0; i < steps.length; i++) { 36 | if(isStop) { 37 | break; 38 | } 39 | Message msg = new Message(); 40 | msg.obj = i; 41 | handler.sendMessage(msg); 42 | try { 43 | Thread.sleep(4000); 44 | } catch (Exception e) { 45 | } 46 | } 47 | Message msg = new Message(); 48 | msg.obj = steps.length; 49 | handler.sendMessage(msg); 50 | } 51 | }.start(); 52 | 53 | 54 | } 55 | 56 | 57 | 58 | @Override 59 | protected void onDestroy() { 60 | super.onDestroy(); 61 | isStop = true; 62 | handler.removeCallbacksAndMessages(null); 63 | } 64 | 65 | private WeakHandler handler = new WeakHandler(new Handler.Callback() { 66 | @Override 67 | public boolean handleMessage(Message msg) { 68 | int i = (int)msg.obj; 69 | stepsView.setCompletedPosition(i); 70 | return false; 71 | } 72 | }); 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/java/com/example/lenovo/modifiedstepsview/WeakHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.lenovo.modifiedstepsview; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | 9 | import java.lang.ref.WeakReference; 10 | import java.util.concurrent.locks.Lock; 11 | import java.util.concurrent.locks.ReentrantLock; 12 | 13 | /** 14 | * Memory safer implementation of android.os.Handler 15 | *

16 | * Original implementation of Handlers always keeps hard reference to handler in queue of execution. 17 | * If you create anonymous handler and post delayed message into it, it will keep all parent class 18 | * for that time in memory even if it could be cleaned. 19 | *

20 | * This implementation is trickier, it will keep WeakReferences to runnables and messages, 21 | * and GC could collect them once WeakHandler instance is not referenced any more 22 | *

23 | * 24 | * @see Handler 25 | * 26 | * Created by Dmytro Voronkevych on 17/06/2014. 27 | */ 28 | public class WeakHandler { 29 | private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory 30 | private final ExecHandler mExec; 31 | private Lock mLock = new ReentrantLock(); 32 | @SuppressWarnings("ConstantConditions") 33 | final ChainedRef mRunnables = new ChainedRef(mLock, null); 34 | 35 | /** 36 | * Default constructor associates this handler with the {@link Looper} for the 37 | * current thread. 38 | * 39 | * If this thread does not have a looper, this handler won't be able to receive messages 40 | * so an exception is thrown. 41 | */ 42 | public WeakHandler() { 43 | mCallback = null; 44 | mExec = new ExecHandler(); 45 | } 46 | 47 | /** 48 | * Constructor associates this handler with the {@link Looper} for the 49 | * current thread and takes a callback interface in which you can handle 50 | * messages. 51 | * 52 | * If this thread does not have a looper, this handler won't be able to receive messages 53 | * so an exception is thrown. 54 | * 55 | * @param callback The callback interface in which to handle messages, or null. 56 | */ 57 | public WeakHandler(@Nullable Handler.Callback callback) { 58 | mCallback = callback; // Hard referencing body 59 | mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler 60 | } 61 | 62 | /** 63 | * Use the provided {@link Looper} instead of the default one. 64 | * 65 | * @param looper The looper, must not be null. 66 | */ 67 | public WeakHandler(@NonNull Looper looper) { 68 | mCallback = null; 69 | mExec = new ExecHandler(looper); 70 | } 71 | 72 | /** 73 | * Use the provided {@link Looper} instead of the default one and take a callback 74 | * interface in which to handle messages. 75 | * 76 | * @param looper The looper, must not be null. 77 | * @param callback The callback interface in which to handle messages, or null. 78 | */ 79 | public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) { 80 | mCallback = callback; 81 | mExec = new ExecHandler(looper, new WeakReference<>(callback)); 82 | } 83 | 84 | /** 85 | * Causes the Runnable r to be added to the message queue. 86 | * The runnable will be run on the thread to which this handler is 87 | * attached. 88 | * 89 | * @param r The Runnable that will be executed. 90 | * 91 | * @return Returns true if the Runnable was successfully placed in to the 92 | * message queue. Returns false on failure, usually because the 93 | * looper processing the message queue is exiting. 94 | */ 95 | public final boolean post(@NonNull Runnable r) { 96 | return mExec.post(wrapRunnable(r)); 97 | } 98 | 99 | /** 100 | * Causes the Runnable r to be added to the message queue, to be run 101 | * at a specific time given by uptimeMillis. 102 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 103 | * The runnable will be run on the thread to which this handler is attached. 104 | * 105 | * @param r The Runnable that will be executed. 106 | * @param uptimeMillis The absolute time at which the callback should run, 107 | * using the {@link android.os.SystemClock#uptimeMillis} time-base. 108 | * 109 | * @return Returns true if the Runnable was successfully placed in to the 110 | * message queue. Returns false on failure, usually because the 111 | * looper processing the message queue is exiting. Note that a 112 | * result of true does not mean the Runnable will be processed -- if 113 | * the looper is quit before the delivery time of the message 114 | * occurs then the message will be dropped. 115 | */ 116 | public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { 117 | return mExec.postAtTime(wrapRunnable(r), uptimeMillis); 118 | } 119 | 120 | /** 121 | * Causes the Runnable r to be added to the message queue, to be run 122 | * at a specific time given by uptimeMillis. 123 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 124 | * The runnable will be run on the thread to which this handler is attached. 125 | * 126 | * @param r The Runnable that will be executed. 127 | * @param uptimeMillis The absolute time at which the callback should run, 128 | * using the {@link android.os.SystemClock#uptimeMillis} time-base. 129 | * 130 | * @return Returns true if the Runnable was successfully placed in to the 131 | * message queue. Returns false on failure, usually because the 132 | * looper processing the message queue is exiting. Note that a 133 | * result of true does not mean the Runnable will be processed -- if 134 | * the looper is quit before the delivery time of the message 135 | * occurs then the message will be dropped. 136 | * 137 | * @see android.os.SystemClock#uptimeMillis 138 | */ 139 | public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { 140 | return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis); 141 | } 142 | 143 | /** 144 | * Causes the Runnable r to be added to the message queue, to be run 145 | * after the specified amount of time elapses. 146 | * The runnable will be run on the thread to which this handler 147 | * is attached. 148 | * 149 | * @param r The Runnable that will be executed. 150 | * @param delayMillis The delay (in milliseconds) until the Runnable 151 | * will be executed. 152 | * 153 | * @return Returns true if the Runnable was successfully placed in to the 154 | * message queue. Returns false on failure, usually because the 155 | * looper processing the message queue is exiting. Note that a 156 | * result of true does not mean the Runnable will be processed -- 157 | * if the looper is quit before the delivery time of the message 158 | * occurs then the message will be dropped. 159 | */ 160 | public final boolean postDelayed(Runnable r, long delayMillis) { 161 | return mExec.postDelayed(wrapRunnable(r), delayMillis); 162 | } 163 | 164 | /** 165 | * Posts a message to an object that implements Runnable. 166 | * Causes the Runnable r to executed on the next iteration through the 167 | * message queue. The runnable will be run on the thread to which this 168 | * handler is attached. 169 | * This method is only for use in very special circumstances -- it 170 | * can easily starve the message queue, cause ordering problems, or have 171 | * other unexpected side-effects. 172 | * 173 | * @param r The Runnable that will be executed. 174 | * 175 | * @return Returns true if the message was successfully placed in to the 176 | * message queue. Returns false on failure, usually because the 177 | * looper processing the message queue is exiting. 178 | */ 179 | public final boolean postAtFrontOfQueue(Runnable r) { 180 | return mExec.postAtFrontOfQueue(wrapRunnable(r)); 181 | } 182 | 183 | /** 184 | * Remove any pending posts of Runnable r that are in the message queue. 185 | */ 186 | public final void removeCallbacks(Runnable r) { 187 | final WeakRunnable runnable = mRunnables.remove(r); 188 | if (runnable != null) { 189 | mExec.removeCallbacks(runnable); 190 | } 191 | } 192 | 193 | /** 194 | * Remove any pending posts of Runnable r with Object 195 | * token that are in the message queue. If token is null, 196 | * all callbacks will be removed. 197 | */ 198 | public final void removeCallbacks(Runnable r, Object token) { 199 | final WeakRunnable runnable = mRunnables.remove(r); 200 | if (runnable != null) { 201 | mExec.removeCallbacks(runnable, token); 202 | } 203 | } 204 | 205 | /** 206 | * Pushes a message onto the end of the message queue after all pending messages 207 | * before the current time. It will be received in callback, 208 | * in the thread attached to this handler. 209 | * 210 | * @return Returns true if the message was successfully placed in to the 211 | * message queue. Returns false on failure, usually because the 212 | * looper processing the message queue is exiting. 213 | */ 214 | public final boolean sendMessage(Message msg) { 215 | return mExec.sendMessage(msg); 216 | } 217 | 218 | /** 219 | * Sends a Message containing only the what value. 220 | * 221 | * @return Returns true if the message was successfully placed in to the 222 | * message queue. Returns false on failure, usually because the 223 | * looper processing the message queue is exiting. 224 | */ 225 | public final boolean sendEmptyMessage(int what) { 226 | return mExec.sendEmptyMessage(what); 227 | } 228 | 229 | /** 230 | * Sends a Message containing only the what value, to be delivered 231 | * after the specified amount of time elapses. 232 | * @see #sendMessageDelayed(Message, long) 233 | * 234 | * @return Returns true if the message was successfully placed in to the 235 | * message queue. Returns false on failure, usually because the 236 | * looper processing the message queue is exiting. 237 | */ 238 | public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 239 | return mExec.sendEmptyMessageDelayed(what, delayMillis); 240 | } 241 | 242 | /** 243 | * Sends a Message containing only the what value, to be delivered 244 | * at a specific time. 245 | * @see #sendMessageAtTime(Message, long) 246 | * 247 | * @return Returns true if the message was successfully placed in to the 248 | * message queue. Returns false on failure, usually because the 249 | * looper processing the message queue is exiting. 250 | */ 251 | public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { 252 | return mExec.sendEmptyMessageAtTime(what, uptimeMillis); 253 | } 254 | 255 | /** 256 | * Enqueue a message into the message queue after all pending messages 257 | * before (current time + delayMillis). You will receive it in 258 | * callback, in the thread attached to this handler. 259 | * 260 | * @return Returns true if the message was successfully placed in to the 261 | * message queue. Returns false on failure, usually because the 262 | * looper processing the message queue is exiting. Note that a 263 | * result of true does not mean the message will be processed -- if 264 | * the looper is quit before the delivery time of the message 265 | * occurs then the message will be dropped. 266 | */ 267 | public final boolean sendMessageDelayed(Message msg, long delayMillis) { 268 | return mExec.sendMessageDelayed(msg, delayMillis); 269 | } 270 | 271 | /** 272 | * Enqueue a message into the message queue after all pending messages 273 | * before the absolute time (in milliseconds) uptimeMillis. 274 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 275 | * You will receive it in callback, in the thread attached 276 | * to this handler. 277 | * 278 | * @param uptimeMillis The absolute time at which the message should be 279 | * delivered, using the 280 | * {@link android.os.SystemClock#uptimeMillis} time-base. 281 | * 282 | * @return Returns true if the message was successfully placed in to the 283 | * message queue. Returns false on failure, usually because the 284 | * looper processing the message queue is exiting. Note that a 285 | * result of true does not mean the message will be processed -- if 286 | * the looper is quit before the delivery time of the message 287 | * occurs then the message will be dropped. 288 | */ 289 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 290 | return mExec.sendMessageAtTime(msg, uptimeMillis); 291 | } 292 | 293 | /** 294 | * Enqueue a message at the front of the message queue, to be processed on 295 | * the next iteration of the message loop. You will receive it in 296 | * callback, in the thread attached to this handler. 297 | * This method is only for use in very special circumstances -- it 298 | * can easily starve the message queue, cause ordering problems, or have 299 | * other unexpected side-effects. 300 | * 301 | * @return Returns true if the message was successfully placed in to the 302 | * message queue. Returns false on failure, usually because the 303 | * looper processing the message queue is exiting. 304 | */ 305 | public final boolean sendMessageAtFrontOfQueue(Message msg) { 306 | return mExec.sendMessageAtFrontOfQueue(msg); 307 | } 308 | 309 | /** 310 | * Remove any pending posts of messages with code 'what' that are in the 311 | * message queue. 312 | */ 313 | public final void removeMessages(int what) { 314 | mExec.removeMessages(what); 315 | } 316 | 317 | /** 318 | * Remove any pending posts of messages with code 'what' and whose obj is 319 | * 'object' that are in the message queue. If object is null, 320 | * all messages will be removed. 321 | */ 322 | public final void removeMessages(int what, Object object) { 323 | mExec.removeMessages(what, object); 324 | } 325 | 326 | /** 327 | * Remove any pending posts of callbacks and sent messages whose 328 | * obj is token. If token is null, 329 | * all callbacks and messages will be removed. 330 | */ 331 | public final void removeCallbacksAndMessages(Object token) { 332 | mExec.removeCallbacksAndMessages(token); 333 | } 334 | 335 | /** 336 | * Check if there are any pending posts of messages with code 'what' in 337 | * the message queue. 338 | */ 339 | public final boolean hasMessages(int what) { 340 | return mExec.hasMessages(what); 341 | } 342 | 343 | /** 344 | * Check if there are any pending posts of messages with code 'what' and 345 | * whose obj is 'object' in the message queue. 346 | */ 347 | public final boolean hasMessages(int what, Object object) { 348 | return mExec.hasMessages(what, object); 349 | } 350 | 351 | public final Looper getLooper() { 352 | return mExec.getLooper(); 353 | } 354 | 355 | private WeakRunnable wrapRunnable(@NonNull Runnable r) { 356 | //noinspection ConstantConditions 357 | if (r == null) { 358 | throw new NullPointerException("Runnable can't be null"); 359 | } 360 | final ChainedRef hardRef = new ChainedRef(mLock, r); 361 | mRunnables.insertAfter(hardRef); 362 | return hardRef.wrapper; 363 | } 364 | 365 | private static class ExecHandler extends Handler { 366 | private final WeakReference mCallback; 367 | 368 | ExecHandler() { 369 | mCallback = null; 370 | } 371 | 372 | ExecHandler(WeakReference callback) { 373 | mCallback = callback; 374 | } 375 | 376 | ExecHandler(Looper looper) { 377 | super(looper); 378 | mCallback = null; 379 | } 380 | 381 | ExecHandler(Looper looper, WeakReference callback) { 382 | super(looper); 383 | mCallback = callback; 384 | } 385 | 386 | @Override 387 | public void handleMessage(@NonNull Message msg) { 388 | if (mCallback == null) { 389 | return; 390 | } 391 | final Callback callback = mCallback.get(); 392 | if (callback == null) { // Already disposed 393 | return; 394 | } 395 | callback.handleMessage(msg); 396 | } 397 | } 398 | 399 | static class WeakRunnable implements Runnable { 400 | private final WeakReference mDelegate; 401 | private final WeakReference mReference; 402 | 403 | WeakRunnable(WeakReference delegate, WeakReference reference) { 404 | mDelegate = delegate; 405 | mReference = reference; 406 | } 407 | 408 | @Override 409 | public void run() { 410 | final Runnable delegate = mDelegate.get(); 411 | final ChainedRef reference = mReference.get(); 412 | if (reference != null) { 413 | reference.remove(); 414 | } 415 | if (delegate != null) { 416 | delegate.run(); 417 | } 418 | } 419 | } 420 | 421 | static class ChainedRef { 422 | @Nullable 423 | ChainedRef next; 424 | @Nullable 425 | ChainedRef prev; 426 | @NonNull 427 | final Runnable runnable; 428 | @NonNull 429 | final WeakRunnable wrapper; 430 | 431 | @NonNull 432 | Lock lock; 433 | 434 | public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) { 435 | this.runnable = r; 436 | this.lock = lock; 437 | this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this)); 438 | } 439 | 440 | public WeakRunnable remove() { 441 | lock.lock(); 442 | try { 443 | if (prev != null) { 444 | prev.next = next; 445 | } 446 | if (next != null) { 447 | next.prev = prev; 448 | } 449 | prev = null; 450 | next = null; 451 | } finally { 452 | lock.unlock(); 453 | } 454 | return wrapper; 455 | } 456 | 457 | public void insertAfter(@NonNull ChainedRef candidate) { 458 | lock.lock(); 459 | try { 460 | if (this.next != null) { 461 | this.next.prev = candidate; 462 | } 463 | 464 | candidate.next = this.next; 465 | this.next = candidate; 466 | candidate.prev = this; 467 | } finally { 468 | lock.unlock(); 469 | } 470 | } 471 | 472 | @Nullable 473 | public WeakRunnable remove(Runnable obj) { 474 | lock.lock(); 475 | try { 476 | ChainedRef curr = this.next; // Skipping head 477 | while (curr != null) { 478 | if (curr.runnable == obj) { // We do comparison exactly how Handler does inside 479 | return curr.remove(); 480 | } 481 | curr = curr.next; 482 | } 483 | } finally { 484 | lock.unlock(); 485 | } 486 | return null; 487 | } 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 |

4 | 6 | 7 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ModifiedStepsView 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /ModifiedStepsView/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ModifiedStepsView/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ModifiedStepsView/gif/device-2015-08-18-135551.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/gif/device-2015-08-18-135551.png -------------------------------------------------------------------------------- /ModifiedStepsView/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /ModifiedStepsView/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ModifiedStepsView/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 18 13:32:30 CST 2015 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-2.4-all.zip 7 | -------------------------------------------------------------------------------- /ModifiedStepsView/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 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /ModifiedStepsView/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 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 22 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 fileTree(dir: 'libs', include: ['*.jar']) 23 | compile 'com.android.support:appcompat-v7:22.2.1' 24 | } 25 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/library.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /ModifiedStepsView/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 D:\developtools\android sdk windows/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 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/androidTest/java/com/example/lenovo/library/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.lenovo.library; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/java/com/example/lenovo/library/StepsView.java: -------------------------------------------------------------------------------- 1 | package com.example.lenovo.library; 2 | 3 | 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.graphics.Typeface; 7 | import android.util.AttributeSet; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.FrameLayout; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | 15 | import java.util.List; 16 | 17 | 18 | /** 19 | * Created by 10129302 guopf on 15-6-26. 20 | */ 21 | public class StepsView extends LinearLayout implements StepsViewIndicator.OnDrawListener 22 | { 23 | 24 | private StepsViewIndicator mStepsViewIndicator; 25 | 26 | private FrameLayout mLabelsLayout; 27 | private String[] mLabels; 28 | 29 | private int mCompletedPosition; 30 | private int mLabelColor = Color.BLACK; 31 | 32 | public StepsView(Context context) 33 | { 34 | this(context, null); 35 | } 36 | 37 | public StepsView(Context context, AttributeSet attrs) 38 | { 39 | this(context, attrs, 0); 40 | } 41 | 42 | public StepsView(Context context, AttributeSet attrs, int defStyleAttr) 43 | { 44 | super(context, attrs, defStyleAttr); 45 | 46 | init(context); 47 | } 48 | 49 | private void init(Context context) 50 | { 51 | View rootView = LayoutInflater.from(context).inflate(R.layout.widget_steps_view, 52 | this); 53 | mStepsViewIndicator = (StepsViewIndicator) rootView 54 | .findViewById(R.id.steps_indicator_view); 55 | mStepsViewIndicator.setDrawListener(this); 56 | mLabelsLayout = (FrameLayout) rootView.findViewById(R.id.labels_container); 57 | } 58 | 59 | public StepsView setLabels(String[] labels) 60 | { 61 | mLabels = labels; 62 | mStepsViewIndicator.setSize(mLabels.length); 63 | return this; 64 | } 65 | 66 | public int getCompletedPosition() 67 | { 68 | return mCompletedPosition; 69 | } 70 | 71 | public StepsView setCompletedPosition(int completedPosition) 72 | { 73 | mCompletedPosition = completedPosition; 74 | mStepsViewIndicator.setCompletedPosition(completedPosition); 75 | 76 | return this; 77 | } 78 | 79 | public StepsView setColorIndicator(int color) 80 | { 81 | mStepsViewIndicator.setThumbColor(color); 82 | return this; 83 | } 84 | 85 | public StepsView setBarColor(int color) 86 | { 87 | mStepsViewIndicator.setBarColor(color); 88 | return this; 89 | } 90 | 91 | public StepsView setLabelColor(int labelColor) 92 | { 93 | mLabelColor = labelColor; 94 | return this; 95 | } 96 | 97 | @Override 98 | public void onFinish() 99 | { 100 | drawLabels(); 101 | } 102 | 103 | private void drawLabels() 104 | { 105 | List indicatorPosition = mStepsViewIndicator.getThumbContainerXPosition(); 106 | 107 | if (mLabels != null) 108 | { 109 | for (int i = 0; i < mLabels.length; i++) 110 | { 111 | TextView textView = new TextView(getContext()); 112 | textView.setText(mLabels[i]); 113 | textView.setTextColor(mLabelColor); 114 | textView.setX(indicatorPosition.get(i)); 115 | textView.setLayoutParams(new ViewGroup.LayoutParams( 116 | ViewGroup.LayoutParams.WRAP_CONTENT, 117 | ViewGroup.LayoutParams.WRAP_CONTENT)); 118 | 119 | if (i <= mCompletedPosition) 120 | { 121 | textView.setTypeface(null, Typeface.BOLD); 122 | } 123 | 124 | mLabelsLayout.addView(textView); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/java/com/example/lenovo/library/StepsViewIndicator.java: -------------------------------------------------------------------------------- 1 | package com.example.lenovo.library; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.Rect; 10 | import android.graphics.drawable.Drawable; 11 | import android.util.AttributeSet; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.view.animation.Animation; 15 | import android.view.animation.DecelerateInterpolator; 16 | import android.view.animation.Transformation; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | 22 | /** 23 | * Created by 10129302 guopf on 15-6-26. 24 | */ 25 | public class StepsViewIndicator extends View 26 | { 27 | 28 | private static final String tag = StepsViewIndicator.class.getSimpleName(); 29 | 30 | private static final int THUMB_SIZE = 100; 31 | private Paint paint; 32 | private Paint selectedPaint; 33 | private int mNumOfStep = 2; 34 | private float mLineHeight; 35 | 36 | private float mThumbRadius; 37 | private float mCircleRadius; 38 | private float mPadding; 39 | 40 | /** 41 | * 已经完成部分的颜色 42 | */ 43 | private int mThumbColor = Color.GRAY; 44 | /** 45 | * 未完成部分的颜色 46 | */ 47 | private int mBarColor = Color.GREEN; 48 | 49 | private float mCenterY, mLeftX, mLeftY, mRightX, mRightY; 50 | private float mDelta; 51 | 52 | private List mThumbContainerXPosition = new ArrayList<>(); 53 | private int mCompletedPosition; 54 | 55 | /** 56 | * 每一步成功的icon 57 | */ 58 | private Drawable complete_icon; 59 | /** 60 | * 每一步,失败的icon 61 | */ 62 | private Drawable failed_icon; 63 | 64 | private Rect rect = new Rect(); 65 | 66 | private boolean isError = false; 67 | private OnDrawListener mDrawListener; 68 | 69 | private int xOffset; 70 | /** 71 | * 动画 0-1的时间 72 | */ 73 | private float mInterpolatedTime; 74 | /** 75 | * 等待动画 76 | */ 77 | private WaitingAnimation waitingAnimation; 78 | /** 79 | * 等待小圆点画笔 80 | */ 81 | private Paint waitDotsPaint; 82 | /** 83 | * 等待小圆点的半径 84 | */ 85 | private int waitDotRadius = 2; 86 | /** 87 | * 动画完成一次 88 | */ 89 | private boolean sign = true; 90 | 91 | public interface OnDrawListener 92 | { 93 | void onFinish(); 94 | } 95 | 96 | public StepsViewIndicator(Context context) 97 | { 98 | this(context, null); 99 | } 100 | 101 | public StepsViewIndicator(Context context, AttributeSet attrs) 102 | { 103 | this(context, attrs, 0); 104 | } 105 | 106 | public StepsViewIndicator(Context context, AttributeSet attrs, int defStyleAttr) 107 | { 108 | super(context, attrs, defStyleAttr); 109 | init(context, attrs); 110 | } 111 | 112 | public List getThumbContainerXPosition() 113 | { 114 | return mThumbContainerXPosition; 115 | } 116 | 117 | @Override 118 | protected void onSizeChanged(int w, int h, int oldw, int oldh) 119 | { 120 | super.onSizeChanged(w, h, oldw, oldh); 121 | 122 | mCenterY = 0.5f * getHeight(); 123 | mLeftX = mPadding; 124 | mLeftY = mCenterY - (mLineHeight / 2); 125 | mRightX = getWidth() - mPadding; 126 | mRightY = 0.5f * (getHeight() + mLineHeight); 127 | mDelta = (mRightX - mLeftX) / (mNumOfStep - 1); 128 | 129 | mThumbContainerXPosition.add(mLeftX); 130 | for (int i = 1; i < mNumOfStep - 1; i++) 131 | { 132 | mThumbContainerXPosition.add(mLeftX + (i * mDelta)); 133 | } 134 | mThumbContainerXPosition.add(mRightX); 135 | mDrawListener.onFinish(); 136 | } 137 | 138 | @Override 139 | protected void onDraw(Canvas canvas) 140 | { 141 | super.onDraw(canvas); 142 | 143 | for (int i = 0; i < mThumbContainerXPosition.size(); i++) 144 | { 145 | canvas.drawCircle(mThumbContainerXPosition.get(i), mCenterY, mCircleRadius, 146 | (i <= mCompletedPosition) ? selectedPaint : paint); 147 | } 148 | 149 | for (int i = 0; i < mThumbContainerXPosition.size() - 1; i++) 150 | { 151 | final float pos = mThumbContainerXPosition.get(i); 152 | final float pos2 = mThumbContainerXPosition.get(i + 1); 153 | canvas.drawRect(pos, mLeftY, pos2, mRightY, (i < mCompletedPosition) 154 | ? selectedPaint 155 | : paint); 156 | } 157 | 158 | for (int i = 0; i < mThumbContainerXPosition.size(); i++) 159 | { 160 | paint.setStyle(Paint.Style.FILL); 161 | final float pos = mThumbContainerXPosition.get(i); 162 | canvas.drawCircle(pos, mCenterY, mCircleRadius, (i <= mCompletedPosition) 163 | ? selectedPaint 164 | : paint); 165 | 166 | rect = new Rect((int) (pos - mCircleRadius), 167 | (int) (mCenterY - mCircleRadius), (int) (pos + mCircleRadius), 168 | (int) (mCenterY + mCircleRadius)); 169 | 170 | //计算偏差 171 | xOffset = (int) ((pos - mCircleRadius) + (mCircleRadius * 2 - waitDotRadius * 10) / 2); 172 | 173 | if (i < mCompletedPosition) 174 | { 175 | complete_icon.setBounds(rect); 176 | complete_icon.draw(canvas); 177 | } 178 | else if (i == mCompletedPosition) 179 | { 180 | if (isError) 181 | { 182 | failed_icon.setBounds(rect); 183 | failed_icon.draw(canvas); 184 | } 185 | else 186 | { //画进度 187 | if (sign) 188 | { 189 | canvas.save(); 190 | canvas.rotate(180 * mInterpolatedTime, xOffset + waitDotRadius 191 | * 3, getHeight() / 2); 192 | canvas.drawCircle(xOffset + waitDotRadius, getHeight() / 2, 193 | waitDotRadius, waitDotsPaint); 194 | canvas.drawCircle(xOffset + waitDotRadius * 5, getHeight() / 2, 195 | waitDotRadius, waitDotsPaint); 196 | canvas.restore(); 197 | canvas.drawCircle(xOffset + waitDotRadius * 9, getHeight() / 2, 198 | waitDotRadius, waitDotsPaint); 199 | } 200 | else 201 | { 202 | canvas.save(); 203 | canvas.drawCircle(xOffset + waitDotRadius, getHeight() / 2, 204 | waitDotRadius, waitDotsPaint); 205 | canvas.rotate(180 * mInterpolatedTime, xOffset + waitDotRadius 206 | * 7, getHeight() / 2); 207 | canvas.drawCircle(xOffset + waitDotRadius * 5, getHeight() / 2, 208 | waitDotRadius, waitDotsPaint); 209 | canvas.drawCircle(xOffset + waitDotRadius * 9, getHeight() / 2, 210 | waitDotRadius, waitDotsPaint); 211 | canvas.restore(); 212 | } 213 | } 214 | } 215 | } 216 | } 217 | 218 | @Override 219 | protected void onDetachedFromWindow() 220 | { 221 | super.onDetachedFromWindow(); 222 | Log.d(tag, " onDetached from window"); 223 | waitingAnimation.cancel(); 224 | } 225 | 226 | @Override 227 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 228 | { 229 | int width = 200; 230 | if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) 231 | { 232 | width = MeasureSpec.getSize(widthMeasureSpec); 233 | } 234 | int height = THUMB_SIZE + 20; 235 | if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) 236 | { 237 | height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec)); 238 | } 239 | 240 | setMeasuredDimension(width, height); 241 | } 242 | 243 | public void setDrawListener(OnDrawListener listener) 244 | { 245 | this.mDrawListener = listener; 246 | } 247 | 248 | public void setSize(int size) 249 | { 250 | mNumOfStep = size; 251 | invalidate(); 252 | } 253 | 254 | /** 255 | * 设置进度结束的位置,这是成功的位置 256 | * 257 | * @param position, 从零开始 258 | */ 259 | public void setCompletedPosition(int position) 260 | { 261 | mCompletedPosition = position; 262 | isError = false; 263 | invalidate(); 264 | } 265 | 266 | /** 267 | * 设置进度失败的位置 268 | * 269 | * @param position,从零开始 270 | */ 271 | public void setErrorPosition(int position) 272 | { 273 | mCompletedPosition = position; 274 | isError = true; 275 | invalidate(); 276 | } 277 | 278 | public void reset() 279 | { 280 | setCompletedPosition(0); 281 | } 282 | 283 | public void setThumbColor(int thumbColor) 284 | { 285 | mThumbColor = thumbColor; 286 | } 287 | 288 | public void setBarColor(int barColor) 289 | { 290 | mBarColor = barColor; 291 | } 292 | 293 | private void init(Context context, AttributeSet attrs) 294 | { 295 | if (attrs != null) 296 | { 297 | TypedArray ta = context.obtainStyledAttributes(attrs, 298 | R.styleable.StepsViewIndicator); 299 | mNumOfStep = ta.getInt(R.styleable.StepsViewIndicator_numOfSteps, 0); 300 | ta.recycle(); 301 | } 302 | 303 | init(); 304 | } 305 | 306 | private void init() 307 | { 308 | mLineHeight = 0.2f * THUMB_SIZE; 309 | mThumbRadius = 0.4f * THUMB_SIZE; 310 | mCircleRadius = 0.7f * mThumbRadius; 311 | mPadding = 0.5f * THUMB_SIZE; 312 | 313 | complete_icon = getResources().getDrawable(R.drawable.load_toast_check); 314 | failed_icon = getResources().getDrawable(R.drawable.load_toast_error); 315 | 316 | waitingAnimation = new WaitingAnimation(); 317 | waitingAnimation.setDuration(500); 318 | waitingAnimation.setInterpolator(new DecelerateInterpolator()); 319 | waitingAnimation.setRepeatCount(Animation.INFINITE); 320 | waitingAnimation.setRepeatMode(Animation.RESTART); 321 | startAnimation(waitingAnimation); 322 | 323 | waitDotRadius = (int) mCircleRadius / 5; 324 | 325 | waitDotsPaint = new Paint(); 326 | waitDotsPaint.setAntiAlias(true); // 设置画笔为抗锯齿 327 | waitDotsPaint.setColor(Color.WHITE); // 设置画笔颜色 328 | waitDotsPaint.setStyle(Paint.Style.FILL); 329 | 330 | paint = new Paint(); 331 | selectedPaint = new Paint(); 332 | paint.setAntiAlias(true); 333 | paint.setColor(mBarColor); 334 | paint.setStyle(Paint.Style.FILL); 335 | 336 | selectedPaint.setAntiAlias(true); 337 | selectedPaint.setColor(mThumbColor); 338 | paint.setStyle(Paint.Style.FILL); 339 | } 340 | 341 | private class WaitingAnimation extends Animation 342 | { 343 | @Override 344 | protected void applyTransformation(float interpolatedTime, Transformation t) 345 | { 346 | super.applyTransformation(interpolatedTime, t); 347 | mInterpolatedTime = interpolatedTime; 348 | invalidate(); 349 | if (mInterpolatedTime == 1) 350 | sign = !sign; 351 | } 352 | } 353 | 354 | } 355 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/res/drawable-xxhdpi/load_toast_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/library/src/main/res/drawable-xxhdpi/load_toast_check.png -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/res/drawable-xxhdpi/load_toast_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpfduoduo/ModifiedStepsView/2405882a56d202385b88255f84281fe388da658c/ModifiedStepsView/library/src/main/res/drawable-xxhdpi/load_toast_error.png -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/res/layout/widget_steps_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/res/values/steps_view_attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ModifiedStepsView/library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | library 3 | 4 | -------------------------------------------------------------------------------- /ModifiedStepsView/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModifiedStepsView 2 | 改进的StepsView 3 | 4 | 根据StepsView进行改进,添加了每一步进行的等待动画和每一步的执行结果动画 5 | 6 | ##具体的如下图所示 7 | 8 | ![image](https://github.com/gpfduoduo/ModifiedStepsView/blob/master/ModifiedStepsView/gif/device-2015-08-18-135551.png "效果图") 9 | 10 | 11 | ##使用方法 12 | 13 | ###activity的xml文件中添加: 14 | 15 | 20 | 21 | ###在你的activity中调用 22 | 23 | 24 | stepsView = (StepsView) findViewById(R.id.steps_view); 25 | final String[] steps = {"step1", "step2", "step3", "step4"}; 26 | stepsView.setLabels(steps) 27 | .setColorIndicator(Color.GRAY) 28 | .setBarColor(Color.GREEN) 29 | .setLabelColor(Color.BLACK); 30 | 31 | new Thread() { 32 | public void run() { 33 | for (int i = 0; i < steps.length; i++) { 34 | if(isStop) { 35 | break; 36 | } 37 | Message msg = new Message(); 38 | msg.obj = i; 39 | handler.sendMessage(msg); 40 | try { 41 | Thread.sleep(4000); 42 | } catch (Exception e) { 43 | } 44 | } 45 | Message msg = new Message(); 46 | msg.obj = steps.length; 47 | handler.sendMessage(msg); 48 | } 49 | }.start(); 50 | ''' 51 | --------------------------------------------------------------------------------