I have a custom java class which extends FrameLayout that sits ontop of another custom parent view that extends FrameLayout
I have the following function which dismisses the view with animation -
private void dismissCard(final View view, int xPos) {
view.animate()
.x(xPos)
.y(0)
.setInterpolator(new AccelerateInterpolator())
.setDuration(DURATION)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
setOnTouchListener(null);
}
@Override
public void onAnimationEnd(Animator animator) {
ViewGroup viewGroup = (ViewGroup) view.getParent();
if (viewGroup != null) {
viewGroup.removeView(view);
}
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
}
The issue is that I have overriden the onTouch()
function for moving the view functionality, and for each touch I am getting the parent of the view, which is a custom view, getting all childs of the view and by that determining which view is on the top. So my issue is that while being animated, the animated view is still the one on the top so it can be moved another time until the animation is gone and viewGroup.removeView(view)
is being called.
Here is my onTouch()
implementation -
@Override
public boolean onTouch(final View view, MotionEvent motionEvent) {
TinderStackLayout tinderStackLayout = ((TinderStackLayout) view.getParent());
TinderCardView topCard = (TinderCardView) tinderStackLayout.getChildAt(tinderStackLayout.getChildCount() - 1);
if (topCard.equals(view)) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
oldX = motionEvent.getX();
oldY = motionEvent.getY();
// cancel any current animations
view.clearAnimation();
return true;
case MotionEvent.ACTION_UP:
if (isCardBeyondLeftBoundary(view)) {
Log.d("top card dismissed - ", topCard.usernameTextView.getText().toString());
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
dismissCard(view, -(screenWidth * 2));
} else if (isCardBeyondRightBoundary(view)) {
showLawyerContactDetailsFragment(topCard);
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
resetCard(view);
} else {
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
resetCard(view);
}
return true;
case MotionEvent.ACTION_MOVE:
newX = motionEvent.getX();
newY = motionEvent.getY();
dX = newX - oldX;
dY = newY - oldY;
float posX = view.getX() + dX;
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
// Set new position
view.setX(view.getX() + dX);
view.setY(view.getY() + dY);
setCardRotation(view, view.getX());
updateAlphaOfBadges(posX);
return true;
default:
return super.onTouchEvent(motionEvent);
}
}
return super.onTouchEvent(motionEvent);
}
What I am missing is a way to block the touch ability while dismissCard()
is animating. How can I implement such thing?
Declare a boolean variable(isAnimating) in the class you are animating and set it to true in onAnimationStart
and false in onAnimationEnd
.
access this variable in your onTouch
@Override
public boolean onTouch(final View view, MotionEvent motionEvent) {
TinderStackLayout tinderStackLayout = ((TinderStackLayout) view.getParent());
TinderCardView topCard = (TinderCardView) tinderStackLayout.getChildAt(tinderStackLayout.getChildCount() - 1);
if (topCard.equals(view)&& !isAnimating) { //access event's only if animation is ended.
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
oldX = motionEvent.getX();
oldY = motionEvent.getY();
// cancel any current animations
view.clearAnimation();
return true;
case MotionEvent.ACTION_UP:
if (isCardBeyondLeftBoundary(view)) {
Log.d("top card dismissed - ", topCard.usernameTextView.getText().toString());
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
dismissCard(view, -(screenWidth * 2));
} else if (isCardBeyondRightBoundary(view)) {
showLawyerContactDetailsFragment(topCard);
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
resetCard(view);
} else {
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
resetCard(view);
}
return true;
case MotionEvent.ACTION_MOVE:
newX = motionEvent.getX();
newY = motionEvent.getY();
dX = newX - oldX;
dY = newY - oldY;
float posX = view.getX() + dX;
onCardSwipedListener.send(new TopCardMovedEvent(-(screenWidth)));
// Set new position
view.setX(view.getX() + dX);
view.setY(view.getY() + dY);
setCardRotation(view, view.getX());
updateAlphaOfBadges(posX);
return true;
default:
return super.onTouchEvent(motionEvent);
}
}
return super.onTouchEvent(motionEvent);
}
P.S- better solution is to return false when isAnimating is true
.In my app i have overriden the dispatch touch in the parent class and don't let any event to reach the parent or any of it's child class.
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (isAnimationOnGoing) {
return false
}
return super.dispatchTouchEvent(ev)
}