├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── engineer │ │ └── echo │ │ └── transition │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── engineer │ │ │ └── echo │ │ │ └── transition │ │ │ ├── App.java │ │ │ ├── Constants.java │ │ │ ├── MainActivity.java │ │ │ ├── cmpts │ │ │ ├── context │ │ │ │ ├── BaseActivity.java │ │ │ │ └── BaseFragment.java │ │ │ ├── events │ │ │ │ ├── CaptureFinishEvent.java │ │ │ │ └── StartCaptureEvent.java │ │ │ ├── utils │ │ │ │ └── CommonUtil.java │ │ │ └── widget │ │ │ │ ├── reflect │ │ │ │ ├── BitmapMemoryCache.java │ │ │ │ └── ReflectItemView.java │ │ │ │ └── transition │ │ │ │ ├── BoundsAndAlpha.java │ │ │ │ ├── ProgressTransition.java │ │ │ │ └── SafeSlide.java │ │ │ └── fragment │ │ │ ├── AppCtrlFragment.java │ │ │ ├── AppDisplayFragment.java │ │ │ ├── GalleryAdapter.java │ │ │ ├── GalleryDetailFragment.java │ │ │ ├── GalleryFragment.java │ │ │ └── SettingsFragment.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ ├── girl_0.webp │ │ ├── girl_1.jpg │ │ ├── girl_2.jpg │ │ ├── girl_3.webp │ │ ├── girl_4.webp │ │ ├── pic.png │ │ ├── play.png │ │ └── set.png │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ ├── shape.xml │ │ ├── shape_1.xml │ │ ├── shape_pink_circle.xml │ │ └── shape_purple_circle.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_ctrl.xml │ │ ├── fragment_display.xml │ │ ├── fragment_gallery.xml │ │ ├── fragment_gallery_detail.xml │ │ ├── fragment_settings.xml │ │ ├── layout_scene_end.xml │ │ └── layout_scene_start.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── transition-v21 │ │ ├── transition_settings_in.xml │ │ └── transition_settings_out.xml │ │ ├── transition │ │ ├── transition_settings_in.xml │ │ ├── transition_settings_out.xml │ │ └── transition_settings_share_in.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── engineer │ └── echo │ └── transition │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── path_normal.gif ├── path_share_element.gif ├── progress.gif ├── scale.gif ├── scene_sort.gif ├── slide_fade.gif ├── transition-0.png ├── transition-1.png ├── transition_in.gif └── transition_out.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | .DS_Store 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | .idea 40 | 41 | # Keystore files 42 | *.jks 43 | 44 | # External native build folder generated in Android Studio 2.2 and later 45 | .externalNativeBuild 46 | 47 | # Google Services (e.g. APIs or Firebase) 48 | google-services.json 49 | 50 | # Freeline 51 | freeline.py 52 | freeline/ 53 | freeline_project_description.json 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HollyTransition 2 | 3 | ### 零、导读 4 | 深入解读Android过渡动画Transition: 5 | 1. **页面切换动画(过场动画)** 6 | 2. **共享元素动画** 7 | 3. **延时动画** 8 | 4. **场景动画** 9 | 10 | ### 一、Transition前世今生 11 | > 为了支持各种交互视觉设计的不断更新,Android对于开发者提供了越来越多的动画API支持。从API 1就存在的Drawable Animation和View Animation,以及API 11(Android 3.0)以后加入的Property Animation。而过渡动画Transition是在API 19(Android 4.4.2)中加入的。说道炫酷的动画,很多人首先想到的是Android5.0开发流行起来的共享元素动画。然而共享元素动画只是Transition的冰山一角。今天就让我们来一一揭开。 12 | 13 | ### 二、为什么要引入Transition 14 | > 那为什么要引入Transition动画呢?由于在Android引入了Metrial Desigon之后,动画的场面越来越大,比如以前我们制作一个动画可能涉及到的View就一个,或者就那么几个,如果我们一个动画中涉及到了当前Activity视图树中的各个View,那么情况就复杂了。比如我们要一次针对视图树中的10个View进行动画,这些View的效果都不同,可能有的是平移,有的是旋转,有的是淡入淡出,那么不管是使用之前哪种方式的动画,我们都需要为每个View定义一个开始状态和结束状态【关键帧,比如放缩,我们得设置fromXScale和toXScale 】,随着View个数的增加,这个情况会越来越复杂。这个时候如果使用一堆Animator去实现这一连串动画,代码将会又臭又长,Transition的出现大大减轻了开发的工作。 15 | 16 | ### 三、页面切换动画(过场动画) 17 | > 以前使用过场动画时,实现共享元素,主要是使用Activity,但是有遇到很多坑:比如最严重的一个是在某些机型上出现做返场动画时,第二个页面的画面还残留在页面上;第二个问题是,在存在虚拟导航栏的手机上,由于不同Activity是在不同的Window上绘制的,在Activity切换时界面存在闪烁的情况。而Fragment的切换则不存在这样的问题。综合考虑,决定使用Fragment。 18 | 19 | #### 示例图1:入场动画 20 | ![入场动画](https://github.com/Pluckypan/HollyTransition/raw/master/images/transition_in.gif) 21 | 22 | #### 示例图2:返场动画 23 | ![返场动画](https://github.com/Pluckypan/HollyTransition/raw/master/images/transition_out.gif) 24 | 25 | #### 三(1)、共享元素动画 26 | > 如上图中 **画廊按钮**、**拍摄按钮**、**设置按钮** ;页面切换时,对共享元素动画来讲,重要的函数有以下几个。共享元素执行的动画主要是针对将要进入的页面的。一个是入场动画 EnterTransition,另一个是返场 ReturnTransition。在做共享元素动画时,需要在前后两个页面对共享元素设定TransitionName,且对应的两个View,TransitionName需要相同。 27 | 28 | ``` 29 | //在第二个页面(即要进入的那个页面)设置共享元素TransitionName ViewCompat可兼容5.0以下版本,不报错 30 | ViewCompat.setTransitionName(mFakeLeft, SHARE_NAME_LEFT); 31 | ViewCompat.setTransitionName(mPhotoBtn, SHARE_NAME_MIDDLE); 32 | ViewCompat.setTransitionName(mFakeRight, SHARE_NAME_RIGHT); 33 | 34 | //设定入场动画和返场动画 35 | fragment.setSharedElementEnterTransition(new ChangeBounds()); 36 | fragment.setSharedElementReturnTransition(new ChangeBounds()); 37 | 38 | //添加共享元素 39 | FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); 40 | fragmentTransaction.replace(R.id.app_ctrl_view, fragment); 41 | fragmentTransaction.addToBackStack(null); 42 | if (shareViews != null && shareViews.length > 0) { 43 | for (Pair shareView : shareViews) { 44 | fragmentTransaction.addSharedElement(shareView.first, shareView.second); 45 | } 46 | } 47 | fragmentTransaction.commit(); 48 | ``` 49 | 50 | ##### 疑问:上图的共享元素是如何实现形变的过程中发生alpha变化的? 51 | > 刚开始,准备直接使用 xml 定制动画,一个changeBounds(左中右三个按钮同时形变) + 一个 fade (左右按钮在形变的同时 fadeout),但是发现只有形变生效。于是尝试使用自定义ChangeBounds来改造共享元素动画,在createAnimator中拿到Animator,从而可以拿到Animator变化的过程 `ValueAnimator.AnimatorUpdateListener`,这样就可以实现形变的同时,发生alpha的变化。 [示例代码](https://github.com/Pluckypan/HollyTransition/blob/master/app/src/main/java/engineer/echo/transition/cmpts/widget/transition/BoundsAndAlpha.java) 52 | 53 | ``` 54 | // 改造需要了解Transition的三大核心方法如下。 55 | @Override 56 | public void captureStartValues(TransitionValues transitionValues) { 57 | super.captureStartValues(transitionValues); 58 | Log.d(TAG, "captureStartValues: transitionValues=" + transitionValues); 59 | } 60 | 61 | @Override 62 | public void captureEndValues(TransitionValues transitionValues) { 63 | super.captureEndValues(transitionValues); 64 | Log.d(TAG, "captureEndValues: transitionValues=" + transitionValues); 65 | } 66 | 67 | @Override 68 | public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { 69 | return super.createAnimator(sceneRoot, startValues, endValues); 70 | } 71 | ``` 72 | 73 | #### 三(2)、内容动画 74 | > 如上面图片中,设置页面的**背景渐变**、**动起来 SlideUp**、**功能说明 SlideUp**;拍摄页面的**进度条封面 Slide Left&Right**。这里所说的`内容动画`,指的是Fragment布局中除共享元素外的View执行的动画,主要是为了区分`共享元素动画`。对内容过场动画比较重要的几个函数,下面这种图比较形象地描述这个关系。Activity和Fragment的意思是一样的。这里以Activity的示意图来说明。 75 | 76 | ##### 示例图3:入场动画对应关系 77 | ![入场动画](https://github.com/Pluckypan/HollyTransition/raw/master/images/transition-0.png) 78 | 79 | ##### 示例图4:返场动画对应关系 80 | ![返场动画](https://github.com/Pluckypan/HollyTransition/raw/master/images/transition-1.png) 81 | 82 | ``` java 83 | TransitionSet transitionSet=new TransitionSet(); 84 | //从 A Fragment 进入 B Fragment,A会执行 ExitTransition,B 会执行EnterTransition 85 | fragment.setEnterTransition(transitionSet); 86 | fragment.setExitTransition(transitionSet); 87 | //按返回键时,B Fragment 会pop出栈,执行ReturnTransition,此时 A Fragment 重新回到栈顶,执行ReEnterTransition. 88 | fragment.setReenterTransition(transitionSet); 89 | fragment.setReturnTransition(transitionSet); 90 | ``` 91 | 92 | ### 四、延时动画(TransitionManager.beginDelayedTransition) 93 | #### 四(1)、进度条平滑过渡 94 | > iOS进度条有个方法:- (void)setProgress:(float)progress animated:(BOOL)animated; 如果animated=true 那么,设置进度时,会从当前进度平滑过渡到目标进度,大大提升了用户体验。Android进度条并不具备这种功能,只能设定一个progress,控件就僵硬地跳到对应的位置。但是借助Transition,可实现这个功能。 [示例代码](https://github.com/Pluckypan/HollyTransition/blob/master/app/src/main/java/engineer/echo/transition/cmpts/widget/transition/ProgressTransition.java) 95 | 96 | ##### 示例图5:实现进度条平滑过渡 97 | ![进度条](https://github.com/Pluckypan/HollyTransition/raw/master/images/progress.gif) 98 | 99 | #### 四(2)、放大缩小效果 100 | ##### 示例图6:实现同一ViewGroup中两个View之间协调放大与缩小效果 101 | ![放大缩小](https://github.com/Pluckypan/HollyTransition/raw/master/images/scale.gif) 102 | 103 | #### 四(3)、Slide与Fade组合动画 104 | ##### 示例图7:从上滑入从下滑出并且渐入渐出 105 | ![渐入渐出](https://github.com/Pluckypan/HollyTransition/raw/master/images/slide_fade.gif) 106 | 107 | 108 | ### 五、路径动画 109 | > 路径动画在日常APP中使用的场景很多,比如京东、天猫 添加商品至购物车的动画。实现起来非常简单。直接使用Transition.setPathMotion(new ArcMotion());然后结合延时动画TransitionManager.beginDelayedTransition()即可。 110 | 111 | ##### 示例图片8:普通路径动画 112 | ![路径动画-普通](https://github.com/Pluckypan/HollyTransition/raw/master/images/path_normal.gif) 113 | ##### 示例图片9:路径动画在共享元素中的运用 114 | ![路径动画-普通](https://github.com/Pluckypan/HollyTransition/raw/master/images/path_share_element.gif) 115 | 116 | ### 六、源码解析 117 | > 入场动画时,是直接拿着 进入页面的View进行动画的,返场动画时,不直接拿着View进行动画,而是在Overlap上,创建与Targets对应的ImageView,然后截取Targets的画面,显示在ImageView上,返场动画主要是在Overlap上进行的。 118 | 119 | ``` java 120 | public Animator onDisappear(ViewGroup sceneRoot, 121 | TransitionValues startValues, int startVisibility, 122 | TransitionValues endValues, int endVisibility) { 123 | if ((mMode & MODE_OUT) != MODE_OUT) { 124 | return null; 125 | } 126 | 127 | View startView = (startValues != null) ? startValues.view : null; 128 | View endView = (endValues != null) ? endValues.view : null; 129 | /** 130 | * 说明几点: 131 | * #1 onDisappear方法位于android.transition.Visibility类中 132 | * #2 在返场动画时,场景中每个被添加进Transition的Target都会执行该方法 133 | * #3 这里定义了一个overlayView,先标记下,后面会用得到 134 | */ 135 | View overlayView = null; 136 | View viewToKeep = null; 137 | if (endView == null || endView.getParent() == null) { 138 | if (endView != null) { 139 | // endView was removed from its parent - add it to the overlay 140 | overlayView = endView; 141 | } else if (startView != null) { 142 | // endView does not exist. Use startView only under certain 143 | // conditions, because placing a view in an overlay necessitates 144 | // it being removed from its current parent 145 | if (startView.getParent() == null) { 146 | // no parent - safe to use 147 | overlayView = startView; 148 | } else if (startView.getParent() instanceof View) { 149 | /** 150 | * 如果StartView存在父布局 151 | */ 152 | View startParent = (View) startView.getParent(); 153 | TransitionValues startParentValues = getTransitionValues(startParent, true); 154 | TransitionValues endParentValues = getMatchedTransitionValues(startParent, 155 | true); 156 | VisibilityInfo parentVisibilityInfo = 157 | getVisibilityChangeInfo(startParentValues, endParentValues); 158 | if (!parentVisibilityInfo.visibilityChange) { 159 | /** 160 | * 如果StartView父布局在动画过程中未参与Visibility变化的话,那么就会 161 | * 创建一个ImageView,并将StartView 画布Canvas上的内容转换为Bitmap设置到新创建的ImageView上 162 | */ 163 | overlayView = TransitionUtils.copyViewImage(sceneRoot, startView, 164 | startParent); 165 | } else if (startParent.getParent() == null) { 166 | int id = startParent.getId(); 167 | if (id != View.NO_ID && sceneRoot.findViewById(id) != null 168 | && mCanRemoveViews) { 169 | // no parent, but its parent is unparented but the parent 170 | // hierarchy has been replaced by a new hierarchy with the same id 171 | // and it is safe to un-parent startView 172 | overlayView = startView; 173 | } 174 | } 175 | } 176 | } 177 | } else { 178 | // visibility change 179 | if (endVisibility == View.INVISIBLE) { 180 | viewToKeep = endView; 181 | } else { 182 | // Becoming GONE 183 | if (startView == endView) { 184 | viewToKeep = endView; 185 | } else { 186 | overlayView = startView; 187 | } 188 | } 189 | } 190 | final int finalVisibility = endVisibility; 191 | final ViewGroup finalSceneRoot = sceneRoot; 192 | 193 | if (overlayView != null) { 194 | // TODO: Need to do this for general case of adding to overlay 195 | int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION); 196 | int screenX = screenLoc[0]; 197 | int screenY = screenLoc[1]; 198 | int[] loc = new int[2]; 199 | sceneRoot.getLocationOnScreen(loc); 200 | overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft()); 201 | overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop()); 202 | /** 203 | * 通过上面一系列的判断,最终会将得到的overlayView添加到 场景一(及A页面)根部局的Overlap上 204 | * 也就是说,其实在做返场动画时,所有的动画都是在 A页面的Overlap上进行的。做完动画再将其从Overlap上移除。 205 | * 这里的overlayView可能是 B页面的控件,也有可能是B页面控件的画面(new了一个ImageView的形式展示) 206 | * 什么是Overlap,可参考文章:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0130/2384.html 207 | */ 208 | sceneRoot.getOverlay().add(overlayView); 209 | Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues); 210 | if (animator == null) { 211 | sceneRoot.getOverlay().remove(overlayView); 212 | } else { 213 | final View finalOverlayView = overlayView; 214 | addListener(new TransitionListenerAdapter() { 215 | @Override 216 | public void onTransitionEnd(Transition transition) { 217 | finalSceneRoot.getOverlay().remove(finalOverlayView); 218 | } 219 | }); 220 | } 221 | return animator; 222 | } 223 | 224 | if (viewToKeep != null) { 225 | int originalVisibility = viewToKeep.getVisibility(); 226 | viewToKeep.setTransitionVisibility(View.VISIBLE); 227 | Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues); 228 | if (animator != null) { 229 | DisappearListener disappearListener = new DisappearListener(viewToKeep, 230 | finalVisibility, mSuppressLayout); 231 | animator.addListener(disappearListener); 232 | animator.addPauseListener(disappearListener); 233 | addListener(disappearListener); 234 | } else { 235 | viewToKeep.setTransitionVisibility(originalVisibility); 236 | } 237 | return animator; 238 | } 239 | return null; 240 | } 241 | 242 | ``` 243 | 244 | ### 七、遇到的问题 245 | 1. Scene切换时会RemoveAllView 导致之前设置点击事件消失 246 | 2. 退场动画时,会截取View的canvas,并在A页面rootview的overlap上添加ImageView,如果自定义控件有影响canvas的绘制过程,则添加到ImageView上的Bitmap可能不正确 247 | 3. 共享元素必须是RootView的直接子View 248 | 4. 做页面间的过渡动画时,两个页面尽量简单,比如加载网络图片时,可以先使用前一个页面的图片做动画,等动画做完后再去加载图片。不可以一边儿做动画,一边儿加载图片,会造成动画很闪烁。 249 | 5. Fragment设置TransitionOverlap无效,还需要研究下。 250 | ``` java 251 | fragment.setAllowEnterTransitionOverlap(false); 252 | fragment.setAllowReturnTransitionOverlap(false); 253 | ``` 254 | 6. 在Fragment中使用 **共享元素动画** 时,需要两个Fragment基于同一个`layout_id`,然后通过`replace`的形式打开。 255 | 7. 要使用`Transition`版本必须API>=19(Android4.4),有些功能 如 `Slide`设定Edge、路径动画`ArcMotion` 更是需要 API>=21(Android 5.0) 256 | 257 | ### 八、TODO 258 | - [ ] 图片切换平滑过渡效果 259 | - [ ] 颜色变化平滑过渡 260 | - [ ] 文字变化平滑过渡 261 | - [ ] 深入研究Transition-EveryWhere,存在类型转化的[BUG](https://github.com/andkulikov/Transitions-Everywhere/issues/11) 262 | 263 | ### 九、参考项目 264 | - [Transitions-Everywhere](https://github.com/andkulikov/Transitions-Everywhere) 265 | > Transitions-Everywhere 可支持Transiton动画到 Android 4.O ,并且兼容 Android 2.2 +(无动画但保证运行). 266 | 267 | - [Material-Animations](https://github.com/lgvalle/Material-Animations) 268 | > 详细解读Fragment与Fragment Activity与Activity Activity与Fragment之间的切换动画 269 | 270 | - [TransitionExample](https://github.com/WakeHao/TransitionExample) 271 | > 早在Android 4.4,Transition 就已经引入,但在5.0才得以真正的实现。而究竟Transition是用来干嘛的呢?这个Demo可以带你熟悉基本的操作。特点是比较熟练的使用xml来定义动画,动画衔接做得比较好。 272 | 273 | - [Android-Material-Examples](https://github.com/saulmm/Android-Material-Examples) 274 | > 主要特点是介绍ViewPager中实现的一些Transition动画 275 | 276 | - [ViewOverlay与animation介绍](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0130/2384.html) 277 | > ViewOverlay与animation介绍。Transition在ReturnTransition动画时,会在第一个页面rootView的overlap上添加ImageView来执行动画。这篇文章详细介绍了Android中ViewOverlay的用处和原理。 278 | 279 | - [Workcation](https://github.com/DroidsOnRoids/Workcation) 280 | > Transition DEMO 281 | 282 | - [continuous-shared-element-transitions](https://android-developers.googleblog.com/2018/02/continuous-shared-element-transitions.html) 283 | 284 | - [shared-element-transitions](https://github.com/mikescamell/shared-element-transitions) 285 | 286 | 287 | ### 十、结尾 288 | [![](https://img.shields.io/github/forks/pluckypan/HollyTransition.svg?style=social)](https://github.com/Pluckypan/HollyTransition) 289 | [![](https://img.shields.io/github/stars/pluckypan/HollyTransition.svg?style=social)](https://github.com/Pluckypan/HollyTransition) 290 | [![](https://img.shields.io/github/followers/pluckypan.svg?style=social)](https://github.com/pluckypan/followers) 291 | 292 | 本文示例代码: `git@github.com:Pluckypan/HollyTransition.git` [预览](https://github.com/Pluckypan/HollyTransition) 欢迎Star、Fork 293 | 294 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion COMPILE_SDK.toInteger() 5 | defaultConfig { 6 | applicationId APP_ID.toString() 7 | minSdkVersion MIN_SDK.toInteger() 8 | targetSdkVersion TARGET_SDK.toInteger() 9 | versionCode APP_VERCODE.toInteger() 10 | versionName APP_VERNAME.toString() 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | println(APP_VERCODE) 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation "com.android.support:appcompat-v7:$APPCOMPAT_V7" 25 | implementation "com.android.support:recyclerview-v7:$APPCOMPAT_V7" 26 | implementation "com.android.support.constraint:constraint-layout:$CONSTRAINT_LAYOUT" 27 | 28 | implementation 'org.greenrobot:eventbus:3.0.0' 29 | implementation 'ch.halcyon:squareprogressbar:1.6.3' 30 | 31 | implementation "com.andkulikov:transitionseverywhere:1.7.8" 32 | 33 | //单元测试 34 | testImplementation 'junit:junit:4.12' 35 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 36 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 37 | } 38 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/engineer/echo/transition/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("engineer.echo.transition", appContext.getPackageName()); 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/App.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition; 2 | 3 | import android.app.Application; 4 | import android.graphics.Point; 5 | import android.util.DisplayMetrics; 6 | 7 | /** 8 | * App 9 | * Created by Plucky on 2018/1/1 下午9:06. 10 | * more about me: http://www.1991th.com 11 | */ 12 | 13 | public class App extends Application { 14 | 15 | private static App mApp = null; 16 | public static Point sScreenSize = new Point(); 17 | public static float sDensity = 1f; 18 | 19 | @Override 20 | public void onCreate() { 21 | super.onCreate(); 22 | mApp = this; 23 | DisplayMetrics metrics = getResources().getDisplayMetrics(); 24 | sScreenSize.set(metrics.widthPixels, metrics.heightPixels); 25 | sDensity = metrics.density; 26 | } 27 | 28 | public static App getApp() { 29 | return mApp; 30 | } 31 | 32 | public static int dpToPx(float dp) { 33 | return (int) (dp * sDensity + 0.5f); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/Constants.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition; 2 | 3 | /** 4 | * Constants 5 | * Created by Plucky on 2018/1/3 - 21:21 6 | * more about me: http://www.1991th.com 7 | */ 8 | 9 | public class Constants { 10 | public static final int TRANSITION_TIME = 600; 11 | public static final int TRANSITION_TIME_SHORT = 400; 12 | public static final int TRANSITION_TIME_LONG = 800; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/MainActivity.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentTransaction; 5 | import android.view.Gravity; 6 | 7 | import engineer.echo.transition.cmpts.context.BaseActivity; 8 | import engineer.echo.transition.cmpts.utils.CommonUtil; 9 | import engineer.echo.transition.cmpts.widget.transition.SafeSlide; 10 | import engineer.echo.transition.fragment.AppCtrlFragment; 11 | import engineer.echo.transition.fragment.AppDisplayFragment; 12 | 13 | /** 14 | * MainActivity 15 | * Created by Plucky on 2018/1/1 下午3:26. 16 | * more about me: http://www.1991th.com 17 | */ 18 | public class MainActivity extends BaseActivity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | initViews(); 25 | } 26 | 27 | private void initViews() { 28 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 29 | //相机预览 30 | AppDisplayFragment displayFragment = AppDisplayFragment.newInstance(); 31 | transaction.add(R.id.app_display_view, displayFragment); 32 | //控制层 33 | AppCtrlFragment ctrlFragment = AppCtrlFragment.newInstance(); 34 | 35 | //设置Fragment离开时执行的动画 --变成不可见 36 | SafeSlide slideExit = new SafeSlide(); 37 | // TODO: Added By Plucky 2018/1/8 11:39 设定Edge需要API版本>=LOLLIPOP 38 | if (CommonUtil.isOverLollipop()) { 39 | slideExit.setSlideEdge(Gravity.LEFT); 40 | } 41 | slideExit.addTarget(R.id.progressBar); 42 | slideExit.setDuration(Constants.TRANSITION_TIME); 43 | ctrlFragment.setExitTransition(slideExit); 44 | //设置Fragment重新回到栈顶时执行的动画 ---重新可见 45 | SafeSlide slideReEnter = new SafeSlide(); 46 | // TODO: Added By Plucky 2018/1/8 11:39 设定Edge需要API版本>=LOLLIPOP 47 | if (CommonUtil.isOverLollipop()) { 48 | slideReEnter.setSlideEdge(Gravity.RIGHT); 49 | } 50 | slideReEnter.addTarget(R.id.progressBar); 51 | slideReEnter.setDuration(Constants.TRANSITION_TIME); 52 | ctrlFragment.setReenterTransition(slideReEnter); 53 | 54 | transaction.add(R.id.app_ctrl_view, ctrlFragment); 55 | transaction.commit(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/context/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.context; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | 5 | /** 6 | * BaseActivity 7 | * Created by Plucky on 2018/1/1 下午3:26. 8 | * more about me: http://www.1991th.com 9 | */ 10 | 11 | public class BaseActivity extends AppCompatActivity { 12 | public String TAG = this.getClass().getSimpleName(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/context/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.context; 2 | 3 | import android.support.v4.app.Fragment; 4 | 5 | /** 6 | * BaseFragment 7 | * Created by Plucky on 2018/1/1 下午3:26. 8 | * more about me: http://www.1991th.com 9 | */ 10 | 11 | public class BaseFragment extends Fragment { 12 | public String TAG = this.getClass().getSimpleName(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/events/CaptureFinishEvent.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.events; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | /** 6 | * CaptureFinishEvent 7 | * Created by Plucky on 2018/1/4 - 15:23 8 | * more about me: http://www.1991th.com 9 | */ 10 | 11 | public class CaptureFinishEvent { 12 | 13 | private Bitmap mPicture; 14 | 15 | public CaptureFinishEvent(Bitmap mPicture) { 16 | this.mPicture = mPicture; 17 | } 18 | 19 | public Bitmap getPicture() { 20 | return mPicture; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/events/StartCaptureEvent.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.events; 2 | 3 | /** 4 | * StartCaptureEvent 5 | * Created by Plucky on 2018/1/4 - 15:23 6 | * more about me: http://www.1991th.com 7 | */ 8 | 9 | public class StartCaptureEvent { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/utils/CommonUtil.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.utils; 2 | 3 | import android.os.Build; 4 | import android.util.DisplayMetrics; 5 | 6 | import engineer.echo.transition.App; 7 | 8 | /** 9 | * AppCtrlFragment 10 | * Created by Plucky on 2018/1/3 - 17:28 11 | * more about me: http://www.1991th.com 12 | */ 13 | 14 | public class CommonUtil { 15 | public static float dip2px(float pDipValue) { 16 | DisplayMetrics dm = App.getApp().getResources().getDisplayMetrics(); 17 | return pDipValue * dm.density; 18 | } 19 | 20 | public static boolean isBelowLollipop() { 21 | return android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP; 22 | } 23 | 24 | //API 21 25 | public static boolean isOverLollipop() { 26 | return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP; 27 | } 28 | 29 | //API 19 30 | public static boolean isOverKITKAT() { 31 | return android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/widget/reflect/BitmapMemoryCache.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.widget.reflect; 2 | 3 | import android.graphics.Bitmap; 4 | import android.support.v4.util.LruCache; 5 | 6 | /** 7 | * BitmapMemoryCache 8 | * Created by Plucky on 2018/1/1 下午4:53. 9 | * more about me: http://www.1991th.com 10 | */ 11 | 12 | public class BitmapMemoryCache { 13 | 14 | private static final String TAG = "BitmapMemoryCache"; 15 | 16 | private static BitmapMemoryCache sInstance = new BitmapMemoryCache(); 17 | 18 | private LruCache mMemoryCache; 19 | 20 | /** 21 | * 单例模式. 22 | */ 23 | public static BitmapMemoryCache getInstance() { 24 | return BitmapMemoryCache.sInstance; 25 | } 26 | 27 | private BitmapMemoryCache() { 28 | int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 29 | int cacheSize = maxMemory / 8; 30 | mMemoryCache = new LruCache(cacheSize) { 31 | @Override 32 | protected int sizeOf(String key, Bitmap bitmap) { 33 | // 重写此方法来衡量每张图片的大小,默认返回图片数量。 34 | return bitmap.getByteCount() / 1024; 35 | } 36 | }; 37 | } 38 | 39 | public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 40 | if (getBitmapFromMemCache(key) == null) { 41 | mMemoryCache.put(key, bitmap); 42 | } 43 | } 44 | 45 | public Bitmap getBitmapFromMemCache(String key) { 46 | return mMemoryCache.get(key); 47 | } 48 | 49 | /** 50 | * 移除缓存 51 | */ 52 | public synchronized void removeImageCache(String key) { 53 | if (key != null) { 54 | if (mMemoryCache != null) { 55 | Bitmap bm = mMemoryCache.remove(key); 56 | if (bm != null) { 57 | bm.recycle(); 58 | } 59 | } 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/widget/reflect/ReflectItemView.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.widget.reflect; 2 | 3 | /** 4 | * ReflectItemView 5 | * Created by Plucky on 2018/1/1 下午4:50. 6 | * more about me: http://www.1991th.com 7 | */ 8 | 9 | import android.content.Context; 10 | import android.content.res.TypedArray; 11 | import android.graphics.Bitmap; 12 | import android.graphics.Canvas; 13 | import android.graphics.LinearGradient; 14 | import android.graphics.Paint; 15 | import android.graphics.Path; 16 | import android.graphics.PorterDuff; 17 | import android.graphics.PorterDuffXfermode; 18 | import android.graphics.RectF; 19 | import android.graphics.Shader; 20 | import android.util.AttributeSet; 21 | import android.widget.FrameLayout; 22 | 23 | import engineer.echo.transition.R; 24 | 25 | /** 26 | * 倒影,圆角控件. 27 | * 28 | * @author hailongqiu 356752238@qq.com 29 | */ 30 | public class ReflectItemView extends FrameLayout { 31 | 32 | private static final String TAG = "ReflectItemView"; 33 | 34 | private static final int DEFUALT_REFHEIGHT = 80; 35 | private static final int DEFUALT_RADIUS = 12; 36 | 37 | private Paint mClearPaint = null; 38 | private Paint mShapePaint = null; 39 | private Paint mRefPaint = null; 40 | private int mRefHeight = DEFUALT_REFHEIGHT; 41 | 42 | private boolean mIsDrawShape = false; 43 | private boolean mIsReflection = false; 44 | 45 | private float mRadius = DEFUALT_RADIUS; 46 | private int mRefleSpacing = 0; 47 | private BitmapMemoryCache mBitmapMemoryCache = BitmapMemoryCache.getInstance(); 48 | private static int sViewIDNum = 0; 49 | private int viewIDNum = 0; 50 | 51 | public ReflectItemView(Context context) { 52 | this(context, null, 0); 53 | } 54 | 55 | public ReflectItemView(Context context, AttributeSet attrs) { 56 | this(context, attrs, 0); 57 | } 58 | 59 | public ReflectItemView(Context context, AttributeSet attrs, int defStyle) { 60 | super(context, attrs, defStyle); 61 | init(context, attrs); 62 | } 63 | 64 | private void init(Context context, AttributeSet attrs) { 65 | setClipChildren(false); 66 | setClipToPadding(false); 67 | setWillNotDraw(false); 68 | // 初始化属性. 69 | if (attrs != null) { 70 | TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.reflectItemView);// 获取配置属性 71 | mIsReflection = tArray.getBoolean(R.styleable.reflectItemView_isReflect, false); 72 | mRefHeight = (int) tArray.getDimension(R.styleable.reflectItemView_reflect_height, DEFUALT_REFHEIGHT); 73 | mIsDrawShape = tArray.getBoolean(R.styleable.reflectItemView_isShape, false); 74 | mRadius = tArray.getDimension(R.styleable.reflectItemView_radius, DEFUALT_RADIUS); // 圆角半径. 75 | mRefleSpacing = (int) tArray.getDimension(R.styleable.reflectItemView_refle_spacing, 0); 76 | setRadius(mRadius); 77 | } 78 | // 初始化圆角矩形. 79 | initShapePaint(); 80 | // 初始化倒影参数. 81 | initRefPaint(); 82 | } 83 | 84 | private void initShapePaint() { 85 | mShapePaint = new Paint(); 86 | mShapePaint.setAntiAlias(true); 87 | // 取两层绘制交集。显示下层。 88 | mShapePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 89 | } 90 | 91 | private void initRefPaint() { 92 | if (mRefPaint == null) { 93 | mRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 94 | // 倒影渐变. 95 | mRefPaint.setShader( 96 | new LinearGradient(0, 0, 0, mRefHeight, new int[]{0x77000000, 0x66AAAAAA, 0x0500000, 0x00000000}, 97 | new float[]{0.0f, 0.1f, 0.9f, 1.0f}, Shader.TileMode.CLAMP)); 98 | mRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); 99 | } 100 | if (mClearPaint == null) { 101 | mClearPaint = new Paint(); 102 | mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 103 | } 104 | } 105 | 106 | /** 107 | * 设置倒影的shader属性. 108 | */ 109 | public void setReflectionShader(Shader shader) { 110 | if (mRefPaint != null) { 111 | mRefPaint.setShader(shader); 112 | invalidate(); 113 | } 114 | } 115 | 116 | public void setReflectionSpacing(int spacing) { 117 | mRefleSpacing = spacing; 118 | invalidate(); 119 | } 120 | 121 | /** 122 | * 设置是否倒影. 123 | */ 124 | public void setReflection(boolean ref) { 125 | mIsReflection = ref; 126 | invalidate(); 127 | } 128 | 129 | public boolean isReflection() { 130 | return this.mIsReflection; 131 | } 132 | 133 | /** 134 | * 设置绘制奇形怪状的形状. 135 | */ 136 | public void setDrawShape(boolean isDrawShape) { 137 | mIsDrawShape = isDrawShape; 138 | invalidate(); 139 | } 140 | 141 | public boolean isDrawShape() { 142 | return this.mIsDrawShape; 143 | } 144 | 145 | /** 146 | * 倒影高度. 147 | */ 148 | public void setRefHeight(int height) { 149 | this.mRefHeight = height; 150 | invalidate(); 151 | } 152 | 153 | public int getRefHeight() { 154 | return this.mRefHeight; 155 | } 156 | 157 | @Override 158 | public void invalidate() { 159 | super.invalidate(); 160 | } 161 | 162 | /** 163 | * 获取缓存ID. 164 | */ 165 | private int getViewCacheID() { 166 | if (viewIDNum == 0) { 167 | sViewIDNum++; 168 | viewIDNum = sViewIDNum; 169 | } 170 | return viewIDNum; 171 | } 172 | 173 | @Override 174 | protected void onDetachedFromWindow() { 175 | super.onDetachedFromWindow(); 176 | if (viewIDNum != 0) { 177 | mBitmapMemoryCache.removeImageCache(viewIDNum + ""); 178 | } 179 | } 180 | 181 | public Path getShapePath(int width, int height, float radius) { 182 | return addRoundPath3(getWidth(), getHeight(), radius); 183 | } 184 | 185 | @Override 186 | public void draw(Canvas canvas) { 187 | try { 188 | if (canvas != null) { 189 | if (mIsDrawShape && (mRadius > 0)) { 190 | drawShapePathCanvas(canvas); 191 | } else { 192 | super.draw(canvas); 193 | } 194 | /** 195 | * 绘制倒影. 4.3 SDK-18,有问题,
196 | * 在使用Canvas.translate(dx, dy)会出现BUG.
197 | */ 198 | if (getSDKVersion() == 18) { 199 | drawRefleCanvas4_3_18(canvas); 200 | } else if (getSDKVersion() == 17) { 201 | // 4.2 不需要倒影,绘制有问题,暂时屏蔽. 202 | drawRefleCanvas(canvas); 203 | } else { // 性能高速-倒影(4.3有问题). 204 | drawRefleCanvas(canvas); 205 | } 206 | } 207 | } catch (Exception e) { 208 | e.printStackTrace(); 209 | } 210 | } 211 | 212 | /** 213 | * 绘制圆角控件. 修复使用clipPath有锯齿问题. 214 | */ 215 | private void drawShapePathCanvas(Canvas shapeCanvas) { 216 | if (shapeCanvas != null) { 217 | int width = getWidth(); 218 | int height = getHeight(); 219 | if (width == 0 || height == 0) 220 | return; 221 | int count = shapeCanvas.save(); 222 | int count2 = shapeCanvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG); 223 | // 224 | Path path = getShapePath(width, height, mRadius); 225 | super.draw(shapeCanvas); 226 | shapeCanvas.drawPath(path, mShapePaint); 227 | // 228 | if (count2 > 0) { 229 | shapeCanvas.restoreToCount(count2); 230 | } 231 | shapeCanvas.restoreToCount(count); 232 | } 233 | } 234 | 235 | /** 236 | * 绘制倒影. 修复原先使用bitmap卡顿的问题. 237 | */ 238 | private void drawRefleCanvas(Canvas refleCanvas) { 239 | if (mIsReflection) { 240 | refleCanvas.save(); 241 | int dy = getHeight(); 242 | int dx = 0; 243 | refleCanvas.translate(dx, dy + mRefleSpacing); 244 | drawReflection(refleCanvas); 245 | refleCanvas.restore(); 246 | } 247 | } 248 | 249 | private void drawRefleCanvas4_3_18(Canvas canvas) { 250 | if (mIsReflection) { 251 | // 创建一个画布. 252 | String cacheID = getViewCacheID() + ""; 253 | // 254 | Bitmap reflectBitmap = mBitmapMemoryCache.getBitmapFromMemCache(cacheID); 255 | if (reflectBitmap == null) { 256 | reflectBitmap = Bitmap.createBitmap(getWidth(), mRefHeight, Bitmap.Config.ARGB_8888); 257 | mBitmapMemoryCache.addBitmapToMemoryCache(cacheID, reflectBitmap); 258 | } 259 | Canvas reflectCanvas = new Canvas(reflectBitmap); 260 | reflectCanvas.drawPaint(mClearPaint); // 清空画布. 261 | /** 262 | * 如果设置了圆角,倒影也需要圆角. 263 | */ 264 | int width = reflectCanvas.getWidth(); 265 | int height = reflectCanvas.getHeight(); 266 | RectF outerRect = new RectF(0, 0, width, height); 267 | Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 268 | if (mIsDrawShape) { 269 | reflectCanvas.drawPath(getShapePath(width, height + 50, mRadius), paint); 270 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 271 | } 272 | reflectCanvas.saveLayer(outerRect, paint, Canvas.ALL_SAVE_FLAG); 273 | drawReflection4_3_18(reflectCanvas); 274 | reflectCanvas.restore(); 275 | canvas.save(); 276 | int dy = getHeight(); 277 | int dx = 0; 278 | canvas.translate(dx, dy + mRefleSpacing); 279 | canvas.drawBitmap(reflectBitmap, 0, 0, null); 280 | canvas.restore(); 281 | } 282 | } 283 | 284 | public void drawReflection4_3_18(Canvas canvas) { 285 | canvas.save(); 286 | canvas.clipRect(0, 0, getWidth(), mRefHeight); 287 | canvas.save(); 288 | canvas.scale(1, -1); 289 | canvas.translate(0, -getHeight()); 290 | super.draw(canvas); 291 | canvas.restore(); 292 | canvas.drawRect(0, 0, getWidth(), mRefHeight, mRefPaint); 293 | canvas.restore(); 294 | } 295 | 296 | /** 297 | * 绘制倒影. 298 | */ 299 | public void drawReflection(Canvas reflectionCanvas) { 300 | int width = getWidth(); 301 | int height = getHeight(); 302 | int count = reflectionCanvas.save(); 303 | int count2 = reflectionCanvas.saveLayer(0, 0, width, mRefHeight, null, Canvas.ALL_SAVE_FLAG); 304 | // 305 | reflectionCanvas.save(); 306 | reflectionCanvas.clipRect(0, 0, getWidth(), mRefHeight); 307 | reflectionCanvas.save(); 308 | reflectionCanvas.scale(1, -1); 309 | reflectionCanvas.translate(0, -getHeight()); 310 | super.draw(reflectionCanvas); 311 | if (mIsDrawShape) { 312 | Path path = getShapePath(width, height, mRadius); 313 | reflectionCanvas.drawPath(path, mShapePaint); 314 | } 315 | reflectionCanvas.restore(); 316 | reflectionCanvas.drawRect(0, 0, getWidth(), mRefHeight, mRefPaint); 317 | reflectionCanvas.restore(); 318 | // 319 | if (count2 > 0) { 320 | reflectionCanvas.restoreToCount(count2); 321 | } 322 | reflectionCanvas.restoreToCount(count); 323 | } 324 | 325 | /* 326 | * 设置/获取-圆角的角度. 327 | */ 328 | 329 | public void setRadius(float radius) { 330 | this.mRadius = radius; 331 | invalidate(); 332 | } 333 | 334 | public static int getSDKVersion() { 335 | int version = 0; 336 | try { 337 | version = Integer.valueOf(android.os.Build.VERSION.SDK); 338 | } catch (NumberFormatException e) { 339 | } 340 | return version; 341 | } 342 | 343 | public static Path addRoundPath3(int width, int height, float radius) { 344 | Path path = new Path(); 345 | path.addRoundRect(new RectF(0, 0, width, height), radius, radius, Path.Direction.CW); 346 | return path; 347 | } 348 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/widget/transition/BoundsAndAlpha.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.widget.transition; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorSet; 5 | import android.animation.ObjectAnimator; 6 | import android.animation.ValueAnimator; 7 | import android.transition.ChangeBounds; 8 | import android.transition.TransitionValues; 9 | import android.util.Log; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | import engineer.echo.transition.R; 14 | 15 | /** 16 | * BoundsAndAlpha 17 | * Created by Plucky on 2018/1/1 下午11:36. 18 | * more about me: http://www.1991th.com 19 | */ 20 | 21 | public class BoundsAndAlpha extends ChangeBounds { 22 | private static final String TAG = "BoundsAndAlpha"; 23 | 24 | //是否为入场动画 25 | private boolean mIn; 26 | 27 | public BoundsAndAlpha(boolean mIn) { 28 | this.mIn = mIn; 29 | } 30 | 31 | @Override 32 | public void captureStartValues(TransitionValues transitionValues) { 33 | super.captureStartValues(transitionValues); 34 | Log.d(TAG, "captureStartValues: transitionValues=" + transitionValues); 35 | } 36 | 37 | @Override 38 | public void captureEndValues(TransitionValues transitionValues) { 39 | super.captureEndValues(transitionValues); 40 | Log.d(TAG, "captureEndValues: transitionValues=" + transitionValues); 41 | } 42 | 43 | @Override 44 | public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { 45 | //对进入的页面的元素进行alpha变化 进入时,渐隐 返回时,渐现 46 | View endView = endValues != null ? endValues.view : null; 47 | View startView = startValues != null ? startValues.view : null; 48 | View shareView = mIn ? endView : startView; 49 | Animator animator = super.createAnimator(sceneRoot, startValues, endValues); 50 | if (animator != null) { 51 | if (animator instanceof AnimatorSet) { 52 | AnimatorSet animatorSet = (AnimatorSet) animator; 53 | Animator first = animatorSet.getChildAnimations().get(0); 54 | executeAlphaChange(first, shareView); 55 | } else { 56 | executeAlphaChange(animator, shareView); 57 | } 58 | } 59 | return animator; 60 | } 61 | 62 | 63 | /** 64 | * 实现共享元素在做形变动画的同时做alpha变化 65 | * 66 | * @param animator Animator 67 | * @param view View 68 | */ 69 | private void executeAlphaChange(Animator animator, final View view) { 70 | if (animator == null || view == null) return; 71 | if (!(animator instanceof ObjectAnimator)) return; 72 | boolean needChangeAlpha = view.getTag(R.id.key_for_alpha_change_when_share_element) != null; 73 | if (!needChangeAlpha) return; 74 | //让共享元素在形变的同时 alpha同时发生变化 75 | //如果是入场动画 则从1~0 76 | //如果是退场动画 则从0~1 77 | view.setAlpha(mIn ? 1 : 0); 78 | ObjectAnimator objectAnimator = (ObjectAnimator) animator; 79 | objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 80 | @Override 81 | public void onAnimationUpdate(ValueAnimator animation) { 82 | float fract = animation.getAnimatedFraction(); 83 | float alpha = mIn ? (1 - fract) : fract; 84 | view.setAlpha(alpha); 85 | } 86 | }); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/widget/transition/ProgressTransition.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.widget.transition; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ObjectAnimator; 5 | import android.transition.Transition; 6 | import android.transition.TransitionValues; 7 | import android.util.IntProperty; 8 | import android.util.Property; 9 | import android.view.ViewGroup; 10 | 11 | import ch.halcyon.squareprogressbar.SquareProgressBar; 12 | 13 | /** 14 | * ProgressTransition 15 | * Created by Plucky on 2018/1/4 - 17:19 16 | * more about me: http://www.1991th.com 17 | */ 18 | 19 | public class ProgressTransition extends Transition { 20 | 21 | private static final String TAG = "ProgressTransition"; 22 | /** 23 | * Property is like a helper that contain setter and getter in one place 24 | */ 25 | private static final Property PROGRESS_PROPERTY = 26 | new IntProperty(TAG) { 27 | 28 | @Override 29 | public void setValue(SquareProgressBar progressBar, int value) { 30 | progressBar.setProgress(value); 31 | } 32 | 33 | @Override 34 | public Integer get(SquareProgressBar progressBar) { 35 | Double pro = progressBar.getProgress(); 36 | return pro.intValue(); 37 | } 38 | }; 39 | 40 | /** 41 | * Internal name of property. Like a intent bundles 42 | */ 43 | private static final String PROPNAME_PROGRESS = "ProgressTransition:progress"; 44 | 45 | @Override 46 | public void captureStartValues(TransitionValues transitionValues) { 47 | captureValues(transitionValues); 48 | } 49 | 50 | @Override 51 | public void captureEndValues(TransitionValues transitionValues) { 52 | captureValues(transitionValues); 53 | } 54 | 55 | private void captureValues(TransitionValues transitionValues) { 56 | if (transitionValues.view instanceof SquareProgressBar) { 57 | // save current progress in the values map 58 | SquareProgressBar progressBar = ((SquareProgressBar) transitionValues.view); 59 | transitionValues.values.put(PROPNAME_PROGRESS, progressBar.getProgress()); 60 | } 61 | } 62 | 63 | @Override 64 | public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, 65 | TransitionValues endValues) { 66 | if (startValues != null && endValues != null && endValues.view instanceof SquareProgressBar) { 67 | SquareProgressBar progressBar = (SquareProgressBar) endValues.view; 68 | int start = ((Double) startValues.values.get(PROPNAME_PROGRESS)).intValue(); 69 | int end = ((Double) endValues.values.get(PROPNAME_PROGRESS)).intValue(); 70 | if (start != end) { 71 | // first of all we need to apply the start value, because right now 72 | // the view is have end value 73 | progressBar.setProgress(start); 74 | // create animator with our progressBar, property and end value 75 | return ObjectAnimator.ofInt(progressBar, PROGRESS_PROPERTY, end); 76 | } 77 | } 78 | return null; 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/cmpts/widget/transition/SafeSlide.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.cmpts.widget.transition; 2 | 3 | import android.os.Build; 4 | import android.support.annotation.RequiresApi; 5 | import android.transition.Slide; 6 | 7 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 8 | public class SafeSlide extends Slide { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/fragment/AppCtrlFragment.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.fragment; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.Paint; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.constraint.ConstraintLayout; 8 | import android.support.v4.app.SharedElementCallback; 9 | import android.support.v4.view.ViewCompat; 10 | import android.support.v7.widget.GridLayoutManager; 11 | import android.support.v7.widget.RecyclerView; 12 | import android.transition.ArcMotion; 13 | import android.transition.ChangeBounds; 14 | import android.transition.TransitionManager; 15 | import android.util.Log; 16 | import android.util.Pair; 17 | import android.view.LayoutInflater; 18 | import android.view.View; 19 | import android.view.ViewGroup; 20 | import android.widget.ImageView; 21 | 22 | import org.greenrobot.eventbus.EventBus; 23 | import org.greenrobot.eventbus.Subscribe; 24 | import org.greenrobot.eventbus.ThreadMode; 25 | 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Random; 29 | 30 | import ch.halcyon.squareprogressbar.SquareProgressBar; 31 | import ch.halcyon.squareprogressbar.utils.PercentStyle; 32 | import engineer.echo.transition.Constants; 33 | import engineer.echo.transition.R; 34 | import engineer.echo.transition.cmpts.context.BaseFragment; 35 | import engineer.echo.transition.cmpts.events.CaptureFinishEvent; 36 | import engineer.echo.transition.cmpts.utils.CommonUtil; 37 | import engineer.echo.transition.cmpts.widget.reflect.ReflectItemView; 38 | import engineer.echo.transition.cmpts.widget.transition.ProgressTransition; 39 | 40 | /** 41 | * AppCtrlFragment 42 | * Created by Plucky on 2018/1/1 下午3:26. 43 | * more about me: http://www.1991th.com 44 | * 点击控制 45 | */ 46 | 47 | public class AppCtrlFragment extends BaseFragment implements View.OnClickListener, GalleryAdapter.GalleryListener { 48 | 49 | public static String SHARE_NAME_LEFT = "share_element_of_left"; 50 | public static String SHARE_NAME_MIDDLE = "share_element_of_middle"; 51 | public static String SHARE_NAME_RIGHT = "share_element_of_right"; 52 | private View mSceneBtn, mPhotoBtn, mSettingsBtn; 53 | private Pair[] mShareViews = new Pair[3]; 54 | private ReflectItemView mCaptureHolder; 55 | private ImageView mCaptureView; 56 | private ConstraintLayout mRootView; 57 | private int mEndSize, mMargin; 58 | private SquareProgressBar mProgressBar; 59 | 60 | public AppCtrlFragment() { 61 | mEndSize = (int) CommonUtil.dip2px(50); 62 | mMargin = (int) CommonUtil.dip2px(30); 63 | } 64 | 65 | public static AppCtrlFragment newInstance() { 66 | AppCtrlFragment fragment = new AppCtrlFragment(); 67 | return fragment; 68 | } 69 | 70 | @Override 71 | public void onCreate(@Nullable Bundle savedInstanceState) { 72 | super.onCreate(savedInstanceState); 73 | } 74 | 75 | @Override 76 | public void onDestroy() { 77 | super.onDestroy(); 78 | } 79 | 80 | @Override 81 | public void onPause() { 82 | super.onPause(); 83 | } 84 | 85 | @Override 86 | public void onResume() { 87 | super.onResume(); 88 | } 89 | 90 | @Nullable 91 | @Override 92 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 93 | EventBus.getDefault().register(this); 94 | View view = inflater.inflate(R.layout.fragment_ctrl, container, false); 95 | 96 | mRootView = view.findViewById(R.id.root_view); 97 | mSceneBtn = view.findViewById(R.id.scene_btn); 98 | mPhotoBtn = view.findViewById(R.id.take_photo_btn); 99 | mSettingsBtn = view.findViewById(R.id.settings_btn); 100 | mCaptureHolder = view.findViewById(R.id.capture_holder); 101 | mCaptureView = view.findViewById(R.id.capture_image); 102 | 103 | mProgressBar = view.findViewById(R.id.progressBar); 104 | mProgressBar.setImage(R.drawable.girl_4); 105 | mProgressBar.setImageScaleType(ImageView.ScaleType.CENTER_CROP); 106 | mProgressBar.setOnClickListener(this); 107 | mProgressBar.setWidth(2); 108 | PercentStyle mStyle = new PercentStyle(Paint.Align.CENTER, 14f, true); 109 | mStyle.setTextColor(Color.WHITE); 110 | mProgressBar.setPercentStyle(mStyle); 111 | mProgressBar.drawCenterline(true); 112 | mProgressBar.drawOutline(true); 113 | 114 | mSceneBtn.setOnClickListener(this); 115 | mPhotoBtn.setOnClickListener(this); 116 | mSettingsBtn.setOnClickListener(this); 117 | 118 | ViewCompat.setTransitionName(mSceneBtn, SHARE_NAME_LEFT); 119 | ViewCompat.setTransitionName(mPhotoBtn, SHARE_NAME_MIDDLE); 120 | ViewCompat.setTransitionName(mSettingsBtn, SHARE_NAME_RIGHT); 121 | 122 | mShareViews[0] = new Pair<>(mSceneBtn, SHARE_NAME_LEFT); 123 | mShareViews[1] = new Pair<>(mPhotoBtn, SHARE_NAME_MIDDLE); 124 | mShareViews[2] = new Pair<>(mSettingsBtn, SHARE_NAME_RIGHT); 125 | return view; 126 | } 127 | 128 | @Override 129 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 130 | super.onViewCreated(view, savedInstanceState); 131 | final RecyclerView recyclerView = view.findViewById(R.id.rcv_gallery_app); 132 | GridLayoutManager manager = new GridLayoutManager(getContext(), 3); 133 | recyclerView.setLayoutManager(manager); 134 | recyclerView.setAdapter(new GalleryAdapter(this)); 135 | } 136 | 137 | @Override 138 | public void onItemClick(ImageView imageView, int position) { 139 | GalleryDetailFragment.gotoFragment(this, imageView, ViewCompat.getTransitionName(imageView)); 140 | } 141 | 142 | @Override 143 | public void onDestroyView() { 144 | super.onDestroyView(); 145 | EventBus.getDefault().unregister(this); 146 | } 147 | 148 | 149 | @Subscribe(threadMode = ThreadMode.MAIN) 150 | public void onCaptureFinishEvent(CaptureFinishEvent event) { 151 | Log.d(TAG, "onCaptureFinishEvent: "); 152 | mCaptureView.setImageResource(R.drawable.girl_4); 153 | 154 | mCaptureHolder.setAlpha(1.0f); 155 | 156 | ChangeBounds changeBounds = new ChangeBounds(); 157 | // TODO: Added By Plucky 2018/1/8 11:39 路径动画需要API版本>=LOLLIPOP 158 | if (CommonUtil.isOverLollipop()) { 159 | changeBounds.setPathMotion(new ArcMotion()); 160 | } 161 | changeBounds.setDuration(Constants.TRANSITION_TIME); 162 | TransitionManager.beginDelayedTransition(mRootView, 163 | changeBounds); 164 | 165 | ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mCaptureHolder.getLayoutParams(); 166 | params.width = mEndSize; 167 | params.height = mEndSize; 168 | 169 | params.endToEnd = -1; 170 | params.startToStart = -1; 171 | params.topToTop = -1; 172 | 173 | params.bottomToBottom = R.id.root_view; 174 | params.endToStart = R.id.take_photo_btn; 175 | 176 | params.bottomMargin = mMargin; 177 | params.rightMargin = mMargin; 178 | params.topMargin = 0; 179 | 180 | mCaptureHolder.setLayoutParams(params); 181 | } 182 | 183 | private void smoothProgress() { 184 | Random random = new Random(); 185 | int value = random.nextInt(100); 186 | TransitionManager.beginDelayedTransition(mRootView, new ProgressTransition()); 187 | value = Math.max(0, Math.min(100, value)); 188 | mProgressBar.setProgress(value); 189 | } 190 | 191 | @Override 192 | public void onClick(View view) { 193 | switch (view.getId()) { 194 | case R.id.scene_btn: 195 | GalleryFragment.gotoPage(getFragmentManager(), mShareViews[0]); 196 | break; 197 | case R.id.take_photo_btn: 198 | //拍照很卡 暂时屏蔽 199 | //EventBus.getDefault().post(new StartCaptureEvent()); 200 | onCaptureFinishEvent(null); 201 | break; 202 | case R.id.settings_btn: 203 | SettingsFragment.gotoPage(getFragmentManager(), mShareViews); 204 | break; 205 | case R.id.progressBar: 206 | smoothProgress(); 207 | break; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/fragment/AppDisplayFragment.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import engineer.echo.transition.R; 10 | import engineer.echo.transition.cmpts.context.BaseFragment; 11 | 12 | /** 13 | * AppDisplayFragment 14 | * Created by Plucky on 2018/1/1 下午3:28. 15 | * more about me: http://www.1991th.com 16 | */ 17 | 18 | public class AppDisplayFragment extends BaseFragment { 19 | 20 | public AppDisplayFragment() { 21 | 22 | } 23 | 24 | public static AppDisplayFragment newInstance() { 25 | AppDisplayFragment fragment = new AppDisplayFragment(); 26 | return fragment; 27 | } 28 | @Nullable 29 | @Override 30 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 31 | View view = inflater.inflate(R.layout.fragment_display, container, false); 32 | return view; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/fragment/GalleryAdapter.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.fragment; 2 | 3 | import android.support.v4.view.ViewCompat; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.FrameLayout; 8 | import android.widget.ImageView; 9 | 10 | import engineer.echo.transition.App; 11 | import engineer.echo.transition.R; 12 | 13 | /** 14 | * GalleryAdapter.java 15 | * Info: 相机 16 | * Created by Plucky(plucky@echo.engineer) on 2019/6/13 - 10:15 AM 17 | * more about me: http://www.1991th.com 18 | */ 19 | public class GalleryAdapter extends RecyclerView.Adapter { 20 | 21 | private GalleryListener mListener; 22 | private int size = (App.sScreenSize.x - App.dpToPx(80)) / 3; 23 | private int margin = App.dpToPx(10); 24 | 25 | public GalleryAdapter(GalleryListener listener) { 26 | this.mListener = listener; 27 | } 28 | 29 | private ItemHolder createHolder(ViewGroup parent, int viewType) { 30 | ImageView itemView = new ImageView(parent.getContext()); 31 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(size, size); 32 | layoutParams.setMargins(margin, margin, margin, margin); 33 | itemView.setLayoutParams(layoutParams); 34 | itemView.setScaleType(ImageView.ScaleType.CENTER_CROP); 35 | itemView.setImageResource(R.drawable.girl_0); 36 | return new ItemHolder(itemView); 37 | } 38 | 39 | @Override 40 | public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { 41 | return createHolder(parent, viewType); 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(ItemHolder holder, int position) { 46 | String shareName = "image-" + position; 47 | ViewCompat.setTransitionName(holder.itemView, shareName); 48 | } 49 | 50 | @Override 51 | public int getItemCount() { 52 | return 20; 53 | } 54 | 55 | class ItemHolder extends RecyclerView.ViewHolder { 56 | 57 | public ItemHolder(final ImageView itemView) { 58 | super(itemView); 59 | itemView.setOnClickListener(new View.OnClickListener() { 60 | @Override 61 | public void onClick(View v) { 62 | if (mListener != null) { 63 | mListener.onItemClick(itemView, getAdapterPosition()); 64 | } 65 | } 66 | }); 67 | } 68 | } 69 | 70 | public interface GalleryListener { 71 | void onItemClick(ImageView imageView, int position); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/fragment/GalleryDetailFragment.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.view.ViewCompat; 7 | import android.transition.ChangeBounds; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ImageView; 12 | 13 | import engineer.echo.transition.R; 14 | import engineer.echo.transition.cmpts.context.BaseFragment; 15 | 16 | /** 17 | * GalleryDetailFragment.java 18 | * Info: 详情页 19 | * Created by Plucky(plucky@echo.engineer) on 2019/6/13 - 10:30 AM 20 | * more about me: http://www.1991th.com 21 | */ 22 | 23 | public class GalleryDetailFragment extends BaseFragment { 24 | 25 | public static final String KEY_TRANSITION = "key_for_transition"; 26 | 27 | public static void gotoFragment(Fragment fragment, View shareView, String shareName) { 28 | Fragment target = GalleryDetailFragment.newInstance(shareName); 29 | target.setSharedElementEnterTransition(new ChangeBounds()); 30 | target.setSharedElementReturnTransition(new ChangeBounds()); 31 | fragment.getFragmentManager() 32 | .beginTransaction() 33 | .replace(R.id.app_ctrl_view, target) 34 | .addSharedElement(shareView, shareName) 35 | .addToBackStack(null) 36 | .commitAllowingStateLoss(); 37 | } 38 | 39 | public GalleryDetailFragment() { 40 | 41 | } 42 | 43 | public static GalleryDetailFragment newInstance(String transitionName) { 44 | GalleryDetailFragment fragment = new GalleryDetailFragment(); 45 | Bundle data = new Bundle(); 46 | data.putString(KEY_TRANSITION, transitionName); 47 | fragment.setArguments(data); 48 | return fragment; 49 | } 50 | 51 | @Nullable 52 | @Override 53 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 54 | View view = inflater.inflate(R.layout.fragment_gallery_detail, container, false); 55 | ImageView imageView = view.findViewById(R.id.iv_gallery_detail_app); 56 | imageView.setOnClickListener(new View.OnClickListener() { 57 | @Override 58 | public void onClick(View v) { 59 | getFragmentManager().popBackStack(); 60 | } 61 | }); 62 | ViewCompat.setTransitionName(imageView, getArguments().getString(KEY_TRANSITION)); 63 | return view; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/fragment/GalleryFragment.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentTransaction; 7 | import android.support.v4.view.ViewCompat; 8 | import android.transition.ArcMotion; 9 | import android.transition.ChangeBounds; 10 | import android.util.Pair; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import engineer.echo.transition.R; 16 | import engineer.echo.transition.cmpts.context.BaseFragment; 17 | import engineer.echo.transition.cmpts.utils.CommonUtil; 18 | import engineer.echo.transition.cmpts.widget.reflect.ReflectItemView; 19 | 20 | import static engineer.echo.transition.Constants.TRANSITION_TIME; 21 | import static engineer.echo.transition.fragment.AppCtrlFragment.SHARE_NAME_LEFT; 22 | 23 | /** 24 | * GalleryFragment 25 | * Created by Plucky on 2018/1/1 下午3:28. 26 | * more about me: http://www.1991th.com 27 | */ 28 | 29 | public class GalleryFragment extends BaseFragment { 30 | 31 | private ReflectItemView mShareView; 32 | 33 | public GalleryFragment() { 34 | 35 | } 36 | 37 | public static GalleryFragment newInstance() { 38 | GalleryFragment fragment = new GalleryFragment(); 39 | return fragment; 40 | } 41 | 42 | @Override 43 | public void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | } 46 | 47 | @Override 48 | public void onDestroy() { 49 | super.onDestroy(); 50 | } 51 | 52 | @Override 53 | public void onPause() { 54 | super.onPause(); 55 | } 56 | 57 | @Override 58 | public void onResume() { 59 | super.onResume(); 60 | } 61 | 62 | @Nullable 63 | @Override 64 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 65 | View view = inflater.inflate(R.layout.fragment_gallery, container, false); 66 | mShareView = view.findViewById(R.id.app_gallery_share_btn); 67 | ViewCompat.setTransitionName(mShareView, SHARE_NAME_LEFT); 68 | return view; 69 | } 70 | 71 | @Override 72 | public void onDestroyView() { 73 | super.onDestroyView(); 74 | } 75 | 76 | 77 | public static void gotoPage(FragmentManager manager, Pair shareView) { 78 | GalleryFragment fragment = GalleryFragment.newInstance(); 79 | fragment.setAllowEnterTransitionOverlap(false); 80 | fragment.setAllowReturnTransitionOverlap(false); 81 | 82 | ChangeBounds boundsIn = new ChangeBounds(); 83 | boundsIn.setDuration(TRANSITION_TIME); 84 | // TODO: Added By Plucky 2018/1/8 11:39 路径动画需要API版本>=LOLLIPOP 85 | if (CommonUtil.isOverLollipop()) { 86 | boundsIn.setPathMotion(new ArcMotion()); 87 | } 88 | 89 | fragment.setSharedElementEnterTransition(boundsIn); 90 | 91 | 92 | ChangeBounds boundsOut = new ChangeBounds(); 93 | boundsOut.setDuration(TRANSITION_TIME); 94 | // TODO: Added By Plucky 2018/1/8 11:39 路径动画需要API版本>=LOLLIPOP 95 | if (CommonUtil.isOverLollipop()) { 96 | boundsIn.setPathMotion(new ArcMotion()); 97 | } 98 | fragment.setSharedElementReturnTransition(boundsOut); 99 | 100 | FragmentTransaction fragmentTransaction = manager.beginTransaction(); 101 | fragmentTransaction.replace(R.id.app_ctrl_view, fragment); 102 | fragmentTransaction.addToBackStack(null); 103 | 104 | fragmentTransaction.addSharedElement(shareView.first, shareView.second); 105 | 106 | fragmentTransaction.commit(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/echo/transition/fragment/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.constraint.ConstraintLayout; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v4.app.FragmentTransaction; 8 | import android.support.v4.view.ViewCompat; 9 | import android.transition.ChangeBounds; 10 | import android.transition.Fade; 11 | import android.transition.Scene; 12 | import android.transition.Transition; 13 | import android.transition.TransitionInflater; 14 | import android.transition.TransitionManager; 15 | import android.transition.TransitionSet; 16 | import android.util.Pair; 17 | import android.view.Gravity; 18 | import android.view.LayoutInflater; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.LinearLayout; 22 | 23 | import engineer.echo.transition.App; 24 | import engineer.echo.transition.R; 25 | import engineer.echo.transition.cmpts.context.BaseFragment; 26 | import engineer.echo.transition.cmpts.utils.CommonUtil; 27 | import engineer.echo.transition.cmpts.widget.transition.BoundsAndAlpha; 28 | import engineer.echo.transition.cmpts.widget.transition.SafeSlide; 29 | 30 | import static engineer.echo.transition.Constants.TRANSITION_TIME; 31 | import static engineer.echo.transition.Constants.TRANSITION_TIME_SHORT; 32 | import static engineer.echo.transition.fragment.AppCtrlFragment.SHARE_NAME_LEFT; 33 | import static engineer.echo.transition.fragment.AppCtrlFragment.SHARE_NAME_MIDDLE; 34 | import static engineer.echo.transition.fragment.AppCtrlFragment.SHARE_NAME_RIGHT; 35 | 36 | /** 37 | * SettingsFragment 38 | * Created by Plucky on 2018/1/1 下午3:28. 39 | * more about me: http://www.1991th.com 40 | * 总结: 41 | * 类似于Flash的关键帧,补间动画概念 42 | * captureStarValues--->CaptureEndValues--->根据createAnimator设定的动画来 执行动画变化 43 | */ 44 | 45 | public class SettingsFragment extends BaseFragment implements View.OnClickListener { 46 | 47 | private View mFakeLeft, mPhotoBtn, mFakeRight; 48 | private LinearLayout mRoot; 49 | private View mOne, mTwo, mThree, mFour; 50 | private View mFadeBtn; 51 | private View mAboutView; 52 | private int mNormalSize, mScaleSize; 53 | private LinearLayout mSceneRoot; 54 | private ConstraintLayout mSettingRoot; 55 | private Scene mSceneStart, mSceneEnd; 56 | 57 | 58 | public SettingsFragment() { 59 | mNormalSize = (int) CommonUtil.dip2px(60); 60 | mScaleSize = (int) (mNormalSize * 1.5f); 61 | } 62 | 63 | public static SettingsFragment newInstance() { 64 | SettingsFragment fragment = new SettingsFragment(); 65 | return fragment; 66 | } 67 | 68 | @Override 69 | public void onCreate(@Nullable Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | } 72 | 73 | @Override 74 | public void onDestroy() { 75 | super.onDestroy(); 76 | } 77 | 78 | @Override 79 | public void onPause() { 80 | super.onPause(); 81 | } 82 | 83 | @Override 84 | public void onResume() { 85 | super.onResume(); 86 | } 87 | 88 | @Nullable 89 | @Override 90 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 91 | View view = inflater.inflate(R.layout.fragment_settings, container, false); 92 | mSettingRoot = view.findViewById(R.id.app_settings_root); 93 | mFakeLeft = view.findViewById(R.id.app_settings_fake_left); 94 | mPhotoBtn = view.findViewById(R.id.take_photo_btn); 95 | mFakeRight = view.findViewById(R.id.app_settings_fake_right); 96 | mFakeLeft.setTag(R.id.key_for_alpha_change_when_share_element, true); 97 | mFakeRight.setTag(R.id.key_for_alpha_change_when_share_element, true); 98 | 99 | mPhotoBtn.setOnClickListener(this); 100 | mRoot = view.findViewById(R.id.app_settings_change); 101 | 102 | mOne = view.findViewById(R.id.app_settings_change_one); 103 | mTwo = view.findViewById(R.id.app_settings_change_two); 104 | mThree = view.findViewById(R.id.app_settings_change_three); 105 | mFour = view.findViewById(R.id.app_settings_change_four); 106 | 107 | mOne.setOnClickListener(this); 108 | mTwo.setOnClickListener(this); 109 | mThree.setOnClickListener(this); 110 | mFour.setOnClickListener(this); 111 | 112 | mAboutView = view.findViewById(R.id.app_settings_about); 113 | mFadeBtn = view.findViewById(R.id.app_settings_fade_btn); 114 | mFadeBtn.setOnClickListener(this); 115 | 116 | mSceneRoot = view.findViewById(R.id.app_settings_scene_change); 117 | mSceneStart = Scene.getSceneForLayout(mSceneRoot, R.layout.layout_scene_start, getContext()); 118 | mSceneEnd = Scene.getSceneForLayout(mSceneRoot, R.layout.layout_scene_end, getContext()); 119 | 120 | view.findViewById(R.id.app_settings_scene_btn).setOnClickListener(this); 121 | 122 | ViewCompat.setTransitionName(mFakeLeft, SHARE_NAME_LEFT); 123 | ViewCompat.setTransitionName(mPhotoBtn, SHARE_NAME_MIDDLE); 124 | ViewCompat.setTransitionName(mFakeRight, SHARE_NAME_RIGHT); 125 | 126 | view.findViewById(R.id.ll_switch_setting).setOnClickListener(this); 127 | return view; 128 | } 129 | 130 | @Override 131 | public void onDestroyView() { 132 | super.onDestroyView(); 133 | } 134 | 135 | 136 | @Override 137 | public void onClick(View view) { 138 | switch (view.getId()) { 139 | case R.id.take_photo_btn: 140 | getActivity().onBackPressed(); 141 | break; 142 | case R.id.app_settings_change_one: 143 | case R.id.app_settings_change_two: 144 | case R.id.app_settings_change_three: 145 | case R.id.app_settings_change_four: 146 | changeAttr(view); 147 | break; 148 | case R.id.app_settings_scene_btn: 149 | sceneChange(); 150 | break; 151 | case R.id.app_settings_fade_btn: 152 | visibilityChange(mAboutView); 153 | break; 154 | case R.id.ll_switch_setting: 155 | onSwitchView((LinearLayout) view); 156 | break; 157 | } 158 | } 159 | 160 | private void onSwitchView(LinearLayout linearLayout) { 161 | ChangeBounds changeBounds = new ChangeBounds(); 162 | changeBounds.setDuration(TRANSITION_TIME_SHORT); 163 | TransitionManager.beginDelayedTransition(mSettingRoot, changeBounds); 164 | ViewGroup.LayoutParams layoutParams = linearLayout.getLayoutParams(); 165 | if (layoutParams.width == mNormalSize) { 166 | layoutParams.width = mNormalSize * 4; 167 | } else { 168 | layoutParams.width = mNormalSize; 169 | } 170 | linearLayout.setLayoutParams(layoutParams); 171 | } 172 | 173 | private void visibilityChange(View view) { 174 | TransitionSet transitionSet = new TransitionSet(); 175 | transitionSet.setDuration(TRANSITION_TIME); 176 | transitionSet.addTransition(new Fade()); 177 | SafeSlide slide = new SafeSlide(); 178 | if (CommonUtil.isOverLollipop()) { 179 | slide.setSlideEdge(Gravity.TOP); 180 | } 181 | transitionSet.addTransition(slide); 182 | TransitionManager.beginDelayedTransition(mSettingRoot, transitionSet); 183 | if (view.getVisibility() == View.VISIBLE) { 184 | view.setVisibility(View.INVISIBLE); 185 | } else { 186 | view.setVisibility(View.VISIBLE); 187 | } 188 | } 189 | 190 | /** 191 | * 场景动画 192 | */ 193 | private void sceneChange() { 194 | boolean isSelected = mSceneRoot.isSelected(); 195 | TransitionManager.go(isSelected ? mSceneStart : mSceneEnd, new ChangeBounds()); 196 | mSceneRoot.setSelected(!isSelected); 197 | } 198 | 199 | /** 200 | * 延迟动画 201 | * 202 | * @param v View 当前点击的View 203 | */ 204 | private void changeAttr(View v) { 205 | ChangeBounds changeBounds = new ChangeBounds(); 206 | changeBounds.setDuration(TRANSITION_TIME_SHORT); 207 | TransitionManager.beginDelayedTransition(mRoot, changeBounds); 208 | int c = mRoot.getChildCount(); 209 | for (int i = 0; i < c; i++) { 210 | View child = mRoot.getChildAt(i); 211 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) child.getLayoutParams(); 212 | if (child == v) { 213 | params.width = mScaleSize; 214 | params.height = mScaleSize; 215 | } else { 216 | params.width = mNormalSize; 217 | params.height = mNormalSize; 218 | } 219 | child.setLayoutParams(params); 220 | } 221 | } 222 | 223 | public static void gotoPage(FragmentManager manager, Pair... shareViews) { 224 | SettingsFragment fragment = SettingsFragment.newInstance(); 225 | fragment.setAllowEnterTransitionOverlap(false); 226 | fragment.setAllowReturnTransitionOverlap(false); 227 | 228 | Transition transitionIn = TransitionInflater.from(App.getApp()).inflateTransition(R.transition.transition_settings_in); 229 | fragment.setEnterTransition(transitionIn); 230 | 231 | Transition transitionOut = TransitionInflater.from(App.getApp()).inflateTransition(R.transition.transition_settings_out); 232 | fragment.setReturnTransition(transitionOut); 233 | 234 | /** 235 | * 进入Setting页面时 被标记的View 会在进行变形的同时 进行alpha的渐变 1~0 236 | */ 237 | //给共享元素从xml中加载动画集 只有ChangeBounds生效 无法组合Fade效果 238 | //Transition boundsIn = TransitionInflater.from(App.getApp()).inflateTransition(R.transition.transition_settings_share_in); 239 | BoundsAndAlpha boundsIn = new BoundsAndAlpha(true); 240 | boundsIn.setDuration(TRANSITION_TIME); 241 | fragment.setSharedElementEnterTransition(boundsIn); 242 | 243 | /** 244 | * 退出Setting页面时 被标记的View 会在进行形变的同时 进行alpha的渐变 0~1 245 | */ 246 | BoundsAndAlpha boundsOut = new BoundsAndAlpha(false); 247 | boundsOut.setDuration(TRANSITION_TIME); 248 | fragment.setSharedElementReturnTransition(boundsOut); 249 | 250 | FragmentTransaction fragmentTransaction = manager.beginTransaction(); 251 | fragmentTransaction.replace(R.id.app_ctrl_view, fragment); 252 | fragmentTransaction.addToBackStack(null); 253 | if (shareViews != null && shareViews.length > 0) { 254 | for (Pair shareView : shareViews) { 255 | fragmentTransaction.addSharedElement(shareView.first, shareView.second); 256 | } 257 | } 258 | 259 | fragmentTransaction.commit(); 260 | 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/girl_0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/girl_0.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/girl_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/girl_1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/girl_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/girl_2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/girl_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/girl_3.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/girl_4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/girl_4.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/pic.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/drawable-xhdpi/set.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_pink_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_purple_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 18 | 19 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_ctrl.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 28 | 29 | 34 | 35 | 36 | 46 | 47 | 57 | 58 | 59 | 70 | 71 | 77 | 78 | 79 | 80 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_display.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_gallery.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_gallery_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 27 | 28 | 34 | 35 | 40 | 41 | 42 | 43 | 59 | 60 | 67 | 68 | 74 | 75 | 80 | 81 | 82 | 83 | 84 | 91 | 92 | 93 | 97 | 98 | 102 | 103 | 107 | 108 | 112 | 113 | 114 | 123 | 124 | 135 | 136 | 147 | 148 | 159 | 160 | 171 | 172 | 173 | 187 | 188 | 193 | 194 | 195 | 196 | 197 | 213 | 214 | 226 | 227 | 232 | 233 | 234 | 244 | 245 | 257 | 258 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_scene_end.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_scene_start.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/transition-v21/transition_settings_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/transition-v21/transition_settings_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/transition/transition_settings_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/transition/transition_settings_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/transition/transition_settings_share_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 动起来 3 | Photo 4 | Settings 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | 18 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/test/java/engineer/echo/transition/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package engineer.echo.transition; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | 18 | @Test 19 | public void print9() { 20 | System.out.println("99乘法表"); 21 | //i控制每行算式个数,j控制共有多少行。 22 | for (int i = 1, j = 1; j <= 9; i++) { 23 | System.out.printf("%d*%d=%d ", i, j, i * j); 24 | //当i = j 这一行输入结束,换行。再把i置0。 25 | if (i == j) { 26 | i = 0; 27 | j++; 28 | System.out.printf("\n"); 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.0' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | # APP Config 20 | APP_VERCODE=1 21 | APP_VERNAME=0.0.1 22 | APP_ID=engineer.echo.transition 23 | 24 | # compile 25 | MIN_SDK=19 26 | COMPILE_SDK=26 27 | TARGET_SDK=26 28 | 29 | # support 30 | APPCOMPAT_V7=26.1.0 31 | CONSTRAINT_LAYOUT=1.0.2 32 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jan 01 14:38:47 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /images/path_normal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/path_normal.gif -------------------------------------------------------------------------------- /images/path_share_element.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/path_share_element.gif -------------------------------------------------------------------------------- /images/progress.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/progress.gif -------------------------------------------------------------------------------- /images/scale.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/scale.gif -------------------------------------------------------------------------------- /images/scene_sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/scene_sort.gif -------------------------------------------------------------------------------- /images/slide_fade.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/slide_fade.gif -------------------------------------------------------------------------------- /images/transition-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/transition-0.png -------------------------------------------------------------------------------- /images/transition-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/transition-1.png -------------------------------------------------------------------------------- /images/transition_in.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/transition_in.gif -------------------------------------------------------------------------------- /images/transition_out.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pluckypan/HollyTransition/8c00d29eab6f5f16c0d9d2870f017a45fa2ab372/images/transition_out.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------