Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add animation for last element appearing #278

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package com.yuyakaido.android.cardstackview;

import android.animation.Animator;
import android.content.Context;
import android.graphics.PointF;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.animation.Interpolator;

import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import com.yuyakaido.android.cardstackview.internal.CardStackAnimatorListener;
import com.yuyakaido.android.cardstackview.internal.CardStackSetting;
import com.yuyakaido.android.cardstackview.internal.CardStackSmoothScroller;
import com.yuyakaido.android.cardstackview.internal.CardStackState;
Expand All @@ -26,16 +30,18 @@ public class CardStackLayoutManager
private final Context context;

private CardStackListener listener = CardStackListener.DEFAULT;
private CardStackSetting setting = new CardStackSetting();
private CardStackSetting setting;
private CardStackState state = new CardStackState();

public CardStackLayoutManager(Context context) {
this(context, CardStackListener.DEFAULT);
this.setting = new CardStackSetting(context.getResources());
}

public CardStackLayoutManager(Context context, CardStackListener listener) {
this.context = context;
this.listener = listener;
this.setting = new CardStackSetting(context.getResources());
}

@Override
Expand Down Expand Up @@ -276,6 +282,7 @@ private void update(RecyclerView.Recycler recycler) {
if (state.topPosition == state.targetPosition) {
state.targetPosition = RecyclerView.NO_POSITION;
}
state.isLastChildWasAnimated = false;

/* Handlerを経由してイベント通知を行っているのは、以下のエラーを回避するため
*
Expand Down Expand Up @@ -361,45 +368,101 @@ private void updateTranslation(View view) {
view.setTranslationY(state.dy);
}

private void updateTranslation(View view, int index) {
private void updateTranslation(View view, final int index) {
boolean isLastVisibleElement = index == setting.visibleCount - 1;
if (state.isLastChildOnAnimation && isLastVisibleElement) {
return;
}

int nextIndex = index - 1;
int translationPx = DisplayUtil.dpToPx(context, setting.translationInterval);
float currentTranslation = index * translationPx;
float nextTranslation = nextIndex * translationPx;
float targetTranslation = currentTranslation - (currentTranslation - nextTranslation) * state.getRatio();
boolean needToStartAnimation = needToStartAnimation(index);
Float translationX = null;
Float translationY = null;

switch (setting.stackFrom) {
case None:
// Do nothing
break;
case Top:
view.setTranslationY(-targetTranslation);
translationY = -targetTranslation;
break;
case TopAndLeft:
view.setTranslationY(-targetTranslation);
view.setTranslationX(-targetTranslation);
translationY = -targetTranslation;
translationX = -targetTranslation;
break;
case TopAndRight:
view.setTranslationY(-targetTranslation);
view.setTranslationX(targetTranslation);
translationY = -targetTranslation;
translationX = targetTranslation;
break;
case Bottom:
view.setTranslationY(targetTranslation);
translationY = targetTranslation;
break;
case BottomAndLeft:
view.setTranslationY(targetTranslation);
view.setTranslationX(-targetTranslation);
translationY = targetTranslation;
translationX = -targetTranslation;
break;
case BottomAndRight:
view.setTranslationY(targetTranslation);
view.setTranslationX(targetTranslation);
translationY = targetTranslation;
translationX = targetTranslation;
break;
case Left:
view.setTranslationX(-targetTranslation);
translationX = -targetTranslation;
break;
case Right:
view.setTranslationX(targetTranslation);
translationX = targetTranslation;
break;
}

if (needToStartAnimation) {
startAnimation(view, translationX, translationY);
} else {
setTranslation(view, translationX, translationY);
}
}

private void startAnimation(@NonNull View view, @Nullable Float translationX, @Nullable Float translationY) {
ViewPropertyAnimator animator = view.animate();

if (translationX != null) {
animator.translationX(translationX);
}

if (translationY != null) {
animator.translationY(translationY);
}

animator.setDuration(setting.lastItemAppearingAnimationDuration).setListener(new CardStackAnimatorListener() {

@Override
public void onAnimationStart(Animator animation) {
state.isLastChildOnAnimation = true;
}

@Override
public void onAnimationEnd(Animator animation) {
state.isLastChildOnAnimation = false;
state.isLastChildWasAnimated = true;
}
});
}

private boolean needToStartAnimation(int index) {
boolean isLastElement = index == setting.visibleCount - 1;
return !state.isLastChildOnAnimation && isLastElement && !state.isLastChildWasAnimated;
}

private void setTranslation(@NonNull View view, @Nullable Float translationX, @Nullable Float translationY) {
if (translationX != null) {
view.setTranslationX(translationX);
}

if (translationY != null) {
view.setTranslationY(translationY);
}
}

private void resetTranslation(View view) {
Expand Down Expand Up @@ -637,4 +700,7 @@ public void setOverlayInterpolator(@NonNull Interpolator overlayInterpolator) {
setting.overlayInterpolator = overlayInterpolator;
}

public void setLastItemAppearingAnimationDuration(@NonNull Integer duration) {
setting.lastItemAppearingAnimationDuration = duration;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.yuyakaido.android.cardstackview.internal;

import android.animation.Animator;

public abstract class CardStackAnimatorListener implements Animator.AnimatorListener {

@Override
public void onAnimationStart(Animator animation, boolean isReverse) {
// not need to implement
}

@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
// not need to implement
}

@Override
public void onAnimationCancel(Animator animation) {
// not need to implement
}

@Override
public void onAnimationRepeat(Animator animation) {
// not need to implement
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package com.yuyakaido.android.cardstackview.internal;

import android.content.res.Resources;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;

import com.yuyakaido.android.cardstackview.Direction;
import com.yuyakaido.android.cardstackview.RewindAnimationSetting;
import com.yuyakaido.android.cardstackview.StackFrom;
import com.yuyakaido.android.cardstackview.SwipeAnimationSetting;
import com.yuyakaido.android.cardstackview.SwipeableMethod;

import java.util.List;

public class CardStackSetting {

public CardStackSetting(Resources resources) {
lastItemAppearingAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime);
}

public StackFrom stackFrom = StackFrom.None;
public int visibleCount = 3;
public float translationInterval = 8.0f;
Expand All @@ -25,4 +29,5 @@ public class CardStackSetting {
public SwipeAnimationSetting swipeAnimationSetting = new SwipeAnimationSetting.Builder().build();
public RewindAnimationSetting rewindAnimationSetting = new RewindAnimationSetting.Builder().build();
public Interpolator overlayInterpolator = new LinearInterpolator();
public Integer lastItemAppearingAnimationDuration;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class CardStackState {
public int topPosition = 0;
public int targetPosition = RecyclerView.NO_POSITION;
public float proportion = 0.0f;
public Boolean isLastChildOnAnimation = false;
public Boolean isLastChildWasAnimated = false;

public enum Status {
Idle,
Expand Down Expand Up @@ -105,5 +107,4 @@ public boolean canScrollToPosition(int position, int itemCount) {
}
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ class MainActivity : AppCompatActivity(), CardStackListener {
manager.setCanScrollVertical(true)
manager.setSwipeableMethod(SwipeableMethod.AutomaticAndManual)
manager.setOverlayInterpolator(LinearInterpolator())
manager.setLastItemAppearingAnimationDuration(150)
manager.setStackFrom(StackFrom.Bottom)
cardStackView.layoutManager = manager
cardStackView.adapter = adapter
cardStackView.itemAnimator.apply {
Expand Down