/* * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.litho.animation; import android.view.View; import android.view.ViewParent; import com.facebook.litho.LithoView; /** * A convenience class for common View properties applicable to all subclasses of View. */ public final class AnimatedProperties { /** * The absolute X-position of a mount item, relative to the {@link com.facebook.litho.LithoView} * that is rendering this component tree. */ public static final AnimatedProperty X = new XAnimatedProperty(); /** * The absolute Y-position of a mount item, relative to the {@link com.facebook.litho.LithoView} * that is rendering this component tree. */ public static final AnimatedProperty Y = new YAnimatedProperty(); /** * The width of a mount item. */ public static final AnimatedProperty WIDTH = new WidthAnimatedProperty(); /** * The height of a mount item. */ public static final AnimatedProperty HEIGHT = new HeightAnimatedProperty(); /** * The transparency of a mount item. */ public static final AnimatedProperty ALPHA = new AlphaAnimatedProperty(); /** * The scale of a mount item: treats both X- and Y-scales as one. */ public static final AnimatedProperty SCALE = new ScaleAnimatedProperty(); /** * The x-scale of a mount item. */ public static final AnimatedProperty SCALE_X = new ScaleXAnimatedProperty(); /** * The y-scale of a mount item. */ public static final AnimatedProperty SCALE_Y = new ScaleYAnimatedProperty(); private AnimatedProperties() { } private static View assertIsView(Object mountItem, AnimatedProperty property) { if (!(mountItem instanceof View)) { throw new RuntimeException( "Animating '" + property.getName() + "' is only supported on Views"); } return (View) mountItem; } private static class XAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "x"; } @Override public float get(Object mountItem) { return getPositionRelativeToLithoView(assertIsView(mountItem, this), true); } @Override public void set(Object mountItem, float value) { View mountView = assertIsView(mountItem, this); float parentX = getPositionRelativeToLithoView((View) mountView.getParent(), true); mountView.setX(value - parentX); } @Override public void reset(Object mountItem) { assertIsView(mountItem, this).setTranslationX(0); } } private static class YAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "y"; } @Override public float get(Object mountItem) { return getPositionRelativeToLithoView(assertIsView(mountItem, this), false); } @Override public void set(Object mountItem, float value) { View mountView = assertIsView(mountItem, this); float parentY = getPositionRelativeToLithoView((View) mountView.getParent(), false); mountView.setY(value - parentY); } @Override public void reset(Object mountItem) { assertIsView(mountItem, this).setTranslationY(0); } }; private static class WidthAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "width"; } @Override public float get(Object mountItem) { return assertIsView(mountItem, this).getWidth(); } @Override public void set(Object mountItem, float value) { throw new UnsupportedOperationException("Setting width in animations is not supported yet."); } @Override public void reset(Object mountItem) { // No-op: height/width are always properly set at mount time so we don't need to reset it. } } private static class HeightAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "height"; } @Override public float get(Object mountItem) { return assertIsView(mountItem, this).getHeight(); } @Override public void set(Object mountItem, float value) { throw new UnsupportedOperationException("Setting height in animations is not supported yet."); } @Override public void reset(Object mountItem) { // No-op: height/width are always properly set at mount time so we don't need to reset it. } }; private static class AlphaAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "alpha"; } @Override public float get(Object mountItem) { return assertIsView(mountItem, this).getAlpha(); } @Override public void set(Object mountItem, float value) { assertIsView(mountItem, this).setAlpha(value); } @Override public void reset(Object mountItem) { assertIsView(mountItem, this).setAlpha(1); } } private static class ScaleAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "scale"; } @Override public float get(Object mountItem) { final View asView = assertIsView(mountItem, this); final float scale = asView.getScaleX(); if (scale != asView.getScaleY()) { throw new RuntimeException( "Tried to get scale of view, but scaleX and scaleY are different"); } return scale; } @Override public void set(Object mountItem, float value) { final View asView = assertIsView(mountItem, this); asView.setScaleX(value); asView.setScaleY(value); } @Override public void reset(Object mountItem) { final View asView = assertIsView(mountItem, this); asView.setScaleX(1); asView.setScaleY(1); } } private static class ScaleXAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "scale_x"; } @Override public float get(Object mountItem) { return assertIsView(mountItem, this).getScaleX(); } @Override public void set(Object mountItem, float value) { assertIsView(mountItem, this).setScaleX(value); } @Override public void reset(Object mountItem) { assertIsView(mountItem, this).setScaleX(1); } } private static class ScaleYAnimatedProperty implements AnimatedProperty { @Override public String getName() { return "scale_y"; } @Override public float get(Object mountItem) { return assertIsView(mountItem, this).getScaleY(); } @Override public void set(Object mountItem, float value) { assertIsView(mountItem, this).setScaleY(value); } @Override public void reset(Object mountItem) { assertIsView(mountItem, this).setScaleY(1); } } /** * @return the x or y position of the given view relative to the LithoView that this ComponentTree * is being rendered in to. */ private static float getPositionRelativeToLithoView(View mountItem, boolean getX) { float pos = 0; View currentView = mountItem; while (true) { if (currentView == null) { throw new RuntimeException("Got unexpected null parent"); } if (currentView instanceof LithoView) { return pos; } pos += getX ? currentView.getX() : currentView.getY(); if (!(mountItem.getParent() instanceof View)) { throw new RuntimeException("Expected parent to be View, was " + mountItem.getParent()); } currentView = (View) currentView.getParent(); } } }