/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * 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 com.pixate.freestyle.styling.adapters; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import com.pixate.freestyle.annotations.PXDocElement; import com.pixate.freestyle.styling.PXRuleSet; import com.pixate.freestyle.styling.cache.PXStyleInfo; import com.pixate.freestyle.styling.stylers.PXAnimationStyler; import com.pixate.freestyle.styling.stylers.PXBorderStyler; import com.pixate.freestyle.styling.stylers.PXBoxShadowStyler; import com.pixate.freestyle.styling.stylers.PXFadingEdgeStyler; import com.pixate.freestyle.styling.stylers.PXFillStyler; import com.pixate.freestyle.styling.stylers.PXLayoutStyler; import com.pixate.freestyle.styling.stylers.PXOpacityStyler; import com.pixate.freestyle.styling.stylers.PXShapeStyler; import com.pixate.freestyle.styling.stylers.PXStyler; import com.pixate.freestyle.styling.stylers.PXStylerContext; import com.pixate.freestyle.styling.stylers.PXTransformStyler; import com.pixate.freestyle.styling.stylers.PXStylerContext.FadingEdgeStyle; import com.pixate.freestyle.util.PXDrawableUtil; import com.pixate.freestyle.util.ViewUtil; @PXDocElement(hide=true) public class PXViewStyleAdapter extends PXStyleAdapter { private static PXViewStyleAdapter sInstance; protected PXViewStyleAdapter() { } /* * (non-Javadoc) * @see com.pixate.freestyle.styling.adapters.PXStyleAdapter#createStylers() */ protected List<PXStyler> createStylers() { List<PXStyler> stylers = new ArrayList<PXStyler>(); stylers.add(PXTransformStyler.getInstance()); stylers.add(PXLayoutStyler.getInstance()); stylers.add(PXOpacityStyler.getInstance()); stylers.add(PXShapeStyler.getInstance()); stylers.add(PXFillStyler.getInstance()); stylers.add(PXBorderStyler.getInstance()); stylers.add(PXBoxShadowStyler.getInstance()); stylers.add(PXAnimationStyler.getInstance()); stylers.add(PXFadingEdgeStyler.getInstance()); return stylers; } /** * Base implementation. Returns the lower-case name of the view. * * @see com.pixate.freestyle.styling.adapters.PXStyleAdapter#getElementName(java * .lang.Object) */ @Override public String getElementName(Object object) { View view = (View) object; String result = null; if (view != null) { result = ViewUtil.getElementName(view); if (result != null) { result = result.toLowerCase(Locale.US); } } return result; } @Override public String getStyleId(Object object) { View view = (View) object; String result = null; if (view != null) { result = ViewUtil.getStyleId(view); } // That view was never tagged, so try to resolve the ID. if (result == null && !ViewUtil.isTagged(view)) { ViewUtil.initTags(view); result = ViewUtil.getStyleId(view); } return result; } @Override public String getStyleClass(Object object) { View view = (View) object; String result = null; if (view != null) { result = ViewUtil.getStyleClass(view); } return result; } @SuppressWarnings("unchecked") @Override public Object getParent(Object styleable) { View view = (View) styleable; ViewParent parent = view.getParent(); // In case the parent is null, we may be dealing with a recycled element // that will be attached to a future parent (like when dealing with // ListViews). At this point, we check if there is a live weak reference // to such a parent. if (parent == null) { WeakReference<ViewParent> futureParent = (WeakReference<ViewParent>) view .getTag(ViewUtil.TAG_ELEMENT_FUTURE_PARENT); if (futureParent != null) { parent = futureParent.get(); } } return parent; } @Override public int getIndexInParent(Object styleable) { View view = (View) styleable; ViewParent parent = view.getParent(); if (parent instanceof ViewGroup) { return ((ViewGroup) parent).indexOfChild(view); } Integer taggedIndex = (Integer) view.getTag(ViewUtil.TAG_ELEMENT_INDEX); if (taggedIndex != null) { return taggedIndex.intValue(); } return -1; } @Override public Object getSiblingAt(Object styleable, int offset) { View view = (View) styleable; ViewParent parent = view.getParent(); if (parent instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) parent; // We don't check the index validity here. The API specify that // the call returns the view at the specified position or null // if the position does not exist within the group return viewGroup.getChildAt(viewGroup.indexOfChild(view) + offset); } return null; } @Override public int getChildCount(Object element) { if (!(element instanceof ViewGroup)) { return 0; } return ((ViewGroup) element).getChildCount(); } @Override public int getSiblingsCount(Object element) { View view = (View) element; ViewParent parent = view.getParent(); if (parent != null) { return getChildCount(parent); } // the parent is null, try to get the value from the tag Integer taggedCount = (Integer) view.getTag(ViewUtil.TAG_ELEMENTS_COUNT); if (taggedCount != null) { return taggedCount.intValue(); } return super.getSiblingsCount(element); } /* * Add the actual children of this styleable to the super's result. * @see * com.pixate.freestyle.styling.adapters.PXStyleAdapter#getElementChildren * (java.lang.Object) */ @Override public List<Object> getElementChildren(Object styleable) { List<Object> children = super.getElementChildren(styleable); if (!(styleable instanceof ViewGroup)) { return children; } ViewGroup viewGroup = (ViewGroup) styleable; List<Object> result = new ArrayList<Object>(children.size() + viewGroup.getChildCount()); result.addAll(children); for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (child != null) { result.add(child); } } return result; } @Override public RectF getBounds(Object styleable) { View view = (View) styleable; Rect r = new Rect(); view.getDrawingRect(r); return new RectF(r); } @Override public boolean updateStyle(List<PXRuleSet> ruleSets, List<PXStylerContext> contexts) { if (!super.updateStyle(ruleSets, contexts)) { return false; } PXStylerContext context = contexts.get(0); // Grab the first view. In case we are dealing with state-list // drawables, this view will be the same for all contexts that we get // here. View view = (View) context.getStyleable(); // Grab the existing states, in case the background is a // StatesListDrawable Map<int[], Drawable> existingStates = PXDrawableUtil .getExistingStates(view.getBackground()); if (existingStates == null || existingStates.isEmpty()) { updateWithNewStates(ruleSets, contexts); return true; } PXDrawableUtil.setBackgroundDrawable(view, PXDrawableUtil.createDrawable(this, existingStates, ruleSets, contexts)); updateFadingEdgeStyle(context, view); return true; } /** * This method is called when the view's background was null, or did not * have any states assigned. The call will directly update the views in the * context list with the background image generated by each of the contexts. * A new StateListDrawable that will be applied as the View's background. * Subclasses may overwrite. * * @param ruleSets * @param contexts */ protected void updateWithNewStates(List<PXRuleSet> ruleSets, List<PXStylerContext> contexts) { // We got to this method because the view did not have a stateful // drawable, so in case we have only one context (e.g. one state), we // keep the drawable simple. In case there are multiple contexts, we // create and assign a new StateListDrawable. Drawable drawable = PXDrawableUtil.createNewDrawable(this, ruleSets, contexts); if (drawable != null) { PXDrawableUtil.setBackgroundDrawable((View) contexts.get(0).getStyleable(), drawable); } } // Private /** * Update the View's fading-edge properties. We only set fading properties * that were changed via the CSS, as each view subclass may have its own * defaults. * * @param context * @param view */ private void updateFadingEdgeStyle(PXStylerContext context, View view) { FadingEdgeStyle fadingStyle = context.getFadingStyle(); if (fadingStyle != null) { if (fadingStyle.verticalEnabled != null) { view.setVerticalFadingEdgeEnabled(fadingStyle.verticalEnabled); } if (fadingStyle.horizontalEnabled != null) { view.setVerticalFadingEdgeEnabled(fadingStyle.horizontalEnabled); } if (fadingStyle.edgeLength != null) { view.setFadingEdgeLength(fadingStyle.edgeLength); } } } /** * Returns the supported pseudo classes that maps to the {@link Drawable} * states. General possible values that are acceptable by a {@link Drawable} * are: * <ul> * <li>"state_focused" * <li>"state_window_focused" * <li>"state_enabled" * <li>"state_checked" * <li>"state_selected" * <li>"state_active" * <li>"state_single" * <li>"state_first" * <li>"state_mid" * <li>"state_last" * <li>"state_pressed" * <li>"state_activated" * <li>"state_hovered" * <li>"state_drag_can_accept" * <li>"state_drag_hovered" * </ul> * Note: The returned View list of pseudo class will omit the "state_" * prefix from those values. * * @return A list of supported pseudo classes. * @see PXDrawableUtil#getSupportedStates() */ @Override public List<String> getSupportedPseudoClasses(Object styleable) { return new ArrayList<String>(PXDrawableUtil.getSupportedStates().keySet()); } @Override public String getDefaultPseudoClass(Object styleable) { // Note: This is just a string we place to indicate the default state of // the view. This key can later be mapped into an integer that // represents a state. The state can be applied as a Drawable state, a // Color state, or any other type of state for that matter. return PXStyleInfo.DEFAULT_STYLE; } public static PXViewStyleAdapter getInstance() { synchronized (PXViewStyleAdapter.class) { if (sInstance == null) { sInstance = new PXViewStyleAdapter(); } } return sInstance; } }