Skip to content

Instantly share code, notes, and snippets.

@cmc5788
Created August 20, 2017 16:28
Show Gist options
  • Save cmc5788/8b0d0ab9c4c4112aa65eda28aae40d8f to your computer and use it in GitHub Desktop.
Save cmc5788/8b0d0ab9c4c4112aa65eda28aae40d8f to your computer and use it in GitHub Desktop.
Rx animation utils for android
public final class AnimUtil {
public static final ViewAction VISIBLE_RESET = new ViewAction() //
.visibility(View.VISIBLE).alpha(1f).scale(1f).translationX(0).translationY(0).lock();
public static final ViewAction GONE_RESET = new ViewAction() //
.visibility(View.GONE).alpha(1f).scale(1f).translationX(0).translationY(0).lock();
public static final ViewAction VISIBLE_RESET_ZERO_ALPHA = new ViewAction() //
.visibility(View.VISIBLE).alpha(0f).scale(1f).translationX(0).translationY(0).lock();
public static final ViewAction GONE_RESET_ZERO_ALPHA = new ViewAction() //
.visibility(View.GONE).alpha(0f).scale(1f).translationX(0).translationY(0).lock();
public static Observable<?> fadeInAndVisible(@NonNull View v) {
TimeInterpolator interp = new AccelerateDecelerateInterpolator();
return new ViewAnim() //
.alpha(1f) //
.interpolator(interp) //
.observe(v) //
.doOnNext(VISIBLE_RESET_ZERO_ALPHA.consumer()) //
.doOnComplete(VISIBLE_RESET.action(v));
}
public static Observable<?> fadeOutAndGone(@NonNull View v) {
TimeInterpolator interp = new AccelerateDecelerateInterpolator();
return new ViewAnim() //
.alpha(0f) //
.interpolator(interp)
.observe(v)
.doOnNext(VISIBLE_RESET.consumer())
.doOnComplete(GONE_RESET.action(v));
}
public static Observable<?> fadeOutAndIn(@NonNull View v) {
return fadeOutAndIn(v, null);
}
public static Observable<?> fadeOutAndIn(@NonNull View v, @Nullable ViewAction halfway) {
halfway = new ViewAction(VISIBLE_RESET_ZERO_ALPHA).include(halfway);
TimeInterpolator interp = new AccelerateDecelerateInterpolator();
return new ViewAnim() //
.alpha(0f) //
.interpolator(interp) //
.observe(v) //
.doOnNext(VISIBLE_RESET.consumer()) //
.concatWith(new ViewAnim() //
.alpha(1f) //
.interpolator(interp) //
.observe(v) //
.doOnNext(halfway.consumer())) //
.doOnComplete(VISIBLE_RESET.action(v));
}
public static Observable<?> fadeOutAndInSlide(@NonNull View v, int px) {
return fadeOutAndInSlide(v, px, null);
}
public static Observable<?> fadeOutAndInSlide(@NonNull View v, int px,
@Nullable ViewAction halfway) {
halfway = new ViewAction(VISIBLE_RESET_ZERO_ALPHA).translationX(px).include(halfway);
TimeInterpolator interp = new AccelerateDecelerateInterpolator();
return new ViewAnim() //
.alpha(0f) //
.translationX(-px) //
.interpolator(interp) //
.observe(v) //
.doOnNext(VISIBLE_RESET.consumer()) //
.concatWith(new ViewAnim() //
.alpha(1f) //
.translationX(0) //
.interpolator(interp) //
.observe(v) //
.doOnNext(halfway.consumer())) //
.doOnComplete(VISIBLE_RESET.action(v));
}
public static Observable<?> fadeOutAndInScale(@NonNull View v, float scale) {
return fadeOutAndInScale(v, scale, null);
}
public static Observable<?> fadeOutAndInScale(@NonNull View v, float scale,
@Nullable ViewAction halfway) {
halfway = new ViewAction(VISIBLE_RESET_ZERO_ALPHA).scale(scale).include(halfway);
TimeInterpolator interp = new AccelerateDecelerateInterpolator();
return new ViewAnim() //
.alpha(0f) //
.scale(scale) //
.interpolator(interp) //
.observe(v) //
.doOnNext(VISIBLE_RESET.consumer()) //
.concatWith(new ViewAnim() //
.alpha(1f) //
.scale(1f) //
.interpolator(interp) //
.observe(v) //
.doOnNext(halfway.consumer())) //
.doOnComplete(VISIBLE_RESET.action(v));
}
@IntDef({ View.VISIBLE, View.INVISIBLE, View.GONE })
@Retention(RetentionPolicy.SOURCE)
public @interface Visibility {
}
public static class ViewAction {
private Float alpha;
private Float scaleX;
private Float scaleY;
private Float translationX;
private Float translationY;
private Integer visibility;
private CharSequence text;
private Integer textSizeUnit;
private Float textSizeSize;
private List<ViewAction> includes;
private boolean locked;
public ViewAction() {
}
public ViewAction(@NonNull ViewAction other) {
this.alpha = other.alpha;
this.scaleX = other.scaleX;
this.scaleY = other.scaleY;
this.translationX = other.translationX;
this.translationY = other.translationY;
this.visibility = other.visibility;
this.text = other.text;
this.textSizeUnit = other.textSizeUnit;
this.textSizeSize = other.textSizeSize;
this.includes = other.includes == null
? null
: new ArrayList<>(other.includes);
}
public ViewAction alpha(float alpha) {
assertNotLocked();
this.alpha = alpha;
return this;
}
public ViewAction scaleX(float scaleX) {
assertNotLocked();
this.scaleX = scaleX;
return this;
}
public ViewAction scaleY(float scaleY) {
assertNotLocked();
this.scaleY = scaleY;
return this;
}
public ViewAction scale(float scale) {
assertNotLocked();
this.scaleX = this.scaleY = scale;
return this;
}
public ViewAction translationX(float translationX) {
assertNotLocked();
this.translationX = translationX;
return this;
}
public ViewAction translationY(float translationY) {
assertNotLocked();
this.translationY = translationY;
return this;
}
public ViewAction visibility(@Visibility int visibility) {
assertNotLocked();
this.visibility = visibility;
return this;
}
public ViewAction text(CharSequence text) {
assertNotLocked();
this.text = text;
return this;
}
public ViewAction textSize(int unit, float size) {
assertNotLocked();
this.textSizeUnit = unit;
this.textSizeSize = size;
return this;
}
public ViewAction include(ViewAction action) {
assertNotLocked();
if (action == null) return this;
if (this.includes == null) this.includes = new ArrayList<>();
this.includes.add(action);
return this;
}
public ViewAction lock() {
assertNotLocked();
this.locked = true;
return this;
}
void assertNotLocked() {
if (locked) throw new IllegalStateException("not while locked.");
}
public Action action(@NonNull View v) {
final WeakReference<View> vref = new WeakReference<>(v);
final Consumer<View> c = consumer();
return new Action() {
@Override
public void run() throws Exception {
View v = vref.get();
if (v != null) c.accept(v);
}
};
}
public Consumer<View> consumer() {
final Float a = alpha;
final Float sx = scaleX;
final Float sy = scaleY;
final Float tx = translationX;
final Float ty = translationY;
final Integer vis = visibility;
final CharSequence txt = text;
final Integer txtUnit = textSizeUnit;
final Float txtSize = textSizeSize;
final List<ViewAction> inc = includes == null
? null
: new ArrayList<>(includes);
return new Consumer<View>() {
@SuppressWarnings("WrongConstant")
@Override
public void accept(@NonNull View v) throws Exception {
if (a != null) v.setAlpha(a);
if (sx != null) v.setScaleX(sx);
if (sy != null) v.setScaleY(sy);
if (tx != null) v.setTranslationX(tx);
if (ty != null) v.setTranslationY(ty);
if (vis != null) v.setVisibility(vis);
if (v instanceof TextView) {
TextView tv = (TextView) v;
if (txt != null) tv.setText(txt);
if (txtUnit != null && txtSize != null) tv.setTextSize(txtUnit, txtSize);
}
if (inc != null) for (ViewAction i : inc) i.action(v).run();
}
};
}
}
public static final class ViewAnim {
private Float alpha;
private Float scaleX;
private Float scaleY;
private Float translationX;
private Float translationY;
private Long duration;
private Long startDelay;
private TimeInterpolator interpolator;
private boolean locked;
public ViewAnim() {
}
public ViewAnim(@NonNull ViewAnim other) {
this.alpha = other.alpha;
this.scaleX = other.scaleX;
this.scaleY = other.scaleY;
this.translationX = other.translationX;
this.translationY = other.translationY;
this.duration = other.duration;
this.startDelay = other.startDelay;
this.interpolator = other.interpolator;
}
public ViewAnim alpha(float alpha) {
assertNotLocked();
this.alpha = alpha;
return this;
}
public ViewAnim scaleX(float scaleX) {
assertNotLocked();
this.scaleX = scaleX;
return this;
}
public ViewAnim scaleY(float scaleY) {
assertNotLocked();
this.scaleY = scaleY;
return this;
}
public ViewAnim scale(float scale) {
assertNotLocked();
this.scaleX = this.scaleY = scale;
return this;
}
public ViewAnim translationX(float translationX) {
assertNotLocked();
this.translationX = translationX;
return this;
}
public ViewAnim translationY(float translationY) {
assertNotLocked();
this.translationY = translationY;
return this;
}
public ViewAnim duration(long duration) {
assertNotLocked();
this.duration = duration;
return this;
}
public ViewAnim startDelay(long startDelay) {
assertNotLocked();
this.startDelay = startDelay;
return this;
}
public ViewAnim interpolator(TimeInterpolator interpolator) {
assertNotLocked();
this.interpolator = interpolator;
return this;
}
public ViewAnim lock() {
assertNotLocked();
this.locked = true;
return this;
}
void assertNotLocked() {
if (locked) throw new IllegalStateException("not while locked.");
}
public Observable<View> observe(@NonNull View v) {
final WeakReference<View> vref = new WeakReference<>(v);
final Float a = this.alpha;
final Float sx = this.scaleX;
final Float sy = this.scaleY;
final Float tx = this.translationX;
final Float ty = this.translationY;
final Long dur = this.duration;
final Long del = this.startDelay;
final TimeInterpolator interp = this.interpolator;
return Observable.create(new ObservableOnSubscribe<View>() {
@Override
public void subscribe(@NonNull final ObservableEmitter<View> e) {
if (e.isDisposed()) return;
if (currentThread() != getMainLooper().getThread()) {
e.onError(new RuntimeException("cannot animate off main thread"));
return;
}
View v = vref.get();
if (v == null) {
e.onComplete();
return;
}
e.onNext(v);
List<PropertyValuesHolder> p = new ArrayList<>();
if (a != null) p.add(PropertyValuesHolder.ofFloat(ALPHA, v.getAlpha(), a));
if (sx != null) p.add(PropertyValuesHolder.ofFloat(SCALE_X, v.getScaleX(), sx));
if (sy != null) p.add(PropertyValuesHolder.ofFloat(SCALE_Y, v.getScaleY(), sy));
if (tx != null) {
p.add(PropertyValuesHolder.ofFloat(TRANSLATION_X, v.getTranslationX(), tx));
}
if (ty != null) {
p.add(PropertyValuesHolder.ofFloat(TRANSLATION_Y, v.getTranslationY(), ty));
}
if (p.isEmpty()) {
e.onComplete();
return;
}
final ObjectAnimator oa =
ObjectAnimator.ofPropertyValuesHolder(v, (PropertyValuesHolder[]) p.toArray());
if (dur != null) {
oa.setDuration(dur);
} else {
oa.setDuration(new ValueAnimator().getDuration());
}
if (del != null) {
oa.setStartDelay(del);
} else {
oa.setStartDelay(0L);
}
if (interp != null) {
oa.setInterpolator(interp);
} else {
oa.setInterpolator(new ValueAnimator().getInterpolator());
}
final AtomicBoolean done = new AtomicBoolean();
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator a) {
if (done.compareAndSet(false, true)) {
if (!e.isDisposed()) e.onComplete();
}
}
@Override
public void onAnimationEnd(Animator a) {
if (done.compareAndSet(false, true)) {
if (!e.isDisposed()) e.onComplete();
}
}
});
e.setDisposable(Disposables.fromRunnable(new Runnable() {
@Override
public void run() {
if (done.compareAndSet(false, true)) {
oa.cancel();
}
}
}));
oa.start();
}
});
}
}
private AnimUtil() {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment