public class

StateSet

extends java.lang.Object

 java.lang.Object

↳androidx.constraintlayout.widget.StateSet

Gradle dependencies

compile group: 'androidx.constraintlayout', name: 'constraintlayout', version: '2.2.0-beta01'

  • groupId: androidx.constraintlayout
  • artifactId: constraintlayout
  • version: 2.2.0-beta01

Artifact androidx.constraintlayout:constraintlayout:2.2.0-beta01 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.constraintlayout:constraintlayout com.android.support.constraint:constraint-layout

Summary

Fields
public static final java.lang.StringTAG

Constructors
publicStateSet(Context context, XmlPullParser parser)

Parse a StateSet

Methods
public intconvertToConstraintSet(int currentConstrainSettId, int stateId, float width, float height)

converts a state to a constraintSet

public booleanneedsToChange(int id, float width, float height)

will the layout need to change

public voidsetOnConstraintsChanged(ConstraintsChangedListener constraintsChangedListener)

listen for changes in constraintSet

public intstateGetConstraintID(int id, int width, int height)

Get the constraint id for a state

public intupdateConstraints(int currentId, int id, float width, float height)

Update the Constraints

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

Fields

public static final java.lang.String TAG

Constructors

public StateSet(Context context, XmlPullParser parser)

Parse a StateSet

Parameters:

context:
parser:

Methods

public boolean needsToChange(int id, float width, float height)

will the layout need to change

Parameters:

id:
width:
height:

Returns:

public void setOnConstraintsChanged(ConstraintsChangedListener constraintsChangedListener)

listen for changes in constraintSet

Parameters:

constraintsChangedListener:

public int stateGetConstraintID(int id, int width, int height)

Get the constraint id for a state

Parameters:

id:
width:
height:

Returns:

public int convertToConstraintSet(int currentConstrainSettId, int stateId, float width, float height)

converts a state to a constraintSet

Parameters:

currentConstrainSettId:
stateId:
width:
height:

Returns:

public int updateConstraints(int currentId, int id, float width, float height)

Update the Constraints

Parameters:

currentId:
id:
width:
height:

Returns:

Source

/*
 * Copyright (C) 2018 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.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;

/**
 *
 */

public class StateSet {
    public static final String TAG = "ConstraintLayoutStates";
    private static final boolean DEBUG = false;
    int mDefaultState = -1;

    int mCurrentStateId = -1; // default
    int mCurrentConstraintNumber = -1; // default
    private SparseArray<State> mStateList = new SparseArray<>();
    @SuppressWarnings("unused")
    private ConstraintsChangedListener mConstraintsChangedListener = null;

    /**
     * Parse a StateSet
     * @param context
     * @param parser
     */
    public StateSet(Context context, XmlPullParser parser) {
        load(context, parser);
    }

    /**
     * Load a constraint set from a constraintSet.xml file
     *
     * @param context    the context for the inflation
     * @param parser  mId of xml file in res/xml/
     */
    private void load(Context context, XmlPullParser parser) {
        if (DEBUG) {
            Log.v(TAG, "#########load stateSet###### ");
        }
        // Parse the stateSet attributes
        AttributeSet attrs = Xml.asAttributeSet(parser);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StateSet);
        final int count = a.getIndexCount();

        for (int i = 0; i < count; i++) {
            int attr = a.getIndex(i);
            if (attr == R.styleable.StateSet_defaultState) {
                mDefaultState = a.getResourceId(attr, mDefaultState);
            }
        }
        a.recycle();

        try {
            Variant match;
            State state = null;
            for (int eventType = parser.getEventType();
                    eventType != XmlResourceParser.END_DOCUMENT;
                    eventType = parser.next()) {

                switch (eventType) {
                    case XmlResourceParser.START_DOCUMENT:
                    case XmlResourceParser.TEXT:
                        break;
                    case XmlResourceParser.START_TAG:
                        String tagName = parser.getName();
                        switch(tagName) {
                            case "LayoutDescription":
                                break;
                            case "StateSet":
                                break;
                            case "State":
                                state = new State(context, parser);
                                mStateList.put(state.mId, state);
                                break;
                            case "Variant":
                                match = new Variant(context, parser);
                                if (state != null) {
                                    state.add(match);
                                }
                                break;

                            default:
                                if (DEBUG) {
                                    Log.v(TAG, "unknown tag " + tagName);
                                }
                        }

                        break;
                    case XmlResourceParser.END_TAG:
                        if ("StateSet".equals(parser.getName())) {
                            if (DEBUG) {
                                Log.v(TAG, "############ finished parsing state set");
                            }
                            return;
                        }
                        break;
                }
            }

        } catch (XmlPullParserException e) {
            Log.e(TAG, "Error parsing XML resource", e);
        } catch (IOException e) {
            Log.e(TAG, "Error parsing XML resource", e);
        }
    }

    /**
     * will the layout need to change
     * @param id
     * @param width
     * @param height
     * @return
     */
    public boolean needsToChange(int id, float width, float height) {
        if (mCurrentStateId != id) {
            return true;
        }

        State state = (id == -1) ? mStateList.valueAt(0) : mStateList.get(mCurrentStateId);

        if (mCurrentConstraintNumber != -1) {
            if (state.mVariants.get(mCurrentConstraintNumber).match(width, height)) {
                return false;
            }
        }

        if (mCurrentConstraintNumber == state.findMatch(width, height)) {
            return false;
        }
        return true;
    }

    /**
     * listen for changes in constraintSet
     * @param constraintsChangedListener
     */
    public void setOnConstraintsChanged(ConstraintsChangedListener constraintsChangedListener) {
        this.mConstraintsChangedListener = constraintsChangedListener;
    }

    /**
     * Get the constraint id for a state
     * @param id
     * @param width
     * @param height
     * @return
     */
    public int stateGetConstraintID(int id, int width, int height) {
        return updateConstraints(-1, id, width, height);
    }

    /**
     * converts a state to a constraintSet
     *
     * @param currentConstrainSettId
     * @param stateId
     * @param width
     * @param height
     * @return
     */
    public int convertToConstraintSet(int currentConstrainSettId,
                                      int stateId,
                                      float width,
                                      float height) {
        State state = mStateList.get(stateId);
        if (state == null) {
            return stateId;
        }
        if (width == -1 || height == -1) {            // for the case without width/height matching
            if (state.mConstraintID == currentConstrainSettId) {
                return currentConstrainSettId;
            }
            for (Variant mVariant : state.mVariants) {
                if (currentConstrainSettId == mVariant.mConstraintID) {
                    return currentConstrainSettId;
                }
            }
            return state.mConstraintID;
        } else {
            Variant match = null;
            for (Variant mVariant : state.mVariants) {
                if (mVariant.match(width, height)) {
                    if (currentConstrainSettId == mVariant.mConstraintID) {
                        return currentConstrainSettId;
                    }
                    match = mVariant;
                }
            }
            if (match != null) {
                return match.mConstraintID;
            }

            return state.mConstraintID;
        }
    }

    /**
     * Update the Constraints
     * @param currentId
     * @param id
     * @param width
     * @param height
     * @return
     */
    public int updateConstraints(int currentId, int id, float width, float height) {
        if (currentId == id) {
            State state;
            if (id == -1) {
                state = mStateList.valueAt(0); // id not being used take the first
            } else {
                state = mStateList.get(mCurrentStateId);

            }
            if (state == null) {
                return -1;
            }
            if (mCurrentConstraintNumber != -1) {
                if (state.mVariants.get(currentId).match(width, height)) {
                    return currentId;
                }
            }
            int match = state.findMatch(width, height);
            if (currentId == match) {
                return currentId;
            }

            return (match == -1) ? state.mConstraintID : state.mVariants.get(match).mConstraintID;

        } else  {
            State state = mStateList.get(id);
            if (state == null) {
                return  -1;
            }
            int match = state.findMatch(width, height);
            return (match == -1) ? state.mConstraintID :  state.mVariants.get(match).mConstraintID;
        }

    }

    /////////////////////////////////////////////////////////////////////////
    //      This represents one state
    /////////////////////////////////////////////////////////////////////////
    static class State {
        int mId;
        ArrayList<Variant> mVariants = new ArrayList<>();
        int mConstraintID = -1;
        boolean mIsLayout = false;

        State(Context context, XmlPullParser parser) {
            AttributeSet attrs = Xml.asAttributeSet(parser);
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.State);
            final int count = a.getIndexCount();
            for (int i = 0; i < count; i++) {
                int attr = a.getIndex(i);
                if (attr == R.styleable.State_android_id) {
                    mId = a.getResourceId(attr, mId);
                } else if (attr == R.styleable.State_constraints) {
                    mConstraintID = a.getResourceId(attr, mConstraintID);
                    String type = context.getResources().getResourceTypeName(mConstraintID);
                    @SuppressWarnings("unused")
                    String name = context.getResources().getResourceName(mConstraintID);

                    if ("layout".equals(type)) {
                        mIsLayout = true;
                    }
                }
            }
            a.recycle();
        }

        void add(Variant size) {
            mVariants.add(size);
        }

        public int findMatch(float width, float height) {
            for (int i = 0; i < mVariants.size(); i++) {
                if (mVariants.get(i).match(width, height)) {
                    return i;
                }
            }
            return -1;
        }
    }

    static class Variant {
        int mId;
        float mMinWidth = Float.NaN;
        float mMinHeight = Float.NaN;
        float mMaxWidth = Float.NaN;
        float mMaxHeight = Float.NaN;
        int mConstraintID = -1;
        boolean mIsLayout = false;

        Variant(Context context, XmlPullParser parser) {
            AttributeSet attrs = Xml.asAttributeSet(parser);
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Variant);
            final int count = a.getIndexCount();
            if (DEBUG) {
                Log.v(TAG, "############### Variant");
            }

            for (int i = 0; i < count; i++) {
                int attr = a.getIndex(i);
                if (attr == R.styleable.Variant_constraints) {
                    mConstraintID = a.getResourceId(attr, mConstraintID);
                    String type = context.getResources().getResourceTypeName(mConstraintID);
                    @SuppressWarnings("unused")
                    String name = context.getResources().getResourceName(mConstraintID);

                    if ("layout".equals(type)) {
                        mIsLayout = true;
                    }
                } else if (attr == R.styleable.Variant_region_heightLessThan) {
                    mMaxHeight = a.getDimension(attr, mMaxHeight);
                } else if (attr == R.styleable.Variant_region_heightMoreThan) {
                    mMinHeight = a.getDimension(attr, mMinHeight);
                } else if (attr == R.styleable.Variant_region_widthLessThan) {
                    mMaxWidth = a.getDimension(attr, mMaxWidth);
                } else if (attr == R.styleable.Variant_region_widthMoreThan) {
                    mMinWidth = a.getDimension(attr, mMinWidth);
                } else {
                    Log.v(TAG, "Unknown tag");
                }
            }
            a.recycle();
            if (DEBUG) {
                Log.v(TAG, "############### Variant");
                if (!Float.isNaN(mMinWidth)) {
                    Log.v(TAG, "############### Variant mMinWidth " + mMinWidth);
                }
                if (!Float.isNaN(mMinHeight)) {
                    Log.v(TAG, "############### Variant mMinHeight " + mMinHeight);
                }
                if (!Float.isNaN(mMaxWidth)) {
                    Log.v(TAG, "############### Variant mMaxWidth " + mMaxWidth);
                }
                if (!Float.isNaN(mMaxHeight)) {
                    Log.v(TAG, "############### Variant mMinWidth " + mMaxHeight);
                }
            }
        }

        boolean match(float widthDp, float heightDp) {
            if (DEBUG) {
                Log.v(TAG, "width = " + (int) widthDp
                        + " < " + mMinWidth + " && " + (int) widthDp + " > " + mMaxWidth
                        + " height = " + (int) heightDp
                        + " < " + mMinHeight + " && " + (int) heightDp + " > " + mMaxHeight);
            }
            if (!Float.isNaN(mMinWidth)) {
                if (widthDp < mMinWidth) return false;
            }
            if (!Float.isNaN(mMinHeight)) {
                if (heightDp < mMinHeight) return false;
            }
            if (!Float.isNaN(mMaxWidth)) {
                if (widthDp > mMaxWidth) return false;
            }
            if (!Float.isNaN(mMaxHeight)) {
                if (heightDp > mMaxHeight) return false;
            }
            return true;
        }
    }

}