diff --git a/.gitignore b/.gitignore index ccf2efe..1286e42 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,7 @@ proguard/ # Log Files *.log + +#IntelliJ files +*.iml +.idea/ diff --git a/DEV/ic_launcher/res/drawable-hdpi/ic_launcher.png b/DEV/ic_launcher/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..a732bf4 Binary files /dev/null and b/DEV/ic_launcher/res/drawable-hdpi/ic_launcher.png differ diff --git a/DEV/ic_launcher/res/drawable-mdpi/ic_launcher.png b/DEV/ic_launcher/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..9e5bcda Binary files /dev/null and b/DEV/ic_launcher/res/drawable-mdpi/ic_launcher.png differ diff --git a/DEV/ic_launcher/res/drawable-xhdpi/ic_launcher.png b/DEV/ic_launcher/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..0aaf206 Binary files /dev/null and b/DEV/ic_launcher/res/drawable-xhdpi/ic_launcher.png differ diff --git a/DEV/ic_launcher/res/drawable-xxhdpi/ic_launcher.png b/DEV/ic_launcher/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..3d70d07 Binary files /dev/null and b/DEV/ic_launcher/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/DEV/ic_launcher/res/drawable-xxxhdpi/ic_launcher.png b/DEV/ic_launcher/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b8ff8f9 Binary files /dev/null and b/DEV/ic_launcher/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/DEV/ic_launcher/web_hi_res_512.png b/DEV/ic_launcher/web_hi_res_512.png new file mode 100644 index 0000000..38701c9 Binary files /dev/null and b/DEV/ic_launcher/web_hi_res_512.png differ diff --git a/DEV/original/DefaultItemAnimator.java b/DEV/original/DefaultItemAnimator.java new file mode 100644 index 0000000..0809efe --- /dev/null +++ b/DEV/original/DefaultItemAnimator.java @@ -0,0 +1,645 @@ +/* + * 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. + */ +package android.support.v7.widget; + +import android.support.v4.animation.AnimatorCompatHelper; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; + +/** + * This implementation of {@link RecyclerView.ItemAnimator} provides basic + * animations on remove, add, and move events that happen to the items in + * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default. + * + * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator) + */ +public class DefaultItemAnimator extends SimpleItemAnimator { + private static final boolean DEBUG = false; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + private ArrayList> mAdditionsList = new ArrayList<>(); + private ArrayList> mMovesList = new ArrayList<>(); + private ArrayList> mChangesList = new ArrayList<>(); + + private ArrayList mAddAnimations = new ArrayList<>(); + private ArrayList mMoveAnimations = new ArrayList<>(); + private ArrayList mRemoveAnimations = new ArrayList<>(); + private ArrayList mChangeAnimations = new ArrayList<>(); + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList<>(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList<>(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList<>(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + resetAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mRemoveAnimations.add(holder); + animation.setDuration(getRemoveDuration()) + .alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateAdd(final ViewHolder holder) { + resetAnimation(holder); + ViewCompat.setAlpha(holder.itemView, 0); + mPendingAdditions.add(holder); + return true; + } + + private void animateAddImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mAddAnimations.add(holder); + animation.alpha(1).setDuration(getAddDuration()). + setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchAddStarting(holder); + } + @Override + public void onAnimationCancel(View view) { + ViewCompat.setAlpha(view, 1); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += ViewCompat.getTranslationX(holder.itemView); + fromY += ViewCompat.getTranslationY(holder.itemView); + resetAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + ViewCompat.setTranslationX(view, -deltaX); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, -deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + ViewCompat.animate(view).translationX(0); + } + if (deltaY != 0) { + ViewCompat.animate(view).translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchMoveStarting(holder); + } + @Override + public void onAnimationCancel(View view) { + if (deltaX != 0) { + ViewCompat.setTranslationX(view, 0); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, 0); + } + } + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + if (oldHolder == newHolder) { + // Don't know how to run change animations when the same view holder is re-used. + // run a move animation to handle position changes. + return animateMove(oldHolder, fromX, fromY, toX, toY); + } + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + ViewCompat.setAlpha(newHolder.itemView, 0); + } + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + private void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration( + getChangeDuration()); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(View view) { + oldViewAnim.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + ViewCompat.setTranslationY(view, 0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). + alpha(1).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + @Override + public void onAnimationEnd(View view) { + newViewAnimation.setListener(null); + ViewCompat.setAlpha(newView, 1); + ViewCompat.setTranslationX(newView, 0); + ViewCompat.setTranslationY(newView, 0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + ViewCompat.setAlpha(item.itemView, 1); + ViewCompat.setTranslationX(item.itemView, 0); + ViewCompat.setTranslationY(item.itemView, 0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + ViewCompat.animate(view).cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + //noinspection PointlessBooleanExpression,ConstantConditions + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + private void resetAnimation(ViewHolder holder) { + AnimatorCompatHelper.clearInterpolator(holder.itemView); + endAnimation(holder); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() || + !mPendingChanges.isEmpty() || + !mPendingMoves.isEmpty() || + !mPendingRemovals.isEmpty() || + !mMoveAnimations.isEmpty() || + !mRemoveAnimations.isEmpty() || + !mAddAnimations.isEmpty() || + !mChangeAnimations.isEmpty() || + !mMovesList.isEmpty() || + !mAdditionsList.isEmpty() || + !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + ViewCompat.animate(viewHolders.get(i).itemView).cancel(); + } + } + + private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { + @Override + public void onAnimationStart(View view) {} + + @Override + public void onAnimationEnd(View view) {} + + @Override + public void onAnimationCancel(View view) {} + } +} diff --git a/LICENSE b/LICENSE index 8dada3e..8f71f43 100644 --- a/LICENSE +++ b/LICENSE @@ -199,3 +199,4 @@ 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. + diff --git a/README.md b/README.md index 2703f6b..82236f5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,56 @@ -# ItemAnimators -ItemAnimators: Supercharged Animators for your RecyclerView +#ItemAnimators [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.mikepenz/itemanimators/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/com.mikepenz/itemanimators) [![Join the chat at https://gitter.im/mikepenz/itemanimators](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mikepenz/itemanimators?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +The **ItemAnimators** library comes with a huge collections of pre-created Animators for your RecyclerView. +It was created so developers can easly animate their RecyclerView. It also takes care about correctly handling all view states so you don't have to. + +> **DISCLAIMER**: this library does not animate items on scroll, just when added, removed, moved, or changed + +#Preview +##Screenshots + +#Include in your project +##Using Maven +```javascript +compile('com.mikepenz:itemanimators:0.1.0-SNAPSHOT@aar') { + transitive = true +} + +//only on the SNAPSHOT repo right now: +repositories { + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } +} +``` + +##How to use +```java +//just provide the animator to your RecyclerView +mRecyclerView.setItemAnimator(new ScaleUpAnimator()); + +//now it will automatically take care of all animations when you add, remove, chanage, move items +//It is very important that you use the correct notify methods if items were changed, otherwise the adapter can't animate the items +//Read more: http://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#notifyDataSetChanged() +//If you want those things out of the box have a look at the **FastAdapter** it handles everything correctly for you +//https://github.com/mikepenz/FastAdapter +``` + +#Developed By + +* Mike Penz + * [mikepenz.com](http://mikepenz.com) - + * [paypal.me/mikepenz](http://paypal.me/mikepenz) + +#License + + Copyright 2016 Mike Penz + + 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. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..26afe04 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,74 @@ +apply plugin: 'com.android.application' +//wrap with try and catch so the build is working even if the signing stuff is missing +try { + apply from: '../../../signing.gradle' +} catch (ex) { +} + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion 11 + targetSdkVersion 23 + versionCode 10 + versionName '0.1.0-SNAPSHOT' + + applicationVariants.all { variant -> + variant.outputs.each { output -> + def file = output.outputFile + def fileName = file.name.replace(".apk", "-v" + versionName + "-c" + versionCode + ".apk") + output.outputFile = new File(file.parentFile, fileName) + } + } + } + buildTypes { + debug { + applicationIdSuffix ".debug" + versionNameSuffix "-DEBUG" + try { + signingConfig signingConfigs.debug + } catch (ex) { + } + minifyEnabled false + } + release { + try { + signingConfig signingConfigs.release + } catch (ex) { + } + zipAlignEnabled true + minifyEnabled false + } + } + lintOptions { + abortOnError false + } +} + +dependencies { + compile project(':library') + + //used to generate the drawer on the left + //https://github.com/mikepenz/MaterialDrawer + compile('com.mikepenz:materialdrawer:5.0.0.fastAdapter.b5-SNAPSHOT@aar') { + transitive = true + exclude module: "itemanimators" + } + //used to generate the Open Source section + //https://github.com/mikepenz/AboutLibraries + compile('com.mikepenz:aboutlibraries:5.3.4@aar') { + transitive = true + } + //used to display the icons in the drawer + //https://github.com/mikepenz/Android-Iconics + compile 'com.mikepenz:material-design-iconic-typeface:2.2.0.1@aar' + + //https://github.com/JakeWharton/butterknife + compile 'com.jakewharton:butterknife:7.0.1' + + //used to load the images in the ImageListSample + //https://github.com/bumptech/glide + compile 'com.github.bumptech.glide:glide:3.6.1' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..2ecd411 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Entwicklung/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..443c1d2 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/mikepenz/itemanimators/app/SampleActivity.java b/app/src/main/java/com/mikepenz/itemanimators/app/SampleActivity.java new file mode 100755 index 0000000..4462a9a --- /dev/null +++ b/app/src/main/java/com/mikepenz/itemanimators/app/SampleActivity.java @@ -0,0 +1,242 @@ +package com.mikepenz.itemanimators.app; + +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; + +import com.mikepenz.aboutlibraries.Libs; +import com.mikepenz.aboutlibraries.LibsBuilder; +import com.mikepenz.fastadapter.FastAdapter; +import com.mikepenz.fastadapter.IItem; +import com.mikepenz.fastadapter.adapters.ItemAdapter; +import com.mikepenz.iconics.IconicsDrawable; +import com.mikepenz.itemanimators.AlphaInAnimator; +import com.mikepenz.itemanimators.BaseItemAnimator; +import com.mikepenz.itemanimators.ScaleUpAnimator; +import com.mikepenz.itemanimators.ScaleXAnimator; +import com.mikepenz.itemanimators.ScaleYAnimator; +import com.mikepenz.itemanimators.SlideDownAlphaAnimator; +import com.mikepenz.itemanimators.SlideLeftAlphaAnimator; +import com.mikepenz.itemanimators.SlideRightAlphaAnimator; +import com.mikepenz.itemanimators.SlideUpAlphaAnimator; +import com.mikepenz.itemanimators.app.dummy.ImageDummyData; +import com.mikepenz.itemanimators.app.items.ImageItem; +import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic; +import com.mikepenz.materialdrawer.Drawer; +import com.mikepenz.materialdrawer.DrawerBuilder; +import com.mikepenz.materialdrawer.model.DividerDrawerItem; +import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; +import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; + +import java.util.List; + +public class SampleActivity extends AppCompatActivity { + enum Type { + FadeIn(new AlphaInAnimator()), + ScaleUp(new ScaleUpAnimator()), + ScaleX(new ScaleXAnimator()), + ScaleY(new ScaleYAnimator()), + SlideDownAlpha(new SlideDownAlphaAnimator()), + SlideLeftAlpha(new SlideLeftAlphaAnimator()), + SlideRightAlpha(new SlideRightAlphaAnimator()), + SlideUpAlpha(new SlideUpAlphaAnimator()); + + private BaseItemAnimator mAnimator; + + Type(BaseItemAnimator animator) { + mAnimator = animator; + } + + public BaseItemAnimator getAnimator() { + return mAnimator; + } + } + + //save our header or result + private Drawer mResult = null; + //our rv + RecyclerView mRecyclerView; + //save our FastAdapter + private FastAdapter mFastAdapter; + //save our FastAdapter + private ItemAdapter mItemAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sample); + + //improve ui + findViewById(android.R.id.content).setSystemUiVisibility(findViewById(android.R.id.content).getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + + // Handle Toolbar + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setTitle(""); + + //Create the drawer + mResult = new DrawerBuilder() + .withActivity(this) + .withToolbar(toolbar) + .withHasStableIds(true) + .withSavedInstance(savedInstanceState) + .withShowDrawerOnFirstLaunch(true) + .addDrawerItems( + new DividerDrawerItem(), + new PrimaryDrawerItem().withName(R.string.open_source).withSelectable(false).withIdentifier(100).withIcon(MaterialDesignIconic.Icon.gmi_github) + ) + .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { + @Override + public boolean onItemClick(View view, int position, IDrawerItem drawerItem) { + if (drawerItem != null) { + Intent intent = null; + if (drawerItem.getIdentifier() == 100) { + intent = new LibsBuilder() + .withFields(R.string.class.getFields()) + .withActivityTitle(getString(R.string.open_source)) + .withActivityStyle(Libs.ActivityStyle.LIGHT) + .intent(SampleActivity.this); + } + if (intent != null) { + SampleActivity.this.startActivity(intent); + } + } + return false; + } + }) + .build(); + + //create our FastAdapter which will manage everything + mFastAdapter = new FastAdapter(); + mFastAdapter.withMultiSelect(true); + mFastAdapter.withMultiSelectOnLongClick(false); + //create our ItemAdapter which will host our items + mItemAdapter = new ItemAdapter(); + + //configure our fastAdapter + //get our recyclerView and do basic setup + mRecyclerView = (RecyclerView) findViewById(R.id.rv); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mRecyclerView.setItemAnimator(new ScaleUpAnimator()); + mRecyclerView.setAdapter(mItemAdapter.wrap(mFastAdapter)); + + //add some dummy data + mItemAdapter.add(ImageDummyData.getImages()); + + //restore selections (this has to be done after the items were added + mFastAdapter.withSavedInstanceState(savedInstanceState); + + + /** + * selection spinner for the different animtors + */ + Spinner spinner = (Spinner) findViewById(R.id.spinner_nav); + ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1); + for (Type type : Type.values()) { + spinnerAdapter.add(type.name()); + } + spinner.setAdapter(spinnerAdapter); + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mRecyclerView.setItemAnimator(Type.values()[position].getAnimator()); + mRecyclerView.getItemAnimator().setAddDuration(500); + mRecyclerView.getItemAnimator().setRemoveDuration(500); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu, menu); + menu.findItem(R.id.item_add).setIcon(new IconicsDrawable(this, MaterialDesignIconic.Icon.gmi_plus_square).color(Color.BLACK).actionBar()); + menu.findItem(R.id.item_delete).setIcon(new IconicsDrawable(this, MaterialDesignIconic.Icon.gmi_minus_square).color(Color.BLACK).actionBar()); + menu.findItem(R.id.item_change).setIcon(new IconicsDrawable(this, MaterialDesignIconic.Icon.gmi_settings_square).color(Color.BLACK).actionBar()); + menu.findItem(R.id.item_move).setIcon(new IconicsDrawable(this, MaterialDesignIconic.Icon.gmi_format_valign_bottom).color(Color.BLACK).actionBar()); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + //find out the current visible position + int position = 0; + if (mRecyclerView.getLayoutManager() instanceof LinearLayoutManager) { + position = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); + } else if (mRecyclerView.getLayoutManager() instanceof GridLayoutManager) { + position = ((GridLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); + } + + //handle the menu item click + switch (item.getItemId()) { + case R.id.item_add: + if (mRecyclerView.getLayoutManager() instanceof LinearLayoutManager) { + position = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); + } else if (mRecyclerView.getLayoutManager() instanceof GridLayoutManager) { + position = ((GridLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); + } + mItemAdapter.add(position + 1, ImageDummyData.getDummyItem()); + return true; + case R.id.item_change: + for (Integer pos : (Iterable) mFastAdapter.getSelections()) { + ImageItem i = (ImageItem) mItemAdapter.getItem(pos); + i.withName("CHANGED"); + i.withDescription("This item was modified"); + mItemAdapter.set(pos, i); + } + return true; + case R.id.item_move: + List items = mItemAdapter.getAdapterItems(); + if (items.size() > position + 3) { + IItem i = (IItem) items.get(position + 1); + items.remove(position + 1); + items.add(position + 3, i); + mFastAdapter.notifyAdapterItemMoved(position + 1, position + 3); + } + return true; + case R.id.item_delete: + mFastAdapter.deleteAllSelectedItems(); + return true; + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + //add the values which need to be saved from the drawer to the bundle + outState = mResult.saveInstanceState(outState); + //add the values which need to be saved from the adapter to the bundel + outState = mFastAdapter.saveInstanceState(outState); + super.onSaveInstanceState(outState); + } + + @Override + public void onBackPressed() { + //handle the back press :D close the drawer first and if the drawer is closed close the activity + if (mResult != null && mResult.isDrawerOpen()) { + mResult.closeDrawer(); + } else { + super.onBackPressed(); + } + } +} diff --git a/app/src/main/java/com/mikepenz/itemanimators/app/dummy/ImageDummyData.java b/app/src/main/java/com/mikepenz/itemanimators/app/dummy/ImageDummyData.java new file mode 100644 index 0000000..83a7b63 --- /dev/null +++ b/app/src/main/java/com/mikepenz/itemanimators/app/dummy/ImageDummyData.java @@ -0,0 +1,66 @@ +package com.mikepenz.itemanimators.app.dummy; + + +import com.mikepenz.itemanimators.app.items.ImageItem; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Created by mikepenz on 08.01.16. + */ +public class ImageDummyData { + + public static ImageItem getDummyItem() { + int ran = new Random().nextInt(3); + if (ran == 0) { + return new ImageItem().withName("NEW").withDescription("Newly added item").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/yang_zhuo_yong_cuo,_tibet-china-63.jpg"); + } else if (ran == 1) { + return new ImageItem().withName("NEW").withDescription("Newly added item").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/yellowstone-united_states-17.jpg"); + } else { + return new ImageItem().withName("NEW").withDescription("Newly added item").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/victoria-australia-31.jpg"); + } + } + + public static List getImages() { + return toList( + new ImageItem().withName("Yang Zhuo Yong Cuo, Tibet China").withDescription("#100063").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/yang_zhuo_yong_cuo,_tibet-china-63.jpg"), + new ImageItem().withName("Yellowstone United States").withDescription("#100017").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/yellowstone-united_states-17.jpg"), + new ImageItem().withName("Victoria Australia").withDescription("#100031").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/victoria-australia-31.jpg"), + new ImageItem().withName("Valencia Spain").withDescription("#100082").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/valencia-spain-82.jpg"), + new ImageItem().withName("Xigaze, Tibet China").withDescription("#100030").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/xigaze,_tibet-china-30.jpg"), + new ImageItem().withName("Utah United States").withDescription("#100096").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/utah-united_states-96.jpg"), + new ImageItem().withName("Utah United States").withDescription("#100015").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/utah-united_states-15.jpg"), + new ImageItem().withName("Utah United States").withDescription("#100088").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/utah-united_states-88.jpg"), + new ImageItem().withName("Umm Al Quwain United Arab Emirates").withDescription("#100013").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/umm_al_quwain-united_arab_emirates-13.jpg"), + new ImageItem().withName("Texas United States").withDescription("#100026").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/texas-united_states-26.jpg"), + new ImageItem().withName("Siuslaw National Forest United States").withDescription("#100092").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/siuslaw_national_forest-united_states-92.jpg"), + new ImageItem().withName("The Minquiers Channel Islands").withDescription("#100069").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/the_minquiers-channel_islands-69.jpg"), + new ImageItem().withName("Texas United States").withDescription("#100084").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/texas-united_states-84.jpg"), + new ImageItem().withName("Tabuaeran Kiribati").withDescription("#100050").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/tabuaeran-kiribati-50.jpg"), + new ImageItem().withName("Stanislaus River United States").withDescription("#100061").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/stanislaus_river-united_states-61.jpg"), + new ImageItem().withName("S?ehitkamil Turkey").withDescription("#100072").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/s?ehitkamil-turkey-72.jpg"), + new ImageItem().withName("Salinas Grandes Argentina").withDescription("#100025").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/salinas_grandes-argentina-25.jpg"), + new ImageItem().withName("Shadegan Refuge Iran").withDescription("#100012").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/shadegan_refuge-iran-12.jpg"), + new ImageItem().withName("San Pedro De Atacama Chile").withDescription("#100043").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/san_pedro_de_atacama-chile-43.jpg"), + new ImageItem().withName("Ragged Island The Bahamas").withDescription("#100064").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/ragged_island-the_bahamas-64.jpg"), + new ImageItem().withName("Qinghai Lake China").withDescription("#100080").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/qinghai_lake-china-80.jpg"), + new ImageItem().withName("Qesm Al Wahat Ad Dakhlah Egypt").withDescription("#100056").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/qesm_al_wahat_ad_dakhlah-egypt-56.jpg"), + new ImageItem().withName("Riedstadt Germany").withDescription("#100042").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/riedstadt-germany-42.jpg"), + new ImageItem().withName("Redwood City United States").withDescription("#100048").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/redwood_city-united_states-48.jpg"), + new ImageItem().withName("Nyingchi, Tibet China").withDescription("#100098").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/nyingchi,_tibet-china-98.jpg"), + new ImageItem().withName("Ngari, Tibet China").withDescription("#100057").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/ngari,_tibet-china-57.jpg"), + new ImageItem().withName("Pozoantiguo Spain").withDescription("#100099").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/pozoantiguo-spain-99.jpg"), + new ImageItem().withName("Ningaloo Australia").withDescription("#100073").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/ningaloo-australia-73.jpg"), + new ImageItem().withName("Niederzier Germany").withDescription("#100079").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/niederzier-germany-79.jpg"), + new ImageItem().withName("Olympic Dam Australia").withDescription("#100065").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/olympic_dam-australia-65.jpg"), + new ImageItem().withName("Peedamulla Australia").withDescription("#100040").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/peedamulla-australia-40.jpg"), + new ImageItem().withName("Nevado Tres Cruces Park Chile").withDescription("#100089").withImage("https://raw.githubusercontent.com/mikepenz/earthview-wallpapers/develop/thumb/nevado_tres_cruces_park-chile-89.jpg") + ); + } + + private static List toList(ImageItem... imageItems) { + return Arrays.asList(imageItems); + } +} diff --git a/app/src/main/java/com/mikepenz/itemanimators/app/items/ImageItem.java b/app/src/main/java/com/mikepenz/itemanimators/app/items/ImageItem.java new file mode 100644 index 0000000..d5fcbfd --- /dev/null +++ b/app/src/main/java/com/mikepenz/itemanimators/app/items/ImageItem.java @@ -0,0 +1,132 @@ +package com.mikepenz.itemanimators.app.items; + +import android.content.Context; +import android.graphics.Color; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.mikepenz.fastadapter.items.AbstractItem; +import com.mikepenz.fastadapter.utils.FastAdapterUIUtils; +import com.mikepenz.fastadapter.utils.ViewHolderFactory; +import com.mikepenz.itemanimators.app.R; +import com.mikepenz.materialize.util.UIUtils; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by mikepenz on 28.12.15. + */ +public class ImageItem extends AbstractItem { + + public String mImageUrl; + public String mName; + public String mDescription; + + public ImageItem withImage(String imageUrl) { + this.mImageUrl = imageUrl; + return this; + } + + public ImageItem withName(String name) { + this.mName = name; + return this; + } + + public ImageItem withDescription(String description) { + this.mDescription = description; + return this; + } + + @Override + public int getType() { + return R.id.fastadapter_image_item_id; + } + + @Override + public int getLayoutRes() { + return R.layout.image_item; + } + + @Override + public void bindView(RecyclerView.ViewHolder holder) { + Context ctx = holder.itemView.getContext(); + //get our viewHolder + final ViewHolder viewHolder = (ViewHolder) holder; + + //set the item selected if it is + viewHolder.itemView.setSelected(isSelected()); + //set itself as tag. (not required) + viewHolder.itemView.setTag(this); + + //define our data for the view + viewHolder.imageName.setText(mName); + viewHolder.imageDescription.setText(mDescription); + viewHolder.imageView.setImageBitmap(null); + + //set the background for the item + int color = UIUtils.getThemeColor(ctx, R.attr.colorPrimary); + viewHolder.imageContent.setForeground(FastAdapterUIUtils.getSelectableBackground(ctx, Color.argb(100, Color.red(color), Color.green(color), Color.blue(color)))); + + //load glide + Glide.clear(viewHolder.imageView); + Glide.with(ctx).load(mImageUrl).animate(R.anim.alpha_on).into(viewHolder.imageView); + } + + /** + * our ItemFactory implementation which creates the ViewHolder for our adapter. + * It is highly recommended to implement a ViewHolderFactory as it is 0-1ms faster for ViewHolder creation, + * and it is also many many times more efficient if you define custom listeners on views within your item. + */ + public class ItemFactory implements ViewHolderFactory { + public ViewHolder create(View v) { + return new ViewHolder(v); + } + } + + /** + * return our ViewHolderFactory implementation here + * + * @return + */ + @Override + public ViewHolderFactory getFactory() { + return new ItemFactory(); + } + + /** + * our ViewHolder + */ + protected static class ViewHolder extends RecyclerView.ViewHolder { + protected View view; + @Bind(R.id.item_image_img) + protected ImageView imageView; + @Bind(R.id.item_image_name) + protected TextView imageName; + @Bind(R.id.item_image_description) + protected TextView imageDescription; + @Bind(R.id.item_image_content) + protected FrameLayout imageContent; + + public ViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + this.view = view; + + //optimization to preset the correct height for our device + int screenWidth = view.getContext().getResources().getDisplayMetrics().widthPixels; + int finalHeight = (int) (screenWidth / 1.5) / 2; + imageView.setMinimumHeight(finalHeight); + imageView.setMaxHeight(finalHeight); + imageView.setAdjustViewBounds(false); + //set height as layoutParameter too + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) imageView.getLayoutParams(); + lp.height = finalHeight; + imageView.setLayoutParams(lp); + } + } +} diff --git a/app/src/main/java/com/mikepenz/itemanimators/app/items/SampleItem.java b/app/src/main/java/com/mikepenz/itemanimators/app/items/SampleItem.java new file mode 100644 index 0000000..7bd57b2 --- /dev/null +++ b/app/src/main/java/com/mikepenz/itemanimators/app/items/SampleItem.java @@ -0,0 +1,134 @@ +package com.mikepenz.itemanimators.app.items; + +import android.content.Context; +import android.support.annotation.StringRes; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import com.mikepenz.fastadapter.ICollapsible; +import com.mikepenz.fastadapter.IItem; +import com.mikepenz.fastadapter.items.AbstractItem; +import com.mikepenz.fastadapter.utils.FastAdapterUIUtils; +import com.mikepenz.fastadapter.utils.ViewHolderFactory; +import com.mikepenz.itemanimators.app.R; +import com.mikepenz.materialdrawer.holder.StringHolder; +import com.mikepenz.materialize.util.UIUtils; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by mikepenz on 28.12.15. + */ +public class SampleItem extends AbstractItem implements ICollapsible { + + public String header; + public StringHolder name; + public StringHolder description; + + private List mSubItems; + private boolean mCollapsed = true; + + public SampleItem withHeader(String header) { + this.header = header; + return this; + } + + public SampleItem withName(String Name) { + this.name = new StringHolder(Name); + return this; + } + + public SampleItem withName(@StringRes int NameRes) { + this.name = new StringHolder(NameRes); + return this; + } + + public SampleItem withDescription(String description) { + this.description = new StringHolder(description); + return this; + } + + public SampleItem withDescription(@StringRes int descriptionRes) { + this.description = new StringHolder(descriptionRes); + return this; + } + + @Override + public boolean isCollapsed() { + return mCollapsed; + } + + @Override + public SampleItem withCollapsed(boolean collapsed) { + mCollapsed = collapsed; + return this; + } + + @Override + public List getSubItems() { + return mSubItems; + } + + public SampleItem withSubItems(List subItems) { + this.mSubItems = subItems; + return this; + } + + @Override + public int getType() { + return R.id.fastadapter_sampleitem_id; + } + + @Override + public int getLayoutRes() { + return R.layout.sample_item; + } + + @Override + public void bindView(RecyclerView.ViewHolder holder) { + Context ctx = holder.itemView.getContext(); + //get our viewHolder + ViewHolder viewHolder = (ViewHolder) holder; + + //set the item selected if it is + viewHolder.itemView.setSelected(isSelected()); + //set itself as tag. (not required) + viewHolder.itemView.setTag(this); + + //set the background for the item + UIUtils.setBackground(viewHolder.view, FastAdapterUIUtils.getSelectableBackground(ctx, UIUtils.getThemeColor(ctx, R.attr.colorPrimary))); + //set the text for the name + StringHolder.applyTo(name, viewHolder.name); + //set the text for the description or hide + StringHolder.applyToOrHide(description, viewHolder.description); + } + + @Override + public ViewHolderFactory getFactory() { + return new ItemFactory(); + } + + public static class ItemFactory implements ViewHolderFactory { + public ViewHolder create(View v) { + return new ViewHolder(v); + } + } + + protected static class ViewHolder extends RecyclerView.ViewHolder { + protected View view; + @Bind(R.id.material_drawer_name) + TextView name; + @Bind(R.id.material_drawer_description) + TextView description; + + public ViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + this.view = view; + } + } +} diff --git a/app/src/main/res/anim/alpha_on.xml b/app/src/main/res/anim/alpha_on.xml new file mode 100644 index 0000000..80736f8 --- /dev/null +++ b/app/src/main/res/anim/alpha_on.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..a732bf4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..9e5bcda Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..0aaf206 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..3d70d07 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b8ff8f9 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable/gradient.xml b/app/src/main/res/drawable/gradient.xml new file mode 100644 index 0000000..f3dd29a --- /dev/null +++ b/app/src/main/res/drawable/gradient.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_sample.xml b/app/src/main/res/layout/activity_sample.xml new file mode 100755 index 0000000..321c55e --- /dev/null +++ b/app/src/main/res/layout/activity_sample.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/image_item.xml b/app/src/main/res/layout/image_item.xml new file mode 100644 index 0000000..f002bb1 --- /dev/null +++ b/app/src/main/res/layout/image_item.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/sample_item.xml b/app/src/main/res/layout/sample_item.xml new file mode 100755 index 0000000..f7dda3a --- /dev/null +++ b/app/src/main/res/layout/sample_item.xml @@ -0,0 +1,36 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu.xml b/app/src/main/res/menu/menu.xml new file mode 100644 index 0000000..c5f48c8 --- /dev/null +++ b/app/src/main/res/menu/menu.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..2816d0a --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #eeff41 + #cddc39 + #ffff00 + #ff5722 + \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml new file mode 100644 index 0000000..201248d --- /dev/null +++ b/app/src/main/res/values/ids.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml new file mode 100644 index 0000000..089a4cc --- /dev/null +++ b/app/src/main/res/values/integers.xml @@ -0,0 +1,4 @@ + + + 300 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..a461216 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + ItemAnimators + + Open Source + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..80a914b --- /dev/null +++ b/build.gradle @@ -0,0 +1,30 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.0.0-alpha3' + classpath 'com.novoda:bintray-release:0.3.4' + } +} + +// To avoid manually setting the same values in all Android modules, set the value on the root +// project and then reference this from the modules +ext { + compileSdkVersion = 23 + buildToolsVersion = "23.0.2" + supportLibVersion = "23.1.1" +} + +allprojects { + repositories { + jcenter() + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.10' +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..0c11840 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,35 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Settings specified in this file will override any Gradle settings +# configured through the IDE. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +org.gradle.daemon=true +org.gradle.parallel=true + +# Maven stuff +VERSION_NAME=0.1.0-SNAPSHOT +VERSION_CODE=10 +GROUP=com.mikepenz + +POM_DESCRIPTION=ItemAnimators Library +POM_URL=https://github.com/mikepenz/itemanimators +POM_SCM_URL=https://github.com/mikepenz/itemanimators +POM_SCM_CONNECTION=scm:git@github.com:mikepenz/itemanimators.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:mikepenz/itemanimators.git +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo +POM_DEVELOPER_ID=mikepenz +POM_DEVELOPER_NAME=Mike Penz diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..05ef575 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a56d549 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jan 08 21:47:56 CET 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/library/.gitignore @@ -0,0 +1 @@ +/build diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..50db719 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,32 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.novoda.bintray-release' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion 10 + targetSdkVersion 23 + versionCode 10 + versionName '0.1.0-SNAPSHOT' + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } + productFlavors { + } + lintOptions { + abortOnError false + } +} + +apply from: 'gradle-mvn-push.gradle' +apply from: 'gradle-jcenter-push.gradle' + +dependencies { + compile "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}" +} diff --git a/library/gradle-jcenter-push.gradle b/library/gradle-jcenter-push.gradle new file mode 100644 index 0000000..bece178 --- /dev/null +++ b/library/gradle-jcenter-push.gradle @@ -0,0 +1,11 @@ +publish { + dryRun = false + bintrayUser = project.hasProperty('bintray.user') ? project.property('bintray.user') : System.getenv('BINTRAY_USER') + bintrayKey = project.hasProperty('bintray.apikey') ? project.property('bintray.apikey') : System.getenv('BINTRAY_API_KEY') + userOrg = project.property("POM_DEVELOPER_ID") + groupId = project.property("GROUP") + artifactId = project.property("GROUP") + ":" + project.property("POM_ARTIFACT_ID") + publishVersion = project.property("VERSION_NAME") + desc = project.property("POM_DESCRIPTION") + website = project.property("POM_URL") +} \ No newline at end of file diff --git a/library/gradle-mvn-push.gradle b/library/gradle-mvn-push.gradle new file mode 100644 index 0000000..60e1c84 --- /dev/null +++ b/library/gradle-mvn-push.gradle @@ -0,0 +1,125 @@ +/* + * Copyright 2013 Chris Banes + * + * 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. + */ + +apply plugin: 'maven' +apply plugin: 'signing' + +def isReleaseBuild() { + return VERSION_NAME.contains("SNAPSHOT") == false +} + +def getReleaseRepositoryUrl() { + return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL + : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" +} + +def getSnapshotRepositoryUrl() { + return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL + : "https://oss.sonatype.org/content/repositories/snapshots/" +} + +def getRepositoryUsername() { + return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" +} + +def getRepositoryPassword() { + return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" +} + +afterEvaluate { project -> + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + pom.groupId = GROUP + pom.artifactId = POM_ARTIFACT_ID + pom.version = VERSION_NAME + + repository(url: getReleaseRepositoryUrl()) { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + snapshotRepository(url: getSnapshotRepositoryUrl()) { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + + pom.project { + name POM_NAME + packaging POM_PACKAGING + description POM_DESCRIPTION + url POM_URL + + scm { + url POM_SCM_URL + connection POM_SCM_CONNECTION + developerConnection POM_SCM_DEV_CONNECTION + } + + licenses { + license { + name POM_LICENCE_NAME + url POM_LICENCE_URL + distribution POM_LICENCE_DIST + } + } + + developers { + developer { + id POM_DEVELOPER_ID + name POM_DEVELOPER_NAME + } + } + } + } + } + } + + signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives + } + + android.libraryVariants.all { variant -> + def javadocTask = task("generate${variant.name.capitalize()}Javadoc", type: Javadoc) { + description "Generates Javadoc for $variant.name." + source = variant.javaCompile.source + ext.androidJar = project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath = files(variant.javaCompile.classpath.files) + files(ext.androidJar) + exclude '**/BuildConfig.java' + exclude '**/R.java' + } + + javadocTask.dependsOn variant.javaCompile + + def jarJavadocTask = task("jar${variant.name.capitalize()}Javadoc", type: Jar) { + description "Generate Javadoc Jar for $variant.name" + classifier = 'javadoc' + from javadocTask.destinationDir + } + + jarJavadocTask.dependsOn javadocTask + artifacts.add('archives', jarJavadocTask) + + def jarSourceTask = task("jar${variant.name.capitalize()}Sources", type: Jar) { + description "Generates Java Sources for $variant.name." + classifier = 'sources' + from variant.javaCompile.source + } + + jarSourceTask.dependsOn variant.javaCompile + artifacts.add('archives', jarSourceTask) + } +} diff --git a/library/gradle.properties b/library/gradle.properties new file mode 100755 index 0000000..8e3ec9a --- /dev/null +++ b/library/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Settings specified in this file will override any Gradle settings +# configured through the IDE. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +POM_NAME=itemanimators Library +POM_ARTIFACT_ID=itemanimators +POM_PACKAGING=aar diff --git a/library/proguard-rules.txt b/library/proguard-rules.txt new file mode 100644 index 0000000..9827206 --- /dev/null +++ b/library/proguard-rules.txt @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Entwicklung/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} \ No newline at end of file diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4c44df5 --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/library/src/main/java/com/mikepenz/itemanimators/AlphaInAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/AlphaInAnimator.java new file mode 100644 index 0000000..94ca547 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/AlphaInAnimator.java @@ -0,0 +1,7 @@ +package com.mikepenz.itemanimators; + +/** + * Created by mikepenz on 08.01.16. + */ +public class AlphaInAnimator extends DefaultAnimator { +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/BaseItemAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/BaseItemAnimator.java new file mode 100644 index 0000000..d75a370 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/BaseItemAnimator.java @@ -0,0 +1,762 @@ +/* + * 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. + */ +package com.mikepenz.itemanimators; + +import android.support.v4.animation.AnimatorCompatHelper; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.animation.Interpolator; + +import java.util.ArrayList; +import java.util.List; + +/** + * This implementation of {@link RecyclerView.ItemAnimator} provides basic + * animations on remove, add, and move events that happen to the items in + * a RecyclerView. RecyclerView uses a CollapsingItemAnimator by default. + * + * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator) + */ +public abstract class BaseItemAnimator extends SimpleItemAnimator { + private static final boolean DEBUG = false; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + private ArrayList> mAdditionsList = new ArrayList<>(); + private ArrayList> mMovesList = new ArrayList<>(); + private ArrayList> mChangesList = new ArrayList<>(); + + private ArrayList mAddAnimations = new ArrayList<>(); + private ArrayList mMoveAnimations = new ArrayList<>(); + private ArrayList mRemoveAnimations = new ArrayList<>(); + private ArrayList mChangeAnimations = new ArrayList<>(); + + Interpolator mInterpolator; + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + protected static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + /** + * defines the interpolator used for the animations + * + * @param interpolator the interpolator used for the animations + * @return the implementing class T + */ + public T withInterpolator(Interpolator interpolator) { + this.mInterpolator = interpolator; + return (T) this; + } + + /** + * @return the interpolator used for the animations + */ + public Interpolator getInterpolator() { + return this.mInterpolator; + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList<>(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, 0); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList<>(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + + long moveDuration = movesPending ? getMoveDuration() : 0; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDelay(getRemoveDuration(), moveDuration, getChangeDuration())); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList<>(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, getAddDelay(removeDuration, moveDuration, changeDuration)); + } else { + adder.run(); + } + } + } + + /** + * used to calculated the delay until the remove animation should start + * + * @param remove the remove duration + * @param move the move duration + * @param change the change duration + * @return the calculated delay for the remove items animation + */ + public long getRemoveDelay(long remove, long move, long change) { + return remove + Math.max(move, change); + } + + /** + * used to calculated the delay until the add animation should start + * + * @param remove the remove duration + * @param move the move duration + * @param change the change duration + * @return the calculated delay for the add items animation + */ + public long getAddDelay(long remove, long move, long change) { + return remove + Math.max(move, change); + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + resetAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = removeAnimation(view); + mRemoveAnimations.add(holder); + animation.setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + removeAnimationCleanup(view); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + abstract public ViewPropertyAnimatorCompat removeAnimation(View view); + + abstract public void removeAnimationCleanup(View view); + + @Override + public boolean animateAdd(final ViewHolder holder) { + resetAnimation(holder); + addAnimationPrepare(holder.itemView); + mPendingAdditions.add(holder); + return true; + } + + private void animateAddImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = addAnimation(view); + mAddAnimations.add(holder); + animation. + setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchAddStarting(holder); + } + + @Override + public void onAnimationCancel(View view) { + addAnimationCleanup(view); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + /** + * the animation to prepare the view before the add animation is run + * + * @param view + */ + abstract public void addAnimationPrepare(View view); + + /** + * the animation for adding a view + * + * @param view + * @return + */ + abstract public ViewPropertyAnimatorCompat addAnimation(View view); + + /** + * the cleanup method if the animation needs to be stopped. and tro prepare for the next view + * + * @param view + */ + abstract void addAnimationCleanup(View view); + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += ViewCompat.getTranslationX(holder.itemView); + fromY += ViewCompat.getTranslationY(holder.itemView); + resetAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + ViewCompat.setTranslationX(view, -deltaX); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, -deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + ViewCompat.animate(view).translationX(0); + } + if (deltaY != 0) { + ViewCompat.animate(view).translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchMoveStarting(holder); + } + + @Override + public void onAnimationCancel(View view) { + if (deltaX != 0) { + ViewCompat.setTranslationX(view, 0); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, 0); + } + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + if (oldHolder == newHolder) { + // Don't know how to run change animations when the same view holder is re-used. + // run a move animation to handle position changes. + return animateMove(oldHolder, fromX, fromY, toX, toY); + } + changeAnimation(oldHolder, newHolder, + fromX, fromY, toX, toY); + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + private void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimatorCompat oldViewAnim = changeOldAnimation(view, changeInfo); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(View view) { + oldViewAnim.setListener(null); + changeAnimationCleanup(view); + ViewCompat.setTranslationX(view, 0); + ViewCompat.setTranslationY(view, 0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimatorCompat newViewAnimation = changeNewAnimation(newView); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + + @Override + public void onAnimationEnd(View view) { + newViewAnimation.setListener(null); + changeAnimationCleanup(newView); + ViewCompat.setTranslationX(newView, 0); + ViewCompat.setTranslationY(newView, 0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + /** + * the whole change animation if we have to cross animate two views + * + * @param oldHolder + * @param newHolder + * @param fromX + * @param fromY + * @param toX + * @param toY + */ + public void changeAnimation(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, int toY) { + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevValue = ViewCompat.getAlpha(oldHolder.itemView); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + + ViewCompat.setAlpha(oldHolder.itemView, prevValue); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + ViewCompat.setAlpha(newHolder.itemView, 0); + } + } + + /** + * the animation for removing the old view + * + * @param view + * @return + */ + abstract public ViewPropertyAnimatorCompat changeOldAnimation(View view, ChangeInfo changeInfo); + + /** + * the animation for changing the new view + * + * @param view + * @return + */ + abstract public ViewPropertyAnimatorCompat changeNewAnimation(View view); + + /** + * the cleanup method if the animation needs to be stopped. and tro prepare for the next view + * + * @param view + */ + abstract public void changeAnimationCleanup(View view); + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + changeAnimationCleanup(item.itemView); + ViewCompat.setTranslationX(item.itemView, 0); + ViewCompat.setTranslationY(item.itemView, 0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + ViewCompat.animate(view).cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + removeAnimationCleanup(view); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + addAnimationCleanup(view); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + addAnimationCleanup(view); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + //noinspection PointlessBooleanExpression,ConstantConditions + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + public void resetAnimation(ViewHolder holder) { + AnimatorCompatHelper.clearInterpolator(holder.itemView); + endAnimation(holder); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() || + !mPendingChanges.isEmpty() || + !mPendingMoves.isEmpty() || + !mPendingRemovals.isEmpty() || + !mMoveAnimations.isEmpty() || + !mRemoveAnimations.isEmpty() || + !mAddAnimations.isEmpty() || + !mChangeAnimations.isEmpty() || + !mMovesList.isEmpty() || + !mAdditionsList.isEmpty() || + !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + View view = item.itemView; + addAnimationCleanup(view); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + addAnimationCleanup(view); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + ViewCompat.animate(viewHolders.get(i).itemView).cancel(); + } + } + + private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { + @Override + public void onAnimationStart(View view) { + } + + @Override + public void onAnimationEnd(View view) { + } + + @Override + public void onAnimationCancel(View view) { + } + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/BaseScaleAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/BaseScaleAnimator.java new file mode 100644 index 0000000..163f42d --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/BaseScaleAnimator.java @@ -0,0 +1,52 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public abstract class BaseScaleAnimator extends BaseItemAnimator { + public void changeAnimation(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) { + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevValue = changeAnimationPrepare1(oldHolder.itemView); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + + changeAnimationPrepare2(oldHolder.itemView, prevValue); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + changeAnimationPrepare3(newHolder.itemView); + } + } + + /** + * @param view + * @return the default value for the animatd attribute + */ + abstract public float changeAnimationPrepare1(View view); + + /** + * animates the view to the previous default value + * + * @param view + * @param prevValue the previous value + */ + abstract public void changeAnimationPrepare2(View view, float prevValue); + + /** + * resets the value + * + * @param view + */ + abstract public void changeAnimationPrepare3(View view); +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/DefaultAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/DefaultAnimator.java new file mode 100644 index 0000000..62a32b2 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/DefaultAnimator.java @@ -0,0 +1,47 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class DefaultAnimator extends BaseItemAnimator { + // ADD ANIMATION METHODS + + public void addAnimationPrepare(View view) { + ViewCompat.setAlpha(view, 0); + } + + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).alpha(1).setDuration(getAddDuration()).setInterpolator(getInterpolator()); + } + + public void addAnimationCleanup(View view) { + ViewCompat.setAlpha(view, 1); + } + + // REMOVE ANIMATION METHODS + + public ViewPropertyAnimatorCompat removeAnimation(View view) { + return ViewCompat.animate(view).setDuration(getRemoveDuration()).alpha(0).setInterpolator(getInterpolator()); + } + + public void removeAnimationCleanup(View view) { + ViewCompat.setAlpha(view, 1); + } + + // CHANGE ANIMATION METHODS + public ViewPropertyAnimatorCompat changeOldAnimation(View view, ChangeInfo changeInfo) { + return ViewCompat.animate(view).setDuration(getChangeDuration()).alpha(0).translationX(changeInfo.toX - changeInfo.fromX).translationY(changeInfo.toY - changeInfo.fromY).setInterpolator(getInterpolator()); + } + + public ViewPropertyAnimatorCompat changeNewAnimation(View view) { + return ViewCompat.animate(view).translationX(0).translationY(0).setDuration(getChangeDuration()).alpha(1).setInterpolator(getInterpolator()); + } + + public void changeAnimationCleanup(View view) { + ViewCompat.setAlpha(view, 1); + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/ScaleUpAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/ScaleUpAnimator.java new file mode 100644 index 0000000..d851ee4 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/ScaleUpAnimator.java @@ -0,0 +1,60 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class ScaleUpAnimator extends BaseScaleAnimator { + public void addAnimationPrepare(View view) { + ViewCompat.setScaleX(view, 0); + ViewCompat.setScaleY(view, 0); + } + + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).scaleX(1).scaleY(1).setDuration(getAddDuration()).setInterpolator(getInterpolator()); + } + + public void addAnimationCleanup(View view) { + ViewCompat.setScaleX(view, 1); + ViewCompat.setScaleY(view, 1); + } + + + public ViewPropertyAnimatorCompat removeAnimation(View view) { + return ViewCompat.animate(view).setDuration(getRemoveDuration()).scaleX(0).scaleY(0).setInterpolator(getInterpolator()); + } + + public void removeAnimationCleanup(View view) { + ViewCompat.setScaleX(view, 1); + ViewCompat.setScaleY(view, 1); + } + + public float changeAnimationPrepare1(View view) { + return ViewCompat.getScaleX(view); + } + + public void changeAnimationPrepare2(View view, float prevValue) { + ViewCompat.setScaleX(view, prevValue); + } + + public void changeAnimationPrepare3(View view) { + ViewCompat.setScaleX(view, 0); + ViewCompat.setScaleY(view, 0); + } + + public ViewPropertyAnimatorCompat changeOldAnimation(View view, ChangeInfo changeInfo) { + return ViewCompat.animate(view).setDuration(getChangeDuration()).scaleX(0).scaleY(0).translationX(changeInfo.toX - changeInfo.fromX).translationY(changeInfo.toY - changeInfo.fromY).setInterpolator(getInterpolator()); + } + + public ViewPropertyAnimatorCompat changeNewAnimation(View view) { + return ViewCompat.animate(view).translationX(0).translationY(0).setDuration(getChangeDuration()).scaleX(1).scaleY(1).setInterpolator(getInterpolator()); + } + + public void changeAnimationCleanup(View view) { + ViewCompat.setScaleX(view, 1); + ViewCompat.setScaleY(view, 1); + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/ScaleXAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/ScaleXAnimator.java new file mode 100644 index 0000000..238448c --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/ScaleXAnimator.java @@ -0,0 +1,57 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class ScaleXAnimator extends BaseScaleAnimator { + public void addAnimationPrepare(View view) { + ViewCompat.setScaleX(view, 0); + } + + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).scaleX(1).setDuration(getAddDuration()); + } + + public void addAnimationCleanup(View view) { + ViewCompat.setScaleX(view, 1); + } + + + public ViewPropertyAnimatorCompat removeAnimation(View view) { + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + return animation.setDuration(getRemoveDuration()).scaleX(0); + } + + public void removeAnimationCleanup(View view) { + ViewCompat.setScaleX(view, 1); + } + + public float changeAnimationPrepare1(View view) { + return ViewCompat.getScaleX(view); + } + + public void changeAnimationPrepare2(View view, float prevValue) { + ViewCompat.setScaleX(view, prevValue); + } + + public void changeAnimationPrepare3(View view) { + ViewCompat.setScaleX(view, 0); + } + + public ViewPropertyAnimatorCompat changeOldAnimation(View view, ChangeInfo changeInfo) { + return ViewCompat.animate(view).setDuration(getChangeDuration()).scaleX(0).translationX(changeInfo.toX - changeInfo.fromX).translationY(changeInfo.toY - changeInfo.fromY); + + } + + public ViewPropertyAnimatorCompat changeNewAnimation(View view) { + return ViewCompat.animate(view).translationX(0).translationY(0).setDuration(getChangeDuration()).scaleX(1); + } + + public void changeAnimationCleanup(View view) { + ViewCompat.setScaleX(view, 1); + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/ScaleYAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/ScaleYAnimator.java new file mode 100644 index 0000000..09044ab --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/ScaleYAnimator.java @@ -0,0 +1,57 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class ScaleYAnimator extends BaseScaleAnimator { + public void addAnimationPrepare(View view) { + ViewCompat.setScaleY(view, 0); + } + + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).scaleY(1).setDuration(getAddDuration()); + } + + public void addAnimationCleanup(View view) { + ViewCompat.setScaleY(view, 1); + } + + + public ViewPropertyAnimatorCompat removeAnimation(View view) { + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + return animation.setDuration(getRemoveDuration()).scaleY(0); + } + + public void removeAnimationCleanup(View view) { + ViewCompat.setScaleY(view, 1); + } + + public float changeAnimationPrepare1(View view) { + return ViewCompat.getScaleY(view); + } + + public void changeAnimationPrepare2(View view, float prevValue) { + ViewCompat.setScaleY(view, prevValue); + } + + public void changeAnimationPrepare3(View view) { + ViewCompat.setScaleY(view, 0); + } + + public ViewPropertyAnimatorCompat changeOldAnimation(View view, ChangeInfo changeInfo) { + return ViewCompat.animate(view).setDuration(getChangeDuration()).scaleY(0).translationX(changeInfo.toX - changeInfo.fromX).translationY(changeInfo.toY - changeInfo.fromY); + + } + + public ViewPropertyAnimatorCompat changeNewAnimation(View view) { + return ViewCompat.animate(view).translationX(0).translationY(0).setDuration(getChangeDuration()).scaleY(1); + } + + public void changeAnimationCleanup(View view) { + ViewCompat.setScaleY(view, 1); + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/SlideDownAlphaAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/SlideDownAlphaAnimator.java new file mode 100644 index 0000000..4492042 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/SlideDownAlphaAnimator.java @@ -0,0 +1,49 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class SlideDownAlphaAnimator extends DefaultAnimator { + @Override + public void addAnimationPrepare(View view) { + ViewCompat.setTranslationY(view, -view.getHeight()); + ViewCompat.setAlpha(view, 0); + } + + @Override + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).translationY(0).alpha(1).setDuration(getMoveDuration()); + } + + @Override + public void addAnimationCleanup(View view) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setAlpha(view, 1); + } + + @Override + public long getAddDelay(long remove, long move, long change) { + return 0; + } + + @Override + public long getRemoveDelay(long remove, long move, long change) { + return remove / 2; + } + + @Override + public ViewPropertyAnimatorCompat removeAnimation(View view) { + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + return animation.setDuration(getMoveDuration()).alpha(0).translationY(-view.getHeight()); + } + + @Override + public void removeAnimationCleanup(View view) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setAlpha(view, 1); + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/SlideLeftAlphaAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/SlideLeftAlphaAnimator.java new file mode 100644 index 0000000..43f0e74 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/SlideLeftAlphaAnimator.java @@ -0,0 +1,49 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class SlideLeftAlphaAnimator extends DefaultAnimator { + @Override + public void addAnimationPrepare(View view) { + ViewCompat.setTranslationX(view, view.getWidth()); + ViewCompat.setAlpha(view, 0); + } + + @Override + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).translationX(0).alpha(1).setDuration(getMoveDuration()); + } + + @Override + public void addAnimationCleanup(View view) { + ViewCompat.setTranslationX(view, 0); + ViewCompat.setAlpha(view, 1); + } + + @Override + public long getAddDelay(long remove, long move, long change) { + return 0; + } + + @Override + public long getRemoveDelay(long remove, long move, long change) { + return 0; + } + + @Override + public ViewPropertyAnimatorCompat removeAnimation(View view) { + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + return animation.setDuration(getMoveDuration()).alpha(0).translationX(view.getWidth()); + } + + @Override + public void removeAnimationCleanup(View view) { + ViewCompat.setTranslationX(view, 0); + ViewCompat.setAlpha(view, 1); + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/SlideRightAlphaAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/SlideRightAlphaAnimator.java new file mode 100644 index 0000000..fa132e1 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/SlideRightAlphaAnimator.java @@ -0,0 +1,49 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class SlideRightAlphaAnimator extends DefaultAnimator { + @Override + public void addAnimationPrepare(View view) { + ViewCompat.setTranslationX(view, -view.getWidth()); + ViewCompat.setAlpha(view, 0); + } + + @Override + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).translationX(0).alpha(1).setDuration(getMoveDuration()); + } + + @Override + public void addAnimationCleanup(View view) { + ViewCompat.setTranslationX(view, 0); + ViewCompat.setAlpha(view, 1); + } + + @Override + public long getAddDelay(long remove, long move, long change) { + return 0; + } + + @Override + public long getRemoveDelay(long remove, long move, long change) { + return 0; + } + + @Override + public ViewPropertyAnimatorCompat removeAnimation(View view) { + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + return animation.setDuration(getMoveDuration()).alpha(0).translationX(-view.getWidth()); + } + + @Override + public void removeAnimationCleanup(View view) { + ViewCompat.setTranslationX(view, 0); + ViewCompat.setAlpha(view, 1); + } +} diff --git a/library/src/main/java/com/mikepenz/itemanimators/SlideUpAlphaAnimator.java b/library/src/main/java/com/mikepenz/itemanimators/SlideUpAlphaAnimator.java new file mode 100644 index 0000000..fc59859 --- /dev/null +++ b/library/src/main/java/com/mikepenz/itemanimators/SlideUpAlphaAnimator.java @@ -0,0 +1,49 @@ +package com.mikepenz.itemanimators; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.view.View; + +/** + * Created by mikepenz on 08.01.16. + */ +public class SlideUpAlphaAnimator extends DefaultAnimator { + @Override + public void addAnimationPrepare(View view) { + ViewCompat.setTranslationY(view, view.getHeight()); + ViewCompat.setAlpha(view, 0); + } + + @Override + public ViewPropertyAnimatorCompat addAnimation(View view) { + return ViewCompat.animate(view).translationY(0).alpha(1).setDuration(getMoveDuration()); + } + + @Override + public void addAnimationCleanup(View view) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setAlpha(view, 1); + } + + @Override + public long getAddDelay(long remove, long move, long change) { + return 0; + } + + @Override + public long getRemoveDelay(long remove, long move, long change) { + return 0; + } + + @Override + public ViewPropertyAnimatorCompat removeAnimation(View view) { + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + return animation.setDuration(getMoveDuration()).alpha(0).translationY(view.getHeight()); + } + + @Override + public void removeAnimationCleanup(View view) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setAlpha(view, 1); + } +} diff --git a/library/src/main/res/values/library_itemanimators_strings.xml b/library/src/main/res/values/library_itemanimators_strings.xml new file mode 100755 index 0000000..9eb289a --- /dev/null +++ b/library/src/main/res/values/library_itemanimators_strings.xml @@ -0,0 +1,21 @@ + + + + year;owner + Mike Penz + http://mikepenz.com/ + itemanimators + + ItemAnimators library comes with a huge collections of pre-created Animators for your RecyclerView. + ]]> + + 0.1.0-SNAPSHOT + https://github.com/mikepenz/itemanimators + apache_2_0 + true + https://github.com/mikepenz/itemanimators + + Mike Penz + 2016 + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ef1bbc7 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':app' +include ':library' \ No newline at end of file