diff options
author | Trumeet <liangyuteng12345@gmail.com> | 2017-08-07 14:37:32 +0800 |
---|---|---|
committer | Trumeet <liangyuteng12345@gmail.com> | 2017-08-07 14:37:32 +0800 |
commit | 5270e353c7c1a33a4005d5517f0f8591e5914916 (patch) | |
tree | fbaa9112c2b3cd28501ef550f8e859b6268d5063 | |
parent | 37429d7aa881801a2bbf3a6111bf01d0f78d6daf (diff) | |
download | Animations-5270e353c7c1a33a4005d5517f0f8591e5914916.tar Animations-5270e353c7c1a33a4005d5517f0f8591e5914916.tar.gz Animations-5270e353c7c1a33a4005d5517f0f8591e5914916.tar.bz2 Animations-5270e353c7c1a33a4005d5517f0f8591e5914916.zip |
feat: add appear/disappear animation
23 files changed, 794 insertions, 51 deletions
@@ -69,6 +69,18 @@ Beautiful animations and views from AOSP 停止動畫: `mFingerprintAnimator.stopIconAnimation()` +## Appear / Disappear Animation + + **設置 中 確認密碼時的進入/退出動畫** (API21+) + + Activity 位於:[ConfirmLockPattern](https://android.googlesource.com/platform/packages/apps/Settings/+/master/src/com/android/settings/ConfirmLockPattern.java) + + ![AppearAnimation](https://raw.githubusercontent.com/AndroidSnippet/Animations/master/art/AppearAnimation.GIF) + + 使用方法:[參照 Demo AppearAnimationActivity](https://github.com/AndroidSnippet/Animations/tree/master/app/src/main/java/top/trumeet/snippet/aospanimation/AppearAnimationActivity.java) + + `startAnimation2d`的第一個參數 T[][] 是可以傳入 X行X列 的二維數組,比如説 TableLayout。詳見 Demo + # Licenses 使用本項目請確保您遵守 `Apache License 2.0` ``` diff --git a/app/build.gradle b/app/build.gradle index 64efa10..705f3ca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ android { buildToolsVersion "26.0.1" defaultConfig { applicationId "top.trumeet.snippet.aospanimation" - minSdkVersion 15 + minSdkVersion 16 targetSdkVersion 26 versionCode 1 versionName "1.0" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 874160c..cc612f8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,11 +6,13 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.AppCompat.Light"> - <activity android:name=".MainActivity"> + <activity android:name=".LauncherActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:name=".FingerprintIsloatedActivity" /> + <activity android:name=".AppearAnimationActivity" /> </application> </manifest> diff --git a/app/src/main/java/top/trumeet/snippet/aospanimation/AppearAnimationActivity.java b/app/src/main/java/top/trumeet/snippet/aospanimation/AppearAnimationActivity.java new file mode 100644 index 0000000..b110f68 --- /dev/null +++ b/app/src/main/java/top/trumeet/snippet/aospanimation/AppearAnimationActivity.java @@ -0,0 +1,147 @@ +package top.trumeet.snippet.aospanimation; + +import android.os.Bundle; +import android.view.View; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.widget.TableLayout; +import android.widget.TableRow; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import top.trumeet.snippet.aospanimation.library.animation.AppearAnimationCreator; +import top.trumeet.snippet.aospanimation.library.animation.AppearAnimationUtils; +import top.trumeet.snippet.aospanimation.library.animation.DisappearAnimationUtils; + +/** + * Created by Trumeet on 2017/8/7. + * @see top.trumeet.snippet.aospanimation.library.animation.AppearAnimationUtils + */ + +public class AppearAnimationActivity extends ViewAnimationActivity implements AppearAnimationCreator<Object> { + private AppearAnimationUtils mAppearAnimationUtils; + private DisappearAnimationUtils mDisappearAnimationUtils; + + private View icon_top; + private TableLayout tableLayout; + + @Override + public void onCreate (Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.appear); + icon_top = findViewById(R.id.icon_top); + tableLayout = findViewById(R.id.table); + mAppearAnimationUtils = new AppearAnimationUtils(this, + AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 2f /* translationScale */, + 1.3f /* delayScale */, AnimationUtils.loadInterpolator( + this, R.anim.linear_out_slow_in)); + mDisappearAnimationUtils = new DisappearAnimationUtils(this, + 125, 4f /* translationScale */, + 0.3f /* delayScale */, AnimationUtils.loadInterpolator( + this, R.anim.fast_out_linear_in), + new AppearAnimationUtils.RowTranslationScaler() { + @Override + public float getRowTranslationScale(int row, int numRows) { + return (float)(numRows - row) / numRows; + } + }); + startAnimation(); + } + + @Override + public void startAnimation() { + mAppearAnimationUtils.startAnimation2d(getViews() + , null, this); + } + + @Override + public void stopAnimation() { + mDisappearAnimationUtils.startAnimation2d(getViews(), + new Runnable() { + @Override + public void run() { + /* + finish(); + overridePendingTransition( + R.anim.confirm_credential_close_enter, + R.anim.confirm_credential_close_exit); + */ + } + }, this); + } + + @Override + public void onBackPressed () { + mDisappearAnimationUtils.startAnimation2d(getViews(), + new Runnable() { + @Override + public void run() { + finish(); + overridePendingTransition( + R.anim.confirm_credential_close_enter, + R.anim.confirm_credential_close_exit); + } + }, this); + } + + @Override + public CharSequence getStartText() { + return "Appear"; + } + + @Override + public CharSequence getStopText() { + return "Disappear"; + } + + private static List<TableRow> getRows (TableLayout table) { + List<TableRow> list = new ArrayList<>(table.getChildCount()); + for(int i = 0, j = table.getChildCount(); i < j; i++) { + View view = table.getChildAt(i); + if (view instanceof TableRow) { + TableRow row = (TableRow) view; + list.add(row); + } + } + return list; + } + + private ArrayList<ArrayList<Object>> getAllChildren() { + ArrayList<ArrayList<Object>> result = new ArrayList<>(); + result.add(new ArrayList<Object>(Collections.singletonList(icon_top))); + List<TableRow> list = getRows(tableLayout); + List<List<View>> radios = new ArrayList<>(list.size() * 5); + for (TableRow row : list) { + List<View> views = new ArrayList<>(row.getChildCount()); + for (int i = 0; i < row.getChildCount(); i ++) { + views.add(row.getChildAt(i)); + } + radios.add(views); + } + for (int i = 0; i < radios.size(); i++) { + ArrayList<Object> row = new ArrayList<>(); + row.addAll(radios.get(i)); + result.add(row); + } + return result; + } + private Object[][] getViews () { + ArrayList<ArrayList<Object>> result = getAllChildren(); + Object[][] resultArr = new Object[result.size()][5]; + for (int i = 0; i < result.size(); i++) { + ArrayList<Object> row = result.get(i); + for (int j = 0; j < row.size(); j++) { + resultArr[i][j] = row.get(j); + } + } + return resultArr; + } + + @Override + public void createAnimation(Object obj, long delay, long duration, float translationY, boolean appearing, Interpolator interpolator, Runnable finishListener) { + mAppearAnimationUtils.createAnimation((View) obj, delay, duration, translationY, + appearing, interpolator, finishListener); + } +} diff --git a/app/src/main/java/top/trumeet/snippet/aospanimation/MainActivity.java b/app/src/main/java/top/trumeet/snippet/aospanimation/FingerprintIsloatedActivity.java index e1cf783..611e273 100644 --- a/app/src/main/java/top/trumeet/snippet/aospanimation/MainActivity.java +++ b/app/src/main/java/top/trumeet/snippet/aospanimation/FingerprintIsloatedActivity.java @@ -4,11 +4,8 @@ import android.annotation.SuppressLint; import android.content.res.ColorStateList; import android.os.Bundle; import android.support.graphics.drawable.AnimatedVectorDrawableCompat; -import android.support.v7.app.AppCompatActivity; import android.support.v7.content.res.AppCompatResources; import android.support.v7.widget.AppCompatImageView; -import android.view.Menu; -import android.view.MenuItem; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -19,18 +16,19 @@ import top.trumeet.snippet.aospanimation.library.drawables.LoopAnimatedVectorDra * Demo activity */ -public class MainActivity extends AppCompatActivity { +public class FingerprintIsloatedActivity extends ViewAnimationActivity { private LoopAnimatedVectorDrawableCompat mFingerprintAnimator; + @SuppressLint("RestrictedApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + setContentView(R.layout.fingerprint_isloated); final AppCompatImageView fingerprintIsolated = findViewById(R.id.fingerprint_animator); mFingerprintAnimator = new LoopAnimatedVectorDrawableCompat(AnimatedVectorDrawableCompat.create(this, R.drawable.enrollment_fingerprint_isolated_animation)); - fingerprintIsolated.setBackgroundDrawable(AppCompatResources.getDrawable(MainActivity.this + fingerprintIsolated.setBackgroundDrawable(AppCompatResources.getDrawable(FingerprintIsloatedActivity.this , top.trumeet.snippet.aospanimation.library.R.drawable.fp_illustration_enrollment)); fingerprintIsolated.setSupportBackgroundTintList(ColorStateList.valueOf(getResources().getColor(top.trumeet.snippet.aospanimation.library.R.color.fingerprint_indicator_background_resting))); @@ -39,7 +37,7 @@ public class MainActivity extends AppCompatActivity { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { if (b) { - fingerprintIsolated.setBackgroundDrawable(AppCompatResources.getDrawable(MainActivity.this + fingerprintIsolated.setBackgroundDrawable(AppCompatResources.getDrawable(FingerprintIsloatedActivity.this , top.trumeet.snippet.aospanimation.library.R.drawable.fp_illustration_enrollment)); fingerprintIsolated.setSupportBackgroundTintList(ColorStateList.valueOf(getResources().getColor(top.trumeet.snippet.aospanimation.library.R.color.fingerprint_indicator_background_resting))); } else { @@ -64,24 +62,12 @@ public class MainActivity extends AppCompatActivity { } @Override - public boolean onCreateOptionsMenu (Menu menu) { - menu.add(0, 0, 0, "Start") - .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - menu.add(0, 1, 0, "Stop") - .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - return true; + public void startAnimation() { + mFingerprintAnimator.startAnimation(); } @Override - public boolean onOptionsItemSelected (MenuItem item) { - switch (item.getItemId()) { - case 0 : - mFingerprintAnimator.startAnimation(); - return true; - case 1 : - mFingerprintAnimator.stopAnimation(); - return true; - } - return false; + public void stopAnimation() { + mFingerprintAnimator.stopAnimation(); } } diff --git a/app/src/main/java/top/trumeet/snippet/aospanimation/LauncherActivity.java b/app/src/main/java/top/trumeet/snippet/aospanimation/LauncherActivity.java new file mode 100644 index 0000000..c24f0d9 --- /dev/null +++ b/app/src/main/java/top/trumeet/snippet/aospanimation/LauncherActivity.java @@ -0,0 +1,53 @@ +package top.trumeet.snippet.aospanimation; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.SimpleAdapter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Trumeet on 2017/8/7. + * @see FingerprintIsloatedActivity + * @see AppearAnimationActivity + */ + +public class LauncherActivity extends AppCompatActivity { + private static final Class[] DATA = new Class[] { + FingerprintIsloatedActivity.class, + AppearAnimationActivity.class + }; + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ArrayList<Map<String, Object>> list = new ArrayList<>(DATA.length); + for (Class clz : DATA) { + Map<String, Object> map = new HashMap<>(1); + map.put("Title", clz.getSimpleName()); + map.put("Class", clz); + list.add(map); + } + + ListView listView = new ListView(this); + listView.setAdapter(new SimpleAdapter(this, list, + android.R.layout.simple_list_item_1, new String[]{"Title"}, new int[]{android.R.id.text1})); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + @SuppressWarnings("unchecked") + public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { + Map<String, Object> data = (Map<String, Object>)adapterView.getItemAtPosition(i); + startActivity(new Intent(LauncherActivity.this, + (Class)data.get("Class"))); + } + }); + setContentView(listView); + } +} diff --git a/app/src/main/java/top/trumeet/snippet/aospanimation/ViewAnimationActivity.java b/app/src/main/java/top/trumeet/snippet/aospanimation/ViewAnimationActivity.java new file mode 100644 index 0000000..a91f566 --- /dev/null +++ b/app/src/main/java/top/trumeet/snippet/aospanimation/ViewAnimationActivity.java @@ -0,0 +1,46 @@ +package top.trumeet.snippet.aospanimation; + +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +/** + * Created by Trumeet on 2017/8/7. + * A abstract activity, include "Start" and "Stop" animation menu + */ + +public abstract class ViewAnimationActivity extends AppCompatActivity { + + @Override + public boolean onCreateOptionsMenu (Menu menu) { + menu.add(0, 0, 0, getStartText()) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menu.add(0, 1, 0, getStopText()) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + return true; + } + + @Override + public boolean onOptionsItemSelected (MenuItem item) { + switch (item.getItemId()) { + case 0 : + startAnimation(); + return true; + case 1 : + stopAnimation(); + return true; + } + return false; + } + + public abstract void startAnimation(); + public abstract void stopAnimation(); + + public CharSequence getStartText () { + return "Start"; + } + + public CharSequence getStopText () { + return "Stop"; + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 1be4af6..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <LinearLayout - android:gravity="center_horizontal" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <android.support.v7.widget.AppCompatImageView - android:id="@+id/fingerprint_animator" - android:layout_width="@dimen/fingerprint_animation_size" - android:layout_height="@dimen/fingerprint_animation_size" - app:background="@drawable/fp_illustration_enrollment" - app:backgroundTint="@color/fingerprint_indicator_background_resting" - /> - <CheckBox - android:id="@+id/check_show_background" - android:checked="true" - android:text="Show Background" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - </LinearLayout> -</ScrollView>
\ No newline at end of file diff --git a/app/src/main/res/layout/appear.xml b/app/src/main/res/layout/appear.xml new file mode 100644 index 0000000..4913ef8 --- /dev/null +++ b/app/src/main/res/layout/appear.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:gravity="center_horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/icon_top" + android:layout_marginTop="16dp" + android:contentDescription="@null" + android:src="@mipmap/ic_launcher_round" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + <TableLayout + android:id="@+id/table" + android:layout_gravity="center" + android:layout_marginTop="16dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <TableRow> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + </TableRow> + <TableRow> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + </TableRow> + <TableRow> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + </TableRow> + <TableRow> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + </TableRow> + <TableRow> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + <RadioButton /> + </TableRow> + </TableLayout> +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fingerprint_isloated.xml b/app/src/main/res/layout/fingerprint_isloated.xml new file mode 100644 index 0000000..961f3bd --- /dev/null +++ b/app/src/main/res/layout/fingerprint_isloated.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout android:gravity="center_horizontal" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <android.support.v7.widget.AppCompatImageView + android:id="@+id/fingerprint_animator" + android:layout_width="@dimen/fingerprint_animation_size" + android:layout_height="@dimen/fingerprint_animation_size" + app:background="@drawable/fp_illustration_enrollment" + app:backgroundTint="@color/fingerprint_indicator_background_resting" + /> + <CheckBox + android:id="@+id/check_show_background" + android:checked="true" + android:text="Show Background" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> +</LinearLayout>
\ No newline at end of file diff --git a/art/AppearAnimation.gif b/art/AppearAnimation.gif Binary files differnew file mode 100644 index 0000000..03c2031 --- /dev/null +++ b/art/AppearAnimation.gif diff --git a/art/appear_animation_demo.mp4 b/art/appear_animation_demo.mp4 Binary files differnew file mode 100644 index 0000000..54ad2e7 --- /dev/null +++ b/art/appear_animation_demo.mp4 diff --git a/build.gradle b/build.gradle index 540ef2b..c1344f9 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,11 @@ buildscript { } allprojects { + gradle.projectsEvaluated { + tasks.withType(JavaCompile) { + options.compilerArgs.add('-Xbootclasspath/p:app\\lib\\classes.jar') + } + } repositories { google() jcenter() diff --git a/library/build.gradle b/library/build.gradle index fe4f348..bdb8c18 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -8,7 +8,7 @@ android { defaultConfig { - minSdkVersion 15 + minSdkVersion 16 targetSdkVersion 26 versionCode 1 versionName "1.0" @@ -31,4 +31,5 @@ dependencies { androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' compile 'com.android.support:appcompat-v7:26.0.0' + provided files('lib/classes.jar') } diff --git a/library/lib/classes.jar b/library/lib/classes.jar Binary files differnew file mode 100644 index 0000000..1036492 --- /dev/null +++ b/library/lib/classes.jar diff --git a/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/AppearAnimationCreator.java b/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/AppearAnimationCreator.java new file mode 100644 index 0000000..c4d38ff --- /dev/null +++ b/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/AppearAnimationCreator.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package top.trumeet.snippet.aospanimation.library.animation; + +import android.view.animation.Interpolator; + +/** + * An interface which can create animations when starting an appear animation with + * {@link AppearAnimationUtils} + */ +public interface AppearAnimationCreator<T> { + void createAnimation(T animatedObject, long delay, long duration, + float translationY, boolean appearing, Interpolator interpolator, + Runnable finishListener); +} diff --git a/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/AppearAnimationUtils.java b/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/AppearAnimationUtils.java new file mode 100644 index 0000000..75237a6 --- /dev/null +++ b/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/AppearAnimationUtils.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package top.trumeet.snippet.aospanimation.library.animation; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.annotation.TargetApi; +import android.content.Context; +import android.view.RenderNodeAnimator; +import android.view.View; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import top.trumeet.snippet.aospanimation.library.R; + +/** + * A class to make nice appear transitions for views in a tabular layout. + * + * TODO: use non-hidden apis and support pre-lollipop roms + */ +@TargetApi(21) +public class AppearAnimationUtils implements AppearAnimationCreator<View> { + + public static final long DEFAULT_APPEAR_DURATION = 220; + + private final Interpolator mInterpolator; + private final float mStartTranslation; + private final AppearAnimationProperties mProperties = new AppearAnimationProperties(); + protected final float mDelayScale; + private final long mDuration; + protected RowTranslationScaler mRowTranslationScaler; + protected boolean mAppearing; + + public AppearAnimationUtils(Context ctx) { + this(ctx, DEFAULT_APPEAR_DURATION, + 1.0f, 1.0f, + AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in)); + } + + public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor, + float delayScaleFactor, Interpolator interpolator) { + mInterpolator = interpolator; + mStartTranslation = ctx.getResources().getDimensionPixelOffset( + R.dimen.appear_y_translation_start) * translationScaleFactor; + mDelayScale = delayScaleFactor; + mDuration = duration; + mAppearing = true; + } + + public void startAnimation2d(View[][] objects, final Runnable finishListener) { + startAnimation2d(objects, finishListener, this); + } + + public void startAnimation(View[] objects, final Runnable finishListener) { + startAnimation(objects, finishListener, this); + } + + public <T> void startAnimation2d(T[][] objects, final Runnable finishListener, + AppearAnimationCreator<T> creator) { + AppearAnimationProperties properties = getDelays(objects); + startAnimations(properties, objects, finishListener, creator); + } + + public <T> void startAnimation(T[] objects, final Runnable finishListener, + AppearAnimationCreator<T> creator) { + AppearAnimationProperties properties = getDelays(objects); + startAnimations(properties, objects, finishListener, creator); + } + + private <T> void startAnimations(AppearAnimationProperties properties, T[] objects, + final Runnable finishListener, AppearAnimationCreator<T> creator) { + if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { + finishListener.run(); + return; + } + for (int row = 0; row < properties.delays.length; row++) { + long[] columns = properties.delays[row]; + long delay = columns[0]; + Runnable endRunnable = null; + if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) { + endRunnable = finishListener; + } + float translationScale = mRowTranslationScaler != null + ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length) + : 1f; + float translation = translationScale * mStartTranslation; + creator.createAnimation(objects[row], delay, mDuration, + mAppearing ? translation : -translation, + mAppearing, mInterpolator, endRunnable); + } + } + + private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects, + final Runnable finishListener, AppearAnimationCreator<T> creator) { + if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { + if (finishListener != null) finishListener.run(); + return; + } + for (int row = 0; row < properties.delays.length; row++) { + long[] columns = properties.delays[row]; + float translationScale = mRowTranslationScaler != null + ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length) + : 1f; + float translation = translationScale * mStartTranslation; + for (int col = 0; col < columns.length; col++) { + long delay = columns[col]; + Runnable endRunnable = null; + if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) { + endRunnable = finishListener; + } + creator.createAnimation(objects[row][col], delay, mDuration, + mAppearing ? translation : -translation, + mAppearing, mInterpolator, endRunnable); + } + } + } + + private <T> AppearAnimationProperties getDelays(T[] items) { + long maxDelay = -1; + mProperties.maxDelayColIndex = -1; + mProperties.maxDelayRowIndex = -1; + mProperties.delays = new long[items.length][]; + for (int row = 0; row < items.length; row++) { + mProperties.delays[row] = new long[1]; + long delay = calculateDelay(row, 0); + mProperties.delays[row][0] = delay; + if (items[row] != null && delay > maxDelay) { + maxDelay = delay; + mProperties.maxDelayColIndex = 0; + mProperties.maxDelayRowIndex = row; + } + } + return mProperties; + } + + private <T> AppearAnimationProperties getDelays(T[][] items) { + long maxDelay = -1; + mProperties.maxDelayColIndex = -1; + mProperties.maxDelayRowIndex = -1; + mProperties.delays = new long[items.length][]; + for (int row = 0; row < items.length; row++) { + T[] columns = items[row]; + mProperties.delays[row] = new long[columns.length]; + for (int col = 0; col < columns.length; col++) { + long delay = calculateDelay(row, col); + mProperties.delays[row][col] = delay; + if (items[row][col] != null && delay > maxDelay) { + maxDelay = delay; + mProperties.maxDelayColIndex = col; + mProperties.maxDelayRowIndex = row; + } + } + } + return mProperties; + } + + protected long calculateDelay(int row, int col) { + return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale); + } + + public Interpolator getInterpolator() { + return mInterpolator; + } + + public float getStartTranslation() { + return mStartTranslation; + } + + @Override + public void createAnimation(final View view, long delay, long duration, float translationY, + boolean appearing, Interpolator interpolator, final Runnable endRunnable) { + if (view != null) { + view.setAlpha(appearing ? 0f : 1.0f); + view.setTranslationY(appearing ? translationY : 0); + Animator alphaAnim; + float targetAlpha = appearing ? 1f : 0f; + if (view.isHardwareAccelerated()) { + // TODO: Use non-hidden and compat API + RenderNodeAnimator alphaAnimRt = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, + targetAlpha); + alphaAnimRt.setTarget(view); + alphaAnim = alphaAnimRt; + } else { + alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), targetAlpha); + } + alphaAnim.setInterpolator(interpolator); + alphaAnim.setDuration(duration); + alphaAnim.setStartDelay(delay); + if (view.hasOverlappingRendering()) { + view.setLayerType(View.LAYER_TYPE_HARDWARE, null); + alphaAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setLayerType(View.LAYER_TYPE_NONE, null); + } + }); + } + if (endRunnable != null) { + alphaAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + endRunnable.run(); + } + }); + } + alphaAnim.start(); + startTranslationYAnimation(view, delay, duration, appearing ? 0 : translationY, + interpolator); + } + } + + public static void startTranslationYAnimation(View view, long delay, long duration, + float endTranslationY, Interpolator interpolator) { + Animator translationAnim; + if (view.isHardwareAccelerated()) { + RenderNodeAnimator translationAnimRt = new RenderNodeAnimator( + RenderNodeAnimator.TRANSLATION_Y, endTranslationY); + translationAnimRt.setTarget(view); + translationAnim = translationAnimRt; + } else { + translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, + view.getTranslationY(), endTranslationY); + } + translationAnim.setInterpolator(interpolator); + translationAnim.setDuration(duration); + translationAnim.setStartDelay(delay); + translationAnim.start(); + } + + public class AppearAnimationProperties { + public long[][] delays; + public int maxDelayRowIndex; + public int maxDelayColIndex; + } + + public interface RowTranslationScaler { + float getRowTranslationScale(int row, int numRows); + } +} diff --git a/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/DisappearAnimationUtils.java b/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/DisappearAnimationUtils.java new file mode 100644 index 0000000..42c99e1 --- /dev/null +++ b/library/src/main/java/top/trumeet/snippet/aospanimation/library/animation/DisappearAnimationUtils.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package top.trumeet.snippet.aospanimation.library.animation; + +import android.content.Context; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +/** + * A class to make nice disappear transitions for views in a tabular layout. + */ +public class DisappearAnimationUtils extends AppearAnimationUtils { + + public DisappearAnimationUtils(Context ctx) { + this(ctx, DEFAULT_APPEAR_DURATION, + 1.0f, 1.0f, + AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_linear_in)); + } + + public DisappearAnimationUtils(Context ctx, long duration, float translationScaleFactor, + float delayScaleFactor, Interpolator interpolator) { + this(ctx, duration, translationScaleFactor, delayScaleFactor, interpolator, + ROW_TRANSLATION_SCALER); + } + + public DisappearAnimationUtils(Context ctx, long duration, float translationScaleFactor, + float delayScaleFactor, Interpolator interpolator, RowTranslationScaler rowScaler) { + super(ctx, duration, translationScaleFactor, delayScaleFactor, interpolator); + mRowTranslationScaler = rowScaler; + mAppearing = false; + } + + protected long calculateDelay(int row, int col) { + return (long) ((row * 60 + col * (Math.pow(row, 0.4) + 0.4) * 10) * mDelayScale); + } + + private static final RowTranslationScaler ROW_TRANSLATION_SCALER = new RowTranslationScaler() { + + @Override + public float getRowTranslationScale(int row, int numRows) { + return (float) (Math.pow((numRows - row), 2) / numRows); + } + }; +} diff --git a/library/src/main/res/anim/confirm_credential_close_enter.xml b/library/src/main/res/anim/confirm_credential_close_enter.xml new file mode 100644 index 0000000..fcc3241 --- /dev/null +++ b/library/src/main/res/anim/confirm_credential_close_enter.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="top"> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:interpolator="@*android:interpolator/decelerate_quart" + android:fillEnabled="true" + android:fillBefore="false" android:fillAfter="true" + android:duration="200"/> + <translate android:fromYDelta="8%" android:toYDelta="0" + android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" + android:interpolator="@*android:interpolator/decelerate_quint" + android:duration="350"/> +</set> diff --git a/library/src/main/res/anim/confirm_credential_close_exit.xml b/library/src/main/res/anim/confirm_credential_close_exit.xml new file mode 100644 index 0000000..5afd8b2 --- /dev/null +++ b/library/src/main/res/anim/confirm_credential_close_exit.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<alpha xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:interpolator/linear_out_slow_in" + android:fromAlpha="1.0" android:toAlpha="0.0" + android:duration="350" /> diff --git a/library/src/main/res/anim/fast_out_linear_in.xml b/library/src/main/res/anim/fast_out_linear_in.xml new file mode 100644 index 0000000..19f95a6 --- /dev/null +++ b/library/src/main/res/anim/fast_out_linear_in.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2014 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0.4" + android:controlY1="0" + android:controlX2="1" + android:controlY2="1"/> diff --git a/library/src/main/res/anim/linear_out_slow_in.xml b/library/src/main/res/anim/linear_out_slow_in.xml new file mode 100644 index 0000000..83fc223 --- /dev/null +++ b/library/src/main/res/anim/linear_out_slow_in.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2014 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0" + android:controlY1="0" + android:controlX2="0.2" + android:controlY2="1"/> diff --git a/library/src/main/res/values/dimens.xml b/library/src/main/res/values/dimens.xml index 7a84eac..6d5e6c4 100644 --- a/library/src/main/res/values/dimens.xml +++ b/library/src/main/res/values/dimens.xml @@ -1,4 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="fingerprint_animation_size">88dp</dimen> + + <!-- The y translation to apply at the start in appear animations. --> + <dimen name="appear_y_translation_start">32dp</dimen> </resources>
\ No newline at end of file |