Implemented show and hide animations for "Your train" card. ListView selection is still wonky.

This commit is contained in:
Doug Keen 2012-10-04 22:52:42 -07:00
parent 94ed29467d
commit 9bd040c3e8
2 changed files with 114 additions and 36 deletions

View File

@ -39,7 +39,7 @@ import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.MenuItem;
import com.dougkeen.bart.BartRunnerApplication; import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.R; import com.dougkeen.bart.R;
import com.dougkeen.bart.controls.SwipeDismissTouchListener; import com.dougkeen.bart.controls.SwipeHelper;
import com.dougkeen.bart.controls.Ticker; import com.dougkeen.bart.controls.Ticker;
import com.dougkeen.bart.controls.YourTrainLayout; import com.dougkeen.bart.controls.YourTrainLayout;
import com.dougkeen.bart.data.DepartureArrayAdapter; import com.dougkeen.bart.data.DepartureArrayAdapter;
@ -189,9 +189,8 @@ public class ViewDeparturesActivity extends SActivity implements
mYourTrainSection = (YourTrainLayout) findViewById(R.id.yourTrainSection); mYourTrainSection = (YourTrainLayout) findViewById(R.id.yourTrainSection);
mYourTrainSection.setOnClickListener(mYourTrainSectionClickListener); mYourTrainSection.setOnClickListener(mYourTrainSectionClickListener);
mYourTrainSection.setOnTouchListener(new SwipeDismissTouchListener( mSwipeHelper = new SwipeHelper(mYourTrainSection, null,
mYourTrainSection, null, new SwipeHelper.OnDismissCallback() {
new SwipeDismissTouchListener.OnDismissCallback() {
@Override @Override
public void onDismiss(View view, Object token) { public void onDismiss(View view, Object token) {
dismissYourTrainSelection(); dismissYourTrainSelection();
@ -201,7 +200,8 @@ public class ViewDeparturesActivity extends SActivity implements
mActionMode.finish(); mActionMode.finish();
} }
} }
})); });
mYourTrainSection.setOnTouchListener(mSwipeHelper);
refreshBoardedDeparture(); refreshBoardedDeparture();
@ -363,6 +363,8 @@ public class ViewDeparturesActivity extends SActivity implements
private YourTrainLayout mYourTrainSection; private YourTrainLayout mYourTrainSection;
private SwipeHelper mSwipeHelper;
protected DepartureArrayAdapter getListAdapter() { protected DepartureArrayAdapter getListAdapter() {
return mDeparturesAdapter; return mDeparturesAdapter;
} }
@ -480,7 +482,7 @@ public class ViewDeparturesActivity extends SActivity implements
mYourTrainSection.updateFromDeparture(boardedDeparture); mYourTrainSection.updateFromDeparture(boardedDeparture);
if (currentVisibility != View.VISIBLE) { if (currentVisibility != View.VISIBLE) {
showYourTrainSection(mYourTrainSection); showYourTrainSection();
} }
if (mActionMode == null) { if (mActionMode == null) {
@ -678,7 +680,7 @@ public class ViewDeparturesActivity extends SActivity implements
startService(intent); startService(intent);
return true; return true;
} else if (itemId == R.id.delete) { } else if (itemId == R.id.delete) {
dismissYourTrainSelection(); mSwipeHelper.dismissWithAnimation(true);
mode.finish(); mode.finish();
return true; return true;
} }
@ -770,11 +772,37 @@ public class ViewDeparturesActivity extends SActivity implements
refreshBoardedDeparture(); refreshBoardedDeparture();
getListAdapter().notifyDataSetChanged(); getListAdapter().notifyDataSetChanged();
refreshListSelection();
} }
} }
}); });
} }
private void refreshListSelection() {
getListView().clearChoices();
final Departure targetDeparture;
if (isDepartureActionModeActive() && mSelectedDeparture != null) {
targetDeparture = mSelectedDeparture;
} else {
targetDeparture = ((BartRunnerApplication) getApplication())
.getBoardedDeparture();
}
for (int i = getListAdapter().getCount() - 1; i >= 0; i--) {
if (getListAdapter().getItem(i).equals(targetDeparture)) {
final int selectedIndex = i;
getListView().post(new Runnable() {
@Override
public void run() {
getListView().setSelection(selectedIndex);
}
});
break;
}
}
getListView().requestLayout();
}
@Override @Override
public void onError(final String errorMessage) { public void onError(final String errorMessage) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@ -816,11 +844,12 @@ public class ViewDeparturesActivity extends SActivity implements
} }
private void hideYourTrainSection() { private void hideYourTrainSection() {
findViewById(R.id.yourTrainSection).setVisibility(View.GONE); mYourTrainSection.setVisibility(View.GONE);
} }
private void showYourTrainSection(final YourTrainLayout yourTrainSection) { private void showYourTrainSection() {
yourTrainSection.setVisibility(View.VISIBLE); mYourTrainSection.setVisibility(View.VISIBLE);
mSwipeHelper.showWithAnimation();
} }
private boolean isYourTrainActionModeActive() { private boolean isYourTrainActionModeActive() {

View File

@ -1,4 +1,5 @@
/* /*
* Copyright 2012 Doug Keen
* Copyright 2012 Roman Nurik * Copyright 2012 Roman Nurik
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,23 +19,27 @@
package com.dougkeen.bart.controls; package com.dougkeen.bart.controls;
import static com.nineoldandroids.view.ViewHelper.setAlpha;
import static com.nineoldandroids.view.ViewHelper.setTranslationX;
import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.VelocityTracker; import android.view.VelocityTracker;
import android.view.View; import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.dougkeen.bart.model.Constants;
import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter; import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator; import com.nineoldandroids.animation.ValueAnimator;
import static com.nineoldandroids.view.ViewHelper.setAlpha;
import static com.nineoldandroids.view.ViewHelper.setTranslationX;
import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
/** /**
* A {@link android.view.View.OnTouchListener} that makes any {@link View} * A utility class for animating the dismissal of a view (with 'onDismiss'
* dismissable when the user swipes (drags her finger) horizontally across the * callback). Also, a {@link android.view.View.OnTouchListener} that makes any
* view. * {@link View} dismissable when the user swipes (drags her finger) horizontally
* across the view.
* *
* <p> * <p>
* <em>For {@link android.widget.ListView} list items that don't manage their own touch events * <em>For {@link android.widget.ListView} list items that don't manage their own touch events
@ -49,10 +54,10 @@ import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
* </p> * </p>
* *
* <pre> * <pre>
* view.setOnTouchListener(new SwipeDismissTouchListener(view, null, // Optional * view.setOnTouchListener(new SwipeDismisser(view, null, // Optional
* // token/cookie * // token/cookie
* // object * // object
* new SwipeDismissTouchListener.OnDismissCallback() { * new SwipeDismisser.OnDismissCallback() {
* public void onDismiss(View view, Object token) { * public void onDismiss(View view, Object token) {
* parent.removeView(view); * parent.removeView(view);
* } * }
@ -61,7 +66,7 @@ import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
* *
* @see SwipeDismissListViewTouchListener * @see SwipeDismissListViewTouchListener
*/ */
public class SwipeDismissTouchListener implements View.OnTouchListener { public class SwipeHelper implements View.OnTouchListener {
// Cached ViewConfiguration and system-wide constant values // Cached ViewConfiguration and system-wide constant values
private int mSlop; private int mSlop;
private int mMinFlingVelocity; private int mMinFlingVelocity;
@ -81,9 +86,8 @@ public class SwipeDismissTouchListener implements View.OnTouchListener {
private float mTranslationX; private float mTranslationX;
/** /**
* The callback interface used by {@link SwipeDismissTouchListener} to * The callback interface used by {@link SwipeHelper} to inform its
* inform its client about a successful dismissal of the view for which it * client about a successful dismissal of the view for which it was created.
* was created.
*/ */
public interface OnDismissCallback { public interface OnDismissCallback {
/** /**
@ -110,8 +114,7 @@ public class SwipeDismissTouchListener implements View.OnTouchListener {
* The callback to trigger when the user has indicated that she * The callback to trigger when the user has indicated that she
* would like to dismiss this view. * would like to dismiss this view.
*/ */
public SwipeDismissTouchListener(View view, Object token, public SwipeHelper(View view, Object token, OnDismissCallback callback) {
OnDismissCallback callback) {
ViewConfiguration vc = ViewConfiguration.get(view.getContext()); ViewConfiguration vc = ViewConfiguration.get(view.getContext());
mSlop = vc.getScaledTouchSlop(); mSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
@ -125,6 +128,7 @@ public class SwipeDismissTouchListener implements View.OnTouchListener {
@Override @Override
public boolean onTouch(View view, MotionEvent motionEvent) { public boolean onTouch(View view, MotionEvent motionEvent) {
Log.v(Constants.TAG, "onTouch()");
// offset because the view is translated during swipe // offset because the view is translated during swipe
motionEvent.offsetLocation(mTranslationX, 0); motionEvent.offsetLocation(mTranslationX, 0);
@ -164,15 +168,7 @@ public class SwipeDismissTouchListener implements View.OnTouchListener {
} }
if (dismiss) { if (dismiss) {
// dismiss // dismiss
animate(mView) dismissWithAnimation(dismissRight);
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0).setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
performDismiss();
}
});
} else { } else {
// cancel // cancel
animate(mView).translationX(0).alpha(1) animate(mView).translationX(0).alpha(1)
@ -222,6 +218,17 @@ public class SwipeDismissTouchListener implements View.OnTouchListener {
return false; return false;
} }
public void dismissWithAnimation(boolean dismissRight) {
animate(mView).translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0).setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
performDismiss();
}
});
}
private void performDismiss() { private void performDismiss() {
// Animate the dismissed view to zero-height and then fire the dismiss // Animate the dismissed view to zero-height and then fire the dismiss
// callback. // callback.
@ -257,4 +264,46 @@ public class SwipeDismissTouchListener implements View.OnTouchListener {
animator.start(); animator.start();
} }
public void showWithAnimation() {
final int measureSpec = MeasureSpec.makeMeasureSpec(
ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY);
mView.measure(measureSpec, measureSpec);
mViewWidth = mView.getMeasuredWidth();
final int viewHeight = mView.getMeasuredHeight();
setAlpha(mView, 0f);
final ViewGroup.LayoutParams lp = mView.getLayoutParams();
lp.width = mViewWidth;
setTranslationX(mView, mViewWidth);
// Grow space
ValueAnimator animator = ValueAnimator.ofInt(1, viewHeight)
.setDuration(mAnimationTime);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Reset view presentation
// mView.requestLayout();
// Swipe view into space that opened up
animate(mView).translationX(0).alpha(1)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
});
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
lp.height = (Integer) valueAnimator.getAnimatedValue();
mView.setLayoutParams(lp);
}
});
animator.start();
}
} }