+ * Also the setting of absolute offsets (similar to translationX/Y), rather than additive
+ * offsets.
+ */
+public final class ViewOffsetHelper {
+
+ private final View mView;
+
+ private int mLayoutTop;
+ private int mLayoutLeft;
+ private int mOffsetTop;
+ private int mOffsetLeft;
+
+ public ViewOffsetHelper(View view) {
+ mView = view;
+ }
+
+ public void onViewLayout() {
+ // Now grab the intended top
+ mLayoutTop = mView.getTop();
+ mLayoutLeft = mView.getLeft();
+
+ // And offset it as needed
+ updateOffsets();
+ }
+
+ private void updateOffsets() {
+ ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop));
+ ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft));
+
+ // Manually invalidate the view and parent to make sure we get drawn pre-M
+ if (Build.VERSION.SDK_INT < 23) {
+ tickleInvalidationFlag(mView);
+ final ViewParent vp = mView.getParent();
+ if (vp instanceof View) {
+ tickleInvalidationFlag((View) vp);
+ }
+ }
+ }
+
+ private static void tickleInvalidationFlag(View view) {
+ final float x = ViewCompat.getTranslationX(view);
+ ViewCompat.setTranslationY(view, x + 1);
+ ViewCompat.setTranslationY(view, x);
+ }
+
+ /**
+ * Set the top and bottom offset for this {@link ViewOffsetHelper}'s view.
+ *
+ * @param offset the offset in px.
+ * @return true if the offset has changed
+ */
+ public boolean setTopAndBottomOffset(int offset) {
+ if (mOffsetTop != offset) {
+ mOffsetTop = offset;
+ updateOffsets();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set the left and right offset for this {@link ViewOffsetHelper}'s view.
+ *
+ * @param offset the offset in px.
+ * @return true if the offset has changed
+ */
+ public boolean setLeftAndRightOffset(int offset) {
+ if (mOffsetLeft != offset) {
+ mOffsetLeft = offset;
+ updateOffsets();
+ return true;
+ }
+ return false;
+ }
+
+ public int getTopAndBottomOffset() {
+ return mOffsetTop;
+ }
+
+ public int getLeftAndRightOffset() {
+ return mOffsetLeft;
+ }
+
+ public static final Propertyfloat
. This type-specific subclass enables performance benefit by allowing
+ * calls to a {@link #set(Object, Float) set()} function that takes the primitive
+ * float
type and avoids autoboxing and other overhead associated with the
+ * Float
class.
+ *
+ * @param float
.
+ */
+ public abstract void setValue(T object, float value);
+
+ @Override
+ final public void set(T object, Float value) {
+ setValue(object, value);
+ }
+ }
+
+ /**
+ * An implementation of {@link android.util.Property} to be used specifically with fields of
+ * type
+ * int
. This type-specific subclass enables performance benefit by allowing
+ * calls to a {@link #set(Object, Integer) set()} function that takes the primitive
+ * int
type and avoids autoboxing and other overhead associated with the
+ * Integer
class.
+ *
+ * @param int
.
+ */
+ public abstract void setValue(T object, int value);
+
+ @Override
+ final public void set(T object, Integer value) {
+ setValue(object, value.intValue());
+ }
+
+ }
+
+ /**
+ * https://halfthought.wordpress.com/2014/11/07/reveal-transition/
+ *
+ * Interrupting Activity transitions can yield an OperationNotSupportedException when the
+ * transition tries to pause the animator. Yikes! We can fix this by wrapping the Animator:
+ */
+ public static class NoPauseAnimator extends Animator {
+ private final Animator mAnimator;
+ private final ArrayMap