imagePieces = new ArrayList<>();
53 | int width = bitmap.getWidth();
54 | int height = bitmap.getHeight();
55 |
56 | int picWidth = Math.min(width, height) / count;
57 |
58 | for (int i = 0; i < count; i++) {
59 | for (int j = 0; j < count; j++) {
60 | ImagePiece imagePiece = new ImagePiece();
61 | imagePiece.setIndex(j + i * count);
62 | //为createBitmap 切割图片获取xy
63 | int x = j * picWidth;
64 | int y = i * picWidth;
65 | if (gameMode.equals(PuzzleLayout.GAME_MODE_NORMAL)) {
66 | if (i == count - 1 && j == count - 1) {
67 | imagePiece.setType(ImagePiece.TYPE_EMPTY);
68 | Bitmap emptyBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.empty);
69 | imagePiece.setBitmap(emptyBitmap);
70 | } else {
71 | imagePiece.setBitmap(Bitmap.createBitmap(bitmap, x, y, picWidth, picWidth));
72 | }
73 | } else {
74 | imagePiece.setBitmap(Bitmap.createBitmap(bitmap, x, y, picWidth, picWidth));
75 | }
76 | imagePieces.add(imagePiece);
77 | }
78 | }
79 | return imagePieces;
80 | }
81 |
82 | /**
83 | * 读取图片,按照缩放比保持长宽比例返回bitmap对象
84 | *
85 | *
86 | * @param scale 缩放比例(1到10, 为2时,长和宽均缩放至原来的2分之1,为3时缩放至3分之1,以此类推)
87 | * @return Bitmap
88 | */
89 | public synchronized static Bitmap readBitmap(Context context, int res, int scale) {
90 | try {
91 | BitmapFactory.Options options = new BitmapFactory.Options();
92 | options.inJustDecodeBounds = false;
93 | options.inSampleSize = scale;
94 | options.inPurgeable = true;
95 | options.inInputShareable = true;
96 | options.inPreferredConfig = Bitmap.Config.RGB_565;
97 | return BitmapFactory.decodeResource(context.getResources(), res, options);
98 | } catch (Exception e) {
99 | return null;
100 | }
101 | }
102 |
103 | public static int getMinLength(int... params) {
104 | int min = params[0];
105 | for (int para : params) {
106 | if (para < min) {
107 | min = para;
108 | }
109 | }
110 | return min;
111 | }
112 |
113 | //dp px
114 | public static int dp2px(Context context, int dpval) {
115 | context = context.getApplicationContext();
116 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpval, context.getResources().getDisplayMetrics());
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/PuzzleGame/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 |
--------------------------------------------------------------------------------
/PuzzleGame/app/src/main/java/com/mg/axe/puzzlegame/ui/PuzzleLayout.java:
--------------------------------------------------------------------------------
1 | package com.mg.axe.puzzlegame.ui;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ObjectAnimator;
7 | import android.content.Context;
8 | import android.graphics.Bitmap;
9 | import android.graphics.BitmapFactory;
10 | import android.graphics.Color;
11 | import android.support.annotation.NonNull;
12 | import android.util.AttributeSet;
13 | import android.view.View;
14 | import android.widget.FrameLayout;
15 | import android.widget.ImageView;
16 | import android.widget.RelativeLayout;
17 | import android.widget.Toast;
18 |
19 | import com.mg.axe.puzzlegame.R;
20 | import com.mg.axe.puzzlegame.Utils.Utils;
21 | import com.mg.axe.puzzlegame.module.ImagePiece;
22 |
23 | import java.util.Collections;
24 | import java.util.Comparator;
25 | import java.util.List;
26 |
27 | /**
28 | * @Author Zaifeng
29 | * @Create 2017/7/13 0013
30 | * @Description Content
31 | */
32 |
33 | public class PuzzleLayout extends FrameLayout implements View.OnClickListener {
34 |
35 | public static final String GAME_MODE_NORMAL = "gameModeNormal";
36 | public static final String GAME_MODE_EXCHANGE = "gameModeExchange";
37 |
38 | private static final int DEFAULT_MARGIN = 3;
39 |
40 | //游戏模式
41 | private String mGameMode = GAME_MODE_EXCHANGE;
42 |
43 | //拼图布局为正方形,宽度为屏幕的宽度
44 | private int mViewWidth = 0;
45 |
46 | //拼图游戏每一行的图片个数(默认为三个)
47 | private int mCount = 3;
48 |
49 | //每张图片的宽度
50 | private int mItemWidth;
51 |
52 | //拼图游戏bitmap集合
53 | private List mImagePieces;
54 |
55 | //用于给每个图片设置大小
56 | private FrameLayout.LayoutParams layoutParams;
57 |
58 | //大图
59 | private Bitmap mBitmap;
60 |
61 | //动画层
62 | private RelativeLayout mAnimLayout;
63 |
64 | //小图之间的margin
65 | private int mMargin;
66 |
67 | //这个view的padding
68 | private int mPadding;
69 |
70 | //选中的第一张图片
71 | private ImageView mFirst;
72 |
73 | //选中的第二张图片
74 | private ImageView mSecond;
75 |
76 | //是否添加了动画层
77 | private boolean isAddAnimatorLayout = false;
78 |
79 | //是否正在进行动画
80 | private boolean isAnimation = false;
81 |
82 | private int res = R.mipmap.sdhy;
83 |
84 | public PuzzleLayout(Context context) {
85 | this(context, null);
86 | }
87 |
88 | public PuzzleLayout(Context context, AttributeSet attrs) {
89 | this(context, attrs, 0);
90 | }
91 |
92 | public PuzzleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
93 | super(context, attrs, defStyleAttr);
94 | init(context);
95 | initBitmaps();
96 | initBitmapsWidth();
97 | }
98 |
99 | @Override
100 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
101 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
102 | setMeasuredDimension(mViewWidth, mViewWidth);
103 | }
104 |
105 | @Override
106 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
107 | for (int i = 0; i < getChildCount(); i++) {
108 | if (getChildAt(i) instanceof ImageView) {
109 | ImageView imageView = (ImageView) getChildAt(i);
110 | imageView.layout(imageView.getLeft(), imageView.getTop(), imageView.getRight(), imageView.getBottom());
111 | } else {
112 | RelativeLayout relativeLayout = (RelativeLayout) getChildAt(i);
113 | relativeLayout.layout(0, 0, mViewWidth, mViewWidth);
114 | }
115 | }
116 | }
117 |
118 | /**
119 | * 初始化初始变量
120 | *
121 | * @param context
122 | */
123 | private void init(Context context) {
124 | mMargin = Utils.dp2px(context, DEFAULT_MARGIN);
125 | mViewWidth = Utils.getScreenWidth(context)[0];
126 | mPadding = Utils.getMinLength(getPaddingBottom(), getPaddingLeft(), getPaddingRight(), getPaddingTop());
127 | mItemWidth = (mViewWidth - mPadding * 2 - mMargin * (mCount - 1)) / mCount;
128 | }
129 |
130 | /**
131 | * 将大图切割成多个小图
132 | */
133 | private void initBitmaps() {
134 | if (mBitmap == null) {
135 | mBitmap = BitmapFactory.decodeResource(getResources(), res);
136 | }
137 | mImagePieces = Utils.splitImage(getContext(), mBitmap, mCount, mGameMode);
138 | sortImagePieces();
139 | }
140 |
141 | /**
142 | * 对ImagePieces进行排序
143 | */
144 | private void sortImagePieces() {
145 | try {
146 | Collections.sort(mImagePieces, new Comparator() {
147 | @Override
148 | public int compare(ImagePiece lhs, ImagePiece rhs) {
149 | return Math.random() > 0.5 ? 1 : -1;
150 | }
151 | });
152 | } catch (Exception e) {
153 | e.printStackTrace();
154 | } finally {
155 | if (mGameMode.equals(GAME_MODE_NORMAL)) {
156 | //如果是第二种模式就将空图放在最后
157 | ImagePiece tempImagePieces = null;
158 | int tempIndex = 0;
159 | for (int i = 0; i < mImagePieces.size(); i++) {
160 | ImagePiece imagePiece = mImagePieces.get(i);
161 | if (imagePiece.getType() == ImagePiece.TYPE_EMPTY) {
162 | tempImagePieces = imagePiece;
163 | tempIndex = i;
164 | break;
165 | }
166 | }
167 | if (tempImagePieces == null) return;
168 | mImagePieces.remove(tempIndex);
169 | mImagePieces.add(mImagePieces.size(), tempImagePieces);
170 | }
171 | }
172 | }
173 |
174 | /**
175 | * 设置图片的大小和layout的属性
176 | */
177 | private void initBitmapsWidth() {
178 | int line = 0;
179 | int left = 0;
180 | int top = 0;
181 | int right = 0;
182 | int bottom = 0;
183 | for (int i = 0; i < mImagePieces.size(); i++) {
184 | ImageView imageView = new ImageView(getContext());
185 | imageView.setImageBitmap(mImagePieces.get(i).getBitmap());
186 | layoutParams = new LayoutParams(mItemWidth, mItemWidth);
187 | imageView.setLayoutParams(layoutParams);
188 | if (i != 0 && i % mCount == 0) {
189 | line++;
190 | }
191 | if (i % mCount == 0) {
192 | left = i % mCount * mItemWidth;
193 | } else {
194 | left = i % mCount * mItemWidth + (i % mCount) * mMargin;
195 | }
196 | top = mItemWidth * line + line * mMargin;
197 | right = left + mItemWidth;
198 | bottom = top + mItemWidth;
199 | imageView.setRight(right);
200 | imageView.setLeft(left);
201 | imageView.setBottom(bottom);
202 | imageView.setTop(top);
203 | imageView.setId(i);
204 | imageView.setOnClickListener(this);
205 | mImagePieces.get(i).setImageView(imageView);
206 | addView(imageView);
207 | }
208 | }
209 |
210 | /**
211 | * 改变游戏模式
212 | *
213 | * @param gameMode
214 | */
215 | public void changeMode(@NonNull String gameMode) {
216 | if (gameMode.equals(mGameMode)) {
217 | return;
218 | }
219 | this.mGameMode = gameMode;
220 | reset();
221 | }
222 |
223 | public void reset() {
224 | mItemWidth = (mViewWidth - mPadding * 2 - mMargin * (mCount - 1)) / mCount;
225 | if (mImagePieces != null) {
226 | mImagePieces.clear();
227 | }
228 | isAddAnimatorLayout = false;
229 | mBitmap = null;
230 | removeAllViews();
231 | initBitmaps();
232 | initBitmapsWidth();
233 | }
234 |
235 | /**
236 | * 添加count 最多每行7个
237 | */
238 | public boolean addCount() {
239 | mCount++;
240 | if (mCount > 7) {
241 | mCount--;
242 | return false;
243 | }
244 | reset();
245 | return true;
246 | }
247 |
248 | /**
249 | * 改变图片
250 | */
251 | public void changeRes(int res) {
252 | this.res = res;
253 | reset();
254 | }
255 |
256 | /**
257 | * 减少count 最少每行三个,否则普通模式无法游戏
258 | */
259 | public boolean reduceCount() {
260 | mCount--;
261 | if (mCount < 3) {
262 | mCount++;
263 | return false;
264 | }
265 | reset();
266 | return true;
267 | }
268 |
269 | @Override
270 | public void onClick(View v) {
271 | if (isAnimation) {
272 | //还在运行动画的时候,不允许点击
273 | return;
274 | }
275 | if (!(v instanceof ImageView)) {
276 | return;
277 | }
278 | if (GAME_MODE_NORMAL.equals(mGameMode)) {
279 | ImageView imageView = (ImageView) v;
280 | ImagePiece imagePiece = mImagePieces.get(imageView.getId());
281 | if (imagePiece.getType() == ImagePiece.TYPE_EMPTY) {
282 | //普通模式,点击到空图不做处理
283 | return;
284 | }
285 | if (mFirst == null) {
286 | mFirst = (ImageView) v;
287 | }
288 | checkEmptyImage(mFirst);
289 | } else {
290 | //点的是同一个View
291 | if (mFirst == v) {
292 | mFirst.setColorFilter(null);
293 | mFirst = null;
294 | return;
295 | }
296 | if (mFirst == null) {
297 | mFirst = (ImageView) v;
298 | //选中之后添加一层颜色
299 | mFirst.setColorFilter(Color.parseColor("#55FF0000"));
300 | } else {
301 | mSecond = (ImageView) v;
302 | exChangeView();
303 | }
304 | }
305 | }
306 |
307 | private void checkEmptyImage(ImageView imageView) {
308 | int index = imageView.getId();
309 | int line = mImagePieces.size() / mCount;
310 | ImagePiece imagePiece = null;
311 | if (index < mCount) {
312 | //第一行(需要额外计算,下一行是否有空图)
313 | imagePiece = checkCurrentLine(index);
314 | //判断下一行同一列的图片是否为空
315 | imagePiece = checkOtherline(index + mCount, imagePiece);
316 | } else if (index < (line - 1) * mCount) {
317 | //中间的行(需要额外计算,上一行和下一行是否有空图)
318 | imagePiece = checkCurrentLine(index);
319 | //判断上一行同一列的图片是否为空
320 | imagePiece = checkOtherline(index - mCount, imagePiece);
321 | //判断下一行同一列的图片是否为空
322 | imagePiece = checkOtherline(index + mCount, imagePiece);
323 | } else {
324 | //最后一行(需要额外计算,上一行是否有空图))
325 | imagePiece = checkCurrentLine(index);
326 | //检查上一行同一列有没有空图
327 | imagePiece = checkOtherline(index - mCount, imagePiece);
328 | }
329 | if (imagePiece == null) {
330 | //周围没有空的imageView
331 | mFirst = null;
332 | mSecond = null;
333 | } else {
334 | //记录下第二张ImageView
335 | mSecond = imagePiece.getImageView();
336 | //选中第二个图片,开启动两张图片替换的动画
337 | exChangeView();
338 | }
339 | }
340 |
341 |
342 | /**
343 | * 检查上其他行同一列有没有空图
344 | *
345 | * @return
346 | */
347 | private ImagePiece checkOtherline(int index, ImagePiece imagePiece) {
348 | if (imagePiece != null) {
349 | return imagePiece;
350 | } else {
351 | return getCheckEmptyImageView(index);
352 | }
353 | }
354 |
355 | /**
356 | * 检查当前行有没有空的图片
357 | *
358 | * @param index
359 | * @return
360 | */
361 | private ImagePiece checkCurrentLine(int index) {
362 | ImagePiece imagePiece = null;
363 | //第一行
364 | if (index % mCount == 0) {
365 | //第一个
366 | imagePiece = getCheckEmptyImageView(index + 1);
367 | } else if (index % mCount == mCount - 1) {
368 | //最后一个
369 | imagePiece = getCheckEmptyImageView(index - 1);
370 | } else {
371 | imagePiece = getCheckEmptyImageView(index + 1);
372 | if (imagePiece == null) {
373 | imagePiece = getCheckEmptyImageView(index - 1);
374 | }
375 | }
376 | return imagePiece;
377 | }
378 |
379 | private ImagePiece getCheckEmptyImageView(int index) {
380 | ImagePiece imagePiece = mImagePieces.get(index);
381 | if (imagePiece.getType() == ImagePiece.TYPE_EMPTY) {
382 | //找到空的imageView
383 | return imagePiece;
384 | }
385 | return null;
386 | }
387 |
388 | private ImageView addAnimationImageView(ImageView imageView) {
389 | ImageView getImage = new ImageView(getContext());
390 | RelativeLayout.LayoutParams firstParams = new RelativeLayout.LayoutParams(mItemWidth, mItemWidth);
391 | firstParams.leftMargin = imageView.getLeft() - mPadding;
392 | firstParams.topMargin = imageView.getTop() - mPadding;
393 | Bitmap firstBitmap = mImagePieces.get(imageView.getId()).getBitmap();
394 | getImage.setImageBitmap(firstBitmap);
395 | getImage.setLayoutParams(firstParams);
396 | mAnimLayout.addView(getImage);
397 | return getImage;
398 | }
399 |
400 | /**
401 | * 添加动画层,并且添加平移的动画
402 | */
403 | private void exChangeView() {
404 |
405 | //添加动画层
406 | setUpAnimLayout();
407 | //添加第一个图片
408 | ImageView first = addAnimationImageView(mFirst);
409 | //添加另一个图片
410 | ImageView second = addAnimationImageView(mSecond);
411 |
412 | ObjectAnimator secondXAnimator = ObjectAnimator.ofFloat(second, "TranslationX", 0f, -(mSecond.getLeft() - mFirst.getLeft()));
413 | ObjectAnimator secondYAnimator = ObjectAnimator.ofFloat(second, "TranslationY", 0f, -(mSecond.getTop() - mFirst.getTop()));
414 | ObjectAnimator firstXAnimator = ObjectAnimator.ofFloat(first, "TranslationX", 0f, mSecond.getLeft() - mFirst.getLeft());
415 | ObjectAnimator firstYAnimator = ObjectAnimator.ofFloat(first, "TranslationY", 0f, mSecond.getTop() - mFirst.getTop());
416 | AnimatorSet secondAnimator = new AnimatorSet();
417 | secondAnimator.play(secondXAnimator).with(secondYAnimator).with(firstXAnimator).with(firstYAnimator);
418 | secondAnimator.setDuration(300);
419 |
420 | final ImagePiece firstPiece = mImagePieces.get(mFirst.getId());
421 | final ImagePiece secondPiece = mImagePieces.get(mSecond.getId());
422 | final int firstType = firstPiece.getType();
423 | final int secondType = secondPiece.getType();
424 | final Bitmap firstBitmap = mImagePieces.get(mFirst.getId()).getBitmap();
425 | final Bitmap secondBitmap = mImagePieces.get(mSecond.getId()).getBitmap();
426 | // final int firstIndex = mImagePieces.get(mFirst.getId()).getIndex();
427 | // final int secondIndex = mImagePieces.get(mFirst.getId()).getIndex();
428 | secondAnimator.addListener(new AnimatorListenerAdapter() {
429 | @Override
430 | public void onAnimationEnd(Animator animation) {
431 | super.onAnimationEnd(animation);
432 | int fristIndex = firstPiece.getIndex();
433 | int secondeIndex = secondPiece.getIndex();
434 | if (mFirst != null) {
435 | mFirst.setColorFilter(null);
436 | mFirst.setVisibility(VISIBLE);
437 | mFirst.setImageBitmap(secondBitmap);
438 | firstPiece.setBitmap(secondBitmap);
439 | firstPiece.setIndex(secondeIndex);
440 | }
441 | if (mSecond != null) {
442 | mSecond.setVisibility(VISIBLE);
443 | mSecond.setImageBitmap(firstBitmap);
444 | secondPiece.setBitmap(firstBitmap);
445 | secondPiece.setIndex(fristIndex);
446 | }
447 | if (mGameMode.equals(GAME_MODE_NORMAL)) {
448 | firstPiece.setType(secondType);
449 | secondPiece.setType(firstType);
450 | }
451 |
452 | mAnimLayout.removeAllViews();
453 | mAnimLayout.setVisibility(GONE);
454 | mFirst = null;
455 | mSecond = null;
456 | isAnimation = false;
457 | invalidate();
458 | if (checkSuccess()) {
459 | Toast.makeText(getContext(), "成功!", Toast.LENGTH_SHORT).show();
460 | if (mSuccessListener != null) {
461 | mSuccessListener.success();
462 | }
463 | }
464 | }
465 |
466 | @Override
467 | public void onAnimationStart(Animator animation) {
468 | super.onAnimationStart(animation);
469 | isAnimation = true;
470 | mAnimLayout.setVisibility(VISIBLE);
471 | mFirst.setVisibility(INVISIBLE);
472 | mSecond.setVisibility(INVISIBLE);
473 | }
474 | });
475 | secondAnimator.start();
476 | }
477 |
478 | /**
479 | * 构造动画层 用于点击之后的动画
480 | * 为什么要做动画层? 要保证动画在整个view上面执行。
481 | */
482 | private void setUpAnimLayout() {
483 | if (mAnimLayout == null) {
484 | mAnimLayout = new RelativeLayout(getContext());
485 | }
486 | if (!isAddAnimatorLayout) {
487 | isAddAnimatorLayout = true;
488 | addView(mAnimLayout);
489 | }
490 | }
491 |
492 | /**
493 | * 检测是否成功
494 | */
495 | private boolean checkSuccess() {
496 |
497 | boolean isSuccess = true;
498 | for (int i = 0; i < mImagePieces.size(); i++) {
499 | ImagePiece imagePiece = mImagePieces.get(i);
500 | if (i != imagePiece.getIndex()) {
501 | isSuccess = false;
502 | }
503 | }
504 | return isSuccess;
505 | }
506 |
507 | public Bitmap getBitmap() {
508 | return mBitmap;
509 | }
510 |
511 | public int getRes() {
512 | return res;
513 | }
514 |
515 | public int getCount() {
516 | return mCount;
517 | }
518 |
519 | private SuccessListener mSuccessListener;
520 |
521 | public void addSuccessListener(SuccessListener successListener) {
522 | this.mSuccessListener = successListener;
523 | }
524 |
525 | public interface SuccessListener {
526 | public void success();
527 | }
528 | }
529 |
--------------------------------------------------------------------------------