package com.mozz.htmlnative.css; import android.content.Context; import android.graphics.Matrix; import android.support.annotation.NonNull; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import android.widget.AbsoluteLayout; import com.mozz.htmlnative.HNLog; import com.mozz.htmlnative.HNSandBoxContext; import com.mozz.htmlnative.HNativeEngine; import com.mozz.htmlnative.InheritStyleStack; import com.mozz.htmlnative.common.PixelValue; import com.mozz.htmlnative.css.stylehandler.LayoutStyleHandler; import com.mozz.htmlnative.css.stylehandler.StyleHandler; import com.mozz.htmlnative.css.stylehandler.StyleHelper; import com.mozz.htmlnative.dom.DomElement; import com.mozz.htmlnative.exception.AttrApplyException; import com.mozz.htmlnative.utils.ParametersUtils; import com.mozz.htmlnative.view.BackgroundViewDelegate; import com.mozz.htmlnative.view.IBackgroundView; import com.mozz.htmlnative.view.LayoutParamsLazyCreator; import java.util.Iterator; /** * @author Yang Tao, 17/3/30. */ public final class Styles { private Styles() { } public static final String ATTR_STYLE = "style"; public static final String ATTR_WIDTH = "width"; public static final String ATTR_HEIGHT = "height"; public static final String ATTR_BACKGROUND = "background"; public static final String ATTR_PADDING = "padding"; public static final String ATTR_PADDING_LEFT = "padding-left"; public static final String ATTR_PADDING_RIGHT = "padding-right"; public static final String ATTR_PADDING_TOP = "padding-top"; public static final String ATTR_PADDING_BOTTOM = "padding-bottom"; public static final String ATTR_MARGIN = "margin"; public static final String ATTR_MARGIN_LEFT = "margin-left"; public static final String ATTR_MARGIN_RIGHT = "margin-right"; public static final String ATTR_MARGIN_TOP = "margin-top"; public static final String ATTR_MARGIN_BOTTOM = "margin-bottom"; public static final String ATTR_LEFT = "left"; public static final String ATTR_TOP = "top"; public static final String ATTR_ALPHA = "alpha"; public static final String ATTR_ONCLICK = "onclick"; public static final String ATTR_VISIBLE = "visibility"; public static final String ATTR_DISPLAY = "display"; public static final String ATTR_DIRECTION = "direction"; // Hn specified styles: public static final String ATTR_HN_BACKGROUND = "-hn-background"; public static final String VAL_FILL_PARENT = "100%"; public static final String VAL_DISPLAY_FLEX = "flex"; public static final String VAL_DISPLAY_BOX = "box"; public static final String VAL_DISPLAY_ABSOLUTE = "absolute"; static { InheritStylesRegistry.register(ATTR_VISIBLE); InheritStylesRegistry.register(ATTR_DIRECTION); } public static void applyStyle(Context context, final HNSandBoxContext sandBoxContext, View v, DomElement domElement, @NonNull LayoutParamsLazyCreator layoutCreator, @NonNull ViewGroup parent, StyleHandler viewStyleHandler, StyleHandler extraStyleHandler, LayoutStyleHandler parentAttr, StyleEntry entry, boolean isParent, InheritStyleStack outStack) throws AttrApplyException { applyStyle(context, sandBoxContext, v, domElement, layoutCreator, parent, viewStyleHandler, extraStyleHandler, parentAttr, entry.getStyleName(), entry .getStyle(), isParent, outStack); } /** * Apply a params with value to a view * * @param context {@link Context} * @param sandBoxContext {@link HNSandBoxContext} * @param v {@link View} view to be processed * @param domElement * @param layoutCreator {@link ViewGroup.LayoutParams}, layoutParams for parent * when add this view to parent * @param parent {@link ViewGroup}, parent of the view * @param styleName parameter name * @param style parameter value @throws AttrApplyException */ public static void applyStyle(Context context, final HNSandBoxContext sandBoxContext, View v, DomElement domElement, @NonNull LayoutParamsLazyCreator layoutCreator, @NonNull ViewGroup parent, StyleHandler viewStyleHandler, StyleHandler extraStyleHandler, LayoutStyleHandler parentAttr, String styleName, Object style, boolean isParent, InheritStyleStack outStack) throws AttrApplyException { if (domElement != null) { HNLog.d(HNLog.STYLE, "set style \"" + styleName + ": " + style + "\" to " + domElement.getType()); } if (isParent) { if (!InheritStylesRegistry.isInherit(styleName)) { return; } } switch (styleName) { case ATTR_WIDTH: { if (style.toString().equalsIgnoreCase(VAL_FILL_PARENT)) { layoutCreator.width = ViewGroup.LayoutParams.MATCH_PARENT; } else { PixelValue pixel = ParametersUtils.toPixel(style); layoutCreator.width = (int) pixel.getPxValue(); } } break; case ATTR_HEIGHT: { if (style.toString().equalsIgnoreCase(VAL_FILL_PARENT)) { layoutCreator.height = ViewGroup.LayoutParams.MATCH_PARENT; } else { PixelValue pixel = ParametersUtils.toPixel(style); layoutCreator.height = (int) pixel.getPxValue(); } } break; case ATTR_BACKGROUND: if (style instanceof Background) { Background background = (Background) style; if (!TextUtils.isEmpty(background.getUrl()) && v instanceof IBackgroundView) { Matrix matrix = Background.createBitmapMatrix(background); HNativeEngine.getImageViewAdapter().setImage(background.getUrl(), new BackgroundViewDelegate(v, matrix, background)); } else if (background.isColorSet()) { if (v instanceof IBackgroundView) { ((IBackgroundView) v).setHtmlBackground(null, background); } else { v.setBackgroundColor(background.getColor()); } } } else { throw new AttrApplyException("Background style is wrong when parsing."); } break; case ATTR_MARGIN: { PixelValue[] pixelValues = ParametersUtils.toPixels(style.toString()); int top = -1; int bottom = -1; int left = -1; int right = -1; if (pixelValues.length == 1) { top = bottom = left = right = (int) pixelValues[0].getPxValue(); } else if (pixelValues.length == 2) { top = bottom = (int) pixelValues[0].getPxValue(); left = right = (int) pixelValues[1].getPxValue(); } else if (pixelValues.length == 4) { top = (int) pixelValues[0].getPxValue(); bottom = (int) pixelValues[2].getPxValue(); left = (int) pixelValues[3].getPxValue(); right = (int) pixelValues[1].getPxValue(); } if (top != -1 && bottom != -1 && left != -1 && right != -1) { layoutCreator.setMargins(left, top, right, bottom); } } break; case ATTR_MARGIN_RIGHT: layoutCreator.marginRight = (int) ParametersUtils.toPixel(style).getPxValue(); break; case ATTR_MARGIN_LEFT: layoutCreator.marginLeft = (int) ParametersUtils.toPixel(style).getPxValue(); break; case ATTR_MARGIN_TOP: layoutCreator.marginTop = (int) ParametersUtils.toPixel(style).getPxValue(); break; case ATTR_MARGIN_BOTTOM: layoutCreator.marginBottom = (int) ParametersUtils.toPixel(style).getPxValue(); break; case ATTR_PADDING: { PixelValue[] pixelValues = ParametersUtils.toPixels(style.toString()); int top = -1; int bottom = -1; int left = -1; int right = -1; if (pixelValues.length == 1) { top = bottom = left = right = (int) pixelValues[0].getValue(); } else if (pixelValues.length == 2) { top = bottom = (int) pixelValues[0].getValue(); left = right = (int) pixelValues[1].getValue(); } else if (pixelValues.length == 4) { top = (int) pixelValues[0].getValue(); bottom = (int) pixelValues[2].getValue(); left = (int) pixelValues[3].getValue(); right = (int) pixelValues[1].getValue(); } if (top != -1 && bottom != -1 && left != -1 && right != -1) { v.setPadding(left, top, right, bottom); } } break; case ATTR_PADDING_LEFT: int paddingLeft = ParametersUtils.toInt(style); StyleHelper.setLeftPadding(v, paddingLeft); break; case ATTR_PADDING_RIGHT: int paddingRight = ParametersUtils.toInt(style); StyleHelper.setRightPadding(v, paddingRight); break; case ATTR_PADDING_TOP: int paddingTop = ParametersUtils.toInt(style); StyleHelper.setTopPadding(v, paddingTop); break; case ATTR_PADDING_BOTTOM: int paddingBottom = ParametersUtils.toInt(style); StyleHelper.setBottomPadding(v, paddingBottom); break; case ATTR_LEFT: layoutCreator.left = (int) ParametersUtils.toPixel(style).getPxValue(); break; case ATTR_TOP: layoutCreator.top = (int) ParametersUtils.toPixel(style).getPxValue(); break; case ATTR_ALPHA: float alpha = ParametersUtils.toFloat(style); v.setAlpha(alpha); break; case ATTR_VISIBLE: String visible = style.toString(); if (visible.equals("visible")) { v.setVisibility(View.VISIBLE); } else if (visible.equals("invisible")) { v.setVisibility(View.INVISIBLE); } break; case ATTR_DIRECTION: String direction = style.toString(); if (direction.equals("ltr")) { v.setTextDirection(View.TEXT_DIRECTION_LTR); } else if (direction.equals("rtl")) { v.setTextDirection(View.TEXT_DIRECTION_RTL); } break; case ATTR_ONCLICK: if (style instanceof String) { final String functionName = (String) style; v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sandBoxContext.executeFun(functionName); } }); } break; default: // If not common attrs, then // 1. apply the corresponding view attr first; // 2. apply the extra attr // 3. use parent view attr to this if (viewStyleHandler != null) { viewStyleHandler.apply(context, v, domElement, parent, layoutCreator, styleName, style, isParent); } // If there extra attr is set, then should be applied also. if (extraStyleHandler != null) { extraStyleHandler.apply(context, v, domElement, parent, layoutCreator, styleName, style, isParent); } // finally apply corresponding parent attr to child if (parentAttr != null) { parentAttr.applyToChild(context, v, domElement, parent, layoutCreator, styleName, style, isParent); } break; } // Put inherit style into stack if (outStack != null && InheritStylesRegistry.isInherit(styleName)) { outStack.newStyle(styleName, style); } } /** * Apply a default style to view */ public static void setDefaultStyle(Context context, final HNSandBoxContext sandBoxContext, View v, DomElement domElement, @NonNull ViewGroup parent, StyleHandler viewStyleHandler, StyleHandler extraStyleHandler, LayoutStyleHandler parentAttr, @NonNull LayoutParamsLazyCreator paramsLazyCreator) throws AttrApplyException { if (viewStyleHandler != null) { viewStyleHandler.setDefault(context, v, domElement, paramsLazyCreator, parent); } if (extraStyleHandler != null) { extraStyleHandler.setDefault(context, v, domElement, paramsLazyCreator, parent); } if (parentAttr != null) { parentAttr.setDefaultToChild(context, v, domElement, parent, paramsLazyCreator); } } /** * apply the attr to the view * * @param context {@link Context} * @param sandBoxContext {@link HNSandBoxContext} * @param v {@link View} * @param tree {@link AttrsSet.AttrsOwner} * @param parent {@link ViewGroup}, parent of the view * @param paramsLazyCreator {@link ViewGroup.LayoutParams}, layoutParams for parent * when add this view to parent * @param viewStyleHandler * @param extraStyleHandler * @param parentAttrHandler @throws AttrApplyException */ public static void apply(Context context, @NonNull final HNSandBoxContext sandBoxContext, AttrsSet source, View v, @NonNull AttrsSet.AttrsOwner tree, DomElement domElement, @NonNull ViewGroup parent, @NonNull LayoutParamsLazyCreator paramsLazyCreator, boolean applyDefault, boolean isParent, StyleHandler viewStyleHandler, StyleHandler extraStyleHandler, LayoutStyleHandler parentAttrHandler, InheritStyleStack stack) throws AttrApplyException { // Apply the default attr to view first; // Then process each parameter. Iterator<StyleEntry> itr = source.iterator(tree); while (itr.hasNext()) { StyleEntry styleEntry = itr.next(); applyStyle(context, sandBoxContext, v, domElement, paramsLazyCreator, parent, viewStyleHandler, extraStyleHandler, parentAttrHandler, styleEntry .getStyleName(), styleEntry.getStyle(), isParent, stack); } } public static Object getStyle(View v, String styleName, StyleHandler styleHandler, StyleHandler extraStyleHandler, LayoutStyleHandler parentHandler) { switch (styleName) { case ATTR_WIDTH: int width = v.getLayoutParams().width; if (width == ViewGroup.LayoutParams.MATCH_PARENT) { return VAL_FILL_PARENT; } else { return v.getLayoutParams().width + "px"; } case ATTR_HEIGHT: int height = v.getLayoutParams().height; if (height == ViewGroup.LayoutParams.MATCH_PARENT) { return VAL_FILL_PARENT; } else { return v.getLayoutParams().height + "px"; } case ATTR_BACKGROUND: if (v instanceof IBackgroundView) { return ((IBackgroundView) v).getHtmlBackground(); } return null; case ATTR_MARGIN_RIGHT: if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { return ((ViewGroup.MarginLayoutParams) v.getLayoutParams()).rightMargin + "px"; } return null; case ATTR_MARGIN_LEFT: if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { return ((ViewGroup.MarginLayoutParams) v.getLayoutParams()).leftMargin + "px"; } return null; case ATTR_MARGIN_TOP: if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { return ((ViewGroup.MarginLayoutParams) v.getLayoutParams()).topMargin + "px"; } return null; case ATTR_MARGIN_BOTTOM: if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { return ((ViewGroup.MarginLayoutParams) v.getLayoutParams()).bottomMargin + "px"; } return null; case ATTR_PADDING_TOP: return v.getPaddingTop() + "px"; case ATTR_PADDING_LEFT: return v.getPaddingLeft() + "px"; case ATTR_PADDING_BOTTOM: return v.getPaddingBottom() + "px"; case ATTR_PADDING_RIGHT: return v.getPaddingRight() + "px"; case ATTR_LEFT: if (v.getLayoutParams() instanceof AbsoluteLayout.LayoutParams) { return ((AbsoluteLayout.LayoutParams) v.getLayoutParams()).x + "px"; } else { return null; } case ATTR_TOP: if (v.getLayoutParams() instanceof AbsoluteLayout.LayoutParams) { return ((AbsoluteLayout.LayoutParams) v.getLayoutParams()).y + "px"; } else { return null; } case ATTR_ALPHA: return v.getAlpha(); case ATTR_VISIBLE: int visibility = v.getVisibility(); if (visibility == View.VISIBLE) { return "visible"; } else if (visibility == View.INVISIBLE) { return "invisible"; } case ATTR_DIRECTION: int textDirection = v.getTextDirection(); if (textDirection == View.TEXT_DIRECTION_LTR) { return "ltr"; } else if (textDirection == View.TEXT_DIRECTION_RTL) { return "rtl"; } return null; default: if (styleHandler != null) { Object val = styleHandler.getStyle(v, styleName); if (val != null) { return val; } } if (extraStyleHandler != null) { Object val = extraStyleHandler.getStyle(v, styleName); if (val != null) { return val; } } } return null; } /** * @author Yang Tao, 17/4/28. */ public static class StyleEntry { private String mStyleName; private Object mStyleValue; public StyleEntry(String param, Object value) { this.mStyleName = param; this.mStyleValue = value; } public String getStyleName() { return mStyleName; } public Object getStyle() { return mStyleValue; } public Object setValue(Object value) { Object oldVal = mStyleValue; mStyleValue = value; return oldVal; } @Override public String toString() { return mStyleName + "=" + mStyleValue; } } }