├── .gitignore
├── AndroidSortAnimation-develop
├── .gitignore
├── .idea
│ ├── caches
│ │ └── build_file_checksums.ser
│ ├── codeStyles
│ │ └── Project.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── README.md
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ ├── release
│ │ ├── app-release.apk
│ │ └── output.json
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── ukhanoff
│ │ │ └── bubblesort
│ │ │ ├── common
│ │ │ ├── AlgorithmAnimationListener.java
│ │ │ ├── AlgorithmStepsInterface.java
│ │ │ ├── AnimationScenarioItem.java
│ │ │ └── AnimationsCoordinator.java
│ │ │ ├── ui
│ │ │ ├── activity
│ │ │ │ └── SortActivity.java
│ │ │ ├── customview
│ │ │ │ └── BubbleView.java
│ │ │ └── fragment
│ │ │ │ └── SortFragment.java
│ │ │ └── util
│ │ │ └── Util.java
│ │ └── res
│ │ ├── anim
│ │ └── first_swap.xml
│ │ ├── layout
│ │ ├── activity_sort.xml
│ │ ├── activity_sorting.xml
│ │ ├── fragment_sort.xml
│ │ └── sorting_main_fragment_view.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── Note.md
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-web.png
│ ├── java
│ └── pri
│ │ └── weiqiang
│ │ └── sortanimation
│ │ ├── algorithm
│ │ ├── Sort.java
│ │ └── SortArrayList.java
│ │ ├── animation
│ │ ├── AlgorithmAnimationListener.java
│ │ ├── AlgorithmStepsInterface.java
│ │ ├── AnimationScenarioItem.java
│ │ ├── AnimationsCoordinator.java
│ │ ├── MergeAnimationListener.java
│ │ ├── MergeAnimationScenarioItem.java
│ │ ├── MergeAnimationsCoordinator.java
│ │ └── MergeStepsInterface.java
│ │ ├── constant
│ │ └── Constant.java
│ │ ├── ui
│ │ ├── activity
│ │ │ ├── CodeActivity.java
│ │ │ └── SortActivity.java
│ │ ├── customview
│ │ │ └── RectView.java
│ │ └── fragment
│ │ │ └── SortFragment.java
│ │ └── util
│ │ └── Util.java
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_code.xml
│ ├── activity_sort.xml
│ ├── fragment_sort.xml
│ └── view_line.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── array.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ic_launcher_background.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshot
├── bubble_ukhanoff.gif
├── heap.gif
├── heer.gif
├── insert.gif
├── merge.gif
├── pubble.gif
├── quick.gif
└── select.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 | .idea
13 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.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 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.idea/codeStyles/Project.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 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.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 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/README.md:
--------------------------------------------------------------------------------
1 | # AndroidSortAnimation
2 |
3 | Small App to visualize bubble sort algorithm. This App is no more than test assignment which just demonstrates how to work with animations, views etc.
4 |
5 | ## VideoDemo
6 |
7 | https://www.dropbox.com/s/bs6nn646udp85na/demo_sort.mov?dl=0
8 |
9 | ## Usage
10 |
11 | Clone the project
12 | ```
13 | git clone https://github.com/ukhanoff/AndroidSortAnimation.git
14 | ```
15 | Open cloned project in your Android Studio (we recommend to use version 2.2+).
16 |
17 | Push run button in Android Studio. And... thats all! Enjoy.
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion '28.0.3'
6 | defaultConfig {
7 | applicationId "com.ukhanoff.bubblesort"
8 | minSdkVersion 21
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation 'com.android.support:appcompat-v7:28.0.0'
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/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 /Users/ukhanoff/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/app/release/app-release.apk
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/common/AlgorithmAnimationListener.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.common;
2 |
3 | /**
4 | * Created by ukhanoff on 2/7/17.
5 | */
6 |
7 | public interface AlgorithmAnimationListener {
8 | void onSwapStepAnimationEnd(int endedPosition);
9 | }
10 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/common/AlgorithmStepsInterface.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.common;
2 |
3 | /**
4 | * Created by ukhanoff on 2/6/17.
5 | */
6 |
7 | public interface AlgorithmStepsInterface {
8 |
9 | /**
10 | * Visualizes step, when elements should change their places with each other
11 | * 交换位置
12 | * @param position position of the firs element, which should be changed
13 | * @param isBubbleOnFinalPlace set true, when element after swapping is on the right place and his position is final
14 | */
15 | void showSwapStep(int position, boolean isBubbleOnFinalPlace);
16 |
17 | /**
18 | * Visualizes step, when elements should stay on the same places;
19 | * 不交换位置
20 | * @param position position of the firs element
21 | * @param isBubbleOnFinalPlace set true, when element on position+1 is on the right place and his position is final
22 | */
23 | void showNonSwapStep(int position, boolean isBubbleOnFinalPlace);
24 |
25 | /**
26 | * Call when last item was sorted. Notifies user that sorting is finished.
27 | * 结束全部动画,小球将处于最后排序完成后的颜色
28 | */
29 | void showFinish();
30 |
31 | /**
32 | * Cancel all current animations
33 | */
34 | void cancelAllVisualisations();
35 | }
36 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/common/AnimationScenarioItem.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.common;
2 |
3 | /**
4 | * Holds data about what kind of animation should be shown.
5 | */
6 |
7 | public class AnimationScenarioItem {
8 |
9 | private boolean isShouldBeSwapped;
10 | private int firstItemPosition;
11 |
12 | private boolean isFinalPlace;
13 |
14 | public AnimationScenarioItem(boolean isShouldBeSwapped, int itemPosition, boolean isFinalPlace) {
15 | this.isShouldBeSwapped = isShouldBeSwapped;
16 | this.firstItemPosition = itemPosition;
17 | this.isFinalPlace = isFinalPlace;
18 | }
19 |
20 | public boolean isShouldBeSwapped() {
21 | return isShouldBeSwapped;
22 | }
23 |
24 | public int getAnimationViewItemPosition() {
25 | return firstItemPosition;
26 | }
27 |
28 | public boolean isFinalPlace() {
29 | return isFinalPlace;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/common/AnimationsCoordinator.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.common;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.util.Log;
7 | import android.view.ViewGroup;
8 | import android.widget.Toast;
9 |
10 | import com.ukhanoff.bubblesort.R;
11 | import com.ukhanoff.bubblesort.ui.customview.BubbleView;
12 |
13 | import java.util.ArrayList;
14 |
15 | /**
16 | * Handles all animation which should be done in scope of sorting algorithm visualization.
17 | */
18 |
19 | public class AnimationsCoordinator implements AlgorithmStepsInterface {
20 |
21 | private String TAG = AnimationsCoordinator.class.getSimpleName();
22 | private ViewGroup bubblesContainer;
23 | private ArrayList listeners;
24 | private ValueAnimator blinkAnimation;
25 |
26 | public AnimationsCoordinator(ViewGroup bubblesContainer) {
27 | Log.e(TAG, "AnimationsCoordinator");
28 | this.bubblesContainer = bubblesContainer;
29 | }
30 |
31 | @Override
32 | public void showSwapStep(final int position, final boolean isBubbleOnFinalPosition) {
33 | Log.e(TAG, "showSwapStep position:"+position+",isBubbleOnFinalPosition:"+isBubbleOnFinalPosition);
34 | if (bubblesContainer != null && bubblesContainer.getChildCount() > 0 && bubblesContainer.getChildCount() > position + 1) {
35 | final BubbleView tempView = (BubbleView) bubblesContainer.getChildAt(position);
36 | final BubbleView nextTempView = (BubbleView) bubblesContainer.getChildAt(position + 1);
37 |
38 | //值为0到5,偶数为选中状态,蓝色,基数为未选中状态,粉色,所以,视觉表现为闪烁2次
39 | blinkAnimation = ValueAnimator.ofInt(0, 5);
40 | blinkAnimation.setDuration(1000);//动画时长为2s
41 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
42 | @Override
43 | public void onAnimationUpdate(ValueAnimator animation) {
44 | int value = ((Integer) animation.getAnimatedValue()).intValue();
45 | // Log.e(TAG,"showSwapStep addUpdateListener value:"+value);
46 | if (value % 2 == 0) {
47 | tempView.setBubbleSelected(false);
48 | nextTempView.setBubbleSelected(false);
49 | } else {
50 | tempView.setBubbleSelected(true);
51 | nextTempView.setBubbleSelected(true);
52 | }
53 | }
54 | });
55 |
56 |
57 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
58 | @Override
59 | public void onAnimationEnd(Animator animation) {//
60 | super.onAnimationEnd(animation);
61 | tempView.setBubbleSelected(false);
62 | tempView.setBubbleIsOnFinalPlace(isBubbleOnFinalPosition);
63 | nextTempView.setBubbleSelected(false);
64 | bubblesContainer.removeView(tempView);
65 | bubblesContainer.addView(tempView, position + 1);
66 |
67 | notifySwapStepAnimationEnd(position);
68 | }
69 | });
70 |
71 | blinkAnimation.start();
72 | }
73 | }
74 |
75 | @Override
76 | public void showNonSwapStep(final int position, final boolean isBubbleOnFinalPlace) {
77 | Log.e(TAG, "showNonSwapStep position:"+position+",isBubbleOnFinalPosition:"+isBubbleOnFinalPlace);
78 | if (bubblesContainer != null && bubblesContainer.getChildCount() > 0 && bubblesContainer.getChildCount() > position + 1) {
79 | final BubbleView tempView = (BubbleView) bubblesContainer.getChildAt(position);
80 | final BubbleView nextTempView = (BubbleView) bubblesContainer.getChildAt(position + 1);
81 |
82 | //值为0到7,偶数为选中状态,蓝色,基数为未选中状态,粉色,所以,视觉表现为闪烁3次
83 | blinkAnimation = ValueAnimator.ofInt(0, 7);
84 | blinkAnimation.setDuration(1200);
85 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
86 | @Override
87 | public void onAnimationUpdate(ValueAnimator animation) {
88 | int value = ((Integer) animation.getAnimatedValue()).intValue();
89 | // Log.e(TAG,"showNonSwapStep addUpdateListener value:"+value);
90 | if (value % 2 == 0) {
91 | tempView.setBubbleSelected(false);
92 | nextTempView.setBubbleSelected(false);
93 | } else {
94 | tempView.setBubbleSelected(true);
95 | nextTempView.setBubbleSelected(true);
96 | }
97 | }
98 | });
99 |
100 | blinkAnimation.start();
101 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
102 | @Override
103 | public void onAnimationEnd(Animator animation) {
104 | super.onAnimationEnd(animation);
105 | tempView.setBubbleSelected(false);
106 | nextTempView.setBubbleSelected(false);
107 | nextTempView.setBubbleIsOnFinalPlace(isBubbleOnFinalPlace);
108 |
109 | notifySwapStepAnimationEnd(position);
110 | }
111 | });
112 | }
113 | }
114 |
115 | @Override
116 | public void showFinish() {
117 | Log.e(TAG, "showFinish");
118 | if (bubblesContainer != null && bubblesContainer.getChildCount() > 0) {
119 | ((BubbleView) bubblesContainer.getChildAt(0)).setBubbleIsOnFinalPlace(true);
120 | }
121 | Toast.makeText(bubblesContainer.getContext(), R.string.sort_finish, Toast.LENGTH_SHORT).show();
122 | }
123 |
124 | @Override
125 | public void cancelAllVisualisations() {
126 | Log.e(TAG, "cancelAllVisualisations");
127 | if (blinkAnimation != null) {
128 | blinkAnimation.removeAllListeners();
129 | blinkAnimation.cancel();
130 | bubblesContainer.clearAnimation();
131 | }
132 | }
133 |
134 | private void notifySwapStepAnimationEnd(int position) {
135 | Log.e(TAG, "notifySwapStepAnimationEnd:"+position);
136 | if (listeners != null && !listeners.isEmpty()) {
137 | int numListeners = listeners.size();
138 | //这里直接只用listeners.get(0).onSwapStepAnimationEnd(position);即可,因为addListener(AlgorithmAnimationListener listener)仅使用了一次
139 | Log.e(TAG, "numListeners:"+numListeners);
140 | for (int i = 0; i < numListeners; ++i) {
141 | Log.e(TAG, "onSwapStepAnimationEnd i:"+i+",position:"+position);
142 | //将会调用SortFragment: onSwapStepAnimationEnd中实现具体的方法
143 | listeners.get(i).onSwapStepAnimationEnd(position);
144 | }
145 | }
146 | }
147 |
148 | public void addListener(AlgorithmAnimationListener listener) {
149 | Log.e(TAG, "addListener");
150 | if (listeners == null) {
151 | listeners = new ArrayList<>();
152 | }
153 | listeners.add(listener);
154 | }
155 |
156 | public void removeListener(Animator.AnimatorListener listener) {
157 | Log.e(TAG, "removeListener");
158 | if (listeners == null) {
159 | return;
160 | }
161 | listeners.remove(listener);
162 | if (listeners.size() == 0) {
163 | listeners = null;
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/ui/activity/SortActivity.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.ui.activity;
2 |
3 | import android.os.Bundle;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import com.ukhanoff.bubblesort.R;
7 | import com.ukhanoff.bubblesort.ui.fragment.SortFragment;
8 |
9 | public class SortActivity extends AppCompatActivity {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.activity_sort);
15 |
16 | if (savedInstanceState == null) {
17 | SortFragment fragment = new SortFragment();
18 | getSupportFragmentManager().beginTransaction().add(R.id.fl_container, fragment)
19 | .commit();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/ui/customview/BubbleView.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.ui.customview;
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.Rect;
8 | import androidx.appcompat.widget.AppCompatImageView;
9 | import android.util.AttributeSet;
10 |
11 | import com.ukhanoff.bubblesort.R;
12 |
13 | import static com.ukhanoff.bubblesort.ui.fragment.SortFragment.PADDING;
14 |
15 | /**
16 | * This is custom ImageView which could draw a "Bubble with a number inside".
17 | */
18 |
19 | public class BubbleView extends AppCompatImageView {
20 | public static final int START_X_POS = 25;
21 | public static final int TEXT_BASELINE_Y = 105;
22 | public static final int BOTTOM_POS = 120;
23 | public static final int TOP_POS = 60;
24 | public static final float TEXT_SIZE = 45f;
25 | //方法2 直接new 避免avoid object allocation during draw/layout operations (prelocate and reuse instead)
26 | // Paint paint = new Paint(Paint.LINEAR_TEXT_FLAG);
27 | // Rect bounds = new Rect();
28 | Paint paint;
29 | Rect bounds;
30 | private String TAG = BubbleView.class.getSimpleName();
31 | private Integer valueToDraw;
32 | private boolean isSelected;
33 | private boolean isOnFinalPlace;
34 |
35 | public BubbleView(Context context) {
36 | this(context, null);
37 | init();
38 | }
39 |
40 | public BubbleView(Context context, AttributeSet attrs) {
41 | this(context, attrs, 0);
42 | init();
43 | }
44 |
45 | public BubbleView(Context context, AttributeSet attrs, int defStyleAttr) {
46 | super(context, attrs, defStyleAttr);
47 | init();
48 | }
49 |
50 | private void init() {
51 | paint = new Paint(Paint.LINEAR_TEXT_FLAG);
52 | paint.setAntiAlias(true);
53 | paint.setTextSize(TEXT_SIZE);
54 | bounds = new Rect();
55 | }
56 |
57 | @Override
58 | protected void onDraw(Canvas canvas) {
59 | // Log.e(TAG,"onDraw()");
60 | super.onDraw(canvas);
61 | if (valueToDraw != null) {
62 | String text = valueToDraw.toString();
63 | paint.getTextBounds(text, 0, text.length(), bounds);
64 | if (isOnFinalPlace) {
65 | paint.setColor(getResources().getColor(R.color.colorPrimaryDark));
66 | } else {
67 | if (isSelected) {
68 | paint.setColor(getResources().getColor(R.color.colorIndigo));
69 | } else {
70 | paint.setColor(getResources().getColor(R.color.colorAccent));
71 | }
72 | }
73 | canvas.drawOval(0, TOP_POS, bounds.width() + PADDING, BOTTOM_POS, paint);
74 | paint.setColor(Color.WHITE);
75 | canvas.drawText(text, START_X_POS, TEXT_BASELINE_Y, paint);
76 | }
77 | }
78 |
79 | /**
80 | * Draws a number as a bitmap inside of the bubble circle.
81 | * 在小球中央绘制数字
82 | * @param numberValueToDraw value which should appears in the center of {@link BubbleView}
83 | */
84 | public void setNumber(Integer numberValueToDraw) {
85 | valueToDraw = numberValueToDraw;
86 | invalidate();
87 | }
88 |
89 | /**
90 | * Background color of bubble will be changed to dark blue.
91 | * 设置小球处于未选中状态,背景颜色将作出相应改变
92 | * @param isOnFinalPlace
93 | */
94 | public void setBubbleIsOnFinalPlace(boolean isOnFinalPlace) {
95 | this.isOnFinalPlace = isOnFinalPlace;
96 | invalidate();
97 | }
98 |
99 | public boolean isBubbleSelected() {
100 | return isSelected;
101 | }
102 |
103 | /**
104 | * Background color will be changed to blue if true
105 | * 设置小球处于选中状态,背景颜色将作出相应改变
106 | *
107 | * @param isSelected
108 | */
109 | public void setBubbleSelected(boolean isSelected) {
110 | this.isSelected = isSelected;
111 | invalidate();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/ui/fragment/SortFragment.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.ui.fragment;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Paint;
5 | import android.graphics.Rect;
6 | import android.os.Bundle;
7 | import androidx.annotation.Nullable;
8 | import androidx.fragment.app.Fragment;
9 | import android.text.TextUtils;
10 | import android.util.Log;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.Button;
15 | import android.widget.EditText;
16 | import android.widget.LinearLayout;
17 | import android.widget.Toast;
18 |
19 | import com.ukhanoff.bubblesort.R;
20 | import com.ukhanoff.bubblesort.common.AlgorithmAnimationListener;
21 | import com.ukhanoff.bubblesort.common.AnimationScenarioItem;
22 | import com.ukhanoff.bubblesort.common.AnimationsCoordinator;
23 | import com.ukhanoff.bubblesort.ui.customview.BubbleView;
24 | import com.ukhanoff.bubblesort.util.Util;
25 |
26 | import java.util.ArrayList;
27 |
28 | /**
29 | * Main fragment where sorting visualisation appears
30 | */
31 |
32 | public class SortFragment extends Fragment {
33 | public static final int PADDING = 50;
34 | public static final int BUBBLE_MARGIN = 4;
35 | private String TAG = SortFragment.class.getSimpleName();
36 | private EditText mEtInput;
37 | private Button mBtnStart;
38 | private boolean isAnimationRunning;
39 | private int scenarioItemIndex = 0;
40 | private LinearLayout mLlContainer;
41 | private AnimationsCoordinator animationsCoordinator;
42 | private ArrayList animationioList;
43 | View.OnClickListener buttonClickListener = new View.OnClickListener() {
44 |
45 | @Override
46 | public void onClick(View v) {
47 | String inputUserArray = mEtInput.getText().toString();
48 | if (!TextUtils.isEmpty(inputUserArray)) {
49 | resetPreviousData();
50 | animationioList = new ArrayList<>();
51 | ArrayList integerArrayList = new ArrayList<>(convertToIntArray(inputUserArray));
52 | drawBubbles(integerArrayList);
53 | generateSortScenario(integerArrayList);
54 | Log.e(TAG, "runAnimationIteration onClick");
55 | runAnimationIteration();
56 | } else {
57 | Toast.makeText(getContext(), R.string.empty_field_warning, Toast.LENGTH_LONG).show();
58 | }
59 | }
60 | };
61 |
62 | private void resetPreviousData() {
63 | Log.e(TAG, "resetPreviousData");
64 | if (isAnimationRunning && animationsCoordinator != null) {
65 | animationsCoordinator.cancelAllVisualisations();
66 | isAnimationRunning = false;
67 | }
68 | scenarioItemIndex = 0;
69 | }
70 |
71 | @Override
72 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
73 | Log.e(TAG, "onCreateView");
74 | View view = inflater.inflate(R.layout.fragment_sort, container, false);
75 | mEtInput = view.findViewById(R.id.et_input);
76 | mBtnStart = view.findViewById(R.id.btn_start);
77 | mBtnStart.setOnClickListener(buttonClickListener);
78 | mLlContainer = view.findViewById(R.id.ll_container);
79 | animationsCoordinator = new AnimationsCoordinator(mLlContainer);
80 | animationsCoordinator.addListener(new AlgorithmAnimationListener() {
81 | @Override
82 | public void onSwapStepAnimationEnd(int endedPosition) {
83 | Log.e(TAG, "onSwapStepAnimationEnd endedPosition:"+endedPosition);
84 | runAnimationIteration();
85 | }
86 | });
87 |
88 | mBtnStart.callOnClick();
89 | return view;
90 | }
91 |
92 | private void runAnimationIteration() {
93 | Log.e(TAG, "runAnimationIteration");
94 | isAnimationRunning = true;
95 | if (animationioList != null && animationioList.size() == scenarioItemIndex) {
96 | animationsCoordinator.showFinish();
97 | return;
98 | }
99 | if (animationioList != null && !animationioList.isEmpty() && animationioList.size() > scenarioItemIndex) {
100 | AnimationScenarioItem animationStep = animationioList.get(scenarioItemIndex);
101 | scenarioItemIndex++;
102 | if (animationStep.isShouldBeSwapped()) {
103 | animationsCoordinator.showSwapStep(animationStep.getAnimationViewItemPosition(), animationStep.isFinalPlace());
104 | } else {
105 | animationsCoordinator.showNonSwapStep(animationStep.getAnimationViewItemPosition(), animationStep.isFinalPlace());
106 | }
107 | }
108 |
109 | }
110 |
111 | private void swap(final ArrayList list, final int inner) {
112 | Log.e(TAG, "swap");
113 | int temp = list.get(inner);
114 | list.set(inner, list.get(inner + 1));
115 | list.set(inner + 1, temp);
116 | }
117 |
118 | private void drawBubbles(ArrayList listToDraw) {
119 | Log.e(TAG, "drawBubbles");
120 | if (mLlContainer != null) {
121 | mLlContainer.removeAllViews();
122 | mLlContainer.clearAnimation();
123 | }
124 |
125 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
126 | int marginInPx = Util.dpToPx(getContext(), BUBBLE_MARGIN);
127 | lp.setMargins(0, 0, marginInPx, 0);
128 |
129 | int pos = 0;
130 | for (Integer currentIntValue : listToDraw) {
131 | BubbleView bubbleView = new BubbleView(getContext());
132 | bubbleView.setImageBitmap(createCalculatedBitmap(currentIntValue));
133 | bubbleView.setMinimumHeight(250);
134 | bubbleView.setNumber(currentIntValue);
135 | bubbleView.setId(pos);
136 | if (mLlContainer != null) {
137 | mLlContainer.addView(bubbleView, lp);
138 | }
139 | pos++;
140 | }
141 | }
142 |
143 | /**
144 | * Calculates size of ImageView which would be generated with current text value.
145 | *
146 | * @param currentIntValue
147 | * @return empty bitmap with calculated size
148 | */
149 | private Bitmap createCalculatedBitmap(Integer currentIntValue) {
150 | Log.e(TAG, "createCalculatedBitmap");
151 | final Rect bounds = new Rect();
152 | Paint paint = new Paint(Paint.LINEAR_TEXT_FLAG);
153 | paint.setTextSize(BubbleView.TEXT_SIZE);
154 | paint.getTextBounds(currentIntValue.toString(), 0, currentIntValue.toString().length(), bounds);
155 | return Bitmap.createBitmap(bounds.width() + PADDING, bounds.height() + PADDING, Bitmap.Config.ALPHA_8);
156 | }
157 |
158 | private ArrayList convertToIntArray(String inputUserArray) {
159 | Log.e(TAG, "convertToIntArray");
160 | ArrayList parsedUserArray = new ArrayList<>();
161 | String[] stringArray = inputUserArray.split(",");
162 | int numberOfElements = stringArray.length;
163 | for (int i = 0; i < numberOfElements; i++) {
164 | if (!TextUtils.isEmpty(stringArray[i])) {
165 | parsedUserArray.add(Integer.parseInt(stringArray[i]));
166 | }
167 | }
168 | return parsedUserArray;
169 |
170 | }
171 |
172 | private ArrayList generateSortScenario(ArrayList unsortedValues) {
173 | Log.e(TAG, "generateSortScenario");
174 | ArrayList values = new ArrayList<>(unsortedValues);
175 | boolean isLastInLoop;
176 | for (int i = 0; i < values.size() - 1; i++) {
177 | for (int j = 0; j < values.size() - i - 1; j++) {
178 | if (j == values.size() - i - 2) {
179 | isLastInLoop = true;
180 | } else {
181 | isLastInLoop = false;
182 | }
183 | if (values.get(j) > values.get(j + 1)) {
184 | swap(values, j);
185 | animationioList.add(new AnimationScenarioItem(true, j, isLastInLoop));
186 | } else {
187 | animationioList.add(new AnimationScenarioItem(false, j, isLastInLoop));
188 | }
189 | }
190 | }
191 | return values;
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/java/com/ukhanoff/bubblesort/util/Util.java:
--------------------------------------------------------------------------------
1 | package com.ukhanoff.bubblesort.util;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * Typical android helpful staff lives here.
7 | */
8 |
9 | public class Util {
10 |
11 | public static int dpToPx(Context context, int sizeInDp) {
12 | float scale = context.getResources().getDisplayMetrics().density;
13 | return (int) (sizeInDp * scale + 0.5f);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/anim/first_swap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/layout/activity_sort.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/layout/activity_sorting.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/layout/fragment_sort.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
26 |
27 |
35 |
36 |
37 |
38 |
45 |
46 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/layout/sorting_main_fragment_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
26 |
27 |
35 |
36 |
37 |
38 |
45 |
46 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #536DFE
7 |
8 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BubbleSort
3 | Sort
4 | Field is empty. Please add some integers here
5 | Sorting is finished, congrats!
6 |
7 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.3.0'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | google()
19 | jcenter()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/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 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/AndroidSortAnimation-develop/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Sep 29 10:36:17 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.6-all.zip
7 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/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 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/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 |
--------------------------------------------------------------------------------
/AndroidSortAnimation-develop/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/Note.md:
--------------------------------------------------------------------------------
1 | # 待作的事情
2 | - [ ] 增加算法说明和代码,使用markdown进行显示
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 | 最近在学习基础的排序算法,发现仅凭算法的定义公式,即使结合代码在IDE下debug查看数组变化,也依然不是很好的理解,于是就在网上搜索排序算法动画,果然已经有人实现了排序演示,有java实现的,有JS实现,但很想在android手机上看简单演示,最终找到了,[ukhanoff/AndroidSortAnimation](https://github.com/ukhanoff/AndroidSortAnimation),一个国际友人,用android实现了基础的冒泡排序法。(左边为他的实现效果,右侧为我的实现效果)
3 |
4 |
5 |
6 | 在这之前,我也尝试过使用RecycleView或者自定义View实现类似效果,但依然还是败下阵来,在参考[ukhanoff/AndroidSortAnimation](https://github.com/ukhanoff/AndroidSortAnimation)后,我增加了其他几种排序算法动画,同时将上边的自定义图形,从球形设置成了长方体,动画效果将和[liusaint/sortAnimation](https://github.com/liusaint/sortAnimation)
7 | 以及[在线动画演示各种排序算法过程 - aTool在线工具](http://www.atool.org/sort.php)两种JS实现效果相一致,达到了相对预期的效果,排序算法分别包括包含冒泡、插入、选择、快速、归并、希尔、堆排序。
8 |
9 |
10 |
11 | 接下来,我将分享下android平台下,如何实现排序动画。
12 | ##### 备注:文章中仅展示关键代码用来说明思路,全部代码请移步:[54wall/SortAnimation](https://github.com/54wall/SortAnimation)
13 |
14 | 首先大概讲解下大神[ukhanoff](https://github.com/ukhanoff/AndroidSortAnimation)
15 | ,参考将大象被装到冰箱,他是如何实现的冒泡排序法。
16 | 他主要用到了三个基础知识:
17 | #### 自定义View
18 | #### Android属性动画之ValueAnimator
19 | #### ViewGroup中addView与removeView
20 | 接下来分步骤展开说明下
21 | ### 借助自定义View实现可以变色的小球
22 | 自定义BubbleView继承AppCompatImageView,新增设置小球处于选中状态,复写onDraw()等方法代码如下:
23 | ```java
24 | /**
25 | * This is custom ImageView which could draw a "Bubble with a number inside".
26 | */
27 |
28 | public class BubbleView extends AppCompatImageView {
29 | public static final int START_X_POS
30 | = 25;
31 | public static final int TEXT_BASELINE_Y = 105;
32 | public static final int BOTTOM_POS = 120;
33 | public static final int TOP_POS = 60;
34 | public static final float TEXT_SIZE = 45f;
35 | //方法2 直接new 避免avoid object allocation during draw/layout operations (prelocate and reuse instead)
36 | // Paint paint = new Paint(Paint.LINEAR_TEXT_FLAG);
37 | // Rect bounds = new Rect();
38 | Paint paint;
39 | Rect bounds;
40 | private String TAG = BubbleView.class.getSimpleName();
41 | private Integer valueToDraw;
42 | private boolean isSelected;
43 | private boolean isOnFinalPlace;
44 |
45 | public BubbleView(Context context) {
46 | this(context, null);
47 | init();
48 | }
49 |
50 | public BubbleView(Context context, AttributeSet attrs) {
51 | this(context, attrs, 0);
52 | init();
53 | }
54 |
55 | public BubbleView(Context context, AttributeSet attrs, int defStyleAttr) {
56 | super(context, attrs, defStyleAttr);
57 | init();
58 | }
59 |
60 | private void init() {
61 | paint = new Paint(Paint.LINEAR_TEXT_FLAG);
62 | paint.setAntiAlias(true);
63 | paint.setTextSize(TEXT_SIZE);
64 | bounds = new Rect();
65 | }
66 |
67 | @Override
68 | protected void onDraw(Canvas canvas) {
69 | // Log.e(TAG,"onDraw()");
70 | super.onDraw(canvas);
71 | if (valueToDraw != null) {
72 | String text = valueToDraw.toString();
73 | paint.getTextBounds(text, 0, text.length(), bounds);
74 | if (isOnFinalPlace) {
75 | paint.setColor(getResources().getColor(R.color.colorPrimaryDark));
76 | } else {
77 | if (isSelected) {
78 | paint.setColor(getResources().getColor(R.color.colorIndigo));
79 | } else {
80 | paint.setColor(getResources().getColor(R.color.colorAccent));
81 | }
82 | }
83 | canvas.drawOval(0, TOP_POS, bounds.width() + PADDING, BOTTOM_POS, paint);
84 | paint.setColor(Color.WHITE);
85 | canvas.drawText(text, START_X_POS, TEXT_BASELINE_Y, paint);
86 | }
87 | }
88 |
89 | /**
90 | * Draws a number as a bitmap inside of the bubble circle.
91 | * 在小球中央绘制数字
92 | * @param numberValueToDraw value which should appears in the center of {@link BubbleView}
93 | */
94 | public void setNumber(Integer numberValueToDraw) {
95 | valueToDraw = numberValueToDraw;
96 | invalidate();
97 | }
98 |
99 | /**
100 | * Background color of bubble will be changed to dark blue.
101 | * 设置小球处于未选中状态,背景颜色将作出相应改变
102 | * @param isOnFinalPlace
103 | */
104 | public void setBubbleIsOnFinalPlace(boolean isOnFinalPlace) {
105 | this.isOnFinalPlace = isOnFinalPlace;
106 | invalidate();
107 | }
108 |
109 | public boolean isBubbleSelected() {
110 | return isSelected;
111 | }
112 |
113 | /**
114 | * Background color will be changed to blue if true
115 | * 设置小球处于选中状态,背景颜色将作出相应改变
116 | *
117 | * @param isSelected
118 | */
119 | public void setBubbleSelected(boolean isSelected) {
120 | this.isSelected = isSelected;
121 | invalidate();
122 | }
123 | }
124 |
125 |
126 | ```
127 | 有了小球之后,我们需要让小球在排序中有选中的状态,并有节奏的闪烁起来,所以属性动画ValueAnimator出场。
128 | ### 借助ValueAnimator让小球闪烁起来
129 | 通过属性动画ValueAnimator,他仅作为数值发生器,来控制小球闪烁的频率,相关代码如下:
130 |
131 | ```java
132 | //值为0到7,偶数为选中状态,蓝色,基数为未选中状态,粉色,所以,视觉表现为闪烁3次
133 | blinkAnimation = ValueAnimator.ofInt(0, 7);
134 | blinkAnimation.setDuration(3000);
135 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
136 | @Override
137 | public void onAnimationUpdate(ValueAnimator animation) {
138 | int value = ((Integer) animation.getAnimatedValue()).intValue();
139 | // Log.e(TAG,"showNonSwapStep addUpdateListener value:"+value);
140 | if (value % 2 == 0) {
141 | tempView.setBubbleSelected(false);
142 | nextTempView.setBubbleSelected(false);
143 | } else {
144 | tempView.setBubbleSelected(true);
145 | nextTempView.setBubbleSelected(true);
146 | }
147 | }
148 | });
149 |
150 | blinkAnimation.start();
151 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
152 | @Override
153 | public void onAnimationEnd(Animator animation) {
154 | super.onAnimationEnd(animation);
155 | tempView.setBubbleSelected(false);
156 | nextTempView.setBubbleSelected(false);
157 | nextTempView.setBubbleIsOnFinalPlace(isBubbleOnFinalPlace);
158 |
159 | notifySwapStepAnimationEnd(position);
160 | }
161 | });
162 |
163 | ```
164 | 小球可以闪烁后,需要比较大小的小球可以交换位置,所以ViewGroup的addView和RemoveView出场
165 | ### addView和removeView实现小位置交换
166 | 为了方便后续扩展,大神首先定义了AnimationsCoordinator的接口,主要定义交换位置,不交换位置,结束排序三个方法:
167 | ```java
168 | /**
169 | * Created by ukhanoff on 2/6/17.
170 | */
171 |
172 | public interface AlgorithmStepsInterface {
173 |
174 | /**
175 | * Visualizes step, when elements should change their places with each other
176 | * 交换位置
177 | * @param position position of the firs element, which should be changed
178 | * @param isBubbleOnFinalPlace set true, when element after swapping is on the right place and his position is final
179 | */
180 | void showSwapStep(int position, boolean isBubbleOnFinalPlace);
181 |
182 | /**
183 | * Visualizes step, when elements should stay on the same places;
184 | * 不交换位置
185 | * @param position position of the firs element
186 | * @param isBubbleOnFinalPlace set true, when element on position+1 is on the right place and his position is final
187 | */
188 | void showNonSwapStep(int position, boolean isBubbleOnFinalPlace);
189 |
190 | /**
191 | * Call when last item was sorted. Notifies user that sorting is finished.
192 | * 结束全部动画,小球将处于最后排序完成后的颜色
193 | */
194 | void showFinish();
195 |
196 | /**
197 | * Cancel all current animations
198 | */
199 | void cancelAllVisualisations();
200 | }
201 | ```
202 | AnimationsCoordinator除了实现AlgorithmStepsInterface接口外,在构造函数引入盛放小球的父容器代码如下:
203 | ```java
204 | public AnimationsCoordinator(ViewGroup bubblesContainer) {
205 | Log.e(TAG, "AnimationsCoordinator");
206 | this.bubblesContainer = bubblesContainer;
207 | }
208 | ```
209 | 实现showSwapStep方法如下:
210 | ```java
211 | @Override
212 | public void showSwapStep(final int position, final boolean isBubbleOnFinalPosition) {
213 | Log.e(TAG, "showSwapStep position:"+position+",isBubbleOnFinalPosition:"+isBubbleOnFinalPosition);
214 | if (bubblesContainer != null && bubblesContainer.getChildCount() > 0 && bubblesContainer.getChildCount() > position + 1) {
215 | final BubbleView tempView = (BubbleView) bubblesContainer.getChildAt(position);
216 | final BubbleView nextTempView = (BubbleView) bubblesContainer.getChildAt(position + 1);
217 | ···
218 | }
219 | ```
220 | 这样便获得了全部的小球,在结合之前的属性动画ValueAnimator,利用ViewGroup的removeView和addView,通过增加子View,移除子View,这样看起来就像是小球实现了移动一样。
221 | ```java
222 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
223 | @Override
224 | public void onAnimationEnd(Animator animation) {//
225 | super.onAnimationEnd(animation);
226 | tempView.setBubbleSelected(false);
227 | tempView.setBubbleIsOnFinalPlace(isBubbleOnFinalPosition);
228 | nextTempView.setBubbleSelected(false);
229 | bubblesContainer.removeView(tempView);
230 | bubblesContainer.addView(tempView, position + 1);
231 |
232 | notifySwapStepAnimationEnd(position);
233 | }
234 | });
235 |
236 | blinkAnimation.start();
237 | ```
238 | 最后还有小球的每次移动都要记录在animationioList,有了小球移动的历史记录,就可以让小球听话的按照冒泡排序法动起来了。
239 | ```java
240 | private ArrayList generateSortScenario(ArrayList unsortedValues) {
241 | Log.e(TAG, "generateSortScenario");
242 | ArrayList values = new ArrayList<>(unsortedValues);
243 | boolean isLastInLoop;
244 | for (int i = 0; i < values.size() - 1; i++) {
245 | for (int j = 0; j < values.size() - i - 1; j++) {
246 | if (j == values.size() - i - 2) {
247 | isLastInLoop = true;
248 | } else {
249 | isLastInLoop = false;
250 | }
251 | if (values.get(j) > values.get(j + 1)) {
252 | swap(values, j);
253 | animationioList.add(new AnimationScenarioItem(true, j, isLastInLoop));
254 | } else {
255 | animationioList.add(new AnimationScenarioItem(false, j, isLastInLoop));
256 | }
257 | }
258 | }
259 | return values;
260 | }
261 | ```
262 | 全部代码请移步[ukhanoff/AndroidSortAnimation](https://github.com/ukhanoff/AndroidSortAnimation)
263 | ## 实现android下归并排序算法动画
264 | 接下来,我来举例讲讲我fork他的项目后,参考JS实现效果,将小球变为长方体,陆续实现七种常见的算法,我这里仅单独举一个实现归并算法的大概步骤,其余排序算法和全部代码请移步[54wall/SortAnimation](https://github.com/54wall/SortAnimation),相对来说,有元素从原数组取出,重新组成新的一组,相对有些难度:
265 | 首先定义归并算法动画控制类MergeStepsInterface,根据归并算法的定义,归并的主要步骤如下:从原数组中选择元素,按照从小到大(或者从大到小)组成新数组,再将新生成的从小到大的数组重新合并到原数组中,所以接口如下:
266 |
267 | ```java
268 |
269 | package pri.weiqiang.sortanimation.animation;
270 |
271 | /**
272 | * Created by weiqiang
273 | */
274 |
275 | public interface MergeStepsInterface {
276 |
277 | /**
278 | * 从原数组中选择元素组成新数组,顺序为从小到大
279 | *
280 | * @param originalPosition 在原数组中的位置
281 | * @param tempPosition 在新生成的数组中的位置
282 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
283 | */
284 | void createTempView(int originalPosition, int tempPosition, boolean isMerge);
285 |
286 | /**
287 | * 将新生成的从小到大的数组重新合并到原数组中去
288 | *
289 | * @param originalPosition 在原数组中的位置
290 | * @param tempPosition 在新生成的数组中的位置
291 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
292 | */
293 | void mergeOriginalView(int originalPosition, int tempPosition, boolean isMerge);
294 |
295 | /**
296 | * Call when last item was sorted. Notifies user that sorting is finished.
297 | */
298 | void showFinish();
299 |
300 | /**
301 | * Cancel all current animations
302 | */
303 | void cancelAllVisualisations();
304 | }
305 |
306 | ```
307 | MergeAnimationsCoordinator实现MergeStepsInterface接口,因为归并需要两个ViewGroup来容纳新生成的数组,所以相应的构造函数要做出改变;
308 | ```java
309 | public MergeAnimationsCoordinator(Context context, ViewGroup originalContainer, ViewGroup tempContainer) {
310 | Log.e(TAG, "MergeAnimationsCoordinator");
311 | this.context = context;
312 | this.originalContainer = originalContainer;
313 | this.tempContainer = tempContainer;
314 | }
315 | ```
316 | 而相应的createTempView和mergeOriginalView方法分别如下:
317 | ```java
318 | /**
319 | * 从原数组拿取元素,按大小添加下方的新矩形数列中
320 | *
321 | * @param originalPosition 在原数组中的位置
322 | * @param tempPosition 在新生成的数组中的位置
323 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
324 | */
325 | @Override
326 | public void createTempView(final int originalPosition, final int tempPosition, final boolean isMerge) {
327 |
328 | final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
329 | int marginInPx = Util.dpToPx(context, SortFragment.RECT_MARGIN);
330 | lp.setMargins(0, 0, marginInPx, 0);
331 |
332 | if (originalContainer != null && originalContainer.getChildCount() > 0 && originalContainer.getChildCount() > tempPosition) {
333 | final RectView originalView = (RectView) originalContainer.getChildAt(originalPosition);
334 | //BLINKING
335 | blinkAnimation = ValueAnimator.ofInt(0, 5);
336 | blinkAnimation.setDuration(1500);
337 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
338 | @Override
339 | public void onAnimationUpdate(ValueAnimator animation) {
340 |
341 | int value = (Integer) animation.getAnimatedValue();
342 | if (value % 2 == 0) {
343 | originalView.setSelected(false);
344 | } else {
345 | originalView.setSelected(true);
346 | }
347 | }
348 | });
349 |
350 |
351 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
352 | @Override
353 | public void onAnimationEnd(Animator animation) {
354 | Log.e(TAG, "生成临时矩形!");
355 | super.onAnimationEnd(animation);
356 | originalView.setSelected(false);
357 | originalView.setIsOnFinalPlace(isMerge);
358 | originalContainer.removeView(originalView);
359 | int tempNumber = originalView.getNumber();
360 | originalView.setMinimumHeight(1);
361 | originalView.setImageBitmap(createSpaceBitmap(SortFragment.mRectWidth));
362 | originalView.setNumber(1);
363 | originalContainer.addView(originalView, originalPosition, lp);
364 |
365 | RectView tempRectView = new RectView(context);
366 | tempRectView.setImageBitmap(createCalculatedBitmap(SortFragment.mRectWidth, tempNumber));
367 | tempRectView.setNumber(tempNumber);
368 | tempContainer.addView(tempRectView, tempPosition, lp);
369 | notifySwapStepAnimationEnd(originalPosition);
370 | }
371 | });
372 |
373 | blinkAnimation.start();
374 | }
375 | }
376 |
377 | /**
378 | * 将下列排序好的矩形按顺序填回到原矩形序列
379 | *
380 | * @param originalPosition 在原数组中的位置
381 | * @param tempPosition 在新生成的数组中的位置
382 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
383 | */
384 | @Override
385 | public void mergeOriginalView(final int originalPosition, final int tempPosition, final boolean isMerge) {
386 |
387 | final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
388 | int marginInPx = Util.dpToPx(context, SortFragment.RECT_MARGIN);
389 | lp.setMargins(0, 0, marginInPx, 0);
390 | if (originalContainer != null && originalContainer.getChildCount() > 0 && originalContainer.getChildCount() > tempPosition) {
391 | final RectView originalView = (RectView) originalContainer.getChildAt(originalPosition);
392 | final RectView tempRectView = (RectView) tempContainer.getChildAt(tempPosition);
393 | //BLINKING
394 | blinkAnimation = ValueAnimator.ofInt(0, 6);
395 | blinkAnimation.setDuration(1200);
396 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
397 | @Override
398 | public void onAnimationUpdate(ValueAnimator animation) {
399 | int value = (Integer) animation.getAnimatedValue();
400 | if (value % 2 == 0) {
401 | originalView.setSelected(false);
402 | tempRectView.setSelected(false);
403 | } else {
404 | originalView.setSelected(true);
405 | tempRectView.setSelected(true);
406 | }
407 | }
408 | });
409 |
410 | blinkAnimation.start();
411 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
412 | @Override
413 | public void onAnimationEnd(Animator animation) {
414 | super.onAnimationEnd(animation);
415 | originalView.setSelected(false);
416 | tempRectView.setSelected(false);
417 | tempRectView.setIsOnFinalPlace(isMerge);
418 | tempContainer.removeView(tempRectView);
419 | int tempNumber = tempRectView.getNumber();
420 |
421 | tempRectView.setMinimumHeight(1);
422 | tempRectView.setImageBitmap(createSpaceBitmap(SortFragment.mRectWidth));
423 | tempRectView.setNumber(1);
424 | // 不能设置矩形不可见,还是会报The specified child already has a parent. You must call removeView() on the child's parent first.
425 | // tempRectView.setVisibility(View.INVISIBLE);
426 | tempContainer.addView(tempRectView, tempPosition, lp);
427 |
428 |
429 | originalContainer.removeView(originalView);
430 | RectView originalView = new RectView(context);
431 | originalView.setImageBitmap(createCalculatedBitmap(SortFragment.mRectWidth, tempNumber));
432 | originalView.setNumber(tempNumber);
433 | originalContainer.addView(originalView, originalPosition, lp);
434 | notifySwapStepAnimationEnd(originalPosition);
435 | }
436 | });
437 | }
438 | }
439 | ```
440 | 我这里为了保证建立好的ViewGroup中移除的后产生的空白,使用了高度为1的长方体占位来实现,这里特别说明一下。
441 | ### 记录归并算法每次比较元素
442 | 这个还是有些难度的,我基本是靠试错,试出来。代码如下:
443 | ```java
444 | // 归并算法 https://www.cnblogs.com/of-fanruice/p/7678801.html
445 | public static void mergeSort(ArrayList unsortedValues, int low, int high, ArrayList mergeAnimationioList) {
446 | Log.e(TAG, "归并排序! mergeSort");
447 | int mid = (low + high) / 2;
448 | if (low < high) {
449 | mergeSort(unsortedValues, low, mid, mergeAnimationioList);
450 | mergeSort(unsortedValues, mid + 1, high, mergeAnimationioList);
451 | // 左右归并
452 | merge(unsortedValues, low, mid, high, mergeAnimationioList);
453 | }
454 | }
455 |
456 | private static void merge(ArrayList unsortedValues, int low, int mid, int high, ArrayList mergeAnimationioList) {
457 | ArrayList temp = new ArrayList<>();
458 | int i = low;
459 | int j = mid + 1;
460 | int k = 0;
461 | // 把较小的数先移到新数组中
462 | Log.e(TAG, "开始拆分");
463 | while (i <= mid && j <= high) {
464 | Log.e(TAG, "子归并merge i:" + i + ":" + unsortedValues.get(i) + ",j:" + j + ":" + unsortedValues.get(j) + ",mid:" + mid);
465 | if (unsortedValues.get(i) < unsortedValues.get(j)) {
466 | //选择原始数组中的较小值直接移动到新数组的最末位
467 | mergeAnimationioList.add(new MergeAnimationScenarioItem(i, k, false));
468 | temp.add(k++, unsortedValues.get(i++));
469 | } else {
470 | mergeAnimationioList.add(new MergeAnimationScenarioItem(j, k, false));
471 | temp.add(k++, unsortedValues.get(j++));
472 | }
473 | }
474 | // i<=mid是剩余全部中的较小的,把左边剩余的数移入数组
475 | while (i <= mid) {
476 | mergeAnimationioList.add(new MergeAnimationScenarioItem(i, k, false));
477 | temp.add(k++, unsortedValues.get(i++));
478 | }
479 | // j <= high是剩余全部中的大的,把右边边剩余的数移入数组,所以在while (i <= mid) 执行
480 | while (j <= high) {
481 | mergeAnimationioList.add(new MergeAnimationScenarioItem(j, k, false));
482 | temp.add(k++, unsortedValues.get(j++));
483 | }
484 | // 把新数组中的数覆盖nums数组
485 | Log.e(TAG, "合并开始");
486 | for (int x = 0; x < temp.size(); x++) {
487 | unsortedValues.set(x + low, temp.get(x));
488 | //返回原始数组
489 | mergeAnimationioList.add(new MergeAnimationScenarioItem(x + low, x, true));
490 | }
491 | }
492 | ```
493 |
494 | ### 自定义长方体RectView
495 | 略。详情[54wall/SortAnimation](https://github.com/54wall/SortAnimation)
496 |
497 |
498 |
499 | ## Forked & Thanks
500 |
501 | - [ukhanoff/AndroidSortAnimation](https://github.com/ukhanoff/AndroidSortAnimation)
502 | - [liusaint/sortAnimation](https://github.com/liusaint/sortAnimation)
503 | - [在线动画演示各种排序算法过程 - aTool在线工具](http://www.atool.org/sort.php)
504 |
505 |
506 | ### 感谢浏览,喜欢请赏star。
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /weiqiang
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | def releaseTime() {
4 | return new Date().format("yyyyMMdd_hhmmss", TimeZone.getTimeZone("GMT+8"))
5 | }
6 |
7 | android {
8 | compileSdkVersion 28
9 | defaultConfig {
10 | applicationId "pri.weiqiang.sortanimation"
11 | minSdkVersion 16
12 | targetSdkVersion 28
13 | versionCode 1
14 | versionName "1.0"
15 | flavorDimensions "versionCode"
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | //签名配置
20 | /* signingConfigs {
21 | debug {
22 | keyAlias "android"
23 | keyPassword "android"
24 | storeFile file("D:/IDE/MyKeystore/54wallKeystore.jks")
25 | storePassword "android"
26 | }
27 | release {
28 | keyAlias "android"
29 | keyPassword "android"
30 | storeFile file("D:/IDE/MyKeystore/54wallKeystore.jks")
31 | storePassword "android"
32 | }
33 | }*/
34 |
35 | signingConfigs {
36 |
37 | release {
38 | keyAlias 'androiddebugkey'
39 | keyPassword 'android'
40 | storeFile file('D:\\Develop\\keystore\\debug.keystore')
41 | storePassword 'android'
42 | }
43 | debug {
44 | keyAlias 'androiddebugkey'
45 | keyPassword 'android'
46 | storeFile file('D:\\Develop\\keystore\\debug.keystore')
47 | storePassword 'android'
48 | }
49 | }
50 |
51 |
52 | buildTypes {
53 |
54 | debug {
55 | // 显示Log
56 | buildConfigField "boolean", "LOG_DEBUG", "true"
57 | //混淆
58 | minifyEnabled true
59 | //Zipalign优化
60 | zipAlignEnabled true
61 | // 移除无用的resource文件
62 | shrinkResources true
63 | //加载默认混淆配置文件
64 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
65 | //签名
66 | signingConfig signingConfigs.debug
67 | }
68 | release {
69 | // 不显示Log
70 | buildConfigField "boolean", "LOG_DEBUG", "false"
71 | //混淆
72 | minifyEnabled true
73 | //Zipalign优化
74 | zipAlignEnabled true
75 | // 移除无用的resource文件
76 | shrinkResources true
77 | //加载默认混淆配置文件
78 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
79 | //签名
80 | signingConfig signingConfigs.release
81 | }
82 |
83 | }
84 |
85 | applicationVariants.all { variant ->
86 | variant.outputs.all {
87 | outputFileName = "${releaseTime()}_Sort_${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk"
88 | }
89 | }
90 |
91 | productFlavors {
92 |
93 | weiqiang {}
94 | productFlavors.all { flavor ->
95 | flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
96 | }
97 |
98 | }
99 | }
100 |
101 | dependencies {
102 | implementation fileTree(dir: 'libs', include: ['*.jar'])
103 | implementation 'androidx.appcompat:appcompat:1.0.2'
104 | implementation "io.noties.markwon:core:4.2.0"
105 | }
106 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/algorithm/Sort.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.algorithm;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * 数组的排序,用于理解各个排序,java debug运行可以查看变量变化
7 | */
8 | public class Sort {
9 | static int[] a8 = {40, 60, 30, 80, 50, 20, 90, 20, 10, 70};
10 | static int[] a7 = {3, 2, 1};
11 | static int[] a5 = {22, 3, 2, 1, 0, 5, 4};
12 | static int[] a4 = {22, 3, 2, 1, 0, 5, 4};
13 | static int[] a3 = {22, 3, 2, 1, 0, 5, 4};
14 | static int[] a = {2, 3, 6, 11};
15 | static int[] b = {1, 4, 8, 9};
16 | static int[] c = new int[a.length + b.length];
17 | static int[] a1 = {2, 3, 6, 11, 2, 3, 4};
18 | static int[] a6 = {5, 4, 3, 1, 0, 2, 6};
19 |
20 | // 直接在Debug下查看数组变化,特别容易理解
21 | public static void main(String[] args) {
22 |
23 | // quickSort(a6, 0, a6.length - 1);
24 | // memeryArray(a, a.length, b, b.length, c);
25 | // sort(a1, 0, a1.length-1);
26 | // selectSort(a3);
27 | // pubbleSort(a4);
28 | // InsertSort(a5);
29 | HeerSort(a7);
30 | // shellSort(a7);
31 | // sort(a7);
32 | // HeapSort.sortHeap();
33 | //查找
34 | // binarySearch(a8);
35 | System.out.println("Finished……");
36 | }
37 |
38 |
39 | private static void binarySearch(int[] a) {
40 |
41 | System.out.println("二分法查找");
42 | int target = 80;
43 | Arrays.sort(a);
44 | boolean result = false;
45 | int min = 0;
46 | int b = 0;
47 | int max = a.length - 1;
48 | while (min <= max) {
49 | b = (min + max) / 2;
50 | if (target > a[b]) {
51 | min = b + 1;
52 | }
53 | if (target < a[b]) {
54 | max = b - 1;
55 | }
56 | if (target == a[b]) {
57 | min++;
58 | result = true;
59 |
60 | }
61 |
62 | }
63 | System.out.println("result:" + result + ",b:" + b);
64 | }
65 |
66 | //正确 https://www.cnblogs.com/zengzhihua/p/4456734.html
67 | public static void shellSort(int[] a) {
68 | System.out.println("希尔排序");
69 | int n = a.length;
70 | int d = n / 2;
71 | while (d > 0) {
72 | for (int i = d; i < n; i++) {
73 | int j = i - d;
74 | while (j >= 0 && a[j] > a[j + d]) {
75 | int tmp = a[j];
76 | a[j] = a[j + d];
77 | a[j + d] = tmp;
78 | j = j - d;
79 | }
80 | }
81 | d = d / 2;
82 | }
83 | for (int o : a) {
84 | System.out.print(o + ",");
85 | }
86 | }
87 |
88 | // static int[] a7 = {3, 2, 5, 4,1}; 错误 排序后的结果为2 1 3 4 5
89 | // 希尔排序 https://blog.csdn.net/csdn_aiyang/article/details/73108606
90 | @Deprecated
91 | private static void HeerSort(int[] a) {
92 | System.out.println("希尔排序");
93 | int d = a.length / 2;
94 | //判断是否为基数 a%2!=0
95 | if ((a.length & 1) == 1) {
96 | System.out.println("数组大小为奇数 间隔需要+1 否则循环将少一次 排序将错误");
97 | d++;
98 | }
99 | while (true) {
100 | for (int i = 0; i < d; i++) {
101 | for (int j = i; j + d < a.length; j += d) {
102 | int temp;
103 | if (a[j] > a[j + d]) {
104 | temp = a[j];
105 | a[j] = a[j + d];
106 | a[j + d] = temp;
107 | }
108 | }
109 | }
110 |
111 | if (d == 1) {
112 | break;
113 | }
114 | d--;
115 | }
116 | for (int o : a) {
117 | System.out.print(o + ",");
118 | }
119 |
120 | }
121 |
122 |
123 | //正确 https://www.cnblogs.com/LeslieXia/p/5814571.html
124 | public static void sort(int[] arr) {
125 | System.out.println("希尔排序 new");
126 | // i表示希尔排序中的第n/2+1个元素(或者n/4+1)
127 | // j表示希尔排序中从0到n/2的元素(n/4)
128 | // r表示希尔排序中n/2+1或者n/4+1的值
129 | int i, j, r, tmp;
130 | // 划组排序
131 | for (r = arr.length / 2; r >= 1; r = r / 2) {
132 | for (i = r; i < arr.length; i++) {
133 | tmp = arr[i];
134 | j = i - r;
135 | // 一轮排序
136 | while (j >= 0 && tmp < arr[j]) {
137 | arr[j + r] = arr[j];
138 | j -= r;
139 | }
140 | arr[j + r] = tmp;
141 | }
142 | System.out.println(i + ":" + Arrays.toString(arr));
143 | }
144 | }
145 |
146 | // 插入排序 https://blog.csdn.net/csdn_aiyang/article/details/73108606
147 | private static void InsertSort(int[] a) {
148 | System.out.println("插入排序");
149 | long t1 = System.nanoTime();
150 | // 直接插入排序
151 | for (int i = 1; i < a.length; i++) {
152 | // 待插入元素
153 | int temp = a[i];
154 | int j;
155 | for (j = i - 1; j >= 0; j--) {
156 | // 将大于temp的往后移动一位
157 | if (a[j] > temp) {
158 | a[j + 1] = a[j];
159 | } else {
160 | break;
161 | }
162 | }
163 | a[j + 1] = temp;// 插入进来
164 | }
165 | for (int q = 0; q < a.length; q++) {
166 | System.out.print(a[q] + ",");
167 | }
168 | }
169 |
170 | // 冒泡 https://blog.csdn.net/csdn_aiyang/article/details/73108606
171 | private static void pubbleSort(int[] numbers) {
172 | System.out.println("冒泡排序");
173 | int temp;// 记录临时变量
174 | int size = numbers.length;// 数组大小
175 | for (int i = 0; i < size - 1; i++) {
176 | for (int j = i + 1; j < size; j++) {// 索引不同的两层for循环
177 | if (numbers[i] < numbers[j]) {// 交互数据从大到小排列顺序 大的放前面
178 | temp = numbers[i];
179 | numbers[i] = numbers[j];
180 | numbers[j] = temp;
181 | }
182 | }
183 | }
184 | for (int q = 0; q < numbers.length; q++) {
185 | System.out.print(numbers[q] + ",");
186 | }
187 | }
188 |
189 | // 堆排序代码实现 https://www.cnblogs.com/Java3y/p/8639937.html
190 |
191 | // 选择排序 https://blog.csdn.net/csdn_aiyang/article/details/73108606
192 | public static void selectSort(int[] array) {
193 | System.out.println("选择排序");
194 | int min;
195 | int tmp = 0;
196 | for (int i = 0; i < array.length; i++) {
197 | min = array[i];
198 | for (int j = i; j < array.length; j++) {
199 | if (array[j] < min) {
200 | min = array[j];// 最小值
201 | tmp = array[i];
202 | array[i] = min;
203 | array[j] = tmp;
204 | }
205 | }
206 | }
207 | for (int q = 0; q < array.length; q++) {
208 | System.out.print(array[q] + ",");
209 | }
210 |
211 | }
212 |
213 | // 将有序数组a[]和b[]合并到c[]中
214 | public static void memeryArray(int a[], int n, int b[], int m, int c[]) {
215 | int i, j, k;
216 |
217 | i = j = k = 0;
218 | while (i < n && j < m) {
219 | if (a[i] < b[j])
220 | c[k++] = a[i++];
221 | else
222 | c[k++] = b[j++];
223 | }
224 | for (int q = 0; q < c.length; q++) {
225 | System.out.print(c[q] + ",");
226 | }
227 | System.out.println();
228 | // 可能会有一组数列没有完全遍历到,因为上边循环是&&
229 | while (i < n)
230 | c[k++] = a[i++];
231 |
232 | for (int q = 0; q < c.length; q++) {
233 | System.out.print(c[q] + ",");
234 | }
235 | System.out.println();
236 |
237 | while (j < m)
238 | c[k++] = b[j++];
239 |
240 | for (int q = 0; q < c.length; q++) {
241 | System.out.print(c[q] + ",");
242 | }
243 | }
244 |
245 | // 归并算法 https://www.cnblogs.com/of-fanruice/p/7678801.html
246 | public static int[] sort(int[] a, int low, int high) {
247 | System.out.println("归并排序!");
248 | int mid = (low + high) / 2;
249 | if (low < high) {
250 | sort(a, low, mid);
251 | sort(a, mid + 1, high);
252 | // 左右归并
253 | merge(a, low, mid, high);
254 | }
255 | return a;
256 | }
257 |
258 | public static void merge(int[] a, int low, int mid, int high) {
259 | int[] temp = new int[high - low + 1];
260 | int i = low;
261 | int j = mid + 1;
262 | int k = 0;
263 | // 把较小的数先移到新数组中
264 | while (i <= mid && j <= high) {
265 | if (a[i] < a[j]) {
266 | temp[k++] = a[i++];
267 | } else {
268 | temp[k++] = a[j++];
269 | }
270 | }
271 | // 把左边剩余的数移入数组
272 | while (i <= mid) {
273 | temp[k++] = a[i++];
274 | }
275 | // 把右边边剩余的数移入数组
276 | while (j <= high) {
277 | temp[k++] = a[j++];
278 | }
279 | // 把新数组中的数覆盖nums数组
280 | for (int x = 0; x < temp.length; x++) {
281 | a[x + low] = temp[x];
282 | }
283 | for (int q = 0; q < a.length; q++) {
284 | System.out.print(a[q] + ",");
285 | }
286 | System.out.println();
287 | }
288 |
289 | // 快速排序
290 | public static void quickSort(int[] a, int low, int high) {
291 | int start = low;
292 | int end = high;
293 | int key = a[low];
294 |
295 | while (end > start) {
296 |
297 | // 从后往前比较// 如果没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后又从前往后比较
298 | while (end > start && a[end] >= key)
299 | end--;
300 | if (a[end] <= key) {
301 | int temp = a[end];
302 | a[end] = a[start];
303 | a[start] = temp;
304 | System.out.println("key:" + key + ",从后往前比较 end:" + end + ",a[end]:" + a[end] + ",start:" + start + ",a[start]:" + a[start]);
305 | System.out.println("---start:" + start);
306 | }
307 | // 从前往后比较// 如果没有比关键值大的,比较下一个,直到有比关键值大的交换位置
308 | while (end > start && a[start] <= key)
309 | start++;
310 | if (a[start] >= key) {
311 | int temp = a[start];
312 | a[start] = a[end];
313 | a[end] = temp;
314 | System.out.println("key:" + key + ",从前往后比较 end:" + end + ",a[end]:" + a[end] + ",start:" + start + ",a[start]:" + a[start]);
315 | System.out.println("---end:" + end);
316 | }
317 | // 此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用
318 | }
319 | StringBuilder sb = new StringBuilder("a6 =");
320 | for (int i = 0; i < a6.length; i++) {
321 | sb.append(a6[i] + ",");
322 |
323 | }
324 | System.out.println("sb:" + sb.toString());
325 | // 递归
326 | if (start > low) {
327 | System.out.println("**************************************************");
328 | System.out.println("迭代******start > low!" + ",start:" + start + ",low:" + low);
329 | quickSort(a, low, start - 1);// 左边序列。第一个索引位置到关键值索引-1
330 | }
331 | if (end < high) {
332 | System.out.println("**************************************************");
333 | System.out.println("迭代*******end < high!" + ",end:" + end + ",high:" + high);
334 | quickSort(a, end + 1, high);// 右边序列。从关键值索引+1到最后一个
335 | }
336 | }
337 |
338 | /**
339 | * 堆排序:Java
340 | *
341 | * @author skywang
342 | * @date 2014/03/11
343 | */
344 |
345 | public static class HeapSort {
346 |
347 | /*
348 | * (最大)堆的向下调整算法
349 | *
350 | * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
351 | * 其中,N为数组下标索引值,如数组中第1个数对应的N为0。
352 | *
353 | * 参数说明:
354 | * a -- 待排序的数组
355 | * start -- 被下调节点的起始位置(一般为0,表示从第1个开始)
356 | * end -- 截至范围(一般为数组中最后一个元素的索引)
357 | */
358 | public static void maxHeapDown(int[] a, int start, int end) {
359 | int c = start; // 当前(current)节点的位置
360 | int l = 2 * c + 1; // 左(left)孩子的位置
361 | int tmp = a[c]; // 当前(current)节点的大小
362 |
363 | for (; l <= end; c = l, l = 2 * l + 1) {
364 | // "l"是左孩子,"l+1"是右孩子
365 | if (l < end && a[l] < a[l + 1])
366 | l++; // 左右两孩子中选择较大者,即m_heap[l+1]
367 | if (tmp >= a[l])
368 | break; // 调整结束
369 | else { // 交换值
370 | a[c] = a[l];
371 | a[l] = tmp;
372 | }
373 | }
374 |
375 | System.out.printf("(最大)二叉堆\n");
376 | for (int i = 0; i < a.length; i++)
377 | System.out.printf("%d ", a[i]);
378 | System.out.printf("\n");
379 | }
380 |
381 | /*
382 | * 堆排序(从小到大)
383 | *
384 | * 参数说明:
385 | * a -- 待排序的数组
386 | * n -- 数组的长度
387 | */
388 | public static void heapSortAsc(int[] a, int n) {
389 | int i, tmp;
390 |
391 | // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
392 | for (i = n / 2 - 1; i >= 0; i--)
393 | maxHeapDown(a, i, n - 1);
394 |
395 |
396 | // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
397 | for (i = n - 1; i > 0; i--) {
398 | // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
399 | tmp = a[0];
400 | a[0] = a[i];
401 | a[i] = tmp;
402 | // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
403 | // 即,保证a[i-1]是a[0...i-1]中的最大值。
404 | maxHeapDown(a, 0, i - 1);
405 | }
406 | }
407 |
408 | /*
409 | * (最小)堆的向下调整算法
410 | *
411 | * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
412 | * 其中,N为数组下标索引值,如数组中第1个数对应的N为0。
413 | *
414 | * 参数说明:
415 | * a -- 待排序的数组
416 | * start -- 被下调节点的起始位置(一般为0,表示从第1个开始)
417 | * end -- 截至范围(一般为数组中最后一个元素的索引)
418 | */
419 | public static void minHeapDown(int[] a, int start, int end) {
420 | int c = start; // 当前(current)节点的位置
421 | int l = 2 * c + 1; // 左(left)孩子的位置
422 | int tmp = a[c]; // 当前(current)节点的大小
423 |
424 | for (; l <= end; c = l, l = 2 * l + 1) {
425 | // "l"是左孩子,"l+1"是右孩子
426 | if (l < end && a[l] > a[l + 1])
427 | l++; // 左右两孩子中选择较小者
428 | if (tmp <= a[l])
429 | break; // 调整结束
430 | else { // 交换值
431 | a[c] = a[l];
432 | a[l] = tmp;
433 | }
434 | }
435 | }
436 |
437 | /*
438 | * 堆排序(从大到小)
439 | *
440 | * 参数说明:
441 | * a -- 待排序的数组
442 | * n -- 数组的长度
443 | */
444 | public static void heapSortDesc(int[] a, int n) {
445 | int i, tmp;
446 |
447 | // 从(n/2-1) --> 0逐次遍历每。遍历之后,得到的数组实际上是一个最小堆。
448 | for (i = n / 2 - 1; i >= 0; i--)
449 | minHeapDown(a, i, n - 1);
450 |
451 | // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
452 | for (i = n - 1; i > 0; i--) {
453 | // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最小的。
454 | tmp = a[0];
455 | a[0] = a[i];
456 | a[i] = tmp;
457 | // 调整a[0...i-1],使得a[0...i-1]仍然是一个最小堆。
458 | // 即,保证a[i-1]是a[0...i-1]中的最小值。
459 | minHeapDown(a, 0, i - 1);
460 | }
461 | }
462 |
463 | public static void sortHeap() {
464 | System.out.println("堆排序");
465 | int i;
466 | int a[] = {1, 2, 4, 3, 0};
467 |
468 | System.out.printf("before sort:");
469 | for (i = 0; i < a.length; i++)
470 | System.out.printf("%d ", a[i]);
471 | System.out.printf("\n");
472 |
473 | heapSortAsc(a, a.length); // 升序排列
474 | // heapSortDesc(a, a.length); // 降序排列
475 |
476 | System.out.printf("after sort:");
477 | for (i = 0; i < a.length; i++)
478 | System.out.printf("%d ", a[i]);
479 | System.out.printf("\n");
480 | }
481 | }
482 |
483 | }
484 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/algorithm/SortArrayList.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.algorithm;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.ArrayList;
6 |
7 | import pri.weiqiang.sortanimation.animation.AnimationScenarioItem;
8 | import pri.weiqiang.sortanimation.animation.MergeAnimationScenarioItem;
9 |
10 | public class SortArrayList {
11 |
12 | private static String TAG = SortArrayList.class.getSimpleName();
13 |
14 | // 冒泡 https://blog.csdn.net/csdn_aiyang/article/details/73108606
15 | public static void pubbleSort(ArrayList unsortedValues, ArrayList animationioList) {
16 | Log.e(TAG, "冒泡排序");
17 | Integer temp;// 记录临时变量
18 | boolean isLastInLoop;
19 | int size = unsortedValues.size();
20 | for (int i = 0; i < size - 1; i++) {
21 | for (int j = 0; j < size - i - 1; j++) {
22 | isLastInLoop = (j == unsortedValues.size() - i - 2);
23 | if (unsortedValues.get(j + 1) < unsortedValues.get(j)) {
24 | temp = unsortedValues.get(j);
25 | unsortedValues.set(j, unsortedValues.get(j + 1));
26 | unsortedValues.set(j + 1, temp);
27 | animationioList.add(new AnimationScenarioItem(true, j, j + 1, isLastInLoop));
28 | } else {
29 | animationioList.add(new AnimationScenarioItem(false, j, j + 1, isLastInLoop));
30 | }
31 | }
32 | }
33 |
34 | }
35 |
36 | // 插入排序 https://blog.csdn.net/csdn_aiyang/article/details/73108606
37 | public static void insertSort(ArrayList unsortedValues, ArrayList animationioList) {
38 | Log.e(TAG, "插入排序!");
39 | // 直接插入排序
40 | for (int i = 1; i < unsortedValues.size(); i++) {
41 | // 待插入元素
42 | int temp = unsortedValues.get(i);
43 | int j;
44 | for (j = i - 1; j >= 0; j--) {
45 | // 将大于temp的往后移动一位
46 | if (unsortedValues.get(j) > temp) {
47 | unsortedValues.set(j + 1, unsortedValues.get(j));
48 | //插入排序,直到最后排序完成后,才会得知是最后位置
49 | animationioList.add(new AnimationScenarioItem(true, j, j + 1, false));
50 | } else {
51 | animationioList.add(new AnimationScenarioItem(false, j, j + 1, false));
52 | break;
53 |
54 | }
55 | }
56 | //务必全部是j+1,因为j最后为-1,
57 | unsortedValues.set(j + 1, temp);// 插入进来
58 | Log.e(TAG, "168 AnimationScenarioItem j:" + j + "temp:" + temp);
59 | }
60 | }
61 |
62 |
63 | // 选择排序 https://blog.csdn.net/csdn_aiyang/article/details/73108606
64 | public static void selectSort(ArrayList unsortedValues, ArrayList animationioList) {
65 | Log.e(TAG, "选择排序");
66 | int min;
67 | int tmp;
68 | for (int i = 0; i < unsortedValues.size(); i++) {
69 | min = unsortedValues.get(i);
70 | for (int j = i + 1; j < unsortedValues.size(); j++) {
71 | if (unsortedValues.get(j) < min) {
72 | min = unsortedValues.get(j);// 最小值
73 | tmp = unsortedValues.get(i);
74 | unsortedValues.set(i, min);
75 | unsortedValues.set(j, tmp);
76 | animationioList.add(new AnimationScenarioItem(true, i, j, false));
77 | } else {
78 | animationioList.add(new AnimationScenarioItem(false, i, j, false));
79 | }
80 | }
81 | //AnimationScenarioItem中的isFinalPlace是针对互换位置后的后边那个项目来讲,如果不增加此处代码,必须更改AnimationScenarioItem
82 | animationioList.add(new AnimationScenarioItem(false, i, i, true));
83 | }
84 | }
85 |
86 | // 快速排序
87 | public static void quickSort(ArrayList unsortedValues, int low, int high, ArrayList animationioList, ArrayList keyList) {
88 |
89 | int start = low;
90 | int end = high;
91 | int key = unsortedValues.get(start);
92 | while (end > start) {
93 |
94 | // 从后往前比较,如果没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后又从前往后比较
95 | while (end > start && unsortedValues.get(end) >= key) {
96 | end--;
97 | animationioList.add(new AnimationScenarioItem(false, start, end, false));
98 | keyList.add(key);
99 | }
100 | if (unsortedValues.get(end) <= key) {
101 | int temp = unsortedValues.get(end);
102 | unsortedValues.set(end, unsortedValues.get(start));
103 | unsortedValues.set(start, temp);
104 | animationioList.add(new AnimationScenarioItem(true, start, end, false));
105 | keyList.add(key);
106 | }
107 | // 从前往后比较,如果没有比关键值大的,比较下一个,直到有比关键值大的交换位置
108 | while (end > start && unsortedValues.get(start) <= key) {
109 | start++;
110 | animationioList.add(new AnimationScenarioItem(false, start, end, false));
111 | keyList.add(key);
112 | }
113 | if (unsortedValues.get(start) >= key) {
114 | int temp = unsortedValues.get(start);
115 | unsortedValues.set(start, unsortedValues.get(end));
116 | unsortedValues.set(end, temp);
117 | animationioList.add(new AnimationScenarioItem(true, start, end, false));
118 | keyList.add(key);
119 | }
120 | // 此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用
121 | }
122 |
123 | // 递归
124 | if (start > low) {
125 | quickSort(unsortedValues, low, start - 1, animationioList, keyList);// 左边序列,第一个索引位置到关键值索引-1
126 | }
127 | if (end < high) {
128 | quickSort(unsortedValues, end + 1, high, animationioList, keyList);// 右边序列,从关键值索引+1到最后一个
129 | }
130 | }
131 |
132 |
133 | // 归并算法 https://www.cnblogs.com/of-fanruice/p/7678801.html
134 | public static void mergeSort(ArrayList unsortedValues, int low, int high, ArrayList mergeAnimationioList) {
135 | Log.e(TAG, "归并排序! mergeSort");
136 | int mid = (low + high) / 2;
137 | if (low < high) {
138 | mergeSort(unsortedValues, low, mid, mergeAnimationioList);
139 | mergeSort(unsortedValues, mid + 1, high, mergeAnimationioList);
140 | // 左右归并
141 | merge(unsortedValues, low, mid, high, mergeAnimationioList);
142 | }
143 | }
144 |
145 | private static void merge(ArrayList unsortedValues, int low, int mid, int high, ArrayList mergeAnimationioList) {
146 | ArrayList temp = new ArrayList<>();
147 | int i = low;
148 | int j = mid + 1;
149 | int k = 0;
150 | // 把较小的数先移到新数组中
151 | Log.e(TAG, "开始拆分");
152 | while (i <= mid && j <= high) {
153 | Log.e(TAG, "子归并merge i:" + i + ":" + unsortedValues.get(i) + ",j:" + j + ":" + unsortedValues.get(j) + ",mid:" + mid);
154 | if (unsortedValues.get(i) < unsortedValues.get(j)) {
155 | //选择原始数组中的较小值直接移动到新数组的最末位
156 | mergeAnimationioList.add(new MergeAnimationScenarioItem(i, k, false));
157 | temp.add(k++, unsortedValues.get(i++));
158 | } else {
159 | mergeAnimationioList.add(new MergeAnimationScenarioItem(j, k, false));
160 | temp.add(k++, unsortedValues.get(j++));
161 | }
162 | }
163 | // i<=mid是剩余全部中的较小的,把左边剩余的数移入数组
164 | while (i <= mid) {
165 | mergeAnimationioList.add(new MergeAnimationScenarioItem(i, k, false));
166 | temp.add(k++, unsortedValues.get(i++));
167 | }
168 | // j <= high是剩余全部中的大的,把右边边剩余的数移入数组,所以在while (i <= mid) 执行
169 | while (j <= high) {
170 | mergeAnimationioList.add(new MergeAnimationScenarioItem(j, k, false));
171 | temp.add(k++, unsortedValues.get(j++));
172 | }
173 | // 把新数组中的数覆盖nums数组
174 | Log.e(TAG, "合并开始");
175 | for (int x = 0; x < temp.size(); x++) {
176 | unsortedValues.set(x + low, temp.get(x));
177 | //返回原始数组
178 | mergeAnimationioList.add(new MergeAnimationScenarioItem(x + low, x, true));
179 | }
180 | }
181 |
182 | //正确 https://www.cnblogs.com/LeslieXia/p/5814571.html
183 | public static void heerSortTrue(ArrayList unsortedValues, ArrayList animationioList) {
184 | System.out.println("希尔排序 new");
185 | // i表示希尔排序中的第n/2+1个元素(或者n/4+1)
186 | // j表示希尔排序中从0到n/2的元素(n/4)
187 | // r表示希尔排序中n/2+1或者n/4+1的值
188 | int i, j, r, tmp;
189 | // 划组排序
190 | for (r = unsortedValues.size() / 2; r >= 1; r = r / 2) {
191 | for (i = r; i < unsortedValues.size(); i++) {
192 | tmp = unsortedValues.get(i);
193 | j = i - r;
194 | // 一轮排序
195 | boolean isSwaped = false;//判断是否出现交换位置的情形
196 | while (j >= 0 && tmp < unsortedValues.get(j)) {
197 | unsortedValues.set(j + r, unsortedValues.get(j));
198 | animationioList.add(new AnimationScenarioItem(true, j, j + r, false));
199 | j -= r;
200 | Log.e(TAG, "199 j:" + j + ",r:" + r);
201 | isSwaped = true;
202 | }
203 | unsortedValues.set(j + r, tmp);
204 | //while 循环中如果没有进行交换,则j=i-r,就是说明仅进行比较,未进行交换
205 | if (!isSwaped) {
206 | animationioList.add(new AnimationScenarioItem(false, j, j + r, false));
207 | }
208 | Log.e(TAG, "204 j:" + j + ",r:" + r);
209 | }
210 |
211 | }
212 | }
213 |
214 | // 希尔排序 https://blog.csdn.net/csdn_aiyang/article/details/73108606
215 |
216 | /**
217 | * 已经改正 这种写法需要判断奇偶性
218 | *
219 | * @param unsortedValues
220 | * @param animationioList
221 | */
222 | @Deprecated
223 | public static void heerSort(ArrayList unsortedValues, ArrayList animationioList) {
224 | Log.e(TAG, "希尔排序");
225 | int d = unsortedValues.size() / 2;
226 | //判断是否为基数 a%2!=0
227 | if ((unsortedValues.size() & 1) == 1) {
228 | Log.e(TAG, "数组大小为奇数 间隔需要+1 否则循环将少一次 排序将错误");
229 | d++;
230 | }
231 | while (true) {
232 | for (int i = 0; i < d; i++) {
233 | for (int j = i; j + d < unsortedValues.size(); j += d) {
234 | int temp;
235 | if (unsortedValues.get(j) > unsortedValues.get(j + d)) {
236 | temp = unsortedValues.get(j);
237 | unsortedValues.set(j, unsortedValues.get(j + d));
238 | unsortedValues.set(j + d, temp);
239 | animationioList.add(new AnimationScenarioItem(true, j, j + d, false));
240 |
241 | } else {
242 | animationioList.add(new AnimationScenarioItem(false, j, j + d, false));
243 | }
244 | }
245 | }
246 | if (d < 1) {
247 | break;
248 | }
249 | d--;
250 | }
251 | StringBuffer stringBuffer = new StringBuffer();
252 | for (int i = 0; i < unsortedValues.size(); i++) {
253 | stringBuffer.append(unsortedValues.get(i) + ",");
254 | }
255 | Log.e(TAG, "heerSort 排序后:" + stringBuffer);
256 | }
257 |
258 | /*
259 | * 堆排序(从小到大)
260 | *
261 | * 参数说明:
262 | * a -- 待排序的数组
263 | * n -- 数组的长度
264 | */
265 | // 堆排序代码实现 https://www.cnblogs.com/Java3y/p/8639937.html
266 | public static void heapSort(ArrayList unsortedValues, int n, ArrayList animationioList) {
267 | int i, tmp;
268 | // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
269 | for (i = n / 2 - 1; i >= 0; i--) {
270 | maxHeapDown(unsortedValues, i, n - 1, animationioList);
271 | }
272 | // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
273 | for (i = n - 1; i > 0; i--) {
274 | // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
275 | tmp = unsortedValues.get(0);
276 | unsortedValues.set(0, unsortedValues.get(i));
277 | unsortedValues.set(i, tmp);
278 | animationioList.add(new AnimationScenarioItem(true, 0, i, true));
279 | // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
280 | // 即,保证a[i-1]是a[0...i-1]中的最大值。
281 | maxHeapDown(unsortedValues, 0, i - 1, animationioList);
282 | }
283 | }
284 |
285 | private static void maxHeapDown(ArrayList unsortedValues, int start, int end, ArrayList animationioList) {
286 | int c = start; // 当前(current)节点的位置
287 | int l = 2 * c + 1; // 左(left)孩子的位置
288 | int tmp = unsortedValues.get(c); // 当前(current)节点的大小
289 | for (; l <= end; c = l, l = 2 * l + 1) {
290 | // "l"是左孩子,"l+1"是右孩子
291 | if (l < end && unsortedValues.get(l) < unsortedValues.get(l + 1))
292 | l++; // 左右两孩子中选择较大者,即m_heap[l+1]
293 | if (tmp >= unsortedValues.get(l))
294 | break; // 调整结束
295 | else { // 交换值
296 | unsortedValues.set(c, unsortedValues.get(l));
297 | unsortedValues.set(l, tmp);
298 | animationioList.add(new AnimationScenarioItem(true, c, l, false));
299 | }
300 | }
301 | }
302 |
303 | public static void logList(ArrayList unsortedValues) {
304 | StringBuffer stringBuffer = new StringBuffer();
305 | for (int i = 0; i < unsortedValues.size(); i++) {
306 | stringBuffer.append(unsortedValues.get(i) + ",");
307 | }
308 | Log.e(TAG, "一趟排序后:" + stringBuffer);
309 | }
310 | }
311 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/AlgorithmAnimationListener.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | /**
4 | * Created by ukhanoff on 2/7/17.
5 | */
6 |
7 | public interface AlgorithmAnimationListener {
8 | void onSwapStepAnimationEnd(int endedPosition);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/AlgorithmStepsInterface.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | /**
4 | * Created by ukhanoff on 2/6/17.
5 | */
6 |
7 | public interface AlgorithmStepsInterface {
8 |
9 | /**
10 | * Visualizes step, when elements should change their places with each other
11 | *
12 | * @param curPosition position of the firs element, which should be changed
13 | * @param isOnFinalPlace set true, when element after swapping is on the right place and his position is final
14 | */
15 | void showSwapStep(int curPosition, int nextPosition, boolean isOnFinalPlace);
16 |
17 | /**
18 | * Visualizes step, when elements should stay on the same places;
19 | *
20 | * @param curPosition position of the firs element
21 | * @param isOnFinalPlace set true, when element on position+1 is on the right place and his position is final
22 | */
23 | void showNonSwapStep(int curPosition, int nextPosition, boolean isOnFinalPlace);
24 |
25 | /**
26 | * Call when last item was sorted. Notifies user that sorting is finished.
27 | */
28 | void showFinish();
29 |
30 | /**
31 | * Cancel all current animations
32 | */
33 | void cancelAllVisualisations();
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/AnimationScenarioItem.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | /**
4 | * Holds data about what kind of animation should be shown.
5 | */
6 |
7 | public class AnimationScenarioItem {
8 |
9 | private boolean isShouldBeSwapped;
10 | private int curPosition;
11 | private int nextPosition;
12 | private boolean isFinalPlace;
13 |
14 | public AnimationScenarioItem(boolean isShouldBeSwapped, int curPosition, int nextPosition, boolean isFinalPlace) {
15 | this.isShouldBeSwapped = isShouldBeSwapped;
16 | this.curPosition = curPosition;
17 | this.nextPosition = nextPosition;
18 | this.isFinalPlace = isFinalPlace;
19 | }
20 |
21 | public boolean isShouldBeSwapped() {
22 | return isShouldBeSwapped;
23 | }
24 |
25 | public int getCurPosition() {
26 | return curPosition;
27 | }
28 |
29 | public int getNextPosition() {
30 | return nextPosition;
31 | }
32 |
33 | public boolean isFinalPlace() {
34 | return isFinalPlace;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/AnimationsCoordinator.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.view.ViewGroup;
7 | import android.widget.Toast;
8 |
9 | import java.util.ArrayList;
10 |
11 | import pri.weiqiang.sortanimation.R;
12 | import pri.weiqiang.sortanimation.ui.customview.RectView;
13 |
14 | /**
15 | * Handles all animation which should be done in scope of sorting algorithm visualization.
16 | */
17 |
18 | public class AnimationsCoordinator implements AlgorithmStepsInterface {
19 |
20 | private String TAG = AnimationsCoordinator.class.getSimpleName();
21 | private ViewGroup container;
22 | private ArrayList listeners;
23 | private ValueAnimator blinkAnimation;
24 |
25 | public AnimationsCoordinator(ViewGroup container) {
26 | // Log.e(TAG, "AnimationsCoordinator");
27 | this.container = container;
28 | }
29 |
30 | @Override
31 | public void showSwapStep(final int curPosition, final int nextPosition, final boolean isOnFinalPlace) {
32 | // Log.e(TAG, "showSwapStep");
33 | if (container != null && container.getChildCount() > 0 && container.getChildCount() > nextPosition) {
34 | // Log.e(TAG, "curPosition:" + curPosition + ",nextPosition:" + nextPosition);
35 | final RectView curRectView = (RectView) container.getChildAt(curPosition);
36 | final RectView nextRectView = (RectView) container.getChildAt(nextPosition);
37 |
38 | //BLINKING
39 | blinkAnimation = ValueAnimator.ofInt(0, 5);
40 | blinkAnimation.setDuration(1000);
41 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
42 | @Override
43 | public void onAnimationUpdate(ValueAnimator animation) {
44 |
45 | int value = (Integer) animation.getAnimatedValue();
46 | if (value % 2 == 0) {
47 | curRectView.setSelected(false);
48 | nextRectView.setSelected(false);
49 | } else {
50 | curRectView.setSelected(true);
51 | nextRectView.setSelected(true);
52 | }
53 | }
54 | });
55 |
56 |
57 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
58 | @Override
59 | public void onAnimationEnd(Animator animation) {
60 | super.onAnimationEnd(animation);
61 | curRectView.setSelected(false);
62 | curRectView.setIsOnFinalPlace(isOnFinalPlace);
63 | nextRectView.setSelected(false);
64 | container.removeView(curRectView);
65 | container.addView(curRectView, nextPosition);
66 | container.removeView(nextRectView);
67 | container.addView(nextRectView, curPosition);
68 | notifySwapStepAnimationEnd(curPosition);
69 | }
70 | });
71 |
72 | blinkAnimation.start();
73 | }
74 | }
75 |
76 | @Override
77 | public void showNonSwapStep(final int curPosition, final int nextPosition, final boolean isOnFinalPlace) {
78 | // Log.e(TAG, "showNonSwapStep");
79 | if (container != null && container.getChildCount() > 0 && container.getChildCount() > nextPosition) {
80 | final RectView curRectView = (RectView) container.getChildAt(curPosition);
81 | final RectView nextRectView = (RectView) container.getChildAt(nextPosition);
82 |
83 | //BLINKING
84 | blinkAnimation = ValueAnimator.ofInt(0, 6);
85 | blinkAnimation.setDuration(1200);
86 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
87 | @Override
88 | public void onAnimationUpdate(ValueAnimator animation) {
89 | int value = (Integer) animation.getAnimatedValue();
90 | if (value % 2 == 0) {
91 | curRectView.setSelected(false);
92 | nextRectView.setSelected(false);
93 | } else {
94 | curRectView.setSelected(true);
95 | nextRectView.setSelected(true);
96 | }
97 | }
98 | });
99 |
100 | blinkAnimation.start();
101 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
102 | @Override
103 | public void onAnimationEnd(Animator animation) {
104 | super.onAnimationEnd(animation);
105 | curRectView.setSelected(false);
106 | nextRectView.setSelected(false);
107 | nextRectView.setIsOnFinalPlace(isOnFinalPlace);
108 | notifySwapStepAnimationEnd(curPosition);
109 | }
110 | });
111 | }
112 | }
113 |
114 | @Override
115 | public void showFinish() {
116 | // Log.e(TAG, "showFinish");
117 | if (container != null && container.getChildCount() > 0) {
118 | // ((RectView) container.getChildAt(0)).setIsOnFinalPlace(true);//仅对冒泡法适用
119 | for (int i = 0; i < container.getChildCount(); i++) {
120 | ((RectView) container.getChildAt(i)).setIsOnFinalPlace(true);//排序完成后,全部设置为最终颜色
121 | }
122 | }
123 | Toast.makeText(container.getContext(), R.string.sort_finish, Toast.LENGTH_SHORT).show();
124 | }
125 |
126 | @Override
127 | public void cancelAllVisualisations() {
128 | // Log.e(TAG, "cancelAllVisualisations");
129 | if (blinkAnimation != null) {
130 | blinkAnimation.removeAllListeners();
131 | blinkAnimation.cancel();
132 | container.clearAnimation();
133 | }
134 | }
135 |
136 | private void notifySwapStepAnimationEnd(int position) {
137 | // Log.e(TAG, "notifySwapStepAnimationEnd");
138 | if (listeners != null && !listeners.isEmpty()) {
139 | int numListeners = listeners.size();
140 | for (int i = 0; i < numListeners; ++i) {
141 | listeners.get(i).onSwapStepAnimationEnd(position);
142 | }
143 | }
144 | }
145 |
146 | public void addListener(AlgorithmAnimationListener listener) {
147 | // Log.e(TAG, "addListener");
148 | if (listeners == null) {
149 | listeners = new ArrayList<>();
150 | }
151 | listeners.add(listener);
152 | }
153 |
154 | public void removeListener(Animator.AnimatorListener listener) {
155 | // Log.e(TAG, "removeListener");
156 | if (listeners == null) {
157 | return;
158 | }
159 | listeners.remove(listener);
160 | if (listeners.size() == 0) {
161 | listeners = null;
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/MergeAnimationListener.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | public interface MergeAnimationListener {
4 | void onSwapStepAnimationEnd(int endedPosition);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/MergeAnimationScenarioItem.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | /**
4 | * 归并排序动画 单元类
5 | */
6 | public class MergeAnimationScenarioItem {
7 |
8 |
9 | private int originalPosition;
10 | private int tempPosition;
11 | private boolean isMerge;
12 |
13 | /**
14 | * @param originalPosition 在原数组中的位置
15 | * @param tempPosition 在新生成的数组中的位置
16 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
17 | */
18 | public MergeAnimationScenarioItem(int originalPosition, int tempPosition, boolean isMerge) {
19 |
20 | this.originalPosition = originalPosition;
21 | this.tempPosition = tempPosition;
22 | this.isMerge = isMerge;
23 | }
24 |
25 |
26 | public int getOriginalPosition() {
27 | return originalPosition;
28 | }
29 |
30 | public int getTempPosition() {
31 | return tempPosition;
32 | }
33 |
34 | public boolean isMerge() {
35 | return isMerge;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/MergeAnimationsCoordinator.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.graphics.Bitmap;
8 | import android.util.Log;
9 | import android.view.ViewGroup;
10 | import android.widget.LinearLayout;
11 | import android.widget.Toast;
12 |
13 | import java.util.ArrayList;
14 |
15 | import pri.weiqiang.sortanimation.R;
16 | import pri.weiqiang.sortanimation.ui.customview.RectView;
17 | import pri.weiqiang.sortanimation.ui.fragment.SortFragment;
18 | import pri.weiqiang.sortanimation.util.Util;
19 |
20 |
21 | /**
22 | * 归并算法动画控制类
23 | */
24 | public class MergeAnimationsCoordinator implements MergeStepsInterface {
25 |
26 | private String TAG = MergeAnimationsCoordinator.class.getSimpleName();
27 | private ViewGroup originalContainer;
28 | private ViewGroup tempContainer;
29 | private ArrayList listeners;
30 | private ValueAnimator blinkAnimation;
31 | private LinearLayout.LayoutParams lp;
32 | private Context context;
33 |
34 | public MergeAnimationsCoordinator(Context context, ViewGroup originalContainer, ViewGroup tempContainer) {
35 | Log.e(TAG, "MergeAnimationsCoordinator");
36 | this.context = context;
37 | this.originalContainer = originalContainer;
38 | this.tempContainer = tempContainer;
39 | //放在这里报错 java.lang.NullPointerException: Attempt to read from field 'int android.view.ViewGroup$LayoutParams.width' on a null object reference
40 | // LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
41 | // int marginInPx = Util.dpToPx(context, SortFragment.RECT_MARGIN);
42 | // lp.setMargins(0, 0, marginInPx, 0);
43 | }
44 |
45 | /**
46 | * 从原数组拿取元素,按大小添加下方的新矩形数列中
47 | *
48 | * @param originalPosition 在原数组中的位置
49 | * @param tempPosition 在新生成的数组中的位置
50 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
51 | */
52 | @Override
53 | public void createTempView(final int originalPosition, final int tempPosition, final boolean isMerge) {
54 |
55 | final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
56 | int marginInPx = Util.dpToPx(context, SortFragment.RECT_MARGIN);
57 | lp.setMargins(0, 0, marginInPx, 0);
58 |
59 | if (originalContainer != null && originalContainer.getChildCount() > 0 && originalContainer.getChildCount() > tempPosition) {
60 | final RectView originalView = (RectView) originalContainer.getChildAt(originalPosition);
61 | //BLINKING
62 | blinkAnimation = ValueAnimator.ofInt(0, 5);
63 | blinkAnimation.setDuration(1000);
64 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
65 | @Override
66 | public void onAnimationUpdate(ValueAnimator animation) {
67 |
68 | int value = (Integer) animation.getAnimatedValue();
69 | if (value % 2 == 0) {
70 | originalView.setSelected(false);
71 | } else {
72 | originalView.setSelected(true);
73 | }
74 | }
75 | });
76 |
77 |
78 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
79 | @Override
80 | public void onAnimationEnd(Animator animation) {
81 | Log.e(TAG, "生成临时矩形!");
82 | super.onAnimationEnd(animation);
83 | originalView.setSelected(false);
84 | originalView.setIsOnFinalPlace(isMerge);
85 | originalContainer.removeView(originalView);
86 | int tempNumber = originalView.getNumber();
87 | originalView.setMinimumHeight(1);
88 | originalView.setImageBitmap(createSpaceBitmap(SortFragment.mRectWidth));
89 | originalView.setNumber(1);
90 | originalContainer.addView(originalView, originalPosition, lp);
91 |
92 | RectView tempRectView = new RectView(context);
93 | tempRectView.setImageBitmap(createCalculatedBitmap(SortFragment.mRectWidth, tempNumber));
94 | tempRectView.setNumber(tempNumber);
95 | tempContainer.addView(tempRectView, tempPosition, lp);
96 | notifySwapStepAnimationEnd(originalPosition);
97 | }
98 | });
99 |
100 | blinkAnimation.start();
101 | }
102 | }
103 |
104 | /**
105 | * 将下列排序好的矩形按顺序填回到原矩形序列
106 | *
107 | * @param originalPosition 在原数组中的位置
108 | * @param tempPosition 在新生成的数组中的位置
109 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
110 | */
111 | @Override
112 | public void mergeOriginalView(final int originalPosition, final int tempPosition, final boolean isMerge) {
113 |
114 | final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
115 | int marginInPx = Util.dpToPx(context, SortFragment.RECT_MARGIN);
116 | lp.setMargins(0, 0, marginInPx, 0);
117 | if (originalContainer != null && originalContainer.getChildCount() > 0 && originalContainer.getChildCount() > tempPosition) {
118 | final RectView originalView = (RectView) originalContainer.getChildAt(originalPosition);
119 | final RectView tempRectView = (RectView) tempContainer.getChildAt(tempPosition);
120 | //BLINKING
121 | blinkAnimation = ValueAnimator.ofInt(0, 6);
122 | blinkAnimation.setDuration(1200);
123 | blinkAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
124 | @Override
125 | public void onAnimationUpdate(ValueAnimator animation) {
126 | int value = (Integer) animation.getAnimatedValue();
127 | if (value % 2 == 0) {
128 | originalView.setSelected(false);
129 | tempRectView.setSelected(false);
130 | } else {
131 | originalView.setSelected(true);
132 | tempRectView.setSelected(true);
133 | }
134 | }
135 | });
136 |
137 | blinkAnimation.start();
138 | blinkAnimation.addListener(new AnimatorListenerAdapter() {
139 | @Override
140 | public void onAnimationEnd(Animator animation) {
141 | super.onAnimationEnd(animation);
142 | originalView.setSelected(false);
143 | tempRectView.setSelected(false);
144 | tempRectView.setIsOnFinalPlace(isMerge);
145 | tempContainer.removeView(tempRectView);
146 | int tempNumber = tempRectView.getNumber();
147 |
148 | tempRectView.setMinimumHeight(1);
149 | tempRectView.setImageBitmap(createSpaceBitmap(SortFragment.mRectWidth));
150 | tempRectView.setNumber(1);
151 | // 不能设置矩形不可见,还是会报The specified child already has a parent. You must call removeView() on the child's parent first.
152 | // tempRectView.setVisibility(View.INVISIBLE);
153 | tempContainer.addView(tempRectView, tempPosition, lp);
154 |
155 |
156 | originalContainer.removeView(originalView);
157 | RectView originalView = new RectView(context);
158 | originalView.setImageBitmap(createCalculatedBitmap(SortFragment.mRectWidth, tempNumber));
159 | originalView.setNumber(tempNumber);
160 | originalContainer.addView(originalView, originalPosition, lp);
161 | notifySwapStepAnimationEnd(originalPosition);
162 | }
163 | });
164 | }
165 | }
166 |
167 | @Override
168 | public void showFinish() {
169 | Log.e(TAG, "showFinish");
170 | if (originalContainer != null && originalContainer.getChildCount() > 0) {
171 | for (int i = 0; i < originalContainer.getChildCount(); i++) {
172 | ((RectView) originalContainer.getChildAt(i)).setIsOnFinalPlace(true);//排序完成后,全部设置为最终颜色
173 | }
174 | }
175 | Toast.makeText(originalContainer.getContext(), R.string.sort_finish, Toast.LENGTH_SHORT).show();
176 | }
177 |
178 | @Override
179 | public void cancelAllVisualisations() {
180 | Log.e(TAG, "cancelAllVisualisations");
181 | if (blinkAnimation != null) {
182 | blinkAnimation.removeAllListeners();
183 | blinkAnimation.cancel();
184 | originalContainer.clearAnimation();
185 | }
186 | }
187 |
188 | private void notifySwapStepAnimationEnd(int position) {
189 | Log.e(TAG, "notifySwapStepAnimationEnd");
190 | if (listeners != null && !listeners.isEmpty()) {
191 | int numListeners = listeners.size();
192 | for (int i = 0; i < numListeners; ++i) {
193 | listeners.get(i).onSwapStepAnimationEnd(position);
194 | }
195 | }
196 | }
197 |
198 | public void addListener(MergeAnimationListener listener) {
199 | Log.e(TAG, "addListener");
200 | if (listeners == null) {
201 | listeners = new ArrayList<>();
202 | }
203 | listeners.add(listener);
204 | }
205 |
206 | public void removeListener(Animator.AnimatorListener listener) {
207 | Log.e(TAG, "removeListener");
208 | if (listeners == null) {
209 | return;
210 | }
211 | listeners.remove(listener);
212 | if (listeners.size() == 0) {
213 | listeners = null;
214 | }
215 | }
216 |
217 | /**
218 | * 绘制指定高度的RectView
219 | */
220 | private Bitmap createCalculatedBitmap(int mRectWidth, Integer currentIntValue) {
221 |
222 | int mRectHeight = SortFragment.lineHeightArray.get(currentIntValue);
223 | if (mRectHeight < SortFragment.minHeight) {
224 | mRectHeight = SortFragment.minHeight;
225 | }
226 | return Bitmap.createBitmap(mRectWidth, mRectHeight, Bitmap.Config.ALPHA_8);
227 | }
228 |
229 |
230 | /**
231 | * 绘制高度为1的空白的RectView 用来占位
232 | * 为什么不从完全空的容器增加,那样每次位置都会乱
233 | */
234 | private Bitmap createSpaceBitmap(int mRectWidth) {
235 | return Bitmap.createBitmap(mRectWidth, 1, Bitmap.Config.ALPHA_8);
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/animation/MergeStepsInterface.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.animation;
2 |
3 | /**
4 | * Created by weiqiang
5 | */
6 |
7 | public interface MergeStepsInterface {
8 |
9 | /**
10 | * 从原数组中选择元素组成新数组,顺序为从小到大
11 | *
12 | * @param originalPosition 在原数组中的位置
13 | * @param tempPosition 在新生成的数组中的位置
14 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
15 | */
16 | void createTempView(int originalPosition, int tempPosition, boolean isMerge);
17 |
18 | /**
19 | * 将新生成的从小到大的数组重新合并到原数组中去
20 | *
21 | * @param originalPosition 在原数组中的位置
22 | * @param tempPosition 在新生成的数组中的位置
23 | * @param isMerge 是否是处于将新生成的数组放置回原数组的那个步骤
24 | */
25 | void mergeOriginalView(int originalPosition, int tempPosition, boolean isMerge);
26 |
27 | /**
28 | * Call when last item was sorted. Notifies user that sorting is finished.
29 | */
30 | void showFinish();
31 |
32 | /**
33 | * Cancel all current animations
34 | */
35 | void cancelAllVisualisations();
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/constant/Constant.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.constant;
2 |
3 | public class Constant {
4 | public final static int ALGORITHM_PUBBLE = 0;//冒泡排序
5 | public final static int ALGORITHM_INSERT = 1;//插入排序
6 | public final static int ALGORITHM_SELECT = 2;//选择排序
7 | public final static int ALGORITHM_QUICK = 3;//快速排序
8 | public final static int ALGORITHM_MERGE = 4;//归并排序
9 | public final static int ALGORITHM_HEER = 5;//希尔排序
10 | public final static int ALGORITHM_HEAP = 6;//堆排序
11 |
12 | public final static String EXTRA_CODE = "pri.weiqiang.sortanimation.EXTRA.CODE";//堆排序
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/ui/activity/CodeActivity.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.ui.activity;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.widget.TextView;
6 |
7 | import java.util.ArrayList;
8 |
9 | import androidx.appcompat.app.AppCompatActivity;
10 | import io.noties.markwon.Markwon;
11 | import pri.weiqiang.sortanimation.R;
12 | import pri.weiqiang.sortanimation.algorithm.SortArrayList;
13 | import pri.weiqiang.sortanimation.constant.Constant;
14 | import pri.weiqiang.sortanimation.ui.fragment.SortFragment;
15 |
16 | public class CodeActivity extends AppCompatActivity {
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | Intent intent = getIntent();
22 | int algorithmSelected = intent.getIntExtra(Constant.EXTRA_CODE,Constant.ALGORITHM_PUBBLE);//从intent中读取信息
23 |
24 | setContentView(R.layout.activity_code);
25 | TextView mTvCode = findViewById(R.id.tv_code);
26 | final Markwon markwon = Markwon.create(this);
27 | String code = "null";
28 | switch (algorithmSelected) {
29 |
30 | case Constant.ALGORITHM_PUBBLE:
31 | code = "' " +
32 | "\n" +
33 | "private static void pubbleSort(int[] a) {\n" +
34 | " System.out.println(\"冒泡排序\");\n" +"\n"+
35 | " int temp;// 记录临时变量\n" +
36 | " int size = a.length;// 数组大小\n" +
37 | " for (int i = 0; i < size - 1; i++) {\n" +
38 | " for (int j = i + 1; j < size; j++) {// 索引不同的两层for循环\n" +
39 | " if (a[i] < a[j]) {// 交互数据从大到小排列顺序 大的放前面\n" +
40 | " temp = a[i];\n" +
41 | " a[i] = a[j];\n" +
42 | " a[j] = temp;\n" +
43 | " }\n" +
44 | " }\n" +
45 | " }\n" +
46 | " for (int q = 0; q < a.length; q++) {\n" +
47 | " System.out.print(a[q] + \",\");\n" +
48 | " }\n" +
49 | " }'";
50 | break;
51 | case Constant.ALGORITHM_INSERT:
52 | code =" ' " +
53 | "\n" +"\n"+
54 | "'private static void insertSort(int[] a) {\n" +"\n"+
55 | " System.out.println(\"插入排序\");\n" +
56 | " // 直接插入排序\n" +
57 | " for (int i = 1; i < a.length; i++) {\n" +
58 | " // 待插入元素\n" +
59 | " int temp = a[i];\n" +
60 | " int j;\n" +
61 | " for (j = i - 1; j >= 0; j--) {\n" +
62 | " // 将大于temp的往后移动一位\n" +
63 | " if (a[j] > temp) {\n" +
64 | " a[j + 1] = a[j];\n" +
65 | " } else {\n" +
66 | " break;\n" +
67 | " }\n" +
68 | " }\n" +
69 | " a[j + 1] = temp;// 插入进来\n" +
70 | " }\n" +
71 | " for (int q = 0; q < a.length; q++) {\n" +
72 | " System.out.print(a[q] + \",\");\n" +
73 | " }\n" +
74 | " }\n" +
75 | " }'";
76 | break;
77 | case Constant.ALGORITHM_SELECT:
78 | code = "' " +
79 | "\n" +"\n"+
80 | "public static void selectSort(int[] a) {\n" +"\n"+
81 | " System.out.println(\"选择排序\");\n" +
82 | " int min;\n" +
83 | " int tmp;\n" +
84 | " for (int i = 0; i < a.length; i++) {\n" +
85 | " min = a[i];\n" +
86 | " for (int j = i; j < a.length; j++) {\n" +
87 | " if (a[j] < min) {\n" +
88 | " min = a[j];// 最小值\n" +
89 | " tmp = a[i];\n" +
90 | " a[i] = a[j];//这么写,更好理解 a[i] = min;//没有前者好理解\n" +
91 | " a[j] = tmp;\n" +
92 | " }\n" +
93 | " }\n" +
94 | " }\n" +
95 | " for (int q = 0; q < a.length; q++) {\n" +
96 | " System.out.print(a[q] + \",\");\n" +
97 | " }\n" +
98 |
99 | " }'";
100 | break;
101 | case Constant.ALGORITHM_QUICK:
102 | code = "' // 快速排序\n" +
103 | "\n" +"\n"+
104 | " private static void quickSort(int[] a, int low, int high) {\n" +"\n"+
105 | " int start = low;\n" +
106 | " int end = high;\n" +
107 | " int key = a[low];\n" +
108 | "\n" +
109 | " while (end > start) {\n" +
110 | "\n" +
111 | " // 从后往前比较// 如果没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后又从前往后比较\n" +
112 | " while (end > start && a[end] >= key)\n" +
113 | " end--;\n" +
114 | " if (a[end] <= key) {\n" +
115 | " int temp = a[end];\n" +
116 | " a[end] = a[start];\n" +
117 | " a[start] = temp;\n" +
118 | " System.out.println(\"key:\" + key + \",从后往前比较 end:\" + end + \",a[end]:\" + a[end] + \",start:\" + start + \",a[start]:\" + a[start]);\n" +
119 | " System.out.println(\"---start:\" + start);\n" +
120 | " }\n" +
121 | " // 从前往后比较// 如果没有比关键值大的,比较下一个,直到有比关键值大的交换位置\n" +
122 | " while (end > start && a[start] <= key)\n" +
123 | " start++;\n" +
124 | " if (a[start] >= key) {\n" +
125 | " int temp = a[start];\n" +
126 | " a[start] = a[end];\n" +
127 | " a[end] = temp;\n" +
128 | " System.out.println(\"key:\" + key + \",从前往后比较 end:\" + end + \",a[end]:\" + a[end] + \",start:\" + start + \",a[start]:\" + a[start]);\n" +
129 | " System.out.println(\"---end:\" + end);\n" +
130 | " }\n" +
131 | " // 此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用\n" +
132 | " }\n" +
133 | " StringBuilder sb = new StringBuilder(\"a6 =\");\n" +
134 | " for (int i = 0; i < a6.length; i++) {\n" +
135 | " sb.append(a6[i] + \",\");\n" +
136 | "\n" +
137 | " }\n" +
138 | " System.out.println(\"sb:\" + sb.toString());\n" +
139 | " // 递归\n" +
140 | " if (start > low) {\n" +
141 | " System.out.println(\"**************************************************\");\n" +
142 | " System.out.println(\"迭代******start > low!\" + \",start:\" + start + \",low:\" + low);\n" +
143 | " quickSort(a, low, start - 1);// 左边序列。第一个索引位置到关键值索引-1\n" +
144 | " }\n" +
145 | " if (end < high) {\n" +
146 | " System.out.println(\"**************************************************\");\n" +
147 | " System.out.println(\"迭代*******end < high!\" + \",end:\" + end + \",high:\" + high);\n" +
148 | " quickSort(a, end + 1, high);// 右边序列。从关键值索引+1到最后一个\n" +
149 | " }\n" +
150 | " }'";
151 | break;
152 | case Constant.ALGORITHM_HEER:
153 | code = "' " +
154 | "\n" +"\n"+
155 | "private static void heerSort(int[] a) {\n" +"\n"+
156 | " System.out.println(\"希尔排序\");\n" +
157 | " int d = a.length / 2;\n" +
158 | " while (true) {\n" +
159 | " for (int i = 0; i < d; i++) {\n" +
160 | " for (int j = i; j + d < a.length; j += d) {\n" +
161 | " int temp;\n" +
162 | " if (a[j] > a[j + d]) {\n" +
163 | " temp = a[j];\n" +
164 | " a[j] = a[j + d];\n" +
165 | " a[j + d] = temp;\n" +
166 | " }\n" +
167 | " }\n" +
168 | " }\n" +
169 | " if (d == 1) {\n" +
170 | " break;\n" +
171 | " }\n" +
172 | " d--;\n" +
173 | " }\n" +
174 | " for (int anA : a) {\n" +
175 | " System.out.print(anA + \",\");\n" +
176 | " }\n" +
177 | " }'";
178 | break;
179 | case Constant.ALGORITHM_HEAP:
180 | code = "' " +
181 | "\n" +"\n"+
182 | " /*\n" +
183 | " * 堆排序(从小到大)\n" +
184 | " *\n" +
185 | " * 参数说明:\n" +
186 | " * a -- 待排序的数组\n" +
187 | " * n -- 数组的长度\n" +
188 | " */\n" +
189 | " static void heapSortAsc(int[] a, int n) {\n" +
190 | " int i, tmp;\n" +
191 | "\n" +
192 | " // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。\n" +
193 | " for (i = n / 2 - 1; i >= 0; i--)\n" +
194 | " maxHeapDown(a, i, n - 1);\n" +
195 | "\n" +
196 | "\n" +
197 | " // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素\n" +
198 | " for (i = n - 1; i > 0; i--) {\n" +
199 | " // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。\n" +
200 | " tmp = a[0];\n" +
201 | " a[0] = a[i];\n" +
202 | " a[i] = tmp;\n" +
203 | " // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。\n" +
204 | " // 即,保证a[i-1]是a[0...i-1]中的最大值。\n" +
205 | " maxHeapDown(a, 0, i - 1);\n" +
206 | " }\n" +
207 | " }\n" +
208 | "\n" +
209 | " /*\n" +
210 | " * (最小)堆的向下调整算法\n" +
211 | " *\n" +
212 | " * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。\n" +
213 | " * 其中,N为数组下标索引值,如数组中第1个数对应的N为0。\n" +
214 | " *\n" +
215 | " * 参数说明:\n" +
216 | " * a -- 待排序的数组\n" +
217 | " * start -- 被下调节点的起始位置(一般为0,表示从第1个开始)\n" +
218 | " * end -- 截至范围(一般为数组中最后一个元素的索引)\n" +
219 | " */\n" +
220 | " static void minHeapDown(int[] a, int start, int end) {\n" +
221 | " int c = start; // 当前(current)节点的位置\n" +
222 | " int l = 2 * c + 1; // 左(left)孩子的位置\n" +
223 | " int tmp = a[c]; // 当前(current)节点的大小\n" +
224 | "\n" +
225 | " for (; l <= end; c = l, l = 2 * l + 1) {\n" +
226 | " // \"l\"是左孩子,\"l+1\"是右孩子\n" +
227 | " if (l < end && a[l] > a[l + 1])\n" +
228 | " l++; // 左右两孩子中选择较小者\n" +
229 | " if (tmp <= a[l])\n" +
230 | " break; // 调整结束\n" +
231 | " else { // 交换值\n" +
232 | " a[c] = a[l];\n" +
233 | " a[l] = tmp;\n" +
234 | " }\n" +
235 | " }\n" +
236 | " }\n" +
237 | "\n" +
238 | " /*\n" +
239 | " * 堆排序(从大到小)\n" +
240 | " *\n" +
241 | " * 参数说明:\n" +
242 | " * a -- 待排序的数组\n" +
243 | " * n -- 数组的长度\n" +
244 | " */\n" +
245 | " public static void heapSortDesc(int[] a, int n) {\n" +
246 | " int i, tmp;\n" +
247 | "\n" +
248 | " // 从(n/2-1) --> 0逐次遍历每。遍历之后,得到的数组实际上是一个最小堆。\n" +
249 | " for (i = n / 2 - 1; i >= 0; i--)\n" +
250 | " minHeapDown(a, i, n - 1);\n" +
251 | "\n" +
252 | " // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素\n" +
253 | " for (i = n - 1; i > 0; i--) {\n" +
254 | " // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最小的。\n" +
255 | " tmp = a[0];\n" +
256 | " a[0] = a[i];\n" +
257 | " a[i] = tmp;\n" +
258 | " // 调整a[0...i-1],使得a[0...i-1]仍然是一个最小堆。\n" +
259 | " // 即,保证a[i-1]是a[0...i-1]中的最小值。\n" +
260 | " minHeapDown(a, 0, i - 1);\n" +
261 | " }\n" +
262 | " }\n" +
263 | "\n" +
264 | " public static void sortHeap() {\n" +
265 | " System.out.println(\"堆排序\");\n" +
266 | " int i;\n" +
267 | " int a[] = {1, 2, 4, 3, 0};\n" +
268 | "\n" +
269 | " System.out.printf(\"before sort:\");\n" +
270 | " for (i = 0; i < a.length; i++)\n" +
271 | " System.out.printf(\"%d \", a[i]);\n" +
272 | " System.out.printf(\"\\n\");\n" +
273 | "\n" +
274 | " heapSortAsc(a, a.length); // 升序排列\n" +
275 | " // heapSortDesc(a, a.length); // 降序排列\n" +
276 | "\n" +
277 | " System.out.printf(\"after sort:\");\n" +
278 | " for (i = 0; i < a.length; i++)\n" +
279 | " System.out.printf(\"%d \", a[i]);\n" +
280 | " System.out.printf(\"\\n\");\n" +
281 | " }\n" +
282 | " }'";
283 | break;
284 |
285 | }
286 |
287 | markwon.setMarkdown(mTvCode, code);
288 |
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/ui/activity/SortActivity.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.ui.activity;
2 |
3 | import android.os.Bundle;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import pri.weiqiang.sortanimation.R;
7 | import pri.weiqiang.sortanimation.ui.fragment.SortFragment;
8 |
9 | public class SortActivity extends AppCompatActivity {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.activity_sort);
15 |
16 | if (savedInstanceState == null) {
17 | SortFragment fragment = new SortFragment();
18 | getSupportFragmentManager().beginTransaction().add(R.id.fl_container, fragment)
19 | .commit();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/ui/customview/RectView.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.ui.customview;
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.Rect;
8 | import androidx.appcompat.widget.AppCompatImageView;
9 | import android.util.AttributeSet;
10 |
11 | import pri.weiqiang.sortanimation.R;
12 |
13 | public class RectView extends AppCompatImageView {
14 | public static final int TEXT_BASELINE_Y = 105;
15 | public static final float TEXT_SIZE = 45f;
16 | Paint paint;
17 | Rect bounds;
18 | private String TAG = RectView.class.getSimpleName();
19 | private Integer valueToDraw;
20 | private boolean isSelected;
21 | private boolean isOnFinalPlace;
22 | private int mWidth;
23 | private int mRectHeight;
24 | private int mRectWidth;
25 |
26 | public RectView(Context context) {
27 | this(context, null);
28 | init();
29 | }
30 |
31 | public RectView(Context context, AttributeSet attrs) {
32 | this(context, attrs, 0);
33 | init();
34 | }
35 |
36 | public RectView(Context context, AttributeSet attrs, int defStyleAttr) {
37 | super(context, attrs, defStyleAttr);
38 | init();
39 | }
40 |
41 | private void init() {
42 | paint = new Paint(Paint.LINEAR_TEXT_FLAG);
43 | paint.setAntiAlias(true);
44 | paint.setTextSize(TEXT_SIZE);
45 | bounds = new Rect();
46 | }
47 |
48 | @Override
49 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
50 | super.onSizeChanged(w, h, oldw, oldh);
51 | mWidth = getMeasuredWidth();
52 | mRectHeight = getMeasuredHeight();
53 | mRectWidth = mWidth;
54 | }
55 |
56 | @Override
57 | protected void onDraw(Canvas canvas) {
58 | super.onDraw(canvas);
59 | if (valueToDraw != null) {
60 | String text = valueToDraw.toString();
61 | if (isOnFinalPlace) {
62 | paint.setColor(getResources().getColor(R.color.colorPrimaryDark));
63 | } else {
64 | if (isSelected) {
65 | paint.setColor(getResources().getColor(R.color.colorPrimary));
66 | } else {
67 | paint.setColor(getResources().getColor(R.color.colorAccent));
68 | }
69 | }
70 | canvas.drawRect(0, 5, mRectWidth, mRectHeight, paint);
71 | paint.setColor(Color.WHITE);
72 | paint.setTextAlign(Paint.Align.CENTER);//以文字中间位置为坐标
73 | canvas.drawText(text, mRectWidth / 2, TEXT_BASELINE_Y, paint);
74 | }
75 | }
76 |
77 | public Integer getNumber() {
78 | return valueToDraw;
79 | }
80 |
81 | /**
82 | * 在矩形中绘制相应数字
83 | *
84 | * @param valueToDraw value which should appears in the center of {@link RectView}
85 | */
86 | public void setNumber(Integer valueToDraw) {
87 | this.valueToDraw = valueToDraw;
88 | invalidate();
89 | }
90 |
91 | /**
92 | * 矩形位于排序的最后位置,背景颜色将会置为最终颜色
93 | */
94 | public void setIsOnFinalPlace(boolean isOnFinalPlace) {
95 | this.isOnFinalPlace = isOnFinalPlace;
96 | invalidate();
97 | }
98 |
99 |
100 | /**
101 | * 处于比较中的矩形,背景颜色处于选中状态
102 | */
103 | public void setSelected(boolean isSelected) {
104 | this.isSelected = isSelected;
105 | invalidate();
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/ui/fragment/SortFragment.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.ui.fragment;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Bitmap;
5 | import android.os.Bundle;
6 | import androidx.annotation.Nullable;
7 | import androidx.fragment.app.Fragment;
8 | import android.text.TextUtils;
9 | import android.util.Log;
10 | import android.util.SparseIntArray;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.AdapterView;
15 | import android.widget.Button;
16 | import android.widget.EditText;
17 | import android.widget.LinearLayout;
18 | import android.widget.RelativeLayout;
19 | import android.widget.Spinner;
20 | import android.widget.Toast;
21 |
22 | import java.util.ArrayList;
23 |
24 | import pri.weiqiang.sortanimation.R;
25 | import pri.weiqiang.sortanimation.algorithm.SortArrayList;
26 | import pri.weiqiang.sortanimation.animation.AlgorithmAnimationListener;
27 | import pri.weiqiang.sortanimation.animation.AnimationScenarioItem;
28 | import pri.weiqiang.sortanimation.animation.AnimationsCoordinator;
29 | import pri.weiqiang.sortanimation.animation.MergeAnimationListener;
30 | import pri.weiqiang.sortanimation.animation.MergeAnimationScenarioItem;
31 | import pri.weiqiang.sortanimation.animation.MergeAnimationsCoordinator;
32 | import pri.weiqiang.sortanimation.constant.Constant;
33 | import pri.weiqiang.sortanimation.ui.activity.CodeActivity;
34 | import pri.weiqiang.sortanimation.ui.customview.RectView;
35 | import pri.weiqiang.sortanimation.util.Util;
36 |
37 | /**
38 | * Main fragment where sorting visualisation appears
39 | */
40 |
41 | public class SortFragment extends Fragment {
42 | public static final int minHeight = 150;
43 | public static final int RECT_MARGIN = 1;//4
44 | public static SparseIntArray lineHeightArray = new SparseIntArray();//统计每个数字对应矩形的高
45 | public static int mRectWidth;//单个长方体的宽
46 | private LinearLayout viewLine;
47 | private String TAG = SortFragment.class.getSimpleName();
48 | private EditText mEtInput;
49 | private Button mBtnStart;
50 | private Button mBtnCode;
51 | private boolean isAnimationRunning;
52 | private int scenarioItemIndex = 0;
53 | private LinearLayout mLlContainer;
54 | private LinearLayout mLlContainerMerge;
55 | private LinearLayout mLlContainerOriginal;
56 | private LinearLayout mLlContainerTemp;
57 | private RelativeLayout mRlContainerParent;
58 | private AnimationsCoordinator animationsCoordinator;
59 | private MergeAnimationsCoordinator mergeAnimationsCoordinator;
60 | private ArrayList animationList;
61 | private ArrayList mergeAnimationList;
62 | private ArrayList keyList;
63 | private Spinner algorithmSpinner;
64 | private int algorithmSelected = Constant.ALGORITHM_PUBBLE;
65 | private int mWidth;
66 | private int mRectHeight;
67 | private int maxRectHeight;
68 | private int minRectHeight;
69 | private View view;
70 | private StringBuffer stringBuffer;
71 | private int rectCount = 0;
72 | private View.OnClickListener buttonClickListener = new View.OnClickListener() {
73 |
74 | @Override
75 | public void onClick(View v) {
76 | String input = mEtInput.getText().toString();
77 | if (!TextUtils.isEmpty(input)) {
78 | resetPreviousData();
79 |
80 | ArrayList integerList = new ArrayList<>(Util.convertToIntArray(input));
81 | //更新数据,minRectHeight maxRectHeight 同步重置
82 | minRectHeight = integerList.get(0);
83 | maxRectHeight = integerList.get(0);
84 | for (int i = 0; i < integerList.size(); i++) {
85 | int height = integerList.get(i);
86 |
87 | if (height > maxRectHeight) {
88 | maxRectHeight = height;
89 | }
90 | if (height < minRectHeight) {
91 | minRectHeight = height;
92 | }
93 | }
94 | rectCount = integerList.size();
95 |
96 | if (algorithmSelected == Constant.ALGORITHM_MERGE) {
97 | drawRectsMerge(integerList);
98 | mergeAnimationList = new ArrayList<>();
99 | sortMerge(integerList, mergeAnimationList);
100 | runAnimationIterationMerge();
101 | } else {
102 | drawRects(integerList);
103 | animationList = new ArrayList<>();
104 | sort(integerList, animationList);
105 | runAnimationIteration();
106 | }
107 | stringBuffer = new StringBuffer();
108 | for (int i = 0; i < integerList.size(); i++) {
109 | stringBuffer.append(integerList.get(i) + ",");
110 | }
111 | Log.e(TAG, "排序后:" + stringBuffer);
112 |
113 | } else {
114 | Toast.makeText(getContext(), R.string.empty_field_warning, Toast.LENGTH_LONG).show();
115 | }
116 | }
117 | };
118 |
119 | private void sortMerge(ArrayList unsortedValues, ArrayList mergeAnimationioList) {
120 | mRlContainerParent.setVisibility(View.GONE);
121 | mLlContainerMerge.setVisibility(View.VISIBLE);
122 | SortArrayList.mergeSort(unsortedValues, 0, unsortedValues.size() - 1, mergeAnimationioList);
123 | }
124 |
125 | private void sort(ArrayList unsortedValues, ArrayList animationioList) {
126 | mRlContainerParent.setVisibility(View.VISIBLE);
127 | mLlContainerMerge.setVisibility(View.GONE);
128 | switch (algorithmSelected) {
129 |
130 | case Constant.ALGORITHM_PUBBLE:
131 | SortArrayList.pubbleSort(unsortedValues, animationioList);
132 | break;
133 | case Constant.ALGORITHM_INSERT:
134 | SortArrayList.insertSort(unsortedValues, animationioList);
135 | break;
136 | case Constant.ALGORITHM_SELECT:
137 | SortArrayList.selectSort(unsortedValues, animationioList);
138 | break;
139 | case Constant.ALGORITHM_QUICK:
140 | keyList = new ArrayList<>();
141 | SortArrayList.quickSort(unsortedValues, 0, unsortedValues.size() - 1, animationioList, keyList);
142 | break;
143 | case Constant.ALGORITHM_HEER:
144 | SortArrayList.heerSort(unsortedValues, animationioList);
145 | break;
146 | case Constant.ALGORITHM_HEAP:
147 | SortArrayList.heapSort(unsortedValues, unsortedValues.size(), animationioList);
148 | break;
149 |
150 | }
151 |
152 | }
153 |
154 | private void resetPreviousData() {
155 | Log.e(TAG, "resetPreviousData");
156 | if (isAnimationRunning && animationsCoordinator != null) {
157 | animationsCoordinator.cancelAllVisualisations();
158 | isAnimationRunning = false;
159 | }
160 | scenarioItemIndex = 0;
161 | if (viewLine != null) {
162 | Log.e(TAG, "removeView viewLine!");
163 | mRlContainerParent.clearAnimation();
164 | mRlContainerParent.removeView(viewLine);
165 | }
166 | }
167 |
168 | @Override
169 | public View onCreateView(@Nullable LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
170 | Log.e(TAG, "onCreateView");
171 | view = inflater.inflate(R.layout.fragment_sort, container, false);
172 | mEtInput = view.findViewById(R.id.et_input);
173 | mBtnCode = view.findViewById(R.id.btn_code);
174 | mBtnCode.setOnClickListener(new View.OnClickListener() {
175 | @Override
176 | public void onClick(View v) {
177 | Intent intent = new Intent();
178 | intent.setClass(getActivity(), CodeActivity.class);
179 | intent.putExtra(Constant.EXTRA_CODE,algorithmSelected);
180 | getActivity().startActivity(intent);
181 | }
182 | });
183 | mBtnStart = view.findViewById(R.id.btn_start);
184 | mBtnStart.setOnClickListener(buttonClickListener);
185 | algorithmSpinner = view.findViewById(R.id.spinner_algorithm);
186 | algorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
187 | @Override
188 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
189 | Log.e(TAG, "algorithmSpinner:" + parent.getItemAtPosition(position).toString());
190 | switch (position) {
191 | case Constant.ALGORITHM_PUBBLE:
192 | algorithmSelected = Constant.ALGORITHM_PUBBLE;
193 | break;
194 | case Constant.ALGORITHM_INSERT:
195 | algorithmSelected = Constant.ALGORITHM_INSERT;
196 | break;
197 | case Constant.ALGORITHM_SELECT:
198 | algorithmSelected = Constant.ALGORITHM_SELECT;
199 | break;
200 | case Constant.ALGORITHM_QUICK:
201 | algorithmSelected = Constant.ALGORITHM_QUICK;
202 | break;
203 | case Constant.ALGORITHM_MERGE:
204 | algorithmSelected = Constant.ALGORITHM_MERGE;
205 | break;
206 | case Constant.ALGORITHM_HEER:
207 | algorithmSelected = Constant.ALGORITHM_HEER;
208 | break;
209 | case Constant.ALGORITHM_HEAP:
210 | algorithmSelected = Constant.ALGORITHM_HEAP;
211 | break;
212 | }
213 |
214 | }
215 |
216 | @Override
217 | public void onNothingSelected(AdapterView> parent) {
218 |
219 | }
220 | });
221 | algorithmSpinner.setSelection(Constant.ALGORITHM_HEER, true);
222 | mLlContainer = view.findViewById(R.id.ll_container);
223 | mRlContainerParent = view.findViewById(R.id.rl_container_parent);
224 | mLlContainerMerge = view.findViewById(R.id.ll_container_merge);
225 | mLlContainerOriginal = view.findViewById(R.id.ll_container_original);
226 | mLlContainerTemp = view.findViewById(R.id.ll_container_temp);
227 | mWidth = view.getMeasuredWidth();
228 | mRectHeight = view.getMeasuredHeight();
229 | animationsCoordinator = new AnimationsCoordinator(mLlContainer);
230 | animationsCoordinator.addListener(new AlgorithmAnimationListener() {
231 | @Override
232 | public void onSwapStepAnimationEnd(int endedPosition) {
233 | // Log.e(TAG, "addListener AlgorithmAnimationListener:runAnimationIteration!!!!");
234 | runAnimationIteration();
235 | }
236 | });
237 | mergeAnimationsCoordinator = new MergeAnimationsCoordinator(getActivity(), mLlContainerOriginal, mLlContainerTemp);
238 | mergeAnimationsCoordinator.addListener(new MergeAnimationListener() {
239 | @Override
240 | public void onSwapStepAnimationEnd(int endedPosition) {
241 | // Log.e(TAG, "addListener AlgorithmAnimationListener:runAnimationIteration!!!!");
242 | runAnimationIterationMerge();
243 | }
244 | });
245 | return view;
246 | }
247 |
248 | private void runAnimationIteration() {
249 | // Log.e(TAG, "runAnimationIteration");
250 | isAnimationRunning = true;
251 | if (animationList != null && animationList.size() == scenarioItemIndex) {
252 | animationsCoordinator.showFinish();
253 | return;
254 | }
255 | if (animationList != null && !animationList.isEmpty() && animationList.size() > scenarioItemIndex) {
256 |
257 | AnimationScenarioItem animationStep = animationList.get(scenarioItemIndex);
258 | scenarioItemIndex++;
259 | if (keyList != null && !keyList.isEmpty() && keyList.size() > scenarioItemIndex) {
260 | int height = lineHeightArray.get(keyList.get(scenarioItemIndex));
261 | if (height < minHeight) {
262 | viewLine.setY(minHeight);
263 | } else {
264 | viewLine.setY(height);
265 | }
266 | }
267 | if (animationStep.isShouldBeSwapped()) {
268 | animationsCoordinator.showSwapStep(animationStep.getCurPosition(), animationStep.getNextPosition(), animationStep.isFinalPlace());
269 | } else {
270 | animationsCoordinator.showNonSwapStep(animationStep.getCurPosition(), animationStep.getNextPosition(), animationStep.isFinalPlace());
271 | }
272 | }
273 |
274 | }
275 |
276 | private void runAnimationIterationMerge() {
277 | // Log.e(TAG, "runAnimationIterationMerge");
278 | isAnimationRunning = true;
279 | if (mergeAnimationList != null && mergeAnimationList.size() == scenarioItemIndex) {
280 | mergeAnimationsCoordinator.showFinish();
281 | return;
282 | }
283 | if (mergeAnimationList != null && !mergeAnimationList.isEmpty() && mergeAnimationList.size() > scenarioItemIndex) {
284 |
285 | MergeAnimationScenarioItem mergeAnimationStep = mergeAnimationList.get(scenarioItemIndex);
286 | scenarioItemIndex++;
287 | if (mergeAnimationStep.isMerge()) {
288 | mergeAnimationsCoordinator.mergeOriginalView(mergeAnimationStep.getOriginalPosition(), mergeAnimationStep.getTempPosition(), true);
289 | } else {
290 | mergeAnimationsCoordinator.createTempView(mergeAnimationStep.getOriginalPosition(), mergeAnimationStep.getTempPosition(), false);
291 | }
292 | }
293 |
294 | }
295 |
296 | private void drawRects(ArrayList listToDraw) {
297 | Log.e(TAG, "drawRects");
298 | if (mLlContainer != null) {
299 | mLlContainer.removeAllViews();
300 | mLlContainer.clearAnimation();
301 | }
302 |
303 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
304 | int marginInPx = Util.dpToPx(getContext(), RECT_MARGIN);
305 | lp.setMargins(0, 0, marginInPx, 0);
306 | int pos = 0;
307 | mWidth = view.getMeasuredWidth();
308 | mRectWidth = mWidth / rectCount - RECT_MARGIN;
309 | for (Integer currentIntValue : listToDraw) {
310 | RectView rectView = new RectView(getContext());
311 | rectView.setImageBitmap(createCalculatedBitmap(mRectWidth, currentIntValue));
312 | rectView.setMinimumHeight(minHeight);//避免0等较小数值,没有高度
313 | rectView.setNumber(currentIntValue);
314 | rectView.setId(pos);
315 | if (mLlContainer != null) {
316 | mLlContainer.addView(rectView, lp);
317 | }
318 | pos++;
319 | }
320 | //快速排序中,绘制关键字比较线
321 | if (algorithmSelected == Constant.ALGORITHM_QUICK) {
322 | RelativeLayout.LayoutParams lpLine = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
323 | lpLine.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
324 | lpLine.addRule(RelativeLayout.ALIGN_PARENT_TOP);
325 | LayoutInflater inflater = LayoutInflater.from(getActivity());
326 | viewLine = (LinearLayout) inflater.inflate(R.layout.view_line, null);
327 | mRlContainerParent.addView(viewLine, lpLine);
328 | }
329 | }
330 |
331 | private void drawRectsMerge(ArrayList listToDraw) {
332 | Log.e(TAG, "drawRectsMerge");
333 | if (mLlContainerOriginal != null) {
334 | mLlContainerOriginal.removeAllViews();
335 | mLlContainerOriginal.clearAnimation();
336 | }
337 | if (mLlContainerTemp != null) {
338 | mLlContainerTemp.removeAllViews();
339 | mLlContainerTemp.clearAnimation();
340 | }
341 |
342 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
343 | int marginInPx = Util.dpToPx(getContext(), RECT_MARGIN);
344 | lp.setMargins(0, 0, marginInPx, 0);
345 | int pos = 0;
346 | mWidth = view.getMeasuredWidth();
347 | mRectWidth = mWidth / rectCount - RECT_MARGIN;
348 | for (Integer currentIntValue : listToDraw) {
349 | RectView rectView = new RectView(getContext());
350 | rectView.setImageBitmap(createCalculatedBitmap(mRectWidth, currentIntValue));
351 | rectView.setMinimumHeight(minHeight);//避免0等较小数值,没有高度
352 | rectView.setNumber(currentIntValue);
353 | rectView.setId(pos);
354 | mLlContainerOriginal.addView(rectView, lp);
355 | pos++;
356 | }
357 | }
358 |
359 | /**
360 | * Calculates size of ImageView which would be generated with current text value.
361 | *
362 | * @param currentIntValue
363 | * @return empty bitmap with calculated size
364 | */
365 | private Bitmap createCalculatedBitmap(int mRectWidth, Integer currentIntValue) {
366 | //maxRectHeight-minRectHeight+1:+1 是因为计算的是距离,否则最高的矩形还是会超出屏幕,minRectHeight即minIntValue
367 | //4/5 是为了使最高高度不超出屏幕,否则当最大值为比较的关键值时,快拍的关键字线viewLine将超出视野,
368 | mRectHeight = view.getMeasuredHeight() * 4 / 5 * (currentIntValue - minRectHeight) / (maxRectHeight - minRectHeight + 1) + 1;
369 | lineHeightArray.put(currentIntValue, mRectHeight);
370 | return Bitmap.createBitmap(mRectWidth, mRectHeight, Bitmap.Config.ALPHA_8);
371 | }
372 |
373 | }
374 |
--------------------------------------------------------------------------------
/app/src/main/java/pri/weiqiang/sortanimation/util/Util.java:
--------------------------------------------------------------------------------
1 | package pri.weiqiang.sortanimation.util;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 |
6 | import java.util.ArrayList;
7 |
8 | public class Util {
9 |
10 | public static int dpToPx(Context context, int sizeInDp) {
11 | float scale = context.getResources().getDisplayMetrics().density;
12 | return (int) (sizeInDp * scale + 0.5f);
13 | }
14 |
15 | public static ArrayList convertToIntArray(String input) {
16 | ArrayList integerList = new ArrayList<>();
17 | String[] stringArray = input.split(",");
18 | int len = stringArray.length;
19 | for (int i = 0; i < len; i++) {
20 | if (!TextUtils.isEmpty(stringArray[i])) {
21 | int height = Integer.parseInt(stringArray[i]);
22 | integerList.add(height);
23 | }
24 | }
25 | return integerList;
26 |
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_code.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
15 |
16 |
20 |
21 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_sort.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_sort.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
25 |
26 |
31 |
32 |
41 |
42 |
51 |
52 |
53 |
54 |
55 |
61 |
62 |
68 |
69 |
70 |
71 |
77 |
78 |
85 |
86 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/array.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 冒泡
5 | - 插入
6 | - 选择
7 | - 快速
8 | - 归并
9 | - 希尔
10 | - 堆
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF5722
4 | #E64A19
5 | #FF9800
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF5722
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SortAnimation
3 | 排序
4 | 代码
5 | 请输入数据分割符为","
6 | 排序完成
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/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.3.0'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | }
22 | }
23 |
24 | task clean(type: Delete) {
25 | delete rootProject.buildDir
26 | }
27 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.enableJetifier=true
10 | android.useAndroidX=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | # When configured, Gradle will run in incubating parallel mode.
13 | # This option should only be used with decoupled projects. More details, visit
14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
15 | # org.gradle.parallel=true
16 |
17 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 28 15:42:40 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/screenshot/bubble_ukhanoff.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/bubble_ukhanoff.gif
--------------------------------------------------------------------------------
/screenshot/heap.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/heap.gif
--------------------------------------------------------------------------------
/screenshot/heer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/heer.gif
--------------------------------------------------------------------------------
/screenshot/insert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/insert.gif
--------------------------------------------------------------------------------
/screenshot/merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/merge.gif
--------------------------------------------------------------------------------
/screenshot/pubble.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/pubble.gif
--------------------------------------------------------------------------------
/screenshot/quick.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/quick.gif
--------------------------------------------------------------------------------
/screenshot/select.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/54wall/SortAnimation/e76667db2747ab6bd3622b5554f51a3c497db364/screenshot/select.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------