/** * Creates a new {@link StateListDrawable} by looking into the contexts and generating one * according to their states. * * @param adapter * @param ruleSets * @param contexts * @return A new {@link StateListDrawable}. <code>null</code> in case the contexts is <code>null * </code> or empty. */ public static StateListDrawable createNewStateListDrawable( PXStyleAdapter adapter, List<PXRuleSet> ruleSets, List<PXStylerContext> contexts) { if (contexts != null && !contexts.isEmpty()) { // No state-lists here, so simply loop and set the backgrounds StateListDrawable stateListDrawable = adapter.shouldAdjustDrawableBounds() ? (new StateListDrawableWithBoundsChange()) : (new StateListDrawable()); int[][] deferredDefaultStates = null; Drawable defaultDrawable = null; int rulesetSize = ruleSets.size(); for (int i = 0; i < rulesetSize; i++) { PXStylerContext context = contexts.get(i); String activeStateName = context.getActiveStateName(); if (activeStateName == null) { activeStateName = PXStyleInfo.DEFAULT_STYLE; } Drawable drawable = (context.usesImage() || context.usesColorOnly()) ? context.getBackgroundImage() : null; // Artificially add states to the one we got. For example, add a // 'pressed' state to a 'checked' state. int stateValue = PXDrawableUtil.getStateValue(activeStateName); if (stateValue == android.R.attr.drawable) { deferredDefaultStates = adapter.createAdditionalDrawableStates(stateValue); defaultDrawable = drawable; } else { int[][] activeStates = adapter.createAdditionalDrawableStates(stateValue); for (int[] activeState : activeStates) { stateListDrawable.addState(activeState, drawable); } } } if (deferredDefaultStates != null && defaultDrawable != null) { // add the defaults at the end of the state list. for (int[] activeState : deferredDefaultStates) { stateListDrawable.addState(activeState, defaultDrawable); } } return stateListDrawable; } return null; }
/** * A utility method that will create a new {@link StateListDrawable} from the given contexts and * the existing {@link View}'s background that is in that context. * * @param adapter A {@link PXStyleAdapter}. This will be used to create additional states (see * {@link PXStyleAdapter#createAdditionalDrawableStates(int)} * @param existingStates The states that exist in the {@link View} that is being styled. Note that * in case that the existing states are empty (or <code>null</code>), there may be a need to * call the {@link #createNewStateListDrawable(PXStyleAdapter, List, List)} method instead. * @param ruleSets * @param contexts * @return A new {@link StateListDrawable} * @see #createNewStateListDrawable(PXStyleAdapter, List, List) */ public static Drawable createDrawable( PXStyleAdapter adapter, Map<int[], Drawable> existingStates, List<PXRuleSet> ruleSets, List<PXStylerContext> contexts) { PXStylerContext context = contexts.get(0); Set<int[]> statesKeys; if (existingStates != null) { statesKeys = existingStates.keySet(); } else { statesKeys = Collections.emptySet(); } // Will hold the new background. StateListDrawable stateListDrawable = adapter.shouldAdjustDrawableBounds() ? (new StateListDrawableWithBoundsChange()) : (new StateListDrawable()); // For every image we have in the contexts, create a drawable and insert // it into the StateListDrawable. The assumption is that every context // will provide a drawable for a different state. Otherwise, we may end // up with multiple drawables for the same state and Android will pick // the first one it hits. We also want to keep the order of the original // state list, so we do that insertion in two parts. First, we collect // all the states indexes that will be 'replaced', and then we do the // actual construction of the new state-list while weaving in drawables // from the original list and our contexts. Map<Integer, Drawable> newStatesPositions = new LinkedHashMap<Integer, Drawable>(); int rulesetSize = ruleSets.size(); for (int i = 0; i < rulesetSize; i++) { context = contexts.get(i); Drawable drawable = (context.usesImage() || context.usesColorOnly()) ? context.getBackgroundImage() : null; if (drawable != null && existingStates != null && !existingStates.isEmpty()) { String activeStateName = context.getActiveStateName(); if (activeStateName == null) { activeStateName = PXStyleInfo.DEFAULT_STYLE; } // Artificially add states to the one we got. For example, add a // 'pressed' state to a 'checked' state. int[][] activeStates = adapter.createAdditionalDrawableStates(PXDrawableUtil.getStateValue(activeStateName)); // Find the index we would like to insert this state. Remember // it and its newly assigned drawable. int index = 0; for (int[] state : statesKeys) { for (int[] activeState : activeStates) { if (Arrays.equals(state, activeState)) { // we set the new position, intentionally not // breaking out of the loop (last occurrence wins) newStatesPositions.put(index, drawable); } } index++; } } } // At this point we have the indexes in the original state list that we // would like to replace with our drawables, so we loop and create a the // new state list. int index = 0; for (int[] state : statesKeys) { Drawable drawable = newStatesPositions.get(index); if (drawable != null) { // NOTE: This is super important for Spinners and // CheckedTextViews! Update the state list drawable bounds size. if (adapter.shouldAdjustDrawableBounds()) { stateListDrawable.getBounds().union(drawable.getBounds()); } stateListDrawable.addState(state, drawable); } else { // add the existing one. stateListDrawable.addState(state, existingStates.get(state)); } index++; } return stateListDrawable.mutate(); }