├── .gitignore
├── AndroidManifest.xml
├── README.md
├── build.gradle
├── download
├── AndroidAnimationDemo-release.apk
└── dimensionalcode.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── ic_launcher-web.png
├── key.jks
├── libs
├── android-support-v4.jar
└── android-support-v4.jar.properties
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ ├── guide_b_bigcloud.png
│ ├── guide_b_bigwords.png
│ ├── guide_b_phone.9.png
│ ├── guide_b_smallcloud.png
│ ├── guide_b_smallwords.png
│ ├── guide_c_bigcloud.png
│ ├── guide_c_feed_1136.png
│ ├── guide_c_leftwords.png
│ ├── guide_c_rightwords.png
│ ├── guide_c_smallcloud.png
│ ├── guide_c_video_pause_1136.png
│ ├── guide_c_video_pause_960.png
│ ├── guide_d_bigcloud.png
│ ├── guide_d_bigwords.png
│ ├── guide_d_earth.png
│ ├── guide_d_earth_cloud.png
│ ├── guide_d_man.png
│ ├── guide_swipe.png
│ ├── ic_launcher.png
│ ├── login_button_big_orange.9.png
│ └── login_button_big_orange_highlighted.9.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── drawable
│ └── login_btn_bg.xml
├── layout
│ ├── activity_demo.xml
│ ├── activity_main.xml
│ ├── layout_tutorial_1.xml
│ ├── layout_tutorial_2.xml
│ ├── layout_tutorial_3.xml
│ └── layout_tutorial_4.xml
├── raw
│ ├── biz_news_guide_version40.mp4
│ └── guide_a_motion.mp4
├── values-w820dp
│ └── dimens.xml
└── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── screenshots
├── movie2.mp4
└── myaa1.mov_1409889442.gif
└── src
└── com
└── kongnan
└── weibo
├── DemoActivity.java
├── MainActivity.java
├── MyAnimatorSet.java
├── MyImageView.java
├── MyInterpolator.java
├── MyVideoImageView.java
├── MyViewGroup.java
├── MyViewPager.java
└── Util.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | #*.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 | .DS_Store
11 |
12 | # generated files
13 | bin/
14 | gen/
15 |
16 | # Local configuration file (sdk path, etc)
17 | local.properties
18 |
19 | # Eclipse project files
20 | .classpath
21 | .project
22 | .settings/
23 |
24 | # Proguard folder generated by Eclipse
25 | proguard/
26 |
27 | #Android Studio
28 | build/
29 |
30 | # Intellij project files
31 | *.iml
32 | *.ipr
33 | *.iws
34 | .idea/
35 | .metadata/
36 |
37 | #gradle
38 | .gradle/
39 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ###AndroidAnimationDemo
2 | -----
3 | Android Animation Demo
4 |
5 | Warning!!!
6 |
7 | The project is only in the Samsung Note 3 Android 4.4 on the test is successful!
8 | 本项目仅在三星 Note 3 Android 4.4上测试成功!
9 |
10 | 更新
11 | 可以配合[facebook rebound](https://github.com/facebook/rebound)跟[JakeWharton NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids)一起,就可以支持Android2.3+
12 |
13 | Screenshot 截图
14 |
15 | 
16 |
17 | ---
18 |
19 | ###Demo
20 |
21 | 
22 |
23 | [Download Demo](https://github.com/kongnanlive/AndroidAnimationDemo/raw/master/download/AndroidAnimationDemo-release.apk)
24 |
25 | [YouTube Video URL](http://youtu.be/9r5MYCzO6fs)
26 |
27 | [Yuku Video URL](http://v.youku.com/v_show/id_XNzc0MTgxMDg4.html)
28 |
29 | ### About me:
30 |
31 | 我来自孔孟之乡,六年代码生涯,略懂java、flex、php、javascript、html5、android等开发语言,热爱移动互联,目前从事Android开发
32 |
33 | ### End
34 |
35 | 图片借用至新浪微博iPhone手机版 [App Store URL](https://itunes.apple.com/us/app/weibo/id350962117),在此表示真心感谢!如有侵权,还望告知[Email:kongnanlive@foxmail.com](mailto:kongnanlive@foxmail.com)。
36 |
37 |
38 | ## License
39 | Copyright (c) 2014 [Kong Nan](http://weibo.com/kongnan)
40 |
41 | Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
42 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 | dependencies {
6 | classpath 'com.android.tools.build:gradle:0.12.+'
7 | }
8 | }
9 | apply plugin: 'android'
10 |
11 | dependencies {
12 | compile fileTree(dir: 'libs', include: '*.jar')
13 | }
14 |
15 | android {
16 | compileSdkVersion 20
17 | buildToolsVersion "20.0.0"
18 |
19 | sourceSets {
20 | main {
21 | manifest.srcFile 'AndroidManifest.xml'
22 | java.srcDirs = ['src']
23 | resources.srcDirs = ['src']
24 | aidl.srcDirs = ['src']
25 | renderscript.srcDirs = ['src']
26 | res.srcDirs = ['res']
27 | assets.srcDirs = ['assets']
28 | }
29 |
30 | // Move the tests to tests/java, tests/res, etc...
31 | instrumentTest.setRoot('tests')
32 |
33 | // Move the build types to build-types/
34 | // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
35 | // This moves them out of them default location under src//... which would
36 | // conflict with src/ being used by the main source set.
37 | // Adding new build types or product flavors should be accompanied
38 | // by a similar customization.
39 | debug.setRoot('build-types/debug')
40 | release.setRoot('build-types/release')
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/download/AndroidAnimationDemo-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/download/AndroidAnimationDemo-release.apk
--------------------------------------------------------------------------------
/download/dimensionalcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/download/dimensionalcode.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/ic_launcher-web.png
--------------------------------------------------------------------------------
/key.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/key.jks
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/android-support-v4.jar.properties:
--------------------------------------------------------------------------------
1 | src=/Users/kongnan/Library/adt-bundle-mac/sdk/extras/android/support/v4/src
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-20
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_b_bigcloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_b_bigcloud.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_b_bigwords.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_b_bigwords.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_b_phone.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_b_phone.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_b_smallcloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_b_smallcloud.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_b_smallwords.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_b_smallwords.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_c_bigcloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_c_bigcloud.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_c_feed_1136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_c_feed_1136.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_c_leftwords.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_c_leftwords.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_c_rightwords.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_c_rightwords.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_c_smallcloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_c_smallcloud.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_c_video_pause_1136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_c_video_pause_1136.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_c_video_pause_960.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_c_video_pause_960.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_d_bigcloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_d_bigcloud.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_d_bigwords.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_d_bigwords.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_d_earth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_d_earth.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_d_earth_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_d_earth_cloud.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_d_man.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_d_man.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/guide_swipe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/guide_swipe.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/login_button_big_orange.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/login_button_big_orange.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/login_button_big_orange_highlighted.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xhdpi/login_button_big_orange_highlighted.9.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable/login_btn_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/layout/activity_demo.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/res/layout/layout_tutorial_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
15 |
16 |
25 |
26 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/res/layout/layout_tutorial_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
15 |
16 |
20 |
21 |
26 |
27 |
36 |
37 |
46 |
47 |
48 |
49 |
65 |
74 |
75 |
84 |
85 |
93 |
94 |
--------------------------------------------------------------------------------
/res/layout/layout_tutorial_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
15 |
16 |
20 |
21 |
26 |
27 |
33 |
34 |
35 |
36 |
45 |
46 |
53 |
54 |
62 |
63 |
71 |
72 |
--------------------------------------------------------------------------------
/res/layout/layout_tutorial_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
26 |
27 |
33 |
34 |
42 |
43 |
51 |
52 |
62 |
63 |
75 |
76 |
--------------------------------------------------------------------------------
/res/raw/biz_news_guide_version40.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/raw/biz_news_guide_version40.mp4
--------------------------------------------------------------------------------
/res/raw/guide_a_motion.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/res/raw/guide_a_motion.mp4
--------------------------------------------------------------------------------
/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | 64dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WeiBo
5 | Hello world!
6 | Settings
7 | DemoActivity
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/screenshots/movie2.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/screenshots/movie2.mp4
--------------------------------------------------------------------------------
/screenshots/myaa1.mov_1409889442.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongnanlive/AndroidAnimationDemo/84e0dd7e3cc27dea03531da21473353ac159d4a4/screenshots/myaa1.mov_1409889442.gif
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/DemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import java.text.DateFormat;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Date;
6 |
7 | import android.animation.Animator;
8 | import android.animation.Animator.AnimatorPauseListener;
9 | import android.animation.Keyframe;
10 | import android.animation.ObjectAnimator;
11 | import android.animation.PropertyValuesHolder;
12 | import android.animation.ValueAnimator;
13 | import android.app.Activity;
14 | import android.content.Context;
15 | import android.graphics.Bitmap;
16 | import android.media.MediaMetadataRetriever;
17 | import android.media.MediaPlayer;
18 | import android.media.MediaPlayer.OnPreparedListener;
19 | import android.net.Uri;
20 | import android.os.Bundle;
21 | import android.os.SystemClock;
22 | import android.util.Log;
23 | import android.view.LayoutInflater;
24 | import android.view.View;
25 | import android.view.animation.AccelerateInterpolator;
26 | import android.view.animation.Animation;
27 | import android.view.animation.DecelerateInterpolator;
28 | import android.view.animation.LinearInterpolator;
29 | import android.widget.Chronometer;
30 | import android.widget.ImageView;
31 | import android.widget.ImageView.ScaleType;
32 | import android.widget.TextView;
33 | import android.widget.VideoView;
34 |
35 | import com.kongnan.weibo.MyAnimatorSet.MyAnimatorListener;
36 |
37 | public class DemoActivity extends Activity implements MyViewPager.OnPageChangeListener {
38 |
39 | private static final String TAG = DemoActivity.class.getSimpleName();
40 |
41 | // PageView 1
42 | private View mPageView1;
43 | private View mP1RootView;
44 | boolean isChronometerRunnig = false;
45 | int stoppedMilliseconds = 0;
46 | private Chronometer mP1Time;
47 | private VideoView mP1VideoView;// VideoView 无法旋转缩放,但是可以左右移动。
48 | private Uri mP1VVUriPath;
49 |
50 | private MyVideoImageView mVideoImageView;
51 |
52 | // PageView 2
53 | private boolean isP2Start = false;
54 | private boolean isP2Reverse = false;
55 |
56 | private MyAnimatorSet mOpenP2AnimatorSet;
57 | private MyAnimatorSet mCloseP2AnimatorSet;
58 | private View mPageView2;
59 | private View mP2RootView;
60 |
61 | private TextView mP2TimeTV;
62 |
63 | private ImageView mP2BBIV;
64 | private ImageView mP2BSIV;
65 |
66 | private ImageView mP2SWIV;
67 | private ImageView mP2BWIV;
68 | // PageView 3
69 | private boolean isP3Start = false;
70 | private boolean isP3Reverse = false;
71 |
72 | private MyAnimatorSet mOpenP3AnimatorSet;
73 | private MyAnimatorSet mCloseP3AnimatorSet;
74 | private View mPageView3;
75 | private View mP3RootView;
76 | private ImageView mP3SWIV;
77 |
78 | private ImageView mP3CBIV;
79 | private ImageView mP3CSIV;
80 |
81 | private ImageView mP3CLIV;
82 | private ImageView mP3CRIV;
83 |
84 | // PageView 4
85 | private boolean isP4Start = false;
86 | private boolean isP4Reverse = false;
87 |
88 | private MyAnimatorSet mOpenP4AnimatorSet;
89 | private MyAnimatorSet mCloseP4AnimatorSet;
90 | private View mPageView4;
91 | private MyAnimatorSet mP4AnimatorSet;
92 | private MyAnimatorSet mP4AnimatorSetAllInOne;
93 | private ImageView mP4EarthIV;
94 | private ImageView mP4EarthCloudIV;
95 | private ImageView mP4ManIV;
96 | private ImageView mP4DBIV;
97 | private ImageView mP4DBCIV;
98 | private TextView mP4DCTV;
99 | private ObjectAnimator mP4MantyBouncer;
100 |
101 | @Override
102 | protected void onCreate(Bundle savedInstanceState) {
103 | super.onCreate(savedInstanceState);
104 | getActionBar().hide();
105 |
106 | MyViewPager contentView = new MyViewPager(this);
107 | contentView.setBackgroundColor(0xFFc5f2fe);
108 | setContentView(contentView);
109 |
110 | contentView.setOnPageChangeListener(this);
111 |
112 | mPageView1 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_1, null);
113 |
114 | mP1RootView = mPageView1.findViewById(R.id.p1RootView);
115 | mP1VideoView = (VideoView) mPageView1.findViewById(R.id.p1VideoView);
116 | mP1VVUriPath = Uri.parse("android.resource://" + getPackageName() + "/"
117 | + R.raw.guide_a_motion);
118 |
119 | mP1Time = (Chronometer) mPageView1.findViewById(R.id.p1Chronometer);
120 |
121 | contentView.addView(mPageView1);
122 |
123 | mPageView2 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_2, null);
124 |
125 | mP2RootView = mPageView2.findViewById(R.id.p2RootView);
126 |
127 | mP2TimeTV = (TextView) mPageView2.findViewById(R.id.p2time);
128 |
129 | mP2BBIV = (ImageView) mPageView2.findViewById(R.id.p2bbIV);
130 | mP2BSIV = (ImageView) mPageView2.findViewById(R.id.p2bsIV);
131 |
132 | mP2SWIV = (ImageView) mPageView2.findViewById(R.id.p2swIV);
133 | mP2BWIV = (ImageView) mPageView2.findViewById(R.id.p2bwIV);
134 | // mPageView2.setVisibility(View.GONE);
135 | mPageView2.setAlpha(0f);
136 | contentView.addView(mPageView2);
137 |
138 | mPageView3 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_3, null);
139 |
140 | mP3RootView = mPageView3.findViewById(R.id.p3RootView);
141 | mP3SWIV = (ImageView) mPageView3.findViewById(R.id.p3swIV);
142 |
143 | mP3CBIV = (ImageView) mPageView3.findViewById(R.id.p3cbIV);
144 | mP3CSIV = (ImageView) mPageView3.findViewById(R.id.p3csIV);
145 |
146 | mP3CLIV = (ImageView) mPageView3.findViewById(R.id.p3clIV);
147 | mP3CRIV = (ImageView) mPageView3.findViewById(R.id.p3crIV);
148 |
149 | mPageView3.setAlpha(0f);
150 | contentView.addView(mPageView3);
151 |
152 | mPageView4 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_4, null);
153 | mP4EarthIV = (ImageView) mPageView4.findViewById(R.id.p4EarthIV);
154 | mP4EarthCloudIV = (ImageView) mPageView4.findViewById(R.id.p4EarthCloudIV);
155 | mP4ManIV = (ImageView) mPageView4.findViewById(R.id.p4ManIV);
156 | mP4DBIV = (ImageView) mPageView4.findViewById(R.id.p4dbIV);
157 | mP4DBCIV = (ImageView) mPageView4.findViewById(R.id.p4dbcIV);
158 | mP4DCTV = (TextView) mPageView4.findViewById(R.id.p4dcTV);
159 |
160 | mPageView4.setAlpha(0f);
161 | // mPageView4.setVisibility(View.GONE);
162 | contentView.addView(mPageView4);
163 |
164 | mVideoImageView = new MyVideoImageView(this);
165 | mVideoImageView.setScaleType(ScaleType.FIT_XY);
166 | contentView.addView(mVideoImageView);
167 | }
168 |
169 | @Override
170 | public void onWindowFocusChanged(boolean hasFocus) {
171 | super.onWindowFocusChanged(hasFocus);
172 | if (hasFocus) {
173 | if (!isChronometerRunnig) {
174 | isChronometerRunnig = true;
175 | // mP1VideoView.getHolder().addCallback(this);
176 | // mP1VideoView.stopPlayback();
177 | mP1VideoView.setVideoURI(mP1VVUriPath);
178 | mP1VideoView.requestFocus();
179 | mP1VideoView.start();
180 | mP1VideoView.setOnPreparedListener(new OnPreparedListener() {
181 |
182 | public void onPrepared(MediaPlayer mp) {
183 | mp.start();
184 | mp.setLooping(true);
185 | }
186 | });
187 | // if (!isChronometerRunnig) {
188 | // isChronometerRunnig = true;
189 | mP1Time.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
190 | @Override
191 | public void onChronometerTick(Chronometer cArg) {
192 | long time = SystemClock.elapsedRealtime() - cArg.getBase();
193 | Date date = new Date(time);
194 | DateFormat formatter = new SimpleDateFormat("mm:ss");
195 | String dateFormatted = formatter.format(date);
196 | if (dateFormatted.equals("01:00"))
197 | mP1Time.stop();
198 | cArg.setText(dateFormatted);
199 |
200 | }
201 | });
202 | mP1Time.setBase(SystemClock.elapsedRealtime());
203 | mP1Time.start();
204 | }
205 | }
206 | }
207 |
208 | @Override
209 | public void onLeft(int position, float ratio) {
210 | if (ratio == 0)
211 | return;
212 | // Log.d(TAG, "onLeft" + position + "/" + ratio);
213 | switch (position) {
214 | case 0: {
215 | createOpenAnimatorSetP2();
216 | mOpenP2AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
217 | }
218 | break;
219 | case 1: {
220 | createOpenAnimatorSetP3();
221 | mOpenP3AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
222 | }
223 | break;
224 | case 2: {
225 | createOpenAnimatorSetP4();
226 | mOpenP4AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
227 | // ArrayList animators = mOpenP4AnimatorSet.getChildAnimations();
228 | // for (Animator animator : animators) {
229 | // ValueAnimator valueAnimator = (ValueAnimator) animator;
230 | // if (valueAnimator.getRepeatCount() != -1) {
231 | // long cpt = (long) (animator.getDuration() * ratio);
232 | // valueAnimator.setCurrentPlayTime(cpt);
233 | // } else if (!valueAnimator.isRunning()) {
234 | // valueAnimator.start();
235 | // }
236 | // }
237 | }
238 | break;
239 |
240 | default:
241 | break;
242 | }
243 | }
244 |
245 | @Override
246 | public void onRight(int position, float ratio) {
247 | if (ratio == 0)
248 | return;
249 | // Log.d(TAG, "onRight" + position + "/" + ratio);
250 | switch (position) {
251 | case 1: {
252 | createCloseAnimatorSetP2();
253 | mCloseP2AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
254 | }
255 | break;
256 | case 2: {
257 | createCloseAnimatorSetP3();
258 | mCloseP3AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
259 | }
260 | break;
261 | case 3: {
262 | createCloseAnimatorSetP4();
263 | mCloseP4AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
264 | }
265 | break;
266 | default:
267 | break;
268 | }
269 | }
270 |
271 | @Override
272 | public void onLeftEnd(int position, float ratio, boolean dismiss) {
273 | Log.d(TAG, "onLeftEnd" + position + "/" + dismiss);
274 | switch (position) {
275 | case 0: {
276 | createOpenAnimatorSetP2();
277 | if (dismiss) {
278 | mOpenP2AnimatorSet.start();
279 | mOpenP2AnimatorSet.pause();
280 | mOpenP2AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
281 | } else {
282 | mOpenP2AnimatorSet.reverse();
283 | mOpenP2AnimatorSet.pause();
284 | mOpenP2AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
285 | }
286 | mOpenP2AnimatorSet.resume();
287 | }
288 | break;
289 | case 1: {
290 | createOpenAnimatorSetP3();
291 | if (dismiss) {
292 | mOpenP3AnimatorSet.start();
293 | mOpenP3AnimatorSet.pause();
294 | mOpenP3AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
295 | } else {
296 | mOpenP3AnimatorSet.reverse();
297 | mOpenP3AnimatorSet.pause();
298 | mOpenP3AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
299 | }
300 | mOpenP3AnimatorSet.resume();
301 | }
302 | break;
303 | case 2: {
304 | createOpenAnimatorSetP4();
305 | if (dismiss) {
306 | mOpenP4AnimatorSet.start();
307 | mOpenP4AnimatorSet.pause();
308 | mOpenP4AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
309 | } else {
310 | mOpenP4AnimatorSet.reverse();
311 | mOpenP4AnimatorSet.pause();
312 | mOpenP4AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
313 | }
314 | mOpenP4AnimatorSet.resume();
315 | // ArrayList animators = mOpenP4AnimatorSet.getChildAnimations();
316 | // for (Animator animator : animators) {
317 | // ValueAnimator valueAnimator = (ValueAnimator) animator;
318 | // if (valueAnimator.getRepeatCount() != -1) {
319 | // // valueAnimator.setStartDelay(1000);
320 | // if (dismiss) {
321 | // valueAnimator.start();
322 | // valueAnimator.pause();
323 | // long cpt = (long) (animator.getDuration() * ratio);
324 | // valueAnimator.setCurrentPlayTime(cpt);
325 | // } else {
326 | // valueAnimator.reverse();
327 | // valueAnimator.pause();
328 | // long cpt = animator.getDuration()
329 | // - (long) (animator.getDuration() * ratio);
330 | // valueAnimator.setCurrentPlayTime(cpt);
331 | // }
332 | //
333 | // valueAnimator.resume();
334 | // }
335 | // }
336 | }
337 | break;
338 |
339 | default:
340 | break;
341 | }
342 |
343 | }
344 |
345 | @Override
346 | public void onRightEnd(int position, float ratio, boolean dismiss) {
347 | Log.d(TAG, "onRightEnd" + position + "/" + dismiss);
348 | switch (position) {
349 | case 1: {
350 | createCloseAnimatorSetP2();
351 | if (!dismiss) {
352 | mCloseP2AnimatorSet.start();
353 | mCloseP2AnimatorSet.pause();
354 | mCloseP2AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
355 | } else {
356 | mCloseP2AnimatorSet.reverse();
357 | mCloseP2AnimatorSet.pause();
358 | mCloseP2AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
359 | }
360 | mCloseP2AnimatorSet.resume();
361 | }
362 | break;
363 | case 2: {
364 | createCloseAnimatorSetP3();
365 | if (!dismiss) {
366 | mCloseP3AnimatorSet.start();
367 | mCloseP3AnimatorSet.pause();
368 | mCloseP3AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
369 | } else {
370 | mCloseP3AnimatorSet.reverse();
371 | mCloseP3AnimatorSet.pause();
372 | mCloseP3AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
373 | }
374 | mCloseP3AnimatorSet.resume();
375 | }
376 | break;
377 | case 3: {
378 | createCloseAnimatorSetP4();
379 | if (!dismiss) {
380 | mCloseP4AnimatorSet.start();
381 | mCloseP4AnimatorSet.pause();
382 | mCloseP4AnimatorSet.setCurrentPlayTimeAtRatio(1 - ratio);
383 | } else {
384 | mCloseP4AnimatorSet.reverse();
385 | mCloseP4AnimatorSet.pause();
386 | mCloseP4AnimatorSet.setCurrentPlayTimeAtRatio(ratio);
387 | }
388 | mCloseP4AnimatorSet.resume();
389 | // ArrayList animators = mCloseP4AnimatorSet.getChildAnimations();
390 | // for (Animator animator : animators) {
391 | // ValueAnimator valueAnimator = (ValueAnimator) animator;
392 | // if (dismiss) {
393 | // valueAnimator.reverse();
394 | // valueAnimator.pause();
395 | // long cpt = animator.getDuration()
396 | // - (long) (animator.getDuration() * ratio);
397 | // valueAnimator.setCurrentPlayTime(cpt);
398 | // } else {
399 | // valueAnimator.start();
400 | // valueAnimator.pause();
401 | // long cpt = animator.getDuration()
402 | // - (long) (animator.getDuration() * ratio);
403 | // valueAnimator.setCurrentPlayTime(cpt);
404 | // }
405 | // valueAnimator.resume();
406 | // }
407 | }
408 | break;
409 | default:
410 | break;
411 | }
412 | }
413 |
414 | private void createCloseAnimatorSetP2() {
415 | if (mCloseP2AnimatorSet == null) {
416 | // mCloseP2AnimatorSet = mOpenP2AnimatorSet.clone();
417 | mCloseP2AnimatorSet = new MyAnimatorSet();
418 | mCloseP2AnimatorSet.playTogether(mOpenP2AnimatorSet.getChildAnimationsClone());
419 | mCloseP2AnimatorSet.addPauseListener(new AnimatorPauseListener() {
420 |
421 | @Override
422 | public void onAnimationResume(Animator animation) {
423 | Log.d(TAG, "mCloseP2AnimatorSet:onAnimationResume");
424 | }
425 |
426 | @Override
427 | public void onAnimationPause(Animator animation) {
428 | Log.d(TAG, "mCloseP2AnimatorSet:onAnimationPause");
429 | }
430 | });
431 | mCloseP2AnimatorSet.addMyListener(new MyAnimatorListener() {
432 |
433 | @Override
434 | public void onAnimationReverse(Animator animation) {
435 | Log.d(TAG, "mCloseP2AnimatorSet:onAnimationReverse");
436 | isP2Reverse = true;
437 | }
438 |
439 | @Override
440 | public void onAnimationStart(Animator animation) {
441 | // Log.d(TAG, "mCloseP2AnimatorSet:onAnimationStart");
442 | // if (!isP2Start) {
443 | // isP2Start = true;
444 | // mPageView1.setAlpha(0f);
445 | // mPageView2.setAlpha(1f);
446 | // mVideoImageView.setAlpha(1f);
447 | // }
448 | }
449 |
450 | @Override
451 | public void onAnimationRepeat(Animator animation) {
452 | Log.d(TAG, "mCloseP2AnimatorSet:onAnimationRepeat");
453 | }
454 |
455 | @Override
456 | public void onAnimationEnd(Animator animation) {
457 | Log.d(TAG, "mCloseP2AnimatorSet:onAnimationEnd");
458 | if (isP2Reverse) {
459 | mPageView1.setAlpha(1f);
460 | mPageView2.setAlpha(0f);
461 | mVideoImageView.setAlpha(0f);
462 | mP1VideoView.requestFocus();
463 | // 重启视频
464 | mP1VideoView.start();
465 | // 重启 计时器
466 | mP1Time.setBase(SystemClock.elapsedRealtime() - stoppedMilliseconds);
467 | mP1Time.start();
468 | } else {
469 |
470 | }
471 | isP2Start = false;
472 | isP2Reverse = false;
473 | }
474 |
475 | @Override
476 | public void onAnimationCancel(Animator animation) {
477 | Log.d(TAG, "mCloseP2AnimatorSet:onAnimationCancel");
478 | }
479 | });
480 | }
481 | }
482 |
483 | private void createCloseAnimatorSetP3() {
484 | if (mCloseP3AnimatorSet == null) {
485 | // mCloseP3AnimatorSet = mOpenP2AnimatorSet.clone();
486 | mCloseP3AnimatorSet = new MyAnimatorSet();
487 | mCloseP3AnimatorSet.playTogether(mOpenP3AnimatorSet.getChildAnimationsClone());
488 | mCloseP3AnimatorSet.addPauseListener(new AnimatorPauseListener() {
489 |
490 | @Override
491 | public void onAnimationResume(Animator animation) {
492 | Log.d(TAG, "mCloseP3AnimatorSet:onAnimationResume");
493 | }
494 |
495 | @Override
496 | public void onAnimationPause(Animator animation) {
497 | Log.d(TAG, "mCloseP3AnimatorSet:onAnimationPause");
498 | }
499 | });
500 | mCloseP3AnimatorSet.addMyListener(new MyAnimatorListener() {
501 |
502 | @Override
503 | public void onAnimationReverse(Animator animation) {
504 | Log.d(TAG, "mCloseP3AnimatorSet:onAnimationReverse");
505 | isP3Reverse = true;
506 | }
507 |
508 | @Override
509 | public void onAnimationStart(Animator animation) {
510 | if (!isP3Start) {
511 | Log.d(TAG, "mCloseP3AnimatorSet:onAnimationStart");
512 | isP3Start = true;
513 | }
514 | }
515 |
516 | @Override
517 | public void onAnimationRepeat(Animator animation) {
518 | Log.d(TAG, "mCloseP3AnimatorSet:onAnimationRepeat");
519 | }
520 |
521 | @Override
522 | public void onAnimationEnd(Animator animation) {
523 | Log.d(TAG, "mCloseP3AnimatorSet:onAnimationEnd");
524 | if (isP3Reverse) {
525 | } else {
526 |
527 | }
528 | isP3Start = false;
529 | isP3Reverse = false;
530 | }
531 |
532 | @Override
533 | public void onAnimationCancel(Animator animation) {
534 | Log.d(TAG, "mCloseP3AnimatorSet:onAnimationCancel");
535 | }
536 | });
537 | }
538 | }
539 |
540 | private void createCloseAnimatorSetP4() {
541 | if (mCloseP4AnimatorSet == null) {
542 | // mCloseP4AnimatorSet = mOpenP4AnimatorSet.clone();
543 | mCloseP4AnimatorSet = new MyAnimatorSet();
544 | mCloseP4AnimatorSet.playTogether(mOpenP4AnimatorSet.getChildAnimationsClone());
545 | mCloseP4AnimatorSet.addPauseListener(new AnimatorPauseListener() {
546 |
547 | @Override
548 | public void onAnimationResume(Animator animation) {
549 | Log.d(TAG, "mCloseP4AnimatorSet:onAnimationResume");
550 | }
551 |
552 | @Override
553 | public void onAnimationPause(Animator animation) {
554 | Log.d(TAG, "mCloseP4AnimatorSet:onAnimationPause");
555 | }
556 | });
557 | mCloseP4AnimatorSet.addMyListener(new MyAnimatorListener() {
558 |
559 | @Override
560 | public void onAnimationReverse(Animator animation) {
561 | Log.d(TAG, "mCloseP4AnimatorSet:onAnimationReverse");
562 | isP4Reverse = true;
563 | }
564 |
565 | @Override
566 | public void onAnimationStart(Animator animation) {
567 | if (!isP4Start) {
568 | Log.d(TAG, "mCloseP4AnimatorSet:onAnimationStart");
569 | isP4Start = true;
570 | if (mP4AnimatorSetAllInOne != null) {
571 | mP4AnimatorSetAllInOne.end();
572 | mP4AnimatorSetAllInOne = null;
573 | }
574 | }
575 | }
576 |
577 | @Override
578 | public void onAnimationRepeat(Animator animation) {
579 | Log.d(TAG, "mCloseP4AnimatorSet:onAnimationRepeat");
580 | }
581 |
582 | @Override
583 | public void onAnimationEnd(Animator animation) {
584 | Log.d(TAG, "mCloseP4AnimatorSet:onAnimationEnd" + isP4Reverse);
585 | if (isP4Reverse) {
586 | mP3SWIV.setAlpha(1f);
587 | } else {
588 | // mP4AnimatorSetAllInOne = new MyAnimatorSet();
589 | // mP4AnimatorSetAllInOne.play(mP4AnimatorSet);
590 | mP4AnimatorSetAllInOne = mP4AnimatorSet.clone();
591 | mP4AnimatorSetAllInOne.start();
592 | }
593 | isP4Start = false;
594 | isP4Reverse = false;
595 | }
596 |
597 | @Override
598 | public void onAnimationCancel(Animator animation) {
599 | Log.d(TAG, "mCloseP4AnimatorSet:onAnimationCancel");
600 | }
601 | });
602 | }
603 | }
604 |
605 | private void createOpenAnimatorSetP2() {
606 |
607 | if (mOpenP2AnimatorSet == null) {
608 |
609 | // ObjectAnimator alPV1IV = ObjectAnimator.ofFloat(mPageView1, "alpha", 0f, 0f);
610 | // ObjectAnimator alPV2IV = ObjectAnimator.ofFloat(mPageView2, "alpha", 1f, 1f);
611 | // ObjectAnimator alVIVIV = ObjectAnimator.ofFloat(mVideoImageView, "alpha", 1f, 1f);
612 |
613 | PropertyValuesHolder pvsX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.7f);
614 | PropertyValuesHolder pvsY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.7f);
615 | PropertyValuesHolder pvrXY = PropertyValuesHolder.ofFloat("rotation", 0f, -5f);
616 | PropertyValuesHolder tlRVX = PropertyValuesHolder.ofFloat("translationX", 0f,
617 | dip2px(this, 20f));
618 | PropertyValuesHolder tlRVY = PropertyValuesHolder.ofFloat("translationY", 0f,
619 | dip2px(this, 100f));
620 | ObjectAnimator whxyBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2RootView,
621 | pvsX, pvsY, pvrXY, tlRVX, tlRVY).setDuration(500);
622 | whxyBouncer.setRepeatCount(0);
623 | whxyBouncer.setRepeatMode(ValueAnimator.INFINITE);
624 |
625 | ObjectAnimator ivxyBouncer = ObjectAnimator.ofPropertyValuesHolder(mVideoImageView,
626 | pvsX, pvsY, pvrXY, tlRVX, tlRVY).setDuration(500);
627 |
628 | PropertyValuesHolder tlSWY = PropertyValuesHolder.ofFloat("translationY", -50f, 0f);
629 | PropertyValuesHolder apSWY = PropertyValuesHolder.ofFloat("alpha", 0.25f, 1f);
630 | PropertyValuesHolder roSWXY = PropertyValuesHolder.ofFloat("rotation", -25f, 0f);
631 | mP2SWIV.setPivotX(-50);
632 | mP2SWIV.setPivotY(-50);
633 | ObjectAnimator swBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2SWIV, tlSWY,
634 | roSWXY, apSWY).setDuration(500);
635 |
636 | PropertyValuesHolder tlBWY = PropertyValuesHolder.ofFloat("translationY", -100f, 0f);
637 | PropertyValuesHolder tlBWX = PropertyValuesHolder.ofFloat("translationX", 50f, 0f);
638 | PropertyValuesHolder apBWY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
639 | PropertyValuesHolder roBWXY = PropertyValuesHolder.ofFloat("rotation", -60f, 0f);
640 | mP2BWIV.setPivotX(-100);
641 | mP2BWIV.setPivotY(-100);
642 | ObjectAnimator bwBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2BWIV, tlBWY,
643 | tlBWX, roBWXY, apBWY).setDuration(500);
644 |
645 | PropertyValuesHolder apBBY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
646 | PropertyValuesHolder roBBXY = PropertyValuesHolder.ofFloat("rotation", -15f, 0f);
647 | mP2BBIV.setPivotX(mP2BBIV.getWidth() / 2);
648 | mP2BBIV.setPivotY(-100);
649 | ObjectAnimator bbBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2BBIV, apBBY,
650 | roBBXY).setDuration(500);
651 |
652 | PropertyValuesHolder scBSX = PropertyValuesHolder.ofFloat("scaleX", 1.5f, 1.1f);
653 | PropertyValuesHolder scBSY = PropertyValuesHolder.ofFloat("scaleY", 1.5f, 1.1f);
654 | PropertyValuesHolder apBSY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
655 | PropertyValuesHolder roBSXY = PropertyValuesHolder.ofFloat("rotation", -45f, 0f);
656 | // mP2BSIV.setPivotX(mP2BBIV.getWidth() / 2);
657 | // mP2BSIV.setPivotY(mP2BBIV.getWidth() / 2);
658 | ObjectAnimator bsBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2BSIV, scBSX,
659 | scBSY, apBSY, roBSXY).setDuration(500);
660 |
661 | mOpenP2AnimatorSet = new MyAnimatorSet();
662 | mOpenP2AnimatorSet.addPauseListener(new AnimatorPauseListener() {
663 |
664 | @Override
665 | public void onAnimationResume(Animator animation) {
666 | Log.d(TAG, "mOpenP2AnimatorSet:onAnimationResume");
667 | }
668 |
669 | @Override
670 | public void onAnimationPause(Animator animation) {
671 | Log.d(TAG, "mOpenP2AnimatorSet:onAnimationPause");
672 | }
673 | });
674 | mOpenP2AnimatorSet.addMyListener(new MyAnimatorListener() {
675 |
676 | @Override
677 | public void onAnimationReverse(Animator animation) {
678 | Log.d(TAG, "mOpenP2AnimatorSet:onAnimationReverse");
679 | isP2Reverse = true;
680 | }
681 |
682 | @Override
683 | public void onAnimationStart(Animator animation) {
684 | if (!isP2Start) {
685 | Log.d(TAG, "mOpenP2AnimatorSet:onAnimationStart");
686 | isP2Start = true;
687 | mPageView1.setAlpha(0f);
688 | mPageView2.setAlpha(1f);
689 | mVideoImageView.setAlpha(1f);
690 |
691 | mP2TimeTV.setText(mP1Time.getText());
692 | // mP1VideoView.stopPlayback();
693 | // mP1VideoView.clearFocus();
694 | mP1VideoView.pause();
695 | mVideoImageView.setTimeText(mP1Time.getText().toString());
696 |
697 | // 暂停计时器
698 | mP1Time.stop();
699 | String chronoText = mP1Time.getText().toString();
700 | String array[] = chronoText.split(":");
701 | if (array.length == 2) {
702 | stoppedMilliseconds = Integer.parseInt(array[0]) * 60 * 1000
703 | + Integer.parseInt(array[1]) * 1000;
704 | } else if (array.length == 3) {
705 | stoppedMilliseconds = Integer.parseInt(array[0]) * 60 * 60 * 1000
706 | + Integer.parseInt(array[1]) * 60 * 1000
707 | + Integer.parseInt(array[2]) * 1000;
708 | }
709 |
710 | // 尽力了,不知道 为什么 截取的图 为什么不是 暂停的画面
711 |
712 | MediaMetadataRetriever rev = new MediaMetadataRetriever();
713 | rev.setDataSource(DemoActivity.this, mP1VVUriPath); // 这里第一个参数需要Context,传this指针
714 | Bitmap bitmap = rev.getFrameAtTime(
715 | mP1VideoView.getCurrentPosition() * 1000,
716 | MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
717 | mVideoImageView.setImageBitmap(bitmap);
718 | }
719 | }
720 |
721 | @Override
722 | public void onAnimationRepeat(Animator animation) {
723 | Log.d(TAG, "mOpenP2AnimatorSet:onAnimationRepeat");
724 | }
725 |
726 | @Override
727 | public void onAnimationEnd(Animator animation) {
728 | Log.d(TAG, "mOpenP2AnimatorSet:onAnimationEnd");
729 | if (isP2Reverse) {
730 | mPageView1.setAlpha(1f);
731 | mPageView2.setAlpha(0f);
732 | mVideoImageView.setAlpha(0f);
733 | mP1VideoView.requestFocus();
734 | // 重启视频
735 | mP1VideoView.start();
736 | // 重启 计时器
737 | mP1Time.setBase(SystemClock.elapsedRealtime() - stoppedMilliseconds);
738 | mP1Time.start();
739 | } else {
740 | mPageView1.setAlpha(0f);
741 | mPageView2.setAlpha(1f);
742 | mVideoImageView.setAlpha(1f);
743 | }
744 | isP2Start = false;
745 | isP2Reverse = false;
746 | }
747 |
748 | @Override
749 | public void onAnimationCancel(Animator animation) {
750 | Log.d(TAG, "mOpenP2AnimatorSet:onAnimationCancel");
751 | }
752 | });
753 | // mOpenP2AnimatorSet.cancel();
754 | mOpenP2AnimatorSet.playTogether(ivxyBouncer, swBouncer, bbBouncer, bsBouncer,
755 | bwBouncer, whxyBouncer);
756 | // mOpenP2AnimatorSet.setStartDelay(1000);
757 | mOpenP2AnimatorSet.setDuration(500);
758 | // mOpenP2AnimatorSet.start();
759 | }
760 | }
761 |
762 | private void createOpenAnimatorSetP3() {
763 |
764 | if (mOpenP3AnimatorSet == null) {
765 |
766 | PropertyValuesHolder scP2RVX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0f);
767 | PropertyValuesHolder scP2RVY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0f);
768 | PropertyValuesHolder apP2RVY = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
769 | ObjectAnimator rootP2Bouncer = ObjectAnimator.ofPropertyValuesHolder(mPageView2,
770 | scP2RVX, scP2RVY, apP2RVY).setDuration(400);
771 | rootP2Bouncer.setRepeatCount(0);
772 |
773 | Keyframe kf0 = Keyframe.ofFloat(0, 1f);
774 | // Keyframe kf1 = Keyframe.ofFloat(0.25f, 1f);
775 | Keyframe kf2 = Keyframe.ofFloat(0.5f, 1f);
776 | Keyframe kf4 = Keyframe.ofFloat(0.9999f, 1f);
777 | Keyframe kf3 = Keyframe.ofFloat(1f, 0f);
778 | PropertyValuesHolder apP2IVY = PropertyValuesHolder.ofKeyframe("alpha", kf0, kf2,
779 | kf4, kf3);
780 | PropertyValuesHolder scP2IVX = PropertyValuesHolder.ofFloat("scaleX", 0.7f, 0.8f,
781 | 0.6f);
782 | PropertyValuesHolder scP2IVY = PropertyValuesHolder.ofFloat("scaleY", 0.7f, 0.8f,
783 | 0.6f);
784 | PropertyValuesHolder roP2IVXY = PropertyValuesHolder
785 | .ofFloat("rotation", -5f, 0f, 0f);
786 | PropertyValuesHolder tlP2IVX = PropertyValuesHolder.ofFloat("translationX",
787 | dip2px(this, 20f), 0f, 0f);
788 | PropertyValuesHolder tlP2IVY = PropertyValuesHolder.ofFloat("translationY",
789 | dip2px(this, 100f), 0f, 0f);
790 |
791 | // 没效果
792 | // PropertyValuesHolder heightP2IVX1 = PropertyValuesHolder.ofInt("height",
793 | // mVideoImageView.getHeight(), 0);
794 |
795 | PropertyValuesHolder heightP2IVX1 = PropertyValuesHolder.ofFloat("translationHight",
796 | 1f, 0f);
797 |
798 | ObjectAnimator roxyBouncerP2IV = ObjectAnimator.ofPropertyValuesHolder(
799 | mVideoImageView, apP2IVY, scP2IVX, scP2IVY, roP2IVXY, tlP2IVX, tlP2IVY,
800 | heightP2IVX1).setDuration(500);
801 | roxyBouncerP2IV.setInterpolator(new AccelerateInterpolator());
802 | roxyBouncerP2IV.setRepeatCount(0);
803 |
804 | // PropertyValuesHolder apP2IVY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
805 | PropertyValuesHolder scP2IVX1 = PropertyValuesHolder.ofFloat("scaleX", 1.3f, 0.7f);
806 | PropertyValuesHolder scP2IVY1 = PropertyValuesHolder.ofFloat("scaleY", 1.3f, 0.7f);
807 |
808 | // ObjectAnimator roxyP2IV = ObjectAnimator.ofFloat(mP2ImageView, "rotation",
809 | // -5f, 0f).setDuration(400);
810 |
811 | // ObjectAnimator roxyBouncerEOne = ObjectAnimator
812 | // .ofFloat(mP2RootView, "alpha", 0f, 0f);
813 | // roxyBouncerEOne.setDuration(400);
814 | // roxyBouncerEOne.setRepeatCount(0);
815 |
816 | // mP3RootView.setPivotX(mP3RootView.getWidth() / 4 * 3);
817 | // int height = mP3RootView.getHeight();
818 | // mP3RootView.setPivotY((float) (height - height * 0.15));
819 |
820 | PropertyValuesHolder apRVY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
821 | PropertyValuesHolder pvsX = PropertyValuesHolder.ofFloat("scaleX", 1.3f, 0.7f);
822 | PropertyValuesHolder pvsY = PropertyValuesHolder.ofFloat("scaleY", 1.3f, 0.7f);
823 | PropertyValuesHolder pvrXY = PropertyValuesHolder.ofFloat("rotation", -6f, 0f);
824 | PropertyValuesHolder tlRVY = PropertyValuesHolder.ofFloat("translationY", 0f,
825 | dip2px(this, 100f));
826 | ObjectAnimator whxyBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3RootView,
827 | apRVY, pvsX, pvsY, pvrXY, tlRVY).setDuration(500);
828 | whxyBouncer.setRepeatCount(0);
829 |
830 | PropertyValuesHolder scCLX = PropertyValuesHolder.ofFloat("scaleX", 1.5f, 1f);
831 | PropertyValuesHolder scCLY = PropertyValuesHolder.ofFloat("scaleY", 1.5f, 1f);
832 | PropertyValuesHolder tlCLY = PropertyValuesHolder.ofFloat("translationY", -50f, 0f);
833 | PropertyValuesHolder apCLY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
834 | PropertyValuesHolder roCLXY = PropertyValuesHolder.ofFloat("rotation", -5f, 0f);
835 | ObjectAnimator swBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CLIV, scCLX,
836 | scCLY, tlCLY, roCLXY, apCLY).setDuration(400);
837 | swBouncer.setInterpolator(new DecelerateInterpolator());
838 |
839 | PropertyValuesHolder scCRX = PropertyValuesHolder.ofFloat("scaleX", 1.5f, 1f);
840 | PropertyValuesHolder scCRY = PropertyValuesHolder.ofFloat("scaleY", 1.5f, 1f);
841 | PropertyValuesHolder tlCRY = PropertyValuesHolder.ofFloat("translationY", -50f, 0f);
842 | PropertyValuesHolder tlCRX = PropertyValuesHolder.ofFloat("translationX", 50f, 0f);
843 | PropertyValuesHolder apCRY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
844 | PropertyValuesHolder roCRXY = PropertyValuesHolder.ofFloat("rotation", 5f, 0f);
845 | ObjectAnimator bwBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CRIV, scCRX,
846 | scCRY, tlCRY, tlCRX, roCRXY, apCRY).setDuration(400);
847 | bwBouncer.setInterpolator(new AccelerateInterpolator());
848 |
849 | PropertyValuesHolder scCBX = PropertyValuesHolder.ofFloat("scaleX", 1.5f, 1f);
850 | PropertyValuesHolder scCBY = PropertyValuesHolder.ofFloat("scaleY", 1.5f, 1f);
851 | PropertyValuesHolder apCBY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
852 | PropertyValuesHolder roCBXY = PropertyValuesHolder.ofFloat("rotation", -15f, 0f);
853 | mP3CBIV.setPivotX(mP3CBIV.getWidth() / 2);
854 | mP3CBIV.setPivotY(-100);
855 | ObjectAnimator cbBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CBIV, scCBX,
856 | scCBY, apCBY, roCBXY).setDuration(500);
857 |
858 | PropertyValuesHolder scBSX = PropertyValuesHolder.ofFloat("scaleX", 1.5f, 1f);
859 | PropertyValuesHolder scBSY = PropertyValuesHolder.ofFloat("scaleY", 1.5f, 1f);
860 | PropertyValuesHolder apBSY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
861 | ObjectAnimator csBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CSIV, scBSX,
862 | scBSY, apBSY).setDuration(500);
863 |
864 | mOpenP3AnimatorSet = new MyAnimatorSet();
865 | mOpenP3AnimatorSet.addPauseListener(new AnimatorPauseListener() {
866 |
867 | @Override
868 | public void onAnimationResume(Animator animation) {
869 | Log.d(TAG, "mOpenP3AnimatorSet:onAnimationResume");
870 | }
871 |
872 | @Override
873 | public void onAnimationPause(Animator animation) {
874 | Log.d(TAG, "mOpenP3AnimatorSet:onAnimationPause");
875 | }
876 | });
877 | mOpenP3AnimatorSet.addMyListener(new MyAnimatorListener() {
878 |
879 | @Override
880 | public void onAnimationReverse(Animator animation) {
881 | Log.d(TAG, "mOpenP3AnimatorSet:onAnimationReverse");
882 | isP3Reverse = true;
883 | }
884 |
885 | @Override
886 | public void onAnimationStart(Animator animation) {
887 | if (!isP3Start) {
888 | Log.d(TAG, "mOpenP3AnimatorSet:onAnimationStart");
889 | isP3Start = true;
890 | mPageView3.setAlpha(1f);
891 | }
892 | }
893 |
894 | @Override
895 | public void onAnimationRepeat(Animator animation) {
896 | Log.d(TAG, "mOpenP3AnimatorSet:onAnimationRepeat");
897 | }
898 |
899 | @Override
900 | public void onAnimationEnd(Animator animation) {
901 | Log.d(TAG, "mOpenP3AnimatorSet:onAnimationEnd" + isP3Reverse);
902 | if (isP3Reverse) {
903 | } else {
904 | }
905 | isP3Start = false;
906 | isP3Reverse = false;
907 | }
908 |
909 | @Override
910 | public void onAnimationCancel(Animator animation) {
911 | Log.d(TAG, "mOpenP3AnimatorSet:onAnimationCancel");
912 | }
913 | });
914 | // mOpenP3AnimatorSet.cancel();
915 | mOpenP3AnimatorSet.playTogether(rootP2Bouncer, roxyBouncerP2IV, whxyBouncer,
916 | swBouncer, bwBouncer, cbBouncer, csBouncer);
917 | // mOpenP3AnimatorSet.setStartDelay(1000);
918 | // mOpenP3AnimatorSet.setDuration(500);
919 | // mOpenP3AnimatorSet.start();
920 |
921 | }
922 | }
923 |
924 | private void createOpenAnimatorSetP4() {
925 |
926 | if (mOpenP4AnimatorSet == null) {
927 |
928 | PropertyValuesHolder scCLX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f);
929 | PropertyValuesHolder scCLY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f);
930 | PropertyValuesHolder tlCLY = PropertyValuesHolder.ofFloat("translationY", 0f,
931 | dip2px(this, 180f));
932 | PropertyValuesHolder tlCLX = PropertyValuesHolder.ofFloat("translationX", 0f,
933 | dip2px(this, 50f));
934 | PropertyValuesHolder apCLY = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
935 | ObjectAnimator p3CLBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CLIV, scCLX,
936 | scCLY, tlCLX, tlCLY, apCLY).setDuration(400);
937 | p3CLBouncer.setInterpolator(new AccelerateInterpolator());
938 |
939 | PropertyValuesHolder scCRX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f);
940 | PropertyValuesHolder scCRY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f);
941 | PropertyValuesHolder tlCRY = PropertyValuesHolder.ofFloat("translationY", 0f,
942 | dip2px(this, 100f));
943 | PropertyValuesHolder tlCRX = PropertyValuesHolder.ofFloat("translationX", 0f,
944 | dip2px(this, 60f));
945 | PropertyValuesHolder apCRY = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
946 | ObjectAnimator p3CRBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CRIV, scCRX,
947 | scCRY, tlCRY, tlCRX, apCRY).setDuration(400);
948 | // p3CRBouncer.setInterpolator(new DecelerateInterpolator());
949 |
950 | PropertyValuesHolder scCBX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f);
951 | PropertyValuesHolder scCBY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f);
952 | PropertyValuesHolder apCBY = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
953 | // mP3CBIV.setPivotX(mP3CBIV.getWidth() / 2);
954 | // mP3CBIV.setPivotY(mP3CBIV.getWidth() / 2);
955 | PropertyValuesHolder tlCBY = PropertyValuesHolder.ofFloat("translationY", 0f,
956 | dip2px(this, 250f));
957 | PropertyValuesHolder tlCBX = PropertyValuesHolder.ofFloat("translationX", 0f,
958 | dip2px(this, 60f));
959 | ObjectAnimator p3CBBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CBIV, scCBX,
960 | scCBY, apCBY, tlCBX, tlCBY).setDuration(600);
961 |
962 | PropertyValuesHolder scCSX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f);
963 | PropertyValuesHolder scCSY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f);
964 | PropertyValuesHolder apCSY = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
965 | ObjectAnimator p3CSBouncer = ObjectAnimator.ofPropertyValuesHolder(mP3CSIV, scCSX,
966 | scCSY, apCSY).setDuration(600);
967 |
968 | PropertyValuesHolder scP3X = PropertyValuesHolder.ofFloat("scaleX", 0.7f, 0.01f);
969 | PropertyValuesHolder scP3Y = PropertyValuesHolder.ofFloat("scaleY", 0.7f, 0.01f);
970 | PropertyValuesHolder apP3Y = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
971 | PropertyValuesHolder roP3XY = PropertyValuesHolder.ofFloat("rotation", 0f, -28f);
972 | PropertyValuesHolder tlP3RVY = PropertyValuesHolder.ofFloat("translationY",
973 | dip2px(this, 100f), 0f);
974 | PropertyValuesHolder tlP3RVX = PropertyValuesHolder.ofFloat("translationX", 0f,
975 | dip2px(this, -75f));
976 | ObjectAnimator p3Bouncer = ObjectAnimator.ofPropertyValuesHolder(mP3RootView, scP3X,
977 | scP3Y, apP3Y, roP3XY, tlP3RVX, tlP3RVY);
978 | p3Bouncer.setInterpolator(new MyInterpolator());
979 | p3Bouncer.setDuration(500);
980 | p3Bouncer.setRepeatCount(0);
981 |
982 | ObjectAnimator rootp4BouncerOne = ObjectAnimator
983 | .ofFloat(mPageView4, "alpha", 0f, 1f);
984 | // rootp4BouncerOne.setInterpolator(new DecelerateInterpolator());
985 | rootp4BouncerOne.setDuration(400);
986 | rootp4BouncerOne.setRepeatCount(0);
987 |
988 | Keyframe kfm0 = Keyframe.ofFloat(0, 2.0f);
989 | Keyframe kfm1 = Keyframe.ofFloat(0.7f, 1.0f);
990 | Keyframe kfm2 = Keyframe.ofFloat(1f, 1.0f);
991 | PropertyValuesHolder scMIX = PropertyValuesHolder.ofKeyframe("scaleX", kfm0, kfm1,
992 | kfm2);
993 | PropertyValuesHolder scMIY = PropertyValuesHolder.ofKeyframe("scaleY", kfm0, kfm1,
994 | kfm2);
995 | Keyframe kfms0 = Keyframe.ofFloat(0, 200f);
996 | Keyframe kfms1 = Keyframe.ofFloat(0.7f, 0f);
997 | Keyframe kfms2 = Keyframe.ofFloat(1f, 0f);
998 | PropertyValuesHolder tlBWX = PropertyValuesHolder.ofKeyframe("translationX", kfms0,
999 | kfms1, kfms2);
1000 |
1001 | // PropertyValuesHolder scMIX = PropertyValuesHolder.ofFloat("scaleX", 2.0f, 1.0f);
1002 | // PropertyValuesHolder scMIY = PropertyValuesHolder.ofFloat("scaleY", 2.0f, 1.0f);
1003 | // PropertyValuesHolder tlBWX = PropertyValuesHolder.ofFloat("translationX", 200f, 0f);
1004 | ObjectAnimator bsBouncer = ObjectAnimator.ofPropertyValuesHolder(mP4ManIV, scMIX,
1005 | scMIY, tlBWX).setDuration(500);
1006 |
1007 | Keyframe kf0 = Keyframe.ofFloat(0, 0f);
1008 | Keyframe kf1 = Keyframe.ofFloat(0.25f, 0f);
1009 | Keyframe kf2 = Keyframe.ofFloat(0.5f, 0.25f);
1010 | Keyframe kf4 = Keyframe.ofFloat(0.75f, 0.6f);
1011 | Keyframe kf3 = Keyframe.ofFloat(1f, 1f);
1012 | PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("alpha", kf0,
1013 | kf1, kf2, kf4, kf3);
1014 | PropertyValuesHolder scDBX = PropertyValuesHolder.ofFloat("scaleX", 2.5f, 1.0f);
1015 | PropertyValuesHolder scDBY = PropertyValuesHolder.ofFloat("scaleY", 2.5f, 1.0f);
1016 | PropertyValuesHolder roDBXY = PropertyValuesHolder.ofFloat("rotation", -15f, 0f);
1017 | PropertyValuesHolder tlDBY = PropertyValuesHolder.ofFloat("translationY", 250f, 0f);
1018 | PropertyValuesHolder tlDBX = PropertyValuesHolder.ofFloat("translationX", -150f, 0f);
1019 | mP4DBIV.setPivotX(0);
1020 | mP4DBIV.setPivotY(200);
1021 | ObjectAnimator dbBouncer = ObjectAnimator.ofPropertyValuesHolder(mP4DBIV,
1022 | pvhRotation, scDBX, tlDBX, tlDBY, scDBY, roDBXY).setDuration(800);
1023 |
1024 | Keyframe kfdbc0 = Keyframe.ofFloat(0, 2.0f);
1025 | Keyframe kfdbc4 = Keyframe.ofFloat(0.9f, 1.0f);
1026 | Keyframe kfdbc3 = Keyframe.ofFloat(1f, 1.0f);
1027 | PropertyValuesHolder scDBCX = PropertyValuesHolder.ofKeyframe("scaleX", kfdbc0,
1028 | kfdbc4, kfdbc3);
1029 | PropertyValuesHolder scDBCY = PropertyValuesHolder.ofKeyframe("scaleY", kfdbc0,
1030 | kfdbc4, kfdbc3);
1031 | // PropertyValuesHolder scDBCX = PropertyValuesHolder.ofFloat("scaleX", 2.0f, 1.0f);
1032 | // PropertyValuesHolder scDBCY = PropertyValuesHolder.ofFloat("scaleY", 2.0f, 1.0f);
1033 | ObjectAnimator dbcBouncer = ObjectAnimator.ofPropertyValuesHolder(mP4DBCIV, scDBCX,
1034 | scDBCY).setDuration(600);
1035 |
1036 | PropertyValuesHolder scDCX = PropertyValuesHolder.ofFloat("scaleX", 2.0f, 1.0f);
1037 | PropertyValuesHolder scDCY = PropertyValuesHolder.ofFloat("scaleY", 2.0f, 1.0f);
1038 | PropertyValuesHolder tlDCY = PropertyValuesHolder.ofFloat("translationY", -150f, 0f);
1039 | PropertyValuesHolder tlDCX = PropertyValuesHolder.ofFloat("translationX", 150f, 0f);
1040 | PropertyValuesHolder roDCXY = PropertyValuesHolder.ofFloat("rotation", -10f, 0f);
1041 | mP4DCTV.setPivotX(0);
1042 | mP4DCTV.setPivotY(0);
1043 | ObjectAnimator dcBouncer = ObjectAnimator.ofPropertyValuesHolder(mP4DCTV, scDCX,
1044 | tlDCY, tlDCX, scDCY, roDCXY).setDuration(600);
1045 |
1046 | PropertyValuesHolder scEX = PropertyValuesHolder.ofFloat("scaleX", 2.5f, 1.5f);
1047 | PropertyValuesHolder scEY = PropertyValuesHolder.ofFloat("scaleY", 2.5f, 1.5f);
1048 | PropertyValuesHolder tlEY = PropertyValuesHolder.ofFloat("translationY",
1049 | dip2px(this, 600f), dip2px(this, 360f));
1050 | PropertyValuesHolder tlEX = PropertyValuesHolder.ofFloat("translationX",
1051 | dip2px(this, -80f), 0f);
1052 | PropertyValuesHolder roEXY = PropertyValuesHolder.ofFloat("rotation", -359f, 0f);
1053 | // mP4EarthIV.setPivotX(600);
1054 | // mP4EarthIV.setPivotY(0);
1055 | ObjectAnimator eBouncer = ObjectAnimator.ofPropertyValuesHolder(mP4EarthIV, scEX,
1056 | scEY, roEXY, tlEY, tlEX).setDuration(600);
1057 |
1058 | ObjectAnimator ecBouncer = ObjectAnimator.ofPropertyValuesHolder(mP4EarthCloudIV,
1059 | scEX, scEY, roEXY, tlEY, tlEX).setDuration(600);
1060 |
1061 | mP4MantyBouncer = ObjectAnimator.ofFloat(mP4ManIV, "translationY", 0f, 40f, 0f);
1062 | // CycleInterpolator interpolator = new CycleInterpolator(3.0f);
1063 | // roxyBouncerE.setInterpolator(lin);
1064 | // roxyBouncer.setStartDelay(500);
1065 | mP4MantyBouncer.setDuration(2000);
1066 | mP4MantyBouncer.setRepeatCount(Animation.INFINITE);// Animation.INFINITE
1067 |
1068 | ObjectAnimator roxyBouncerE = ObjectAnimator.ofFloat(mP4EarthIV, "rotation", 0f,
1069 | 359f);
1070 | // CycleInterpolator interpolator = new CycleInterpolator(3.0f);
1071 | LinearInterpolator lin = new LinearInterpolator();
1072 | // roxyBouncerE.setInterpolator(lin);
1073 | // roxyBouncer.setStartDelay(500);
1074 | roxyBouncerE.setDuration(40000);
1075 | roxyBouncerE.setRepeatCount(Animation.INFINITE);// Animation.INFINITE
1076 |
1077 | ObjectAnimator roxyBouncerEC = ObjectAnimator.ofFloat(mP4EarthCloudIV, "rotation",
1078 | 0f, 359f);
1079 | // roxyBouncerEC.setInterpolator(lin);
1080 | roxyBouncerEC.setDuration(60000);
1081 | roxyBouncerEC.setRepeatCount(Animation.INFINITE);
1082 |
1083 | // 效果不对
1084 | // PropertyValuesHolder roEECX = PropertyValuesHolder.ofFloat("rotation", 360f, 0f);
1085 | //
1086 | // ObjectAnimator roxyBouncerEOne = ObjectAnimator.ofPropertyValuesHolder(mP4EarthIV,
1087 | // roEECX).setDuration(400);
1088 | // roxyBouncerEOne.setRepeatCount(0);
1089 | //
1090 | // ObjectAnimator roxyBouncerECOne = ObjectAnimator.ofPropertyValuesHolder(
1091 | // mP4EarthCloudIV, roEECX).setDuration(400);
1092 | // roxyBouncerECOne.setRepeatCount(0);
1093 | // MyAnimatorSet mas1 = new MyAnimatorSet();
1094 | // mas1.playTogether(roxyBouncerEOne, roxyBouncerECOne);
1095 | //
1096 | // MyAnimatorSet mas2 = new MyAnimatorSet();
1097 | // mas2.playTogether(roxyBouncerE, roxyBouncerEC);
1098 |
1099 | mP4AnimatorSet = new MyAnimatorSet();
1100 | // mP4AnimatorSet.setDuration(50000);
1101 | mP4AnimatorSet.setInterpolator(lin);
1102 | mP4AnimatorSet.playTogether(roxyBouncerE, roxyBouncerEC);
1103 | // mP4AnimatorSet.play(roxyBouncerEOne).with(roxyBouncerECOne).after(roxyBouncerE)
1104 | // .with(roxyBouncerEC);
1105 | // mP4AnimatorSet.playSequentially(mas1, mas2);
1106 | // mP4AnimatorSet.start();
1107 |
1108 | mOpenP4AnimatorSet = new MyAnimatorSet();
1109 | mOpenP4AnimatorSet.addPauseListener(new AnimatorPauseListener() {
1110 |
1111 | @Override
1112 | public void onAnimationResume(Animator animation) {
1113 | Log.d(TAG, "mOpenP4AnimatorSet:onAnimationResume");
1114 | }
1115 |
1116 | @Override
1117 | public void onAnimationPause(Animator animation) {
1118 | Log.d(TAG, "mOpenP4AnimatorSet:onAnimationPause");
1119 | }
1120 | });
1121 | mOpenP4AnimatorSet.addMyListener(new MyAnimatorListener() {
1122 |
1123 | @Override
1124 | public void onAnimationReverse(Animator animation) {
1125 | Log.d(TAG, "mOpenP4AnimatorSet:onAnimationReverse");
1126 | isP3Reverse = true;
1127 | }
1128 |
1129 | @Override
1130 | public void onAnimationStart(Animator animation) {
1131 | if (!isP4Start) {
1132 | Log.d(TAG, "mOpenP4AnimatorSet:onAnimationStart");
1133 | isP4Start = true;
1134 | if (mP4AnimatorSetAllInOne != null) {
1135 | mP4AnimatorSetAllInOne.end();
1136 | }
1137 | if (!mP4MantyBouncer.isStarted()) {
1138 | mP4MantyBouncer.start();
1139 | }
1140 | }
1141 | }
1142 |
1143 | @Override
1144 | public void onAnimationRepeat(Animator animation) {
1145 | Log.d(TAG, "mOpenP4AnimatorSet:onAnimationRepeat");
1146 | }
1147 |
1148 | @Override
1149 | public void onAnimationEnd(Animator animation) {
1150 | Log.d(TAG, "mOpenP4AnimatorSet:onAnimationEnd" + isP4Reverse);
1151 |
1152 | // mP4AnimatorSetAllInOne = new MyAnimatorSet();
1153 | // mP4AnimatorSetAllInOne.play(mP4AnimatorSet);
1154 | mP4AnimatorSetAllInOne = mP4AnimatorSet.clone();
1155 | mP4AnimatorSetAllInOne.start();
1156 |
1157 | if (isP4Reverse) {
1158 | mP3SWIV.setAlpha(1f);
1159 | } else {
1160 | mP3SWIV.setAlpha(0f);
1161 | }
1162 | isP4Start = false;
1163 | isP4Reverse = false;
1164 | }
1165 |
1166 | @Override
1167 | public void onAnimationCancel(Animator animation) {
1168 | Log.d(TAG, "mOpenP4AnimatorSet:onAnimationCancel");
1169 | }
1170 | });
1171 | // mOpenP4AnimatorSet.setDuration(400);
1172 | // mOpenP4AnimatorSet.setInterpolator(lin);
1173 | mOpenP4AnimatorSet.playTogether(p3CLBouncer, p3CRBouncer, p3CBBouncer, p3CSBouncer,
1174 | p3Bouncer, rootp4BouncerOne, bsBouncer, dbBouncer, eBouncer, ecBouncer,
1175 | dcBouncer, dbcBouncer);
1176 | // mOpenP4AnimatorSet.start();
1177 | }
1178 |
1179 | // mP4AnimatorSetAllInOne = new MyAnimatorSet();
1180 | // mP4AnimatorSetAllInOne.playSequentially(mOpenP4AnimatorSet, mP4AnimatorSet);
1181 | // mP4AnimatorSetAllInOne.start();
1182 | }
1183 |
1184 | public static float density;
1185 |
1186 | public static float dip2px(Context context, float dipValue) {
1187 | if (density == 0) {
1188 | density = context.getApplicationContext().getResources().getDisplayMetrics().density;
1189 | }
1190 | return (dipValue * density + 0.5f);
1191 | }
1192 | }
1193 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import java.text.DateFormat;
4 | import java.text.SimpleDateFormat;
5 | import java.util.ArrayList;
6 | import java.util.Date;
7 | import java.util.List;
8 |
9 | import android.animation.AnimatorSet;
10 | import android.animation.ObjectAnimator;
11 | import android.animation.PropertyValuesHolder;
12 | import android.animation.ValueAnimator;
13 | import android.app.Activity;
14 | import android.media.MediaPlayer;
15 | import android.media.MediaPlayer.OnPreparedListener;
16 | import android.net.Uri;
17 | import android.os.Bundle;
18 | import android.os.SystemClock;
19 | import android.support.v4.view.PagerAdapter;
20 | import android.support.v4.view.ViewPager;
21 | import android.support.v4.view.ViewPager.OnPageChangeListener;
22 | import android.support.v4.view.ViewPager.PageTransformer;
23 | import android.view.LayoutInflater;
24 | import android.view.View;
25 | import android.view.ViewGroup;
26 | import android.view.animation.Animation;
27 | import android.view.animation.LinearInterpolator;
28 | import android.widget.Chronometer;
29 | import android.widget.ImageView;
30 | import android.widget.TextView;
31 | import android.widget.VideoView;
32 |
33 | public class MainActivity extends Activity implements OnPageChangeListener {
34 |
35 | private ViewPager mViewPager;
36 |
37 | private FragementPagerAdapter mAdapter;
38 |
39 | private List mPagerViews = new ArrayList();
40 |
41 | // PageView 1
42 | private View mP1RootView;
43 | boolean isChronometerRunnig = false;
44 | private Chronometer mP1Time;
45 | private VideoView mP1VideoView;// VideoView 无法旋转缩放,但是可以左右移动。
46 | private Uri mP1VVUriPath;
47 |
48 | // PageView 2
49 | AnimatorSet mP2AnimatorSet = new AnimatorSet();
50 | private View mP2RootView;
51 |
52 | private TextView mP2TimeTV;
53 | private ImageView mP2ImageView;
54 |
55 | private ImageView mP2BBIV;
56 | private ImageView mP2BSIV;
57 |
58 | private ImageView mP2SWIV;
59 | private ImageView mP2BWIV;
60 | // PageView 4
61 | AnimatorSet mP4AnimatorSetOne = new AnimatorSet();
62 | AnimatorSet mP4AnimatorSet = new AnimatorSet();
63 | AnimatorSet mP4AnimatorSetAllInOne = new AnimatorSet();
64 | private ImageView mP4EarthIV;
65 | private ImageView mP4EarthCloudIV;
66 | private ImageView mP4ManIV;
67 |
68 | @Override
69 | protected void onCreate(Bundle savedInstanceState) {
70 | super.onCreate(savedInstanceState);
71 | setContentView(R.layout.activity_main);
72 |
73 | getActionBar().hide();
74 |
75 | mViewPager = (ViewPager) findViewById(R.id.viewPager);
76 |
77 | View pageView1 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_1, null);
78 |
79 | mP1RootView = pageView1.findViewById(R.id.p1RootView);
80 | mP1VideoView = (VideoView) pageView1.findViewById(R.id.p1VideoView);
81 | mP1VVUriPath = Uri.parse("android.resource://" + getPackageName() + "/"
82 | + R.raw.guide_a_motion);
83 |
84 | mP1Time = (Chronometer) pageView1.findViewById(R.id.p1Chronometer);
85 |
86 | mPagerViews.add(pageView1);
87 |
88 | View pageView2 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_2, null);
89 |
90 | mP2RootView = pageView2.findViewById(R.id.p2RootView);
91 |
92 | mP2TimeTV = (TextView) pageView2.findViewById(R.id.p2time);
93 |
94 | // mP2ImageView = (ImageView) pageView2.findViewById(R.id.p2iv);
95 | mP2BBIV = (ImageView) pageView2.findViewById(R.id.p2bbIV);
96 | mP2BSIV = (ImageView) pageView2.findViewById(R.id.p2bsIV);
97 |
98 | mP2SWIV = (ImageView) pageView2.findViewById(R.id.p2swIV);
99 | mP2BWIV = (ImageView) pageView2.findViewById(R.id.p2bwIV);
100 |
101 | mPagerViews.add(pageView2);
102 |
103 | View pageView4 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_4, null);
104 | mP4EarthIV = (ImageView) pageView4.findViewById(R.id.p4EarthIV);
105 | mP4EarthCloudIV = (ImageView) pageView4.findViewById(R.id.p4EarthCloudIV);
106 | mP4ManIV = (ImageView) pageView4.findViewById(R.id.p4ManIV);
107 | mPagerViews.add(pageView4);
108 |
109 | mAdapter = new FragementPagerAdapter();
110 | mViewPager.setPageTransformer(true, new PageTransformer() {
111 |
112 | @Override
113 | public void transformPage(View page, float position) {
114 |
115 | }
116 | });
117 | mViewPager.setAdapter(mAdapter);
118 | mViewPager.setOnPageChangeListener(this);
119 |
120 | animal(0);
121 | }
122 |
123 | @Override
124 | public void onWindowFocusChanged(boolean hasFocus) {
125 | super.onWindowFocusChanged(hasFocus);
126 | if (hasFocus) {
127 | mP2RootView.setPivotX(mP2RootView.getWidth() / 4 * 3);
128 | int height = mP2RootView.getHeight();
129 | mP2RootView.setPivotY((float) (height - height * 0.15));
130 | }
131 | }
132 |
133 | private class FragementPagerAdapter extends PagerAdapter {
134 |
135 | @Override
136 | public int getCount() {
137 | return mPagerViews.size();
138 | }
139 |
140 | @Override
141 | public boolean isViewFromObject(View view, Object o) {
142 | return view == o;
143 | }
144 |
145 | @Override
146 | public Object instantiateItem(ViewGroup container, int position) {
147 | container.addView(mPagerViews.get(position));
148 | return mPagerViews.get(position);
149 |
150 | }
151 |
152 | @Override
153 | public void destroyItem(ViewGroup container, int position, Object object) {
154 | container.removeView((View) object);
155 | }
156 |
157 | }
158 |
159 | @Override
160 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
161 | // mP1VideoView.stopPlayback();
162 | // mP1VideoView.clearFocus();
163 | }
164 |
165 | @Override
166 | public void onPageSelected(int position) {
167 | animal(position);
168 | }
169 |
170 | @Override
171 | public void onPageScrollStateChanged(int state) {
172 | // mP1VideoView.stopPlayback();
173 | // mP1VideoView.clearFocus();
174 | }
175 |
176 | float BALL_SIZE = 100f;
177 |
178 | private void animal(int position) {
179 |
180 | // mP2AnimatorSet.end();
181 | mP4AnimatorSetAllInOne.end();
182 |
183 | switch (position) {
184 | case 0:
185 | // mP2AnimatorSet.cancel();
186 | // mP2RootView.clearAnimation();
187 | mP1VideoView.setVisibility(View.VISIBLE);
188 |
189 | if (!isChronometerRunnig) {
190 | isChronometerRunnig = true;
191 | // mP1VideoView.getHolder().addCallback(this);
192 | // mP1VideoView.stopPlayback();
193 | mP1VideoView.setVideoURI(mP1VVUriPath);
194 | mP1VideoView.requestFocus();
195 | mP1VideoView.start();
196 | mP1VideoView.setOnPreparedListener(new OnPreparedListener() {
197 |
198 | public void onPrepared(MediaPlayer mp) {
199 | mp.start();
200 | mp.setLooping(true);
201 | }
202 | });
203 | // if (!isChronometerRunnig) {
204 | // isChronometerRunnig = true;
205 | mP1Time.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
206 | @Override
207 | public void onChronometerTick(Chronometer cArg) {
208 | long time = SystemClock.elapsedRealtime() - cArg.getBase();
209 | Date date = new Date(time);
210 | DateFormat formatter = new SimpleDateFormat("mm:ss");
211 | String dateFormatted = formatter.format(date);
212 | if (dateFormatted.equals("01:00"))
213 | mP1Time.stop();
214 | cArg.setText(dateFormatted);
215 |
216 | }
217 | });
218 | mP1Time.setBase(SystemClock.elapsedRealtime());
219 | mP1Time.start();
220 | } else {
221 | // TODO 尝试N次没法解决黑屏不重新播放的问题
222 | mP1VideoView.requestLayout();
223 | mP1VideoView.invalidate();
224 | mP1VideoView.resume();
225 | mP1VideoView.pause();
226 | }
227 | break;
228 | case 1:
229 | mP2TimeTV.setText(mP1Time.getText());
230 | mP1VideoView.stopPlayback();
231 | mP1VideoView.clearFocus();
232 | mP1VideoView.setVisibility(View.GONE);
233 | // mP2RootView.requestFocus();
234 | // mP2RootView.requestLayout();
235 | PropertyValuesHolder pvsX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.7f);
236 | PropertyValuesHolder pvsY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.7f);
237 | PropertyValuesHolder pvrXY = PropertyValuesHolder.ofFloat("rotation", 0f, -5f);
238 | ObjectAnimator whxyBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2RootView,
239 | pvsX, pvsY, pvrXY).setDuration(1000);
240 | whxyBouncer.setRepeatCount(0);
241 | whxyBouncer.setRepeatMode(ValueAnimator.INFINITE);
242 |
243 | PropertyValuesHolder tlSWY = PropertyValuesHolder.ofFloat("translationY", -50f,
244 | 0f);
245 | PropertyValuesHolder apSWY = PropertyValuesHolder.ofFloat("alpha", 0.25f, 1f);
246 | PropertyValuesHolder roSWXY = PropertyValuesHolder.ofFloat("rotation", -25f, 0f);
247 | mP2SWIV.setPivotX(-50);
248 | mP2SWIV.setPivotY(-50);
249 | ObjectAnimator swBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2SWIV, tlSWY,
250 | roSWXY, apSWY).setDuration(500);
251 |
252 | PropertyValuesHolder tlBWY = PropertyValuesHolder.ofFloat("translationY", -100f,
253 | 0f);
254 | PropertyValuesHolder tlBWX = PropertyValuesHolder.ofFloat("translationX", 50f,
255 | 0f);
256 | PropertyValuesHolder apBWY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
257 | PropertyValuesHolder roBWXY = PropertyValuesHolder.ofFloat("rotation", -60f, 0f);
258 | mP2BWIV.setPivotX(-100);
259 | mP2BWIV.setPivotY(-100);
260 | ObjectAnimator bwBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2BWIV, tlBWY,
261 | tlBWX, roBWXY, apBWY).setDuration(500);
262 |
263 | PropertyValuesHolder apBBY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
264 | PropertyValuesHolder roBBXY = PropertyValuesHolder.ofFloat("rotation", -15f, 0f);
265 | mP2BBIV.setPivotX(mP2BBIV.getWidth() / 2);
266 | mP2BBIV.setPivotY(-100);
267 | ObjectAnimator bbBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2BBIV, apBBY,
268 | roBBXY);
269 |
270 | PropertyValuesHolder scBSX = PropertyValuesHolder.ofFloat("scaleX", 1.5f, 1.1f);
271 | PropertyValuesHolder scBSY = PropertyValuesHolder.ofFloat("scaleY", 1.5f, 1.1f);
272 | PropertyValuesHolder apBSY = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
273 | PropertyValuesHolder roBSXY = PropertyValuesHolder.ofFloat("rotation", -45f, 0f);
274 | // mP2BSIV.setPivotX(mP2BBIV.getWidth() / 2);
275 | // mP2BSIV.setPivotY(mP2BBIV.getWidth() / 2);
276 | ObjectAnimator bsBouncer = ObjectAnimator.ofPropertyValuesHolder(mP2BSIV, scBSX,
277 | scBSY, apBSY, roBSXY);
278 |
279 | // mP2AnimatorSet.cancel();
280 | mP2AnimatorSet.playTogether(swBouncer, bbBouncer, bsBouncer, bwBouncer,
281 | whxyBouncer);
282 | // mP2AnimatorSet.setStartDelay(1000);
283 | mP2AnimatorSet.setDuration(500);
284 | mP2AnimatorSet.start();
285 | break;
286 |
287 | case 2:
288 |
289 | ObjectAnimator roxyBouncerEOne = ObjectAnimator.ofFloat(mP4EarthIV, "rotation",
290 | 0f, 180f);
291 | // roxyBouncerEOne.setDuration(500);
292 | roxyBouncerEOne.setRepeatCount(0);
293 |
294 | ObjectAnimator roxyBouncerECOne = ObjectAnimator.ofFloat(mP4EarthCloudIV,
295 | "rotation", 0f, 180f);
296 | // roxyBouncerECOne.setDuration(500);
297 | roxyBouncerECOne.setRepeatCount(0);
298 |
299 | ObjectAnimator tyBouncer = ObjectAnimator.ofFloat(mP4ManIV, "translationY", 0f,
300 | 60f, 0f);
301 | // CycleInterpolator interpolator = new CycleInterpolator(3.0f);
302 | // roxyBouncerE.setInterpolator(lin);
303 | // roxyBouncer.setStartDelay(500);
304 | tyBouncer.setDuration(2000);
305 | tyBouncer.setRepeatCount(Animation.INFINITE);// Animation.INFINITE
306 |
307 | ObjectAnimator roxyBouncerE = ObjectAnimator.ofFloat(mP4EarthIV, "rotation", 0f,
308 | 359f);
309 | // CycleInterpolator interpolator = new CycleInterpolator(3.0f);
310 | LinearInterpolator lin = new LinearInterpolator();
311 | // roxyBouncerE.setInterpolator(lin);
312 | // roxyBouncer.setStartDelay(500);
313 | roxyBouncerE.setDuration(40000);
314 | roxyBouncerE.setRepeatCount(Animation.INFINITE);// Animation.INFINITE
315 | // roxyBouncer.setInterpolator(interpolator);
316 | // mP4EarthIV.setPivotX(mP4EarthIV.getWidth() * 0.5f);
317 | // mP4EarthIV.setPivotY(mP4EarthIV.getHeight() * 0.5f);
318 | // roxyBouncer.start();
319 |
320 | ObjectAnimator roxyBouncerEC = ObjectAnimator.ofFloat(mP4EarthCloudIV,
321 | "rotation", 0f, 359f);
322 | // roxyBouncerEC.setInterpolator(lin);
323 | roxyBouncerEC.setDuration(60000);
324 | roxyBouncerEC.setRepeatCount(Animation.INFINITE);
325 |
326 | // mP4AnimatorSet.setDuration(50000);
327 | mP4AnimatorSet.setInterpolator(lin);
328 | mP4AnimatorSet.playTogether(roxyBouncerE, roxyBouncerEC, tyBouncer);
329 | // mP4AnimatorSet.start();
330 |
331 | mP4AnimatorSetOne.setDuration(400);
332 | mP4AnimatorSetOne.setInterpolator(lin);
333 | mP4AnimatorSetOne.playTogether(roxyBouncerEOne, roxyBouncerECOne);
334 | // mP4AnimatorSetOne.start();
335 |
336 | mP4AnimatorSetAllInOne = new AnimatorSet();
337 | mP4AnimatorSetAllInOne.playSequentially(mP4AnimatorSetOne, mP4AnimatorSet);
338 | mP4AnimatorSetAllInOne.start();
339 |
340 | break;
341 | }
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/MyAnimatorSet.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.HashMap;
6 | import java.util.List;
7 |
8 | import android.animation.Animator;
9 | import android.animation.AnimatorListenerAdapter;
10 | import android.animation.ObjectAnimator;
11 | import android.animation.TimeInterpolator;
12 | import android.animation.ValueAnimator;
13 |
14 | public class MyAnimatorSet extends Animator {
15 |
16 | /**
17 | * Internal variables
18 | * NOTE: This object implements the clone() method, making a deep copy of any referenced
19 | * objects. As other non-trivial fields are added to this class, make sure to add logic
20 | * to clone() to make deep copies of them.
21 | */
22 |
23 | /**
24 | * Tracks animations currently being played, so that we know what to
25 | * cancel or end when cancel() or end() is called on this MyAnimatorSet
26 | */
27 | private ArrayList mPlayingSet = new ArrayList();
28 |
29 | /**
30 | * Contains all nodes, mapped to their respective Animators. When new
31 | * dependency information is added for an Animator, we want to add it
32 | * to a single node representing that Animator, not create a new Node
33 | * if one already exists.
34 | */
35 | private HashMap mNodeMap = new HashMap();
36 |
37 | /**
38 | * Set of all nodes created for this MyAnimatorSet. This list is used upon
39 | * starting the set, and the nodes are placed in sorted order into the
40 | * sortedNodes collection.
41 | */
42 | private ArrayList mNodes = new ArrayList();
43 |
44 | /**
45 | * The sorted list of nodes. This is the order in which the animations will
46 | * be played. The details about when exactly they will be played depend
47 | * on the dependency relationships of the nodes.
48 | */
49 | private ArrayList mSortedNodes = new ArrayList();
50 |
51 | /**
52 | * Flag indicating whether the nodes should be sorted prior to playing. This
53 | * flag allows us to cache the previous sorted nodes so that if the sequence
54 | * is replayed with no changes, it does not have to re-sort the nodes again.
55 | */
56 | private boolean mNeedsSort = true;
57 |
58 | private AnimatorSetListener mSetListener = null;
59 |
60 | /**
61 | * Flag indicating that the MyAnimatorSet has been manually
62 | * terminated (by calling cancel() or end()).
63 | * This flag is used to avoid starting other animations when currently-playing
64 | * child animations of this MyAnimatorSet end. It also determines whether cancel/end
65 | * notifications are sent out via the normal AnimatorSetListener mechanism.
66 | */
67 | boolean mTerminated = false;
68 |
69 | /**
70 | * Indicates whether an MyAnimatorSet has been start()'d, whether or
71 | * not there is a nonzero startDelay.
72 | */
73 | private boolean mStarted = false;
74 |
75 | // The amount of time in ms to delay starting the animation after start() is called
76 | private long mStartDelay = 0;
77 |
78 | // Animator used for a nonzero startDelay
79 | private ValueAnimator mDelayAnim = null;
80 |
81 | // How long the child animations should last in ms. The default value is negative, which
82 | // simply means that there is no duration set on the MyAnimatorSet. When a real duration is
83 | // set, it is passed along to the child animations.
84 | private long mDuration = -1;
85 |
86 | // Records the interpolator for the set. Null value indicates that no interpolator
87 | // was set on this MyAnimatorSet, so it should not be passed down to the children.
88 | private TimeInterpolator mInterpolator = null;
89 |
90 | /**
91 | * Sets up this MyAnimatorSet to play all of the supplied animations at the same time.
92 | * This is equivalent to calling {@link #play(Animator)} with the first animator in the
93 | * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
94 | * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
95 | * start until that delay elapses, which means that if the first animator in the list
96 | * supplied to this constructor has a startDelay, none of the other animators will start
97 | * until that first animator's startDelay has elapsed.
98 | *
99 | * @param items
100 | * The animations that will be started simultaneously.
101 | */
102 | public void playTogether(Animator... items) {
103 | if (items != null) {
104 | mNeedsSort = true;
105 | Builder builder = play(items[0]);
106 | for (int i = 1; i < items.length; ++i) {
107 | builder.with(items[i]);
108 | }
109 | }
110 | }
111 |
112 | /**
113 | * Sets up this MyAnimatorSet to play all of the supplied animations at the same time.
114 | *
115 | * @param items
116 | * The animations that will be started simultaneously.
117 | */
118 | public void playTogether(Collection items) {
119 | if (items != null && items.size() > 0) {
120 | mNeedsSort = true;
121 | Builder builder = null;
122 | for (Animator anim : items) {
123 | if (builder == null) {
124 | builder = play(anim);
125 | } else {
126 | builder.with(anim);
127 | }
128 | }
129 | }
130 | }
131 |
132 | /**
133 | * Sets up this MyAnimatorSet to play each of the supplied animations when the
134 | * previous animation ends.
135 | *
136 | * @param items
137 | * The animations that will be started one after another.
138 | */
139 | public void playSequentially(Animator... items) {
140 | if (items != null) {
141 | mNeedsSort = true;
142 | if (items.length == 1) {
143 | play(items[0]);
144 | } else {
145 | for (int i = 0; i < items.length - 1; ++i) {
146 | play(items[i]).before(items[i + 1]);
147 | }
148 | }
149 | }
150 | }
151 |
152 | /**
153 | * Sets up this MyAnimatorSet to play each of the supplied animations when the
154 | * previous animation ends.
155 | *
156 | * @param items
157 | * The animations that will be started one after another.
158 | */
159 | public void playSequentially(List items) {
160 | if (items != null && items.size() > 0) {
161 | mNeedsSort = true;
162 | if (items.size() == 1) {
163 | play(items.get(0));
164 | } else {
165 | for (int i = 0; i < items.size() - 1; ++i) {
166 | play(items.get(i)).before(items.get(i + 1));
167 | }
168 | }
169 | }
170 | }
171 |
172 | /**
173 | * Returns the current list of child Animator objects controlled by this
174 | * MyAnimatorSet. This is a copy of the internal list; modifications to the returned list
175 | * will not affect the MyAnimatorSet, although changes to the underlying Animator objects
176 | * will affect those objects being managed by the MyAnimatorSet.
177 | *
178 | * @return ArrayList The list of child animations of this MyAnimatorSet.
179 | */
180 | public ArrayList getChildAnimations() {
181 | ArrayList childList = new ArrayList();
182 | for (Node node : mNodes) {
183 | childList.add(node.animation);
184 | }
185 | return childList;
186 | }
187 |
188 | /**
189 | * Sets the target object for all current {@link #getChildAnimations() child animations} of this
190 | * MyAnimatorSet that take targets ({@link ObjectAnimator} and
191 | * MyAnimatorSet).
192 | *
193 | * @param target
194 | * The object being animated
195 | */
196 | @Override
197 | public void setTarget(Object target) {
198 | for (Node node : mNodes) {
199 | Animator animation = node.animation;
200 | if (animation instanceof MyAnimatorSet) {
201 | ((MyAnimatorSet) animation).setTarget(target);
202 | } else if (animation instanceof ObjectAnimator) {
203 | ((ObjectAnimator) animation).setTarget(target);
204 | }
205 | }
206 | }
207 |
208 | /**
209 | * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations} of
210 | * this MyAnimatorSet. The default value is null, which means that no interpolator
211 | * is set on this MyAnimatorSet. Setting the interpolator to any non-null value
212 | * will cause that interpolator to be set on the child animations
213 | * when the set is started.
214 | *
215 | * @param interpolator
216 | * the interpolator to be used by each child animation of this MyAnimatorSet
217 | */
218 | @Override
219 | public void setInterpolator(TimeInterpolator interpolator) {
220 | mInterpolator = interpolator;
221 | }
222 |
223 | @Override
224 | public TimeInterpolator getInterpolator() {
225 | return mInterpolator;
226 | }
227 |
228 | /**
229 | * This method creates a Builder
object, which is used to
230 | * set up playing constraints. This initial play()
method
231 | * tells the Builder
the animation that is the dependency for
232 | * the succeeding commands to the Builder
. For example,
233 | * calling play(a1).with(a2)
sets up the MyAnimatorSet to play a1
and
234 | * a2
at the same time, play(a1).before(a2)
sets up the MyAnimatorSet
235 | * to
236 | * play a1
first, followed by a2
, and play(a1).after(a2)
237 | * sets up the MyAnimatorSet to play a2
first, followed by a1
.
238 | *
239 | * Note that play()
is the only way to tell the Builder
the animation
240 | * upon which the dependency is created, so successive calls to the various functions in
241 | * Builder
will all refer to the initial parameter supplied in play()
242 | * as the dependency of the other animations. For example, calling
243 | * play(a1).before(a2).before(a3)
will play both a2
and
244 | * a3
when a1 ends; it does not set up a dependency between a2
and
245 | * a3
.
246 | *
247 | *
248 | * @param anim
249 | * The animation that is the dependency used in later calls to the
250 | * methods in the returned Builder
object. A null parameter will result
251 | * in a null Builder
return value.
252 | * @return Builder The object that constructs the MyAnimatorSet based on the dependencies
253 | * outlined in the calls to play
and the other methods in the
254 | * Builder
267 | * Note that canceling a MyAnimatorSet
also cancels all of the animations that it
268 | * is responsible for.
269 | *
270 | */
271 | @SuppressWarnings("unchecked")
272 | @Override
273 | public void cancel() {
274 | mTerminated = true;
275 | if (isStarted()) {
276 | ArrayList tmpListeners = null;
277 | if (getListeners() != null) {
278 | tmpListeners = (ArrayList) getListeners().clone();
279 | for (AnimatorListener listener : tmpListeners) {
280 | listener.onAnimationCancel(this);
281 | }
282 | }
283 | if (mDelayAnim != null && mDelayAnim.isRunning()) {
284 | // If we're currently in the startDelay period, just cancel that animator and
285 | // send out the end event to all listeners
286 | mDelayAnim.cancel();
287 | } else if (mSortedNodes.size() > 0) {
288 | for (Node node : mSortedNodes) {
289 | node.animation.cancel();
290 | }
291 | }
292 | if (tmpListeners != null) {
293 | for (AnimatorListener listener : tmpListeners) {
294 | listener.onAnimationEnd(this);
295 | }
296 | }
297 | mStarted = false;
298 | }
299 | }
300 |
301 | /**
302 | * {@inheritDoc}
303 | *
304 | * Note that ending a MyAnimatorSet
also ends all of the animations that it is
305 | * responsible for.
306 | *
307 | */
308 | @Override
309 | public void end() {
310 | mTerminated = true;
311 | if (isStarted()) {
312 | if (mSortedNodes.size() != mNodes.size()) {
313 | // hasn't been started yet - sort the nodes now, then end them
314 | sortNodes();
315 | for (Node node : mSortedNodes) {
316 | if (mSetListener == null) {
317 | mSetListener = new AnimatorSetListener(this);
318 | }
319 | node.animation.addListener(mSetListener);
320 | }
321 | }
322 | if (mDelayAnim != null) {
323 | mDelayAnim.cancel();
324 | }
325 | if (mSortedNodes.size() > 0) {
326 | for (Node node : mSortedNodes) {
327 | node.animation.end();
328 | }
329 | }
330 | if (getListeners() != null) {
331 | ArrayList tmpListeners = (ArrayList) getListeners()
332 | .clone();
333 | for (AnimatorListener listener : tmpListeners) {
334 | listener.onAnimationEnd(this);
335 | }
336 | }
337 | mStarted = false;
338 | }
339 | }
340 |
341 | /**
342 | * Returns true if any of the child animations of this MyAnimatorSet have been started and have
343 | * not yet ended.
344 | *
345 | * @return Whether this MyAnimatorSet has been started and has not yet ended.
346 | */
347 | @Override
348 | public boolean isRunning() {
349 | for (Node node : mNodes) {
350 | if (node.animation.isRunning()) {
351 | return true;
352 | }
353 | }
354 | return false;
355 | }
356 |
357 | @Override
358 | public boolean isStarted() {
359 | return mStarted;
360 | }
361 |
362 | /**
363 | * The amount of time, in milliseconds, to delay starting the animation after {@link #start()}
364 | * is called.
365 | *
366 | * @return the number of milliseconds to delay running the animation
367 | */
368 | @Override
369 | public long getStartDelay() {
370 | return mStartDelay;
371 | }
372 |
373 | /**
374 | * The amount of time, in milliseconds, to delay starting the animation after {@link #start()}
375 | * is called.
376 | *
377 | * @param startDelay
378 | * The amount of the delay, in milliseconds
379 | */
380 | @Override
381 | public void setStartDelay(long startDelay) {
382 | mStartDelay = startDelay;
383 | }
384 |
385 | /**
386 | * Gets the length of each of the child animations of this MyAnimatorSet. This value may
387 | * be less than 0, which indicates that no duration has been set on this MyAnimatorSet
388 | * and each of the child animations will use their own duration.
389 | *
390 | * @return The length of the animation, in milliseconds, of each of the child
391 | * animations of this MyAnimatorSet.
392 | */
393 | @Override
394 | public long getDuration() {
395 | return mDuration;
396 | }
397 |
398 | /**
399 | * Sets the length of each of the current child animations of this MyAnimatorSet. By default,
400 | * each child animation will use its own duration. If the duration is set on the MyAnimatorSet,
401 | * then each child animation inherits this duration.
402 | *
403 | * @param duration
404 | * The length of the animation, in milliseconds, of each of the child
405 | * animations of this MyAnimatorSet.
406 | */
407 | @Override
408 | public MyAnimatorSet setDuration(long duration) {
409 | if (duration < 0) {
410 | throw new IllegalArgumentException("duration must be a value of zero or greater");
411 | }
412 | // Just record the value for now - it will be used later when the MyAnimatorSet starts
413 | mDuration = duration;
414 | return this;
415 | }
416 |
417 | @Override
418 | public void setupStartValues() {
419 | for (Node node : mNodes) {
420 | node.animation.setupStartValues();
421 | }
422 | }
423 |
424 | @Override
425 | public void setupEndValues() {
426 | for (Node node : mNodes) {
427 | node.animation.setupEndValues();
428 | }
429 | }
430 |
431 | @Override
432 | public void pause() {
433 | boolean previouslyPaused = isPaused();
434 | super.pause();
435 | if (!previouslyPaused && isPaused()) {
436 | if (mDelayAnim != null) {
437 | mDelayAnim.pause();
438 | } else {
439 | for (Node node : mNodes) {
440 | node.animation.pause();
441 | }
442 | }
443 | }
444 | }
445 |
446 | @Override
447 | public void resume() {
448 | boolean previouslyPaused = isPaused();
449 | super.resume();
450 | if (previouslyPaused && !isPaused()) {
451 | if (mDelayAnim != null) {
452 | mDelayAnim.resume();
453 | } else {
454 | for (Node node : mNodes) {
455 | node.animation.resume();
456 | }
457 | }
458 | }
459 | }
460 |
461 | /**
462 | * {@inheritDoc}
463 | *
464 | * Starting this MyAnimatorSet
will, in turn, start the animations for which it is
465 | * responsible. The details of when exactly those animations are started depends on the
466 | * dependency relationships that have been set up between the animations.
467 | */
468 | @SuppressWarnings("unchecked")
469 | @Override
470 | public void start() {
471 | mTerminated = false;
472 | mStarted = true;
473 | // isPaused() = false;
474 |
475 | if (mDuration >= 0) {
476 | // If the duration was set on this MyAnimatorSet, pass it along to all child animations
477 | for (Node node : mNodes) {
478 | // TODO: don't set the duration of the timing-only nodes created by MyAnimatorSet to
479 | // insert "play-after" delays
480 | node.animation.setDuration(mDuration);
481 | }
482 | }
483 | if (mInterpolator != null) {
484 | for (Node node : mNodes) {
485 | node.animation.setInterpolator(mInterpolator);
486 | }
487 | }
488 | // First, sort the nodes (if necessary). This will ensure that sortedNodes
489 | // contains the animation nodes in the correct order.
490 | sortNodes();
491 |
492 | int numSortedNodes = mSortedNodes.size();
493 | for (int i = 0; i < numSortedNodes; ++i) {
494 | Node node = mSortedNodes.get(i);
495 | // First, clear out the old listeners
496 | ArrayList oldListeners = node.animation.getListeners();
497 | if (oldListeners != null && oldListeners.size() > 0) {
498 | final ArrayList clonedListeners = new ArrayList(
499 | oldListeners);
500 |
501 | for (AnimatorListener listener : clonedListeners) {
502 | if (listener instanceof DependencyListener
503 | || listener instanceof AnimatorSetListener) {
504 | node.animation.removeListener(listener);
505 | }
506 | }
507 | }
508 | }
509 |
510 | // nodesToStart holds the list of nodes to be started immediately. We don't want to
511 | // start the animations in the loop directly because we first need to set up
512 | // dependencies on all of the nodes. For example, we don't want to start an animation
513 | // when some other animation also wants to start when the first animation begins.
514 | final ArrayList nodesToStart = new ArrayList();
515 | for (int i = 0; i < numSortedNodes; ++i) {
516 | Node node = mSortedNodes.get(i);
517 | if (mSetListener == null) {
518 | mSetListener = new AnimatorSetListener(this);
519 | }
520 | if (node.dependencies == null || node.dependencies.size() == 0) {
521 | nodesToStart.add(node);
522 | } else {
523 | int numDependencies = node.dependencies.size();
524 | for (int j = 0; j < numDependencies; ++j) {
525 | Dependency dependency = node.dependencies.get(j);
526 | dependency.node.animation.addListener(new DependencyListener(this, node,
527 | dependency.rule));
528 | }
529 | node.tmpDependencies = (ArrayList) node.dependencies.clone();
530 | }
531 | node.animation.addListener(mSetListener);
532 | }
533 | // Now that all dependencies are set up, start the animations that should be started.
534 | if (mStartDelay <= 0) {
535 | for (Node node : nodesToStart) {
536 | node.animation.start();
537 | mPlayingSet.add(node.animation);
538 | }
539 | } else {
540 | mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
541 | mDelayAnim.setDuration(mStartDelay);
542 | mDelayAnim.addListener(new AnimatorListenerAdapter() {
543 | boolean canceled = false;
544 |
545 | public void onAnimationCancel(Animator anim) {
546 | canceled = true;
547 | }
548 |
549 | public void onAnimationEnd(Animator anim) {
550 | if (!canceled) {
551 | int numNodes = nodesToStart.size();
552 | for (int i = 0; i < numNodes; ++i) {
553 | Node node = nodesToStart.get(i);
554 | node.animation.start();
555 | mPlayingSet.add(node.animation);
556 | }
557 | }
558 | mDelayAnim = null;
559 | }
560 | });
561 | mDelayAnim.start();
562 | }
563 | if (getListeners() != null) {
564 | ArrayList tmpListeners = (ArrayList) getListeners()
565 | .clone();
566 | int numListeners = tmpListeners.size();
567 | for (int i = 0; i < numListeners; ++i) {
568 | tmpListeners.get(i).onAnimationStart(this);
569 | }
570 | }
571 | if (mNodes.size() == 0 && mStartDelay == 0) {
572 | // Handle unusual case where empty MyAnimatorSet is started - should send out
573 | // end event immediately since the event will not be sent out at all otherwise
574 | mStarted = false;
575 | if (getListeners() != null) {
576 | ArrayList tmpListeners = (ArrayList) getListeners()
577 | .clone();
578 | int numListeners = tmpListeners.size();
579 | for (int i = 0; i < numListeners; ++i) {
580 | tmpListeners.get(i).onAnimationEnd(this);
581 | }
582 | }
583 | }
584 | }
585 |
586 | @Override
587 | public MyAnimatorSet clone() {
588 | final MyAnimatorSet anim = (MyAnimatorSet) super.clone();
589 | /*
590 | * The basic clone() operation copies all items. This doesn't work very well for
591 | * MyAnimatorSet, because it will copy references that need to be recreated and state
592 | * that may not apply. What we need to do now is put the clone in an uninitialized
593 | * state, with fresh, empty data structures. Then we will build up the nodes list
594 | * manually, as we clone each Node (and its animation). The clone will then be sorted,
595 | * and will populate any appropriate lists, when it is started.
596 | */
597 | anim.mNeedsSort = true;
598 | anim.mTerminated = false;
599 | anim.mStarted = false;
600 | anim.mPlayingSet = new ArrayList();
601 | anim.mNodeMap = new HashMap();
602 | anim.mNodes = new ArrayList();
603 | anim.mSortedNodes = new ArrayList();
604 |
605 | // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
606 | // One problem is that the old node dependencies point to nodes in the old MyAnimatorSet.
607 | // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
608 | HashMap nodeCloneMap = new HashMap(); //
609 | for (Node node : mNodes) {
610 | Node nodeClone = node.clone();
611 | nodeCloneMap.put(node, nodeClone);
612 | anim.mNodes.add(nodeClone);
613 | anim.mNodeMap.put(nodeClone.animation, nodeClone);
614 | // Clear out the dependencies in the clone; we'll set these up manually later
615 | nodeClone.dependencies = null;
616 | nodeClone.tmpDependencies = null;
617 | nodeClone.nodeDependents = null;
618 | nodeClone.nodeDependencies = null;
619 | // clear out any listeners that were set up by the MyAnimatorSet; these will
620 | // be set up when the clone's nodes are sorted
621 | ArrayList cloneListeners = nodeClone.animation.getListeners();
622 | if (cloneListeners != null) {
623 | ArrayList listenersToRemove = null;
624 | for (AnimatorListener listener : cloneListeners) {
625 | if (listener instanceof AnimatorSetListener) {
626 | if (listenersToRemove == null) {
627 | listenersToRemove = new ArrayList();
628 | }
629 | listenersToRemove.add(listener);
630 | }
631 | }
632 | if (listenersToRemove != null) {
633 | for (AnimatorListener listener : listenersToRemove) {
634 | cloneListeners.remove(listener);
635 | }
636 | }
637 | }
638 | }
639 | // Now that we've cloned all of the nodes, we're ready to walk through their
640 | // dependencies, mapping the old dependencies to the new nodes
641 | for (Node node : mNodes) {
642 | Node nodeClone = nodeCloneMap.get(node);
643 | if (node.dependencies != null) {
644 | for (Dependency dependency : node.dependencies) {
645 | Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
646 | Dependency cloneDependency = new Dependency(clonedDependencyNode,
647 | dependency.rule);
648 | nodeClone.addDependency(cloneDependency);
649 | }
650 | }
651 | }
652 |
653 | return anim;
654 | }
655 |
656 | /**
657 | * This class is the mechanism by which animations are started based on events in other
658 | * animations. If an animation has multiple dependencies on other animations, then
659 | * all dependencies must be satisfied before the animation is started.
660 | */
661 | private static class DependencyListener implements AnimatorListener {
662 |
663 | private MyAnimatorSet mAnimatorSet;
664 |
665 | // The node upon which the dependency is based.
666 | private Node mNode;
667 |
668 | // The Dependency rule (WITH or AFTER) that the listener should wait for on
669 | // the node
670 | private int mRule;
671 |
672 | public DependencyListener(MyAnimatorSet animatorSet, Node node, int rule) {
673 | this.mAnimatorSet = animatorSet;
674 | this.mNode = node;
675 | this.mRule = rule;
676 | }
677 |
678 | /**
679 | * Ignore cancel events for now. We may want to handle this eventually,
680 | * to prevent follow-on animations from running when some dependency
681 | * animation is canceled.
682 | */
683 | public void onAnimationCancel(Animator animation) {
684 | }
685 |
686 | /**
687 | * An end event is received - see if this is an event we are listening for
688 | */
689 | public void onAnimationEnd(Animator animation) {
690 | if (mRule == Dependency.AFTER) {
691 | startIfReady(animation);
692 | }
693 | }
694 |
695 | /**
696 | * Ignore repeat events for now
697 | */
698 | public void onAnimationRepeat(Animator animation) {
699 | }
700 |
701 | /**
702 | * A start event is received - see if this is an event we are listening for
703 | */
704 | public void onAnimationStart(Animator animation) {
705 | if (mRule == Dependency.WITH) {
706 | startIfReady(animation);
707 | }
708 | }
709 |
710 | /**
711 | * Check whether the event received is one that the node was waiting for.
712 | * If so, mark it as complete and see whether it's time to start
713 | * the animation.
714 | *
715 | * @param dependencyAnimation
716 | * the animation that sent the event.
717 | */
718 | private void startIfReady(Animator dependencyAnimation) {
719 | if (mAnimatorSet.mTerminated) {
720 | // if the parent MyAnimatorSet was canceled, then don't start any dependent anims
721 | return;
722 | }
723 | Dependency dependencyToRemove = null;
724 | int numDependencies = mNode.tmpDependencies.size();
725 | for (int i = 0; i < numDependencies; ++i) {
726 | Dependency dependency = mNode.tmpDependencies.get(i);
727 | if (dependency.rule == mRule && dependency.node.animation == dependencyAnimation) {
728 | // rule fired - remove the dependency and listener and check to
729 | // see whether it's time to start the animation
730 | dependencyToRemove = dependency;
731 | dependencyAnimation.removeListener(this);
732 | break;
733 | }
734 | }
735 | mNode.tmpDependencies.remove(dependencyToRemove);
736 | if (mNode.tmpDependencies.size() == 0) {
737 | // all dependencies satisfied: start the animation
738 | mNode.animation.start();
739 | mAnimatorSet.mPlayingSet.add(mNode.animation);
740 | }
741 | }
742 |
743 | }
744 |
745 | private class AnimatorSetListener implements AnimatorListener {
746 |
747 | private MyAnimatorSet mAnimatorSet;
748 |
749 | AnimatorSetListener(MyAnimatorSet animatorSet) {
750 | mAnimatorSet = animatorSet;
751 | }
752 |
753 | public void onAnimationCancel(Animator animation) {
754 | if (!mTerminated) {
755 | // Listeners are already notified of the MyAnimatorSet canceling in cancel().
756 | // The logic below only kicks in when animations end normally
757 | if (mPlayingSet.size() == 0) {
758 | if (getListeners() != null) {
759 | int numListeners = getListeners().size();
760 | for (int i = 0; i < numListeners; ++i) {
761 | getListeners().get(i).onAnimationCancel(mAnimatorSet);
762 | }
763 | }
764 | }
765 | }
766 | }
767 |
768 | @SuppressWarnings("unchecked")
769 | public void onAnimationEnd(Animator animation) {
770 | animation.removeListener(this);
771 | mPlayingSet.remove(animation);
772 | Node animNode = mAnimatorSet.mNodeMap.get(animation);
773 | animNode.done = true;
774 | if (!mTerminated) {
775 | // Listeners are already notified of the MyAnimatorSet ending in cancel() or
776 | // end(); the logic below only kicks in when animations end normally
777 | ArrayList sortedNodes = mAnimatorSet.mSortedNodes;
778 | boolean allDone = true;
779 | int numSortedNodes = sortedNodes.size();
780 | for (int i = 0; i < numSortedNodes; ++i) {
781 | if (!sortedNodes.get(i).done) {
782 | allDone = false;
783 | break;
784 | }
785 | }
786 | if (allDone) {
787 | // If this was the last child animation to end, then notify listeners that this
788 | // MyAnimatorSet has ended
789 | if (getListeners() != null) {
790 | ArrayList tmpListeners = (ArrayList) getListeners()
791 | .clone();
792 | int numListeners = tmpListeners.size();
793 | for (int i = 0; i < numListeners; ++i) {
794 | tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
795 | }
796 | }
797 | mAnimatorSet.mStarted = false;
798 | // mAnimatorSet.set = false;
799 | }
800 | }
801 | }
802 |
803 | // Nothing to do
804 | public void onAnimationRepeat(Animator animation) {
805 | }
806 |
807 | // Nothing to do
808 | public void onAnimationStart(Animator animation) {
809 | }
810 |
811 | }
812 |
813 | /**
814 | * This method sorts the current set of nodes, if needed. The sort is a simple
815 | * DependencyGraph sort, which goes like this:
816 | * - All nodes without dependencies become 'roots'
817 | * - while roots list is not null
818 | * - for each root r
819 | * - add r to sorted list
820 | * - remove r as a dependency from any other node
821 | * - any nodes with no dependencies are added to the roots list
822 | */
823 | private void sortNodes() {
824 | if (mNeedsSort) {
825 | mSortedNodes.clear();
826 | ArrayList roots = new ArrayList();
827 | int numNodes = mNodes.size();
828 | for (int i = 0; i < numNodes; ++i) {
829 | Node node = mNodes.get(i);
830 | if (node.dependencies == null || node.dependencies.size() == 0) {
831 | roots.add(node);
832 | }
833 | }
834 | ArrayList tmpRoots = new ArrayList();
835 | while (roots.size() > 0) {
836 | int numRoots = roots.size();
837 | for (int i = 0; i < numRoots; ++i) {
838 | Node root = roots.get(i);
839 | mSortedNodes.add(root);
840 | if (root.nodeDependents != null) {
841 | int numDependents = root.nodeDependents.size();
842 | for (int j = 0; j < numDependents; ++j) {
843 | Node node = root.nodeDependents.get(j);
844 | node.nodeDependencies.remove(root);
845 | if (node.nodeDependencies.size() == 0) {
846 | tmpRoots.add(node);
847 | }
848 | }
849 | }
850 | }
851 | roots.clear();
852 | roots.addAll(tmpRoots);
853 | tmpRoots.clear();
854 | }
855 | mNeedsSort = false;
856 | if (mSortedNodes.size() != mNodes.size()) {
857 | throw new IllegalStateException("Circular dependencies cannot exist"
858 | + " in MyAnimatorSet");
859 | }
860 | } else {
861 | // Doesn't need sorting, but still need to add in the nodeDependencies list
862 | // because these get removed as the event listeners fire and the dependencies
863 | // are satisfied
864 | int numNodes = mNodes.size();
865 | for (int i = 0; i < numNodes; ++i) {
866 | Node node = mNodes.get(i);
867 | if (node.dependencies != null && node.dependencies.size() > 0) {
868 | int numDependencies = node.dependencies.size();
869 | for (int j = 0; j < numDependencies; ++j) {
870 | Dependency dependency = node.dependencies.get(j);
871 | if (node.nodeDependencies == null) {
872 | node.nodeDependencies = new ArrayList();
873 | }
874 | if (!node.nodeDependencies.contains(dependency.node)) {
875 | node.nodeDependencies.add(dependency.node);
876 | }
877 | }
878 | }
879 | // nodes are 'done' by default; they become un-done when started, and done
880 | // again when ended
881 | node.done = false;
882 | }
883 | }
884 | }
885 |
886 | /**
887 | * Dependency holds information about the node that some other node is
888 | * dependent upon and the nature of that dependency.
889 | */
890 | private static class Dependency {
891 | static final int WITH = 0; // dependent node must start with this dependency node
892 | static final int AFTER = 1; // dependent node must start when this dependency node finishes
893 |
894 | // The node that the other node with this Dependency is dependent upon
895 | public Node node;
896 |
897 | // The nature of the dependency (WITH or AFTER)
898 | public int rule;
899 |
900 | public Dependency(Node node, int rule) {
901 | this.node = node;
902 | this.rule = rule;
903 | }
904 | }
905 |
906 | /**
907 | * A Node is an embodiment of both the Animator that it wraps as well as
908 | * any dependencies that are associated with that Animation. This includes
909 | * both dependencies upon other nodes (in the dependencies list) as
910 | * well as dependencies of other nodes upon this (in the nodeDependents list).
911 | */
912 | private static class Node implements Cloneable {
913 | public Animator animation;
914 |
915 | /**
916 | * These are the dependencies that this node's animation has on other
917 | * nodes. For example, if this node's animation should begin with some
918 | * other animation ends, then there will be an item in this node's
919 | * dependencies list for that other animation's node.
920 | */
921 | public ArrayList dependencies = null;
922 |
923 | /**
924 | * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
925 | * But we also use the list to keep track of when multiple dependencies are satisfied,
926 | * but removing each dependency as it is satisfied. We do not want to remove
927 | * the dependency itself from the list, because we need to retain that information
928 | * if the MyAnimatorSet is launched in the future. So we create a copy of the dependency
929 | * list when the MyAnimatorSet starts and use this tmpDependencies list to track the
930 | * list of satisfied dependencies.
931 | */
932 | public ArrayList tmpDependencies = null;
933 |
934 | /**
935 | * nodeDependencies is just a list of the nodes that this Node is dependent upon.
936 | * This information is used in sortNodes(), to determine when a node is a root.
937 | */
938 | public ArrayList nodeDependencies = null;
939 |
940 | /**
941 | * nodeDepdendents is the list of nodes that have this node as a dependency. This
942 | * is a utility field used in sortNodes to facilitate removing this node as a
943 | * dependency when it is a root node.
944 | */
945 | public ArrayList nodeDependents = null;
946 |
947 | /**
948 | * Flag indicating whether the animation in this node is finished. This flag
949 | * is used by MyAnimatorSet to check, as each animation ends, whether all child animations
950 | * are done and it's time to send out an end event for the entire MyAnimatorSet.
951 | */
952 | public boolean done = false;
953 |
954 | /**
955 | * Constructs the Node with the animation that it encapsulates. A Node has no
956 | * dependencies by default; dependencies are added via the addDependency()
957 | * method.
958 | *
959 | * @param animation
960 | * The animation that the Node encapsulates.
961 | */
962 | public Node(Animator animation) {
963 | this.animation = animation;
964 | }
965 |
966 | /**
967 | * Add a dependency to this Node. The dependency includes information about the
968 | * node that this node is dependency upon and the nature of the dependency.
969 | *
970 | * @param dependency
971 | */
972 | public void addDependency(Dependency dependency) {
973 | if (dependencies == null) {
974 | dependencies = new ArrayList();
975 | nodeDependencies = new ArrayList();
976 | }
977 | dependencies.add(dependency);
978 | if (!nodeDependencies.contains(dependency.node)) {
979 | nodeDependencies.add(dependency.node);
980 | }
981 | Node dependencyNode = dependency.node;
982 | if (dependencyNode.nodeDependents == null) {
983 | dependencyNode.nodeDependents = new ArrayList();
984 | }
985 | dependencyNode.nodeDependents.add(this);
986 | }
987 |
988 | @Override
989 | public Node clone() {
990 | try {
991 | Node node = (Node) super.clone();
992 | node.animation = (Animator) animation.clone();
993 | return node;
994 | } catch (CloneNotSupportedException e) {
995 | throw new AssertionError();
996 | }
997 | }
998 | }
999 |
1000 | /**
1001 | * The Builder
object is a utility class to facilitate adding animations to a
1002 | * MyAnimatorSet
along with the relationships between the various animations. The
1003 | * intention of the Builder
methods, along with the
1004 | * {@link MyAnimatorSet#play(Animator) play()} method of MyAnimatorSet
is to make
1005 | * it
1006 | * possible
1007 | * to express the dependency relationships of animations in a natural way. Developers can also
1008 | * use the {@link MyAnimatorSet#playTogether(Animator[]) playTogether()} and
1009 | * {@link MyAnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit
1010 | * the
1011 | * need,
1012 | * but it might be easier in some situations to express the MyAnimatorSet of animations in
1013 | * pairs.
1014 | *
1015 | *
1016 | * The Builder
object cannot be constructed directly, but is rather constructed
1017 | * internally via a call to {@link MyAnimatorSet#play(Animator)}.
1018 | *
1019 | *
1020 | *
1021 | * For example, this sets up a MyAnimatorSet to play anim1 and anim2 at the same time, anim3 to
1022 | * play when anim2 finishes, and anim4 to play when anim3 finishes:
1023 | *
1024 | *
1025 | *
1026 | * MyAnimatorSet s = new MyAnimatorSet();
1027 | * s.play(anim1).with(anim2);
1028 | * s.play(anim2).before(anim3);
1029 | * s.play(anim4).after(anim3);
1030 | *
1031 | *
1032 | *
1033 | * Note in the example that both {@link Builder#before(Animator)} and
1034 | * {@link Builder#after(Animator)} are used. These are just different ways of expressing the
1035 | * same relationship and are provided to make it easier to say things in a way that is more
1036 | * natural, depending on the situation.
1037 | *
1038 | *
1039 | *
1040 | * It is possible to make several calls into the same Builder
object to express
1041 | * multiple relationships. However, note that it is only the animation passed into the initial
1042 | * {@link MyAnimatorSet#play(Animator)} method that is the dependency in any of the successive
1043 | * calls to the Builder
object. For example, the following code starts both anim2
1044 | * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1045 | * anim3:
1046 | *
1047 | *
1048 | * MyAnimatorSet s = new MyAnimatorSet();
1049 | * s.play(anim1).before(anim2).before(anim3);
1050 | *
1051 | *
1052 | * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1053 | * relationship correctly:
1054 | *
1055 | *
1056 | *
1057 | * MyAnimatorSet s = new MyAnimatorSet();
1058 | * s.play(anim1).before(anim2);
1059 | * s.play(anim2).before(anim3);
1060 | *
1061 | *
1062 | *
1063 | * Note that it is possible to express relationships that cannot be resolved and will not result
1064 | * in sensible results. For example, play(anim1).after(anim1)
makes no sense. In
1065 | * general, circular dependencies like this one (or more indirect ones where a depends on b,
1066 | * which depends on c, which depends on a) should be avoided. Only create AnimatorSets that can
1067 | * boil down to a simple, one-way relationship of animations starting with, before, and after
1068 | * other, different, animations.
1069 | *
1070 | */
1071 | public class Builder {
1072 |
1073 | /**
1074 | * This tracks the current node being processed. It is supplied to the play() method
1075 | * of MyAnimatorSet and passed into the constructor of Builder.
1076 | */
1077 | private Node mCurrentNode;
1078 |
1079 | /**
1080 | * package-private constructor. Builders are only constructed by MyAnimatorSet, when the
1081 | * play() method is called.
1082 | *
1083 | * @param anim
1084 | * The animation that is the dependency for the other animations passed into
1085 | * the other methods of this Builder object.
1086 | */
1087 | Builder(Animator anim) {
1088 | mCurrentNode = mNodeMap.get(anim);
1089 | if (mCurrentNode == null) {
1090 | mCurrentNode = new Node(anim);
1091 | mNodeMap.put(anim, mCurrentNode);
1092 | mNodes.add(mCurrentNode);
1093 | }
1094 | }
1095 |
1096 | /**
1097 | * Sets up the given animation to play at the same time as the animation supplied in the
1098 | * {@link MyAnimatorSet#play(Animator)} call that created this Builder
object.
1099 | *
1100 | * @param anim
1101 | * The animation that will play when the animation supplied to the
1102 | * {@link MyAnimatorSet#play(Animator)} method starts.
1103 | */
1104 | public Builder with(Animator anim) {
1105 | Node node = mNodeMap.get(anim);
1106 | if (node == null) {
1107 | node = new Node(anim);
1108 | mNodeMap.put(anim, node);
1109 | mNodes.add(node);
1110 | }
1111 | Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
1112 | node.addDependency(dependency);
1113 | return this;
1114 | }
1115 |
1116 | /**
1117 | * Sets up the given animation to play when the animation supplied in the
1118 | * {@link MyAnimatorSet#play(Animator)} call that created this Builder
object
1119 | * ends.
1120 | *
1121 | * @param anim
1122 | * The animation that will play when the animation supplied to the
1123 | * {@link MyAnimatorSet#play(Animator)} method ends.
1124 | */
1125 | public Builder before(Animator anim) {
1126 | Node node = mNodeMap.get(anim);
1127 | if (node == null) {
1128 | node = new Node(anim);
1129 | mNodeMap.put(anim, node);
1130 | mNodes.add(node);
1131 | }
1132 | Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
1133 | node.addDependency(dependency);
1134 | return this;
1135 | }
1136 |
1137 | /**
1138 | * Sets up the given animation to play when the animation supplied in the
1139 | * {@link MyAnimatorSet#play(Animator)} call that created this Builder
object
1140 | * to start when the animation supplied in this method call ends.
1141 | *
1142 | * @param anim
1143 | * The animation whose end will cause the animation supplied to the
1144 | * {@link MyAnimatorSet#play(Animator)} method to play.
1145 | */
1146 | public Builder after(Animator anim) {
1147 | Node node = mNodeMap.get(anim);
1148 | if (node == null) {
1149 | node = new Node(anim);
1150 | mNodeMap.put(anim, node);
1151 | mNodes.add(node);
1152 | }
1153 | Dependency dependency = new Dependency(node, Dependency.AFTER);
1154 | mCurrentNode.addDependency(dependency);
1155 | return this;
1156 | }
1157 |
1158 | /**
1159 | * Sets up the animation supplied in the {@link MyAnimatorSet#play(Animator)} call that
1160 | * created this Builder
object
1161 | * to play when the given amount of time elapses.
1162 | *
1163 | * @param delay
1164 | * The number of milliseconds that should elapse before the
1165 | * animation starts.
1166 | */
1167 | public Builder after(long delay) {
1168 | // setup dummy ValueAnimator just to run the clock
1169 | ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1170 | anim.setDuration(delay);
1171 | after(anim);
1172 | return this;
1173 | }
1174 |
1175 | }
1176 |
1177 | public void setCurrentPlayTime(long cpt) {
1178 | int numNodes = mNodes.size();
1179 | for (int i = 0; i < numNodes; ++i) {
1180 | Node node = mNodes.get(i);
1181 | ((ValueAnimator) node.animation).setCurrentPlayTime(cpt);
1182 | }
1183 | }
1184 |
1185 | public long getMaxDuration() {
1186 | long maxDuration = 0;
1187 | int numNodes = mNodes.size();
1188 | for (int i = 0; i < numNodes; ++i) {
1189 | Node node = mNodes.get(i);
1190 | maxDuration = Math.max(node.animation.getDuration(), maxDuration);
1191 | }
1192 | return maxDuration;
1193 | }
1194 |
1195 | MyAnimatorListener listener;
1196 |
1197 | public void setCurrentPlayTimeAtRatio(float ratio) {
1198 | int numNodes = mNodes.size();
1199 | // AnimatorListener listener = getListeners().get(0);
1200 | listener.onAnimationStart(this);
1201 | for (int i = 0; i < numNodes; ++i) {
1202 | Node node = mNodes.get(i);
1203 | ValueAnimator valueAnimator = (ValueAnimator) node.animation;
1204 | // if (valueAnimator.getRepeatCount() != -1) {
1205 | long cpt = (long) (valueAnimator.getDuration() * ratio);
1206 | valueAnimator.setCurrentPlayTime(cpt);
1207 | // } else {
1208 | // valueAnimator.start();
1209 | // }
1210 | }
1211 | }
1212 |
1213 | public void reverse() {
1214 | this.listener.onAnimationReverse(this);
1215 |
1216 | mTerminated = false;
1217 | mStarted = true;
1218 |
1219 | sortNodes();
1220 |
1221 | int numSortedNodes = mSortedNodes.size();
1222 | for (int i = 0; i < numSortedNodes; ++i) {
1223 | Node node = mSortedNodes.get(i);
1224 | // First, clear out the old listeners
1225 | ArrayList oldListeners = node.animation.getListeners();
1226 | if (oldListeners != null && oldListeners.size() > 0) {
1227 | final ArrayList clonedListeners = new ArrayList(
1228 | oldListeners);
1229 |
1230 | for (AnimatorListener listener : clonedListeners) {
1231 | if (listener instanceof DependencyListener
1232 | || listener instanceof AnimatorSetListener) {
1233 | node.animation.removeListener(listener);
1234 | }
1235 | }
1236 | }
1237 | }
1238 |
1239 | int numNodes = mNodes.size();
1240 | for (int i = 0; i < numNodes; ++i) {
1241 | Node node = mNodes.get(i);
1242 |
1243 | // 不顶用
1244 | // ArrayList oldListeners = node.animation.getListeners();
1245 | // if (oldListeners != null && oldListeners.size() > 0) {
1246 | // final ArrayList clonedListeners = new ArrayList(
1247 | // oldListeners);
1248 | //
1249 | // for (AnimatorListener listener : clonedListeners) {
1250 | // if (listener instanceof DependencyListener
1251 | // || listener instanceof AnimatorSetListener) {
1252 | // node.animation.removeListener(listener);
1253 | // }
1254 | // }
1255 | // }
1256 |
1257 | ((ValueAnimator) node.animation).reverse();
1258 |
1259 | if (mSetListener == null) {
1260 | mSetListener = new AnimatorSetListener(this);
1261 | }
1262 | node.animation.addListener(mSetListener);
1263 | }
1264 | }
1265 |
1266 | // @Override
1267 | // public void addListener(AnimatorListener listener) {
1268 | // super.addListener(listener);
1269 | //
1270 | // }
1271 |
1272 | public void addMyListener(MyAnimatorListener listener) {
1273 | super.addListener(listener);
1274 | this.listener = listener;
1275 | }
1276 |
1277 | public static class MyAnimatorListener implements AnimatorListener {
1278 |
1279 | public void onAnimationReverse(Animator animation) {
1280 |
1281 | }
1282 |
1283 | @Override
1284 | public void onAnimationStart(Animator animation) {
1285 |
1286 | }
1287 |
1288 | @Override
1289 | public void onAnimationEnd(Animator animation) {
1290 |
1291 | }
1292 |
1293 | @Override
1294 | public void onAnimationCancel(Animator animation) {
1295 |
1296 | }
1297 |
1298 | @Override
1299 | public void onAnimationRepeat(Animator animation) {
1300 |
1301 | }
1302 |
1303 | }
1304 |
1305 | public ArrayList getChildAnimationsClone() {
1306 | ArrayList childList = new ArrayList();
1307 | for (Node node : mNodes) {
1308 | Node nodeClone = node.clone();
1309 | childList.add(nodeClone.animation);
1310 | }
1311 | return childList;
1312 | }
1313 | }
1314 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/MyImageView.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import java.lang.reflect.Field;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 | import android.util.DisplayMetrics;
8 | import android.util.Log;
9 | import android.widget.ImageView;
10 |
11 | public class MyImageView extends ImageView {
12 |
13 | private static final String TAG = MyImageView.class.getSimpleName();
14 |
15 | public MyImageView(Context context, AttributeSet attrs, int defStyle) {
16 | super(context, attrs, defStyle);
17 | init(context);
18 | }
19 |
20 | public MyImageView(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | init(context);
23 | }
24 |
25 | public MyImageView(Context context) {
26 | super(context);
27 | init(context);
28 | }
29 |
30 | private int width, height = 0;
31 |
32 | private void init(Context context) {
33 | // 获取分辨率
34 | DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
35 | width = dm.widthPixels;
36 | // 高度不对
37 | // 应该去掉 状态栏
38 | height = dm.heightPixels;
39 | Class> c = null;
40 | Object obj = null;
41 | Field field = null;
42 | int x = 0, sbar = 0;
43 | try {
44 | c = Class.forName("com.android.internal.R$dimen");
45 | obj = c.newInstance();
46 | field = c.getField("status_bar_height");
47 | x = Integer.parseInt(field.get(obj).toString());
48 | sbar = getResources().getDimensionPixelSize(x);
49 | } catch (Exception e) {
50 | e.printStackTrace();
51 | }
52 | height = height - sbar;
53 | Log.d(TAG, "width" + width + "height" + height);
54 | }
55 |
56 | @Override
57 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
58 | // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
59 | setMeasuredDimension(width, height);
60 | // Log.d(TAG, "onMeasure :" + widthMeasureSpec + "/" + heightMeasureSpec);
61 | }
62 |
63 | @Override
64 | public void layout(int l, int t, int r, int b) {
65 | super.layout(l, t, r, b);
66 |
67 | Log.d(TAG, "layout :" + l + t + r + b);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/MyInterpolator.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import android.view.animation.Interpolator;
4 |
5 | public class MyInterpolator implements Interpolator {
6 |
7 | @Override
8 | public float getInterpolation(float t) {
9 | t = t * 1.43f;
10 | if (t == 1.0f) {
11 | return 1.0f;
12 | }
13 | return t;
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/MyVideoImageView.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapShader;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Matrix;
9 | import android.graphics.Paint;
10 | import android.graphics.PixelFormat;
11 | import android.graphics.Rect;
12 | import android.graphics.RectF;
13 | import android.graphics.Shader;
14 | import android.graphics.drawable.Drawable;
15 | import android.util.AttributeSet;
16 | import android.util.Log;
17 | import android.widget.ImageView;
18 |
19 | public class MyVideoImageView extends ImageView {
20 |
21 | private static final String TAG = MyVideoImageView.class.getSimpleName();
22 |
23 | private Rect mDrawableRect;
24 | private Bitmap mBitmap;
25 | private BitmapShader mBitmapShader;
26 | private Paint mBitmapPaint;
27 | private Matrix mShaderMatrix;
28 | private int mBitmapWidth;
29 | private int mBitmapHeight;
30 |
31 | private int mTopRect;
32 | private int mBottomRect;
33 |
34 | private String mTime;
35 |
36 | private float mTextFontSize;
37 | private float mTextX;
38 | private float mTextY;
39 |
40 | public MyVideoImageView(Context context, AttributeSet attrs, int defStyle) {
41 | super(context, attrs, defStyle);
42 | init(context);
43 | }
44 |
45 | public MyVideoImageView(Context context, AttributeSet attrs) {
46 | super(context, attrs);
47 | init(context);
48 | }
49 |
50 | public MyVideoImageView(Context context) {
51 | super(context);
52 | init(context);
53 | }
54 |
55 | public void setTimeText(String time) {
56 | mTime = time;
57 | }
58 |
59 | private boolean isReverse = false;
60 |
61 | public void setTranslationHight(float i) {
62 | Log.d(TAG, "translationHight :" + i);
63 | Log.d(TAG, "mTopRect :" + mTopRect + "/mBottomRect" + mBottomRect);
64 | // 做个保护
65 | if (i > 1 || i < 0)
66 | return;
67 | if (i == 1) {
68 | // mTopRect = 0;
69 | // mBottomRect = getBottom();
70 | invalidate();
71 | return;
72 | }
73 | // 动画一半,不做任何改变
74 | if (i > 0.5) {
75 | // return;
76 | i = 0.5f;
77 | }
78 | // 小于 10分之2 停止裁剪
79 | if (i < 0.2) {
80 | return;
81 | }
82 | // Log.d(TAG, "setTranslationHight :" + i);
83 |
84 | mTopRect = (int) (-getHeight() * (i - 0.5));
85 | mBottomRect = getHeight() - mTopRect;
86 |
87 | invalidate();
88 | }
89 |
90 | private void init(Context context) {
91 | mTextX = Util.dp2px(getContext(), 180);
92 | mTextY = Util.dp2px(getContext(), 40);
93 | mTextFontSize = Util.sp2px(getContext(), 18);
94 | }
95 |
96 | @Override
97 | protected void onDraw(Canvas canvas) {
98 |
99 | // super.onDraw(canvas);
100 | if (getDrawable() == null) {
101 | return;
102 | }
103 | // Log.d(TAG, "onDraw()");
104 | // if (isStart) {
105 |
106 | RectF rect = new RectF(0, mTopRect, getRight(), mBottomRect);
107 |
108 | // RectF rect = new RectF(getPaddingLeft(), getPaddingTop(), getRight() - getLeft()
109 | // - getPaddingRight(), getBottom() - getTop() - getPaddingBottom());
110 |
111 | canvas.drawRect(rect, mBitmapPaint);
112 | // }
113 |
114 | Paint p = new Paint();
115 | p.setColor(Color.BLACK);
116 | p.setTextSize(mTextFontSize);
117 | canvas.drawText(mTime, mTextX, mTextY, p);// 画文本
118 | }
119 |
120 | @Override
121 | public void setImageBitmap(Bitmap bm) {
122 | super.setImageBitmap(bm);
123 | mBitmap = bm;
124 | // mBitmap = drawableToBitmap(getDrawable());
125 | setup();
126 | }
127 |
128 | public static Bitmap drawableToBitmap(Drawable drawable) {
129 | // 取 drawable 的长宽
130 | int w = drawable.getIntrinsicWidth();
131 | int h = drawable.getIntrinsicHeight();
132 |
133 | // 取 drawable 的颜色格式
134 | Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
135 | : Bitmap.Config.RGB_565;
136 | // 建立对应 bitmap
137 | Bitmap bitmap = Bitmap.createBitmap(w, h, config);
138 | // 建立对应 bitmap 的画布
139 | Canvas canvas = new Canvas(bitmap);
140 | drawable.setBounds(0, 0, w, h);
141 | // 把 drawable 内容画到画布中
142 | drawable.draw(canvas);
143 | return bitmap;
144 | }
145 |
146 | private void setup() {
147 | if (mBitmap == null) {
148 | return;
149 | }
150 |
151 | mTopRect = 0;
152 | mBottomRect = getBottom();
153 |
154 | mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
155 |
156 | mBitmapPaint = new Paint();
157 | mBitmapPaint.setAntiAlias(true);
158 | mBitmapPaint.setShader(mBitmapShader);
159 |
160 | mBitmapHeight = mBitmap.getHeight();
161 | mBitmapWidth = mBitmap.getWidth();
162 |
163 | updateShaderMatrix();
164 | invalidate();
165 | }
166 |
167 | private void updateShaderMatrix() {
168 |
169 | float scaleX;
170 | float scaleY;
171 |
172 | mShaderMatrix = new Matrix();
173 | mShaderMatrix.set(null);
174 |
175 | mDrawableRect = new Rect(0, 0, getRight() - getLeft(), getBottom() - getTop());
176 |
177 | scaleX = mDrawableRect.width() / (float) mBitmapWidth;
178 | scaleY = mDrawableRect.height() / (float) mBitmapHeight;
179 | mShaderMatrix.setScale(scaleX, scaleY);
180 |
181 | mBitmapShader.setLocalMatrix(mShaderMatrix);
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/MyViewGroup.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.graphics.drawable.Drawable;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | public class MyViewGroup extends ViewGroup {
11 |
12 | public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
13 | super(context, attrs, defStyle);
14 | }
15 |
16 | public MyViewGroup(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | public MyViewGroup(Context context) {
21 | super(context);
22 |
23 | // AbsoluteLayout
24 | }
25 |
26 | @Override
27 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
28 | int count = getChildCount();
29 |
30 | int maxHeight = 0;
31 | int maxWidth = 0;
32 |
33 | // Find out how big everyone wants to be
34 | measureChildren(widthMeasureSpec, heightMeasureSpec);
35 |
36 | // Find rightmost and bottom-most child
37 | for (int i = 0; i < count; i++) {
38 | View child = getChildAt(i);
39 | if (child.getVisibility() != GONE) {
40 | int childRight;
41 | int childBottom;
42 |
43 | // ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) child.getLayoutParams();
44 |
45 | childRight = child.getMeasuredWidth();
46 | childBottom = child.getMeasuredHeight();
47 |
48 | // Rect mCachedBounds = new Rect();
49 | // Drawable drawable = child.getBackground();
50 | // if (i == 0 && drawable != null) {
51 | //
52 | // drawable.getPadding(mCachedBounds);
53 | // int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
54 | // int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
55 | //
56 | // // child.measure(
57 | // // MeasureSpec.makeMeasureSpec(widthSpecSize + mCachedBounds.left
58 | // // + mCachedBounds.right, MeasureSpec.EXACTLY),
59 | // // MeasureSpec.makeMeasureSpec(heightSpecSize + mCachedBounds.top
60 | // // + mCachedBounds.bottom, MeasureSpec.EXACTLY));
61 | // }
62 |
63 | maxWidth = Math.max(maxWidth, childRight);
64 | maxHeight = Math.max(maxHeight, childBottom);
65 | }
66 | }
67 |
68 | // Account for padding too
69 | maxWidth += getPaddingLeft() + getPaddingRight();
70 | maxHeight += getPaddingTop() + getPaddingBottom();
71 |
72 | // Check against minimum height and width
73 | maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
74 | maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
75 |
76 | setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
77 | resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
78 | }
79 |
80 | @Override
81 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
82 | int count = getChildCount();
83 |
84 | for (int i = 0; i < count; i++) {
85 | View child = getChildAt(i);
86 | if (child.getVisibility() != GONE) {
87 |
88 | Rect mCachedBounds = new Rect();
89 | Drawable drawable = child.getBackground();
90 | if (drawable != null) {
91 | // child.measure(0, 0);
92 | drawable.getPadding(mCachedBounds);
93 | }
94 |
95 | int childLeft = -mCachedBounds.left;
96 | int childTop = -mCachedBounds.top;
97 |
98 | child.layout(childLeft, childTop,
99 | child.getMeasuredWidth() + mCachedBounds.right,
100 | child.getMeasuredHeight() + mCachedBounds.bottom);
101 |
102 | // child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
103 | }
104 | }
105 | }
106 |
107 | @Override
108 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
109 | return new LayoutParams(p);
110 | }
111 |
112 | @Override
113 | public boolean shouldDelayChildPressedState() {
114 | return false;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/MyViewPager.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.DisplayMetrics;
6 | import android.view.MotionEvent;
7 | import android.view.VelocityTracker;
8 | import android.view.View;
9 | import android.view.ViewConfiguration;
10 | import android.widget.FrameLayout;
11 |
12 | public class MyViewPager extends FrameLayout {
13 |
14 | private static final String TAG = MyViewPager.class.getSimpleName();
15 | /**
16 | * 认为是用户滑动的最小距离
17 | */
18 | private int mSlop;
19 | /**
20 | * 滑动的最小速度
21 | */
22 | private int mMinFlingVelocity;
23 | /**
24 | * 滑动的最大速度
25 | */
26 | private int mMaxFlingVelocity;
27 | /**
28 | * 执行动画的时间
29 | */
30 | protected long mAnimationTime = 150;
31 | /**
32 | * 用来标记用户是否正在滑动中
33 | */
34 | private boolean mSwiping;
35 | /**
36 | * 滑动速度检测类
37 | */
38 | private VelocityTracker mVelocityTracker;
39 | /**
40 | * 手指按下的position
41 | */
42 | private int mDownPosition;
43 | /**
44 | * 按下的item对应的View
45 | */
46 | private View mDownView;
47 | private float mDownX;
48 | private float mDownY;
49 |
50 | private int mWidth;
51 |
52 | private int mFlaggingWidth;
53 |
54 | public MyViewPager(Context context, AttributeSet attrs, int defStyle) {
55 | super(context, attrs, defStyle);
56 | init(context);
57 | }
58 |
59 | public MyViewPager(Context context, AttributeSet attrs) {
60 | super(context, attrs);
61 | init(context);
62 | }
63 |
64 | public MyViewPager(Context context) {
65 | super(context);
66 | init(context);
67 | }
68 |
69 | private void init(Context context) {
70 | ViewConfiguration vc = ViewConfiguration.get(context);
71 | mSlop = vc.getScaledTouchSlop();
72 | mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 8; // 获取滑动的最小速度
73 | mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); // 获取滑动的最大速度
74 |
75 | // 获取分辨率
76 | DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
77 | mWidth = dm.widthPixels;
78 | mFlaggingWidth = mWidth / 4;
79 | }
80 |
81 | private OnPageChangeListener onPageChangeListener;
82 |
83 | public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
84 | this.onPageChangeListener = onPageChangeListener;
85 | }
86 |
87 | public static float clamp(float value, float max, float min) {
88 | return Math.max(Math.min(value, min), max);
89 | }
90 |
91 | @Override
92 | public boolean onTouchEvent(MotionEvent ev) {
93 | // Log.d(TAG, "onTouchEvent" + ev.getAction());
94 | switch (ev.getAction()) {
95 | case MotionEvent.ACTION_DOWN:
96 | handleActionDown(ev);
97 | break;
98 | case MotionEvent.ACTION_MOVE:
99 | handleActionMove(ev);
100 | return super.onTouchEvent(ev);
101 | case MotionEvent.ACTION_UP:
102 | handleActionUp(ev);
103 | break;
104 | }
105 | // return super.onTouchEvent(ev);
106 | return true;
107 | }
108 |
109 | /**
110 | * 按下事件处理
111 | *
112 | * @param ev
113 | * @return
114 | */
115 | private void handleActionDown(MotionEvent ev) {
116 | mSwiping = false;
117 | mIsStartRight = false;
118 | mIsStart = false;
119 | mDownX = ev.getX();
120 | mDownY = ev.getY();
121 |
122 | // mDownView = getChildAt(0);
123 |
124 | // 加入速度检测
125 | mVelocityTracker = VelocityTracker.obtain();
126 | mVelocityTracker.addMovement(ev);
127 | }
128 |
129 | private boolean mIsStart = false;
130 | private boolean mIsStartRight = false;
131 |
132 | /**
133 | * 处理手指滑动的方法
134 | *
135 | * @param ev
136 | * @return
137 | */
138 | private void handleActionMove(MotionEvent ev) {
139 |
140 | float deltaX = ev.getX() - mDownX;
141 | float deltaY = ev.getY() - mDownY;
142 |
143 | boolean dismissRight = deltaX > 0;// 是否往右边
144 | if (deltaX != 0 && !mIsStart) {
145 | mIsStart = true;
146 | mIsStartRight = dismissRight;
147 | }
148 | mSwiping = true;
149 |
150 | if (dismissRight) {
151 | float ratio = clamp(deltaX / (float) mWidth, 0.0f, 1.0f);
152 | onPageChangeListener.onRight(mDownPosition, ratio);
153 | } else {
154 | float ratio = clamp(-deltaX / (float) mWidth, 0.0f, 1.0f);
155 | onPageChangeListener.onLeft(mDownPosition, ratio);
156 | }
157 | }
158 |
159 | /**
160 | * 手指抬起的事件处理
161 | *
162 | * @param ev
163 | */
164 | private void handleActionUp(MotionEvent ev) {
165 | if (mVelocityTracker == null || !mSwiping) {
166 | return;
167 | }
168 |
169 | float deltaX = ev.getX() - mDownX;
170 |
171 | // 通过滑动的距离计算出X,Y方向的速度
172 | mVelocityTracker.computeCurrentVelocity(1000);
173 | float velocityX = Math.abs(mVelocityTracker.getXVelocity());
174 | float velocityY = Math.abs(mVelocityTracker.getYVelocity());
175 |
176 | boolean dismiss = false; // item是否要滑出屏幕
177 | boolean dismissRight = deltaX > 0;// 是否往右边
178 | // 当拖动item的距离大于item的 4/1,item滑出屏幕
179 | if (Math.abs(deltaX) > mFlaggingWidth) {
180 | dismiss = true;
181 | // 手指在屏幕滑动的速度在某个范围内,也使得item滑出屏幕
182 | } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity
183 | && velocityY < velocityX) {
184 | dismiss = true;
185 | }
186 |
187 | // 动画
188 | if (mIsStartRight) {
189 | if (mDownPosition != 0) {
190 | float ratio = clamp(deltaX / (float) mWidth, 0.0f, 1.0f);
191 | onPageChangeListener.onRightEnd(mDownPosition, ratio, dismiss);
192 | }
193 | } else {
194 | float ratio = clamp(-deltaX / (float) mWidth, 0.0f, 1.0f);
195 | onPageChangeListener.onLeftEnd(mDownPosition, ratio, dismiss);
196 | }
197 | // 切换
198 | if (dismiss) {
199 | if (mIsStartRight) {
200 | mDownPosition = Math.max(mDownPosition - 1, 0);
201 | } else {
202 | mDownPosition = Math.min(mDownPosition + 1, getChildCount() - 2);
203 | }
204 | }
205 |
206 | // 移除速度检测
207 | if (mVelocityTracker != null) {
208 | mVelocityTracker.recycle();
209 | mVelocityTracker = null;
210 | }
211 | mSwiping = false;
212 | mIsStartRight = false;
213 | mIsStart = false;
214 | }
215 |
216 | public interface OnPageChangeListener {
217 | public void onLeft(int position, float ratio);
218 |
219 | public void onLeftEnd(int position, float ratio, boolean dismiss);
220 |
221 | public void onRight(int position, float ratio);
222 |
223 | public void onRightEnd(int position, float ratio, boolean dismiss);
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/src/com/kongnan/weibo/Util.java:
--------------------------------------------------------------------------------
1 | package com.kongnan.weibo;
2 |
3 | import android.content.Context;
4 | import android.util.TypedValue;
5 |
6 | public class Util {
7 | public static float dp2px(Context context, int dp) {
8 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources()
9 | .getDisplayMetrics());
10 | }
11 |
12 | public static float sp2px(Context context, int sp) {
13 | // final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
14 | // return (int) (spValue * fontScale + 0.5f);
15 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources()
16 | .getDisplayMetrics());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------