public class

WidgetFrame

extends java.lang.Object

 java.lang.Object

↳androidx.constraintlayout.core.state.WidgetFrame

Gradle dependencies

compile group: 'androidx.constraintlayout', name: 'constraintlayout-core', version: '1.1.0-alpha01'

  • groupId: androidx.constraintlayout
  • artifactId: constraintlayout-core
  • version: 1.1.0-alpha01

Artifact androidx.constraintlayout:constraintlayout-core:1.1.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Utility class to encapsulate layout of a widget

Summary

Fields
public floatalpha

public intbottom

public floatinterpolatedPos

public intleft

public final java.util.HashMap<java.lang.String, CustomVariable>mCustom

public java.lang.Stringname

public static floatphone_orientation

public floatpivotX

public floatpivotY

public intright

public floatrotationX

public floatrotationY

public floatrotationZ

public floatscaleX

public floatscaleY

public inttop

public floattranslationX

public floattranslationY

public floattranslationZ

public intvisibility

public ConstraintWidgetwidget

Constructors
publicWidgetFrame()

publicWidgetFrame(ConstraintWidget widget)

publicWidgetFrame(WidgetFrame frame)

Methods
public voidaddCustomColor(java.lang.String name, int color)

public voidaddCustomFloat(java.lang.String name, float value)

public floatcenterX()

public floatcenterY()

public CustomVariablegetCustomAttribute(java.lang.String name)

public java.util.Set<java.lang.String>getCustomAttributeNames()

public intgetCustomColor(java.lang.String name)

public floatgetCustomFloat(java.lang.String name)

public java.lang.StringgetId()

public TypedBundlegetMotionProperties()

get the property bundle associated with MotionAttributes

public intheight()

public static voidinterpolate(int parentWidth, int parentHeight, WidgetFrame frame, WidgetFrame start, WidgetFrame end, Transition transition, float progress)

public booleanisDefaultTransform()

public java.lang.StringBuilderserialize(java.lang.StringBuilder ret)

public java.lang.StringBuilderserialize(java.lang.StringBuilder ret, boolean sendPhoneOrientation)

If true also send the phone orientation

public voidsetCustomAttribute(java.lang.String name, int type, boolean value)

public voidsetCustomAttribute(java.lang.String name, int type, float value)

public voidsetCustomAttribute(java.lang.String name, int type, int value)

public voidsetCustomAttribute(java.lang.String name, int type, java.lang.String value)

public voidsetCustomValue(CustomAttribute valueAt, float[] mTempValues[])

public booleansetValue(java.lang.String key, CLElement value)

public WidgetFrameupdate()

public WidgetFrameupdate(ConstraintWidget widget)

public voidupdateAttributes(WidgetFrame frame)

public intwidth()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public ConstraintWidget widget

public int left

public int top

public int right

public int bottom

public float pivotX

public float pivotY

public float rotationX

public float rotationY

public float rotationZ

public float translationX

public float translationY

public float translationZ

public static float phone_orientation

public float scaleX

public float scaleY

public float alpha

public float interpolatedPos

public int visibility

public final java.util.HashMap<java.lang.String, CustomVariable> mCustom

public java.lang.String name

Constructors

public WidgetFrame()

public WidgetFrame(ConstraintWidget widget)

public WidgetFrame(WidgetFrame frame)

Methods

public int width()

public int height()

public void updateAttributes(WidgetFrame frame)

public boolean isDefaultTransform()

public static void interpolate(int parentWidth, int parentHeight, WidgetFrame frame, WidgetFrame start, WidgetFrame end, Transition transition, float progress)

public float centerX()

public float centerY()

public WidgetFrame update()

public WidgetFrame update(ConstraintWidget widget)

public void addCustomColor(java.lang.String name, int color)

public int getCustomColor(java.lang.String name)

public void addCustomFloat(java.lang.String name, float value)

public float getCustomFloat(java.lang.String name)

public void setCustomAttribute(java.lang.String name, int type, float value)

public void setCustomAttribute(java.lang.String name, int type, int value)

public void setCustomAttribute(java.lang.String name, int type, boolean value)

public void setCustomAttribute(java.lang.String name, int type, java.lang.String value)

public CustomVariable getCustomAttribute(java.lang.String name)

public java.util.Set<java.lang.String> getCustomAttributeNames()

public boolean setValue(java.lang.String key, CLElement value)

public java.lang.String getId()

public java.lang.StringBuilder serialize(java.lang.StringBuilder ret)

public java.lang.StringBuilder serialize(java.lang.StringBuilder ret, boolean sendPhoneOrientation)

If true also send the phone orientation

public void setCustomValue(CustomAttribute valueAt, float[] mTempValues[])

public TypedBundle getMotionProperties()

get the property bundle associated with MotionAttributes

Returns:

the property bundle associated with MotionAttributes or null

Source

/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.constraintlayout.core.state;

import androidx.constraintlayout.core.motion.CustomAttribute;
import androidx.constraintlayout.core.motion.CustomVariable;
import androidx.constraintlayout.core.motion.utils.TypedBundle;
import androidx.constraintlayout.core.motion.utils.TypedValues;
import androidx.constraintlayout.core.parser.CLElement;
import androidx.constraintlayout.core.parser.CLKey;
import androidx.constraintlayout.core.parser.CLNumber;
import androidx.constraintlayout.core.parser.CLObject;
import androidx.constraintlayout.core.parser.CLParsingException;
import androidx.constraintlayout.core.widgets.ConstraintAnchor;
import androidx.constraintlayout.core.widgets.ConstraintWidget;

import java.util.HashMap;
import java.util.Set;

/**
 * Utility class to encapsulate layout of a widget
 */
public class WidgetFrame {
    private static final boolean OLD_SYSTEM = true;
    public ConstraintWidget widget = null;
    public int left = 0;
    public int top = 0;
    public int right = 0;
    public int bottom = 0;

    // transforms

    public float pivotX = Float.NaN;
    public float pivotY = Float.NaN;

    public float rotationX = Float.NaN;
    public float rotationY = Float.NaN;
    public float rotationZ = Float.NaN;

    public float translationX = Float.NaN;
    public float translationY = Float.NaN;
    public float translationZ = Float.NaN;
    public static float phone_orientation = Float.NaN;

    public float scaleX = Float.NaN;
    public float scaleY = Float.NaN;

    public float alpha = Float.NaN;
    public float interpolatedPos = Float.NaN;

    public int visibility = ConstraintWidget.VISIBLE;

    public final HashMap<String, CustomVariable> mCustom = new HashMap<>();

    public String name = null;

    TypedBundle mMotionProperties;

    /**
     * @TODO: add description
     */
    public int width() {
        return Math.max(0, right - left);
    }

    /**
     * @TODO: add description
     */
    public int height() {
        return Math.max(0, bottom - top);
    }

    public WidgetFrame() {
    }

    public WidgetFrame(ConstraintWidget widget) {
        this.widget = widget;
    }

    public WidgetFrame(WidgetFrame frame) {
        widget = frame.widget;
        left = frame.left;
        top = frame.top;
        right = frame.right;
        bottom = frame.bottom;
        updateAttributes(frame);
    }

    /**
     * @TODO: add description
     */
    public void updateAttributes(WidgetFrame frame) {
        pivotX = frame.pivotX;
        pivotY = frame.pivotY;
        rotationX = frame.rotationX;
        rotationY = frame.rotationY;
        rotationZ = frame.rotationZ;
        translationX = frame.translationX;
        translationY = frame.translationY;
        translationZ = frame.translationZ;
        scaleX = frame.scaleX;
        scaleY = frame.scaleY;
        alpha = frame.alpha;
        visibility = frame.visibility;
        setMotionAttributes(frame.mMotionProperties);
        mCustom.clear();
        if (frame != null) {
            for (CustomVariable c : frame.mCustom.values()) {
                mCustom.put(c.getName(), c.copy());
            }
        }
    }

    public boolean isDefaultTransform() {
        return Float.isNaN(rotationX)
                && Float.isNaN(rotationY)
                && Float.isNaN(rotationZ)
                && Float.isNaN(translationX)
                && Float.isNaN(translationY)
                && Float.isNaN(translationZ)
                && Float.isNaN(scaleX)
                && Float.isNaN(scaleY)
                && Float.isNaN(alpha);
    }

    /**
     * @TODO: add description
     */
    public static void interpolate(int parentWidth,
            int parentHeight,
            WidgetFrame frame,
            WidgetFrame start,
            WidgetFrame end,
            Transition transition,
            float progress) {
        int frameNumber = (int) (progress * 100);
        int startX = start.left;
        int startY = start.top;
        int endX = end.left;
        int endY = end.top;
        int startWidth = start.right - start.left;
        int startHeight = start.bottom - start.top;
        int endWidth = end.right - end.left;
        int endHeight = end.bottom - end.top;

        float progressPosition = progress;

        float startAlpha = start.alpha;
        float endAlpha = end.alpha;

        if (start.visibility == ConstraintWidget.GONE) {
            // On visibility gone, keep the same size to do an alpha to zero
            startX -= endWidth / 2f;
            startY -= endHeight / 2f;
            startWidth = endWidth;
            startHeight = endHeight;
            if (Float.isNaN(startAlpha)) {
                // override only if not defined...
                startAlpha = 0f;
            }
        }

        if (end.visibility == ConstraintWidget.GONE) {
            // On visibility gone, keep the same size to do an alpha to zero
            endX -= startWidth / 2f;
            endY -= startHeight / 2f;
            endWidth = startWidth;
            endHeight = startHeight;
            if (Float.isNaN(endAlpha)) {
                // override only if not defined...
                endAlpha = 0f;
            }
        }

        if (Float.isNaN(startAlpha) && !Float.isNaN(endAlpha)) {
            startAlpha = 1f;
        }
        if (!Float.isNaN(startAlpha) && Float.isNaN(endAlpha)) {
            endAlpha = 1f;
        }

        if (start.visibility == ConstraintWidget.INVISIBLE) {
            startAlpha = 0f;
        }

        if (end.visibility == ConstraintWidget.INVISIBLE) {
            endAlpha = 0f;
        }

        if (frame.widget != null && transition.hasPositionKeyframes()) {
            Transition.KeyPosition firstPosition =
                    transition.findPreviousPosition(frame.widget.stringId, frameNumber);
            Transition.KeyPosition lastPosition =
                    transition.findNextPosition(frame.widget.stringId, frameNumber);

            if (firstPosition == lastPosition) {
                lastPosition = null;
            }
            int interpolateStartFrame = 0;
            int interpolateEndFrame = 100;

            if (firstPosition != null) {
                startX = (int) (firstPosition.mX * parentWidth);
                startY = (int) (firstPosition.mY * parentHeight);
                interpolateStartFrame = firstPosition.mFrame;
            }
            if (lastPosition != null) {
                endX = (int) (lastPosition.mX * parentWidth);
                endY = (int) (lastPosition.mY * parentHeight);
                interpolateEndFrame = lastPosition.mFrame;
            }

            progressPosition = (progress * 100f - interpolateStartFrame)
                    / (float) (interpolateEndFrame - interpolateStartFrame);
        }

        frame.widget = start.widget;

        frame.left = (int) (startX + progressPosition * (endX - startX));
        frame.top = (int) (startY + progressPosition * (endY - startY));
        int width = (int) ((1 - progress) * startWidth + (progress * endWidth));
        int height = (int) ((1 - progress) * startHeight + (progress * endHeight));
        frame.right = frame.left + width;
        frame.bottom = frame.top + height;

        frame.pivotX = interpolate(start.pivotX, end.pivotX, 0.5f, progress);
        frame.pivotY = interpolate(start.pivotY, end.pivotY, 0.5f, progress);

        frame.rotationX = interpolate(start.rotationX, end.rotationX, 0f, progress);
        frame.rotationY = interpolate(start.rotationY, end.rotationY, 0f, progress);
        frame.rotationZ = interpolate(start.rotationZ, end.rotationZ, 0f, progress);

        frame.scaleX = interpolate(start.scaleX, end.scaleX, 1f, progress);
        frame.scaleY = interpolate(start.scaleY, end.scaleY, 1f, progress);

        frame.translationX = interpolate(start.translationX, end.translationX, 0f, progress);
        frame.translationY = interpolate(start.translationY, end.translationY, 0f, progress);
        frame.translationZ = interpolate(start.translationZ, end.translationZ, 0f, progress);

        frame.alpha = interpolate(startAlpha, endAlpha, 1f, progress);

        Set<String> keys = end.mCustom.keySet();
        frame.mCustom.clear();
        for (String key : keys) {
            if (start.mCustom.containsKey(key)) {
                CustomVariable startVariable = start.mCustom.get(key);
                CustomVariable endVariable = end.mCustom.get(key);
                CustomVariable interpolated = new CustomVariable(startVariable);
                frame.mCustom.put(key, interpolated);
                if (startVariable.numberOfInterpolatedValues() == 1) {
                    interpolated.setValue(interpolate(startVariable.getValueToInterpolate(),
                            endVariable.getValueToInterpolate(), 0f, progress));
                } else {
                    int n = startVariable.numberOfInterpolatedValues();
                    float[] startValues = new float[n];
                    float[] endValues = new float[n];
                    startVariable.getValuesToInterpolate(startValues);
                    endVariable.getValuesToInterpolate(endValues);
                    for (int i = 0; i < n; i++) {
                        startValues[i] = interpolate(startValues[i], endValues[i], 0f, progress);
                        interpolated.setValue(startValues);
                    }
                }
            }
        }
    }

    private static float interpolate(float start, float end, float defaultValue, float progress) {
        boolean isStartUnset = Float.isNaN(start);
        boolean isEndUnset = Float.isNaN(end);
        if (isStartUnset && isEndUnset) {
            return Float.NaN;
        }
        if (isStartUnset) {
            start = defaultValue;
        }
        if (isEndUnset) {
            end = defaultValue;
        }
        return (start + progress * (end - start));
    }

    /**
     * @TODO: add description
     */
    public float centerX() {
        return left + (right - left) / 2f;
    }

    /**
     * @TODO: add description
     */
    public float centerY() {
        return top + (bottom - top) / 2f;
    }

    /**
     * @TODO: add description
     */
    public WidgetFrame update() {
        if (widget != null) {
            left = widget.getLeft();
            top = widget.getTop();
            right = widget.getRight();
            bottom = widget.getBottom();
            WidgetFrame frame = widget.frame;
            updateAttributes(frame);
        }
        return this;
    }

    /**
     * @TODO: add description
     */
    public WidgetFrame update(ConstraintWidget widget) {
        if (widget == null) {
            return this;
        }

        this.widget = widget;
        update();
        return this;
    }

    /**
     * @TODO: add description
     */
    public void addCustomColor(String name, int color) {
        setCustomAttribute(name, TypedValues.Custom.TYPE_COLOR, color);
    }

    /**
     * @TODO: add description
     */
    public int getCustomColor(String name) {
        if (mCustom.containsKey(name)) {
            return mCustom.get(name).getColorValue();
        }
        return 0xFFFFAA88;
    }

    /**
     * @TODO: add description
     */
    public void addCustomFloat(String name, float value) {
        setCustomAttribute(name, TypedValues.Custom.TYPE_FLOAT, value);
    }

    /**
     * @TODO: add description
     */
    public float getCustomFloat(String name) {
        if (mCustom.containsKey(name)) {
            return mCustom.get(name).getFloatValue();
        }
        return Float.NaN;
    }

    /**
     * @TODO: add description
     */
    public void setCustomAttribute(String name, int type, float value) {
        if (mCustom.containsKey(name)) {
            mCustom.get(name).setFloatValue(value);
        } else {
            mCustom.put(name, new CustomVariable(name, type, value));
        }
    }

    /**
     * @TODO: add description
     */
    public void setCustomAttribute(String name, int type, int value) {
        if (mCustom.containsKey(name)) {
            mCustom.get(name).setIntValue(value);
        } else {
            mCustom.put(name, new CustomVariable(name, type, value));
        }
    }

    /**
     * @TODO: add description
     */
    public void setCustomAttribute(String name, int type, boolean value) {
        if (mCustom.containsKey(name)) {
            mCustom.get(name).setBooleanValue(value);
        } else {
            mCustom.put(name, new CustomVariable(name, type, value));
        }
    }

    /**
     * @TODO: add description
     */
    public void setCustomAttribute(String name, int type, String value) {
        if (mCustom.containsKey(name)) {
            mCustom.get(name).setStringValue(value);
        } else {
            mCustom.put(name, new CustomVariable(name, type, value));
        }
    }

    /**
     * @TODO: add description
     */
    public CustomVariable getCustomAttribute(String name) {
        return mCustom.get(name);
    }

    public Set<String> getCustomAttributeNames() {
        return mCustom.keySet();
    }

    /**
     * @TODO: add description
     */
    public boolean setValue(String key, CLElement value) throws CLParsingException {
        switch (key) {
            case "pivotX":
                pivotX = value.getFloat();
                break;
            case "pivotY":
                pivotY = value.getFloat();
                break;
            case "rotationX":
                rotationX = value.getFloat();
                break;
            case "rotationY":
                rotationY = value.getFloat();
                break;
            case "rotationZ":
                rotationZ = value.getFloat();
                break;
            case "translationX":
                translationX = value.getFloat();
                break;
            case "translationY":
                translationY = value.getFloat();
                break;
            case "translationZ":
                translationZ = value.getFloat();
                break;
            case "scaleX":
                scaleX = value.getFloat();
                break;
            case "scaleY":
                scaleY = value.getFloat();
                break;
            case "alpha":
                alpha = value.getFloat();
                break;
            case "interpolatedPos":
                interpolatedPos = value.getFloat();
                break;
            case "phone_orientation":
                phone_orientation = value.getFloat();
                break;
            case "top":
                top = value.getInt();
                break;
            case "left":
                left = value.getInt();
                break;
            case "right":
                right = value.getInt();
                break;
            case "bottom":
                bottom = value.getInt();
                break;
            case "custom":
                parseCustom(value);
                break;

            default:
                return false;
        }
        return true;
    }

    /**
     * @TODO: add description
     */
    public String getId() {
        if (widget == null) {
            return "unknown";
        }
        return widget.stringId;
    }

    void parseCustom(CLElement custom) throws CLParsingException {
        CLObject obj = ((CLObject) custom);
        int n = obj.size();
        for (int i = 0; i < n; i++) {
            CLElement tmp = obj.get(i);
            CLKey k = ((CLKey) tmp);
            String name = k.content();
            CLElement v = k.getValue();
            String vStr = v.content();
            if (vStr.matches("#[0-9a-fA-F]+")) {
                int color = Integer.parseInt(vStr.substring(1), 16);
                setCustomAttribute(k.content(), TypedValues.Custom.TYPE_COLOR, color);
            } else if (v instanceof CLNumber) {
                setCustomAttribute(k.content(), TypedValues.Custom.TYPE_FLOAT, v.getFloat());
            } else {
                setCustomAttribute(k.content(), TypedValues.Custom.TYPE_STRING, vStr);

            }
        }
    }

    /**
     * @TODO: add description
     */
    public StringBuilder serialize(StringBuilder ret) {
        return serialize(ret, false);
    }

    /**
     * If true also send the phone orientation
     */
    public StringBuilder serialize(StringBuilder ret, boolean sendPhoneOrientation) {
        WidgetFrame frame = this;
        ret.append("{\n");
        add(ret, "left", frame.left);
        add(ret, "top", frame.top);
        add(ret, "right", frame.right);
        add(ret, "bottom", frame.bottom);
        add(ret, "pivotX", frame.pivotX);
        add(ret, "pivotY", frame.pivotY);
        add(ret, "rotationX", frame.rotationX);
        add(ret, "rotationY", frame.rotationY);
        add(ret, "rotationZ", frame.rotationZ);
        add(ret, "translationX", frame.translationX);
        add(ret, "translationY", frame.translationY);
        add(ret, "translationZ", frame.translationZ);
        add(ret, "scaleX", frame.scaleX);
        add(ret, "scaleY", frame.scaleY);
        add(ret, "alpha", frame.alpha);
        add(ret, "visibility", frame.visibility);
        add(ret, "interpolatedPos", frame.interpolatedPos);
        if (widget != null) {
            for (ConstraintAnchor.Type side : ConstraintAnchor.Type.values()) {
                serializeAnchor(ret, side);
            }
        }
        if (sendPhoneOrientation) {
            add(ret, "phone_orientation", phone_orientation);
        }
        if (sendPhoneOrientation) {
            add(ret, "phone_orientation", phone_orientation);
        }

        if (frame.mCustom.size() != 0) {
            ret.append("custom : {\n");
            for (String s : frame.mCustom.keySet()) {
                CustomVariable value = frame.mCustom.get(s);
                ret.append(s);
                ret.append(": ");
                switch (value.getType()) {
                    case TypedValues.Custom.TYPE_INT:
                        ret.append(value.getIntegerValue());
                        ret.append(",\n");
                        break;
                    case TypedValues.Custom.TYPE_FLOAT:
                    case TypedValues.Custom.TYPE_DIMENSION:
                        ret.append(value.getFloatValue());
                        ret.append(",\n");
                        break;
                    case TypedValues.Custom.TYPE_COLOR:
                        ret.append("'");
                        ret.append(CustomVariable.colorString(value.getIntegerValue()));
                        ret.append("',\n");
                        break;
                    case TypedValues.Custom.TYPE_STRING:
                        ret.append("'");
                        ret.append(value.getStringValue());
                        ret.append("',\n");
                        break;
                    case TypedValues.Custom.TYPE_BOOLEAN:
                        ret.append("'");
                        ret.append(value.getBooleanValue());
                        ret.append("',\n");
                        break;
                }
            }
            ret.append("}\n");
        }

        ret.append("}\n");
        return ret;
    }

    private void serializeAnchor(StringBuilder ret, ConstraintAnchor.Type type) {
        ConstraintAnchor anchor = widget.getAnchor(type);
        if (anchor == null || anchor.mTarget == null) {
            return;
        }
        ret.append("Anchor");
        ret.append(type.name());
        ret.append(": ['");
        String str = anchor.mTarget.getOwner().stringId;
        ret.append(str == null ? "#PARENT" : str);
        ret.append("', '");
        ret.append(anchor.mTarget.getType().name());
        ret.append("', '");
        ret.append(anchor.mMargin);
        ret.append("'],\n");

    }

    private static void add(StringBuilder s, String title, int value) {
        s.append(title);
        s.append(": ");
        s.append(value);
        s.append(",\n");
    }

    private static void add(StringBuilder s, String title, float value) {
        if (Float.isNaN(value)) {
            return;
        }
        s.append(title);
        s.append(": ");
        s.append(value);
        s.append(",\n");
    }

    /**
     * For debugging only
     */
    void printCustomAttributes() {
        StackTraceElement s = new Throwable().getStackTrace()[1];
        String ss = ".(" + s.getFileName() + ":" + s.getLineNumber() + ") " + s.getMethodName();
        ss += " " + (this.hashCode() % 1000);
        if (widget != null) {
            ss += "/" + (widget.hashCode() % 1000) + " ";
        } else {
            ss += "/NULL ";
        }
        if (mCustom != null) {
            for (String key : mCustom.keySet()) {
                System.out.println(ss + mCustom.get(key).toString());
            }
        }
    }

    /**
     * For debugging only
     */
    void logv(String str) {
        StackTraceElement s = new Throwable().getStackTrace()[1];
        String ss = ".(" + s.getFileName() + ":" + s.getLineNumber() + ") " + s.getMethodName();
        ss += " " + (this.hashCode() % 1000);
        if (widget != null) {
            ss += "/" + (widget.hashCode() % 1000);
        } else {
            ss += "/NULL";
        }

        System.out.println(ss + " " + str);
    }

    /**
     * @TODO: add description
     */
    public void setCustomValue(CustomAttribute valueAt, float[] mTempValues) {
    }

    void setMotionAttributes(TypedBundle motionProperties) {
        mMotionProperties = motionProperties;
    }

    /**
     * get the property bundle associated with MotionAttributes
     *
     * @return the property bundle associated with MotionAttributes or null
     */
    public TypedBundle getMotionProperties() {
        return mMotionProperties;
    }
}