blob: 55e9895a2d313e15ecd86b305ba00ab7e7c02f2f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.taobao.weex.ui.component;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.os.Message;
import android.support.annotation.CallSuper;
import android.support.annotation.CheckResult;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.support.annotation.RestrictTo.Scope;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewOverlay;
import android.widget.FrameLayout;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.taobao.weex.ComponentObserver;
import com.taobao.weex.IWXActivityStateListener;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.bridge.Invoker;
import com.taobao.weex.common.Constants;
import com.taobao.weex.common.IWXObject;
import com.taobao.weex.common.WXRuntimeException;
import com.taobao.weex.dom.ImmutableDomObject;
import com.taobao.weex.dom.WXDomHandler;
import com.taobao.weex.dom.WXDomObject;
import com.taobao.weex.dom.WXDomTask;
import com.taobao.weex.dom.WXEvent;
import com.taobao.weex.dom.WXStyle;
import com.taobao.weex.dom.action.Actions;
import com.taobao.weex.dom.flex.Spacing;
import com.taobao.weex.tracing.Stopwatch;
import com.taobao.weex.tracing.WXTracing;
import com.taobao.weex.ui.IFComponentHolder;
import com.taobao.weex.ui.animation.WXAnimationModule;
import com.taobao.weex.ui.component.pesudo.OnActivePseudoListner;
import com.taobao.weex.ui.component.pesudo.PesudoStatus;
import com.taobao.weex.ui.component.pesudo.TouchActivePseudoListener;
import com.taobao.weex.ui.flat.FlatComponent;
import com.taobao.weex.ui.flat.FlatGUIContext;
import com.taobao.weex.ui.flat.WidgetContainer;
import com.taobao.weex.ui.flat.widget.AndroidViewWidget;
import com.taobao.weex.ui.flat.widget.Widget;
import com.taobao.weex.ui.view.border.BorderDrawable;
import com.taobao.weex.ui.view.gesture.WXGesture;
import com.taobao.weex.ui.view.gesture.WXGestureObservable;
import com.taobao.weex.ui.view.gesture.WXGestureType;
import com.taobao.weex.utils.BoxShadowUtil;
import com.taobao.weex.utils.WXDataStructureUtil;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.WXReflectionUtils;
import com.taobao.weex.utils.WXResourceUtils;
import com.taobao.weex.utils.WXUtils;
import com.taobao.weex.utils.WXViewUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* abstract component
*
*/
public abstract class WXComponent<T extends View> implements IWXObject, IWXActivityStateListener,OnActivePseudoListner {
public static final String PROP_FIXED_SIZE = "fixedSize";
public static final String PROP_FS_MATCH_PARENT = "m";
public static final String PROP_FS_WRAP_CONTENT = "w";
private int mFixedProp = 0;
public static int mComponentNum = 0;
/** package **/ T mHost;
private volatile WXVContainer mParent;
private volatile ImmutableDomObject mDomObj;
private WXSDKInstance mInstance;
private Context mContext;
private int mAbsoluteY = 0;
private int mAbsoluteX = 0;
private Set<String> mGestureType;
private BorderDrawable mBackgroundDrawable;
private int mPreRealWidth = 0;
private int mPreRealHeight = 0;
private int mPreRealLeft = 0;
private int mPreRealTop = 0;
private int mStickyOffset = 0;
private WXGesture mGesture;
private IFComponentHolder mHolder;
private boolean isUsing = false;
private List<OnClickListener> mHostClickListeners;
private List<OnFocusChangeListener> mFocusChangeListeners;
private String mCurrentRef;
private Set<String> mAppendEvents = new HashSet<>();
private WXAnimationModule.AnimationHolder mAnimationHolder;
private PesudoStatus mPesudoStatus = new PesudoStatus();
private boolean mIsDestroyed = false;
private boolean mIsDisabled = false;
private int mType = TYPE_COMMON;
private boolean mNeedLayoutOnAnimation = false;
public WXTracing.TraceInfo mTraceInfo = new WXTracing.TraceInfo();
public static final int TYPE_COMMON = 0;
public static final int TYPE_VIRTUAL = 1;
private boolean waste = false;
//Holding the animation bean when component is uninitialized
public void postAnimation(WXAnimationModule.AnimationHolder holder) {
this.mAnimationHolder = holder;
}
//This method will be removed once flatGUI is completed.
@RestrictTo(Scope.LIBRARY)
public boolean isFlatUIEnabled(){
return mParent != null && mParent.isFlatUIEnabled();
}
private OnClickListener mClickEventListener = new OnClickListener() {
@Override
public void onHostViewClick() {
Map<String, Object> param= WXDataStructureUtil.newHashMapWithExpectedSize(1);
Map<String, Object> position = WXDataStructureUtil.newHashMapWithExpectedSize(4);
int[] location = new int[2];
mHost.getLocationOnScreen(location);
position.put("x", WXViewUtils.getWebPxByWidth(location[0],mInstance.getInstanceViewPortWidth()));
position.put("y", WXViewUtils.getWebPxByWidth(location[1],mInstance.getInstanceViewPortWidth()));
position.put("width", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutWidth(),mInstance.getInstanceViewPortWidth()));
position.put("height", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutHeight(),mInstance.getInstanceViewPortWidth()));
param.put(Constants.Name.POSITION, position);
fireEvent(Constants.Event.CLICK,param);
}
};
public String getInstanceId() {
return mInstance.getInstanceId();
}
public Rect getComponentSize() {
Rect size = new Rect();
if (mHost != null) {
int[] location = new int[2];
int[] anchor = new int[2];
mHost.getLocationOnScreen(location);
mInstance.getContainerView().getLocationOnScreen(anchor);
int left = location[0] - anchor[0];
int top = (location[1] - mStickyOffset) - anchor[1];
int width = (int) mDomObj.getLayoutWidth();
int height = (int) mDomObj.getLayoutHeight();
size.set(left, top, left + width, top + height);
}
return size;
}
public final void invoke(String method, JSONArray args) {
final Invoker invoker = mHolder.getMethodInvoker(method);
if (invoker != null) {
try {
getInstance()
.getNativeInvokeHelper()
.invoke(this,invoker,args);
} catch (Exception e) {
WXLogUtils.e("[WXComponent] updateProperties :" + "class:" + getClass() + "method:" + invoker.toString() + " function " + WXLogUtils.getStackTrace(e));
}
}else{
onInvokeUnknownMethod(method,args);
}
}
/**
* Will be invoked when request method not found.
* Subclass should override this method, If you return hard-code method list in {@link IFComponentHolder#getMethods()}
* @param method
* @param args
*/
protected void onInvokeUnknownMethod(String method, JSONArray args){
}
public interface OnClickListener{
void onHostViewClick();
}
interface OnFocusChangeListener{
void onFocusChange(boolean hasFocus);
}
@Deprecated
public WXComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, String instanceId, boolean isLazy) {
this(instance,dom,parent,isLazy);
}
@Deprecated
public WXComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, boolean isLazy) {
this(instance,dom,parent);
}
public WXComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent) {
this(instance, dom, parent, TYPE_COMMON);
}
public WXComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, int type) {
mInstance = instance;
mContext = mInstance.getContext();
mParent = parent;
mType = type;
mDomObj = dom.clone();
mCurrentRef = mDomObj.getRef();
mGestureType = new HashSet<>();
++mComponentNum;
onCreate();
ComponentObserver observer;
if ((observer = getInstance().getComponentObserver()) != null) {
observer.onCreate(this);
}
}
protected void onCreate(){
}
public void bindHolder(IFComponentHolder holder){
mHolder = holder;
}
public WXSDKInstance getInstance(){
return mInstance;
}
public Context getContext(){
return mContext;
}
/**
* Find component by component reference.
* @param ref
* @return
*/
protected final WXComponent findComponent(String ref){
if(mInstance != null && ref != null){
return WXSDKManager.getInstance()
.getWXRenderManager()
.getWXComponent(mInstance.getInstanceId(), ref);
}
return null;
}
public final void fireEvent(String type){
fireEvent(type,null);
}
public final void fireEvent(String type, Map<String, Object> params){
fireEvent(type,params,null);
}
protected final void fireEvent(String type, Map<String, Object> params,Map<String, Object> domChanges){
if(mInstance != null && mDomObj != null) {
List<Object> eventArgsValues = null;
if(mDomObj.getEvents() != null && mDomObj.getEvents().getEventBindingArgsValues() != null){
eventArgsValues = mDomObj.getEvents().getEventBindingArgsValues().get(type);
}
mInstance.fireEvent(mCurrentRef, type, params,domChanges, eventArgsValues);
}
}
/**
* find certain class type parent
* */
public Object findTypeParent(WXComponent component, Class type){
if(component.getClass() == type){
return component;
}
if(component.getParent() != null) {
findTypeParent(component.getParent(), type);
}
return null;
}
/**
* The view is created as needed
* @return true for lazy
*/
public boolean isLazy() {
return mParent != null && mParent.isLazy();
}
public void applyLayoutAndEvent(WXComponent component) {
long startNanos = System.nanoTime();
if(!isLazy()) {
if (component == null) {
component = this;
}
setLayout(component.getDomObject());
setPadding(component.getDomObject().getPadding(), component.getDomObject().getBorder());
addEvents();
}
mTraceInfo.uiThreadNanos += (System.nanoTime() - startNanos);
}
protected final void addFocusChangeListener(OnFocusChangeListener l){
View view;
if(l != null && (view = getRealView()) != null) {
if( mFocusChangeListeners == null){
mFocusChangeListeners = new ArrayList<>();
view.setFocusable(true);
view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
for (OnFocusChangeListener listener : mFocusChangeListeners){
if(listener != null){
listener.onFocusChange(hasFocus);
}
}
}
});
}
mFocusChangeListeners.add(l);
}
}
protected final void addClickListener(OnClickListener l){
View view;
if(l != null && (view = getRealView()) != null) {
if(mHostClickListeners == null){
mHostClickListeners = new ArrayList<>();
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGesture != null && mGesture.isTouchEventConsumedByAdvancedGesture()){
//event is already consumed by gesture
return;
}
for (OnClickListener listener : mHostClickListeners){
if(listener != null) {
listener.onHostViewClick();
}
}
}
});
}
mHostClickListeners.add(l);
}
}
protected final void removeClickListener(OnClickListener l){
mHostClickListeners.remove(l);
}
public void bindData(WXComponent component){
long startNanos = System.nanoTime();
if(!isLazy()) {
if (component == null) {
component = this;
}
mCurrentRef = component.getDomObject().getRef();
updateStyle(component);
updateAttrs(component);
updateExtra(component.getDomObject().getExtra());
}
mTraceInfo.uiThreadNanos += (System.nanoTime() - startNanos);
}
public void updateStyle(WXComponent component){
ImmutableDomObject domObject = component.getDomObject();
if(domObject !=null){
updateProperties(domObject.getStyles());
}
}
public void updateAttrs(WXComponent component){
ImmutableDomObject domObject = component.getDomObject();
if(domObject !=null){
updateProperties(domObject.getAttrs());
}
}
public void refreshData(WXComponent component){
}
protected BorderDrawable getOrCreateBorder() {
if (mBackgroundDrawable == null) {
mBackgroundDrawable = new BorderDrawable();
if (mHost != null) {
Drawable backgroundDrawable = mHost.getBackground();
WXViewUtils.setBackGround(mHost, null);
if (backgroundDrawable == null) {
WXViewUtils.setBackGround(mHost, mBackgroundDrawable);
} else {
//TODO Not strictly clip according to background-clip:border-box
WXViewUtils.setBackGround(mHost, new LayerDrawable(new Drawable[]{
mBackgroundDrawable, backgroundDrawable}));
}
}
}
return mBackgroundDrawable;
}
/**
* layout view
*/
public final void setLayout(ImmutableDomObject domObject) {
if ( domObject == null || TextUtils.isEmpty(mCurrentRef)) {
return;
}
boolean nullParent = mParent == null;//parent is nullable
mDomObj = domObject;
//offset by sibling
int siblingOffset = nullParent?0:mParent.getChildrenLayoutTopOffset();
Spacing parentPadding = (nullParent?new Spacing():mParent.getDomObject().getPadding());
Spacing parentBorder = (nullParent?new Spacing():mParent.getDomObject().getBorder());
Spacing margin = mDomObj.getMargin();
int realWidth = (int) mDomObj.getLayoutWidth();
int realHeight = (int) mDomObj.getLayoutHeight();
int realLeft = (int) (mDomObj.getLayoutX() - parentPadding.get(Spacing.LEFT) -
parentBorder.get(Spacing.LEFT));
int realTop = (int) (mDomObj.getLayoutY() - parentPadding.get(Spacing.TOP) -
parentBorder.get(Spacing.TOP)) + siblingOffset;
int realRight = (int) margin.get(Spacing.RIGHT);
int realBottom = (int) margin.get(Spacing.BOTTOM);
Point rawOffset = new Point(
(int) mDomObj.getCSSLayoutLeft(),
(int) mDomObj.getCSSLayoutTop());
if (mPreRealWidth == realWidth && mPreRealHeight == realHeight && mPreRealLeft == realLeft && mPreRealTop == realTop) {
return;
}
mAbsoluteY = (int) (nullParent?0:mParent.getAbsoluteY() + mDomObj.getLayoutY());
mAbsoluteX = (int) (nullParent?0:mParent.getAbsoluteX() + mDomObj.getLayoutX());
//calculate first screen time
if (!mInstance.mEnd &&!(mHost instanceof ViewGroup) && mAbsoluteY+realHeight > mInstance.getWeexHeight()+1) {
mInstance.firstScreenRenderFinished();
}
MeasureOutput measureOutput = measure(realWidth, realHeight);
realWidth = measureOutput.width;
realHeight = measureOutput.height;
setComponentLayoutParams(realWidth, realHeight, realLeft, realTop, realRight, realBottom, rawOffset);
}
private void setComponentLayoutParams(int realWidth, int realHeight, int realLeft, int realTop,
int realRight, int realBottom, Point rawOffset) {
FlatGUIContext UIImp = getInstance().getFlatUIContext();
WidgetContainer ancestor;
Widget widget;
if ((ancestor = UIImp.getFlatComponentAncestor(this)) != null) {
if (this instanceof FlatComponent && !((FlatComponent) this).promoteToView(true)) {
widget = ((FlatComponent) this).getOrCreateFlatWidget();
} else {
widget = UIImp.getAndroidViewWidget(this);
}
setWidgetParams(widget, UIImp, rawOffset, realWidth, realHeight, realLeft, realRight, realTop,
realBottom);
} else if (mHost != null) {
// clear box shadow before host's size changed
clearBoxShadow();
if (mDomObj.isFixed()) {
setFixedHostLayoutParams(mHost, realWidth, realHeight, realLeft, realRight, realTop,
realBottom);
} else {
setHostLayoutParams(mHost, realWidth, realHeight, realLeft, realRight, realTop, realBottom);
}
mPreRealWidth = realWidth;
mPreRealHeight = realHeight;
mPreRealLeft = realLeft;
mPreRealTop = realTop;
onFinishLayout();
// restore box shadow
updateBoxShadow();
}
}
private void setWidgetParams(Widget widget, FlatGUIContext UIImp, Point rawoffset,
int width, int height, int left, int right, int top, int bottom) {
Point childOffset = new Point();
if (mParent != null) {
if (mParent instanceof FlatComponent &&
UIImp.getFlatComponentAncestor(mParent) != null &&
UIImp.getAndroidViewWidget(mParent) == null) {
childOffset.set(rawoffset.x, rawoffset.y);
}
else{
childOffset.set(left, top);
}
if (mParent instanceof FlatComponent &&
UIImp.getFlatComponentAncestor(mParent) != null &&
UIImp.getAndroidViewWidget(mParent) == null) {
Point parentLayoutOffset = ((FlatComponent) mParent).getOrCreateFlatWidget().getLocInFlatContainer();
childOffset.offset(parentLayoutOffset.x, parentLayoutOffset.y);
}
ViewGroup.LayoutParams lp = mParent
.getChildLayoutParams(this, mHost, width, height, left, right, top, bottom);
if (lp instanceof MarginLayoutParams) {
width = lp.width;
height = lp.height;
left = ((MarginLayoutParams) lp).leftMargin;
right = ((MarginLayoutParams) lp).rightMargin;
top = ((MarginLayoutParams) lp).topMargin;
bottom = ((MarginLayoutParams) lp).bottomMargin;
}
}
widget.setLayout(width, height, left, right, top, bottom, childOffset);
if (widget instanceof AndroidViewWidget && ((AndroidViewWidget) widget).getView()!=null) {
//TODO generic method if ever possible
setHostLayoutParams((T) ((AndroidViewWidget) widget).getView(),
width, height, childOffset.x, right, childOffset.y, bottom);
}
}
protected void setHostLayoutParams(T host, int width, int height, int left, int right, int top, int bottom){
ViewGroup.LayoutParams lp;
if(mParent == null){
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width,height);
params.setMargins(left, top, right, bottom);
lp = params;
}else{
lp = mParent.getChildLayoutParams(this,host,width, height, left,right,top,bottom);
}
if(lp != null) {
mHost.setLayoutParams(lp);
}
}
private void setFixedHostLayoutParams(T host, int width, int height, int left, int right, int top, int bottom){
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.width = width;
params.height = height;
params.setMargins(left, top, right, bottom);
host.setLayoutParams(params);
mInstance.moveFixedView(host);
if (WXEnvironment.isApkDebugable()) {
WXLogUtils.d("Weex_Fixed_Style", "WXComponent:setLayout :" + left + " " + top + " " + width + " " + height);
WXLogUtils.d("Weex_Fixed_Style", "WXComponent:setLayout Left:" + mDomObj.getStyles().getLeft() + " " + (int) mDomObj.getStyles().getTop());
}
}
/**
* After component's layout result is apply to view. May be invoke multiple times since
* DOM can be changed in js runtime.
*/
protected void onFinishLayout() {
Object param = mDomObj.getStyles() != null ? mDomObj.getStyles().get(Constants.Name.BACKGROUND_IMAGE) : null;
if (param != null) {
setBackgroundImage(param.toString());
}
}
public int getLayoutTopOffsetForSibling(){
return 0;
}
public float getLayoutWidth(){
float w = 0f;
if (mDomObj != null) {
w = mDomObj.getLayoutWidth();
w = Float.isNaN(w) ? 0f : w;
}
return w;
}
public float getLayoutHeight(){
float h = 0f;
if (mDomObj != null) {
h = mDomObj.getLayoutHeight();
h = Float.isNaN(h) ? 0f : h;
}
return h;
}
public void setPadding(Spacing padding, Spacing border) {
int left = (int) (padding.get(Spacing.LEFT) + border.get(Spacing.LEFT));
int top = (int) (padding.get(Spacing.TOP) + border.get(Spacing.TOP));
int right = (int) (padding.get(Spacing.RIGHT) + border.get(Spacing.RIGHT));
int bottom = (int) (padding.get(Spacing.BOTTOM) + border.get(Spacing.BOTTOM));
if (this instanceof FlatComponent && !((FlatComponent) this).promoteToView(true)) {
((FlatComponent) this).getOrCreateFlatWidget().setContentBox(left, top, right, bottom);
} else if (mHost != null) {
mHost.setPadding(left, top, right, bottom);
}
}
private void addEvents() {
int count = mDomObj.getEvents().size();
for (int i = 0; i < count; ++i) {
addEvent(mDomObj.getEvents().get(i));
}
setActiveTouchListener();
}
public void updateExtra(Object extra) {
}
public ImmutableDomObject getDomObject() {
return mDomObj;
}
/**
* measure
*/
protected MeasureOutput measure(int width, int height) {
MeasureOutput measureOutput = new MeasureOutput();
if(mFixedProp != 0){
measureOutput.width = mFixedProp;
measureOutput.height = mFixedProp;
}else {
measureOutput.width = width;
measureOutput.height = height;
}
return measureOutput;
}
@Deprecated
public void updateProperties(Map<String, Object> props) {
if (props == null || (mHost == null && !isVirtualComponent())) {
return;
}
for (Map.Entry<String, Object> entry : props.entrySet()) {
String key = entry.getKey();
Object param = entry.getValue();
String value = WXUtils.getString(param, null);
if (TextUtils.isEmpty(value)) {
param = convertEmptyProperty(key, value);
}
if (!setProperty(key, param)) {
if (mHolder == null) {
return;
}
Invoker invoker = mHolder.getPropertyInvoker(key);
if (invoker != null) {
try {
Type[] paramClazzs = invoker.getParameterTypes();
if (paramClazzs.length != 1) {
WXLogUtils.e("[WXComponent] setX method only one parameter:" + invoker);
return;
}
param = WXReflectionUtils.parseArgument(paramClazzs[0], param);
invoker.invoke(this, param);
} catch (Exception e) {
WXLogUtils.e("[WXComponent] updateProperties :" + "class:" + getClass() + "method:" + invoker.toString() + " function " + WXLogUtils.getStackTrace(e));
}
}
}
}
readyToRender();
if (this instanceof FlatComponent && mBackgroundDrawable != null) {
FlatComponent flatComponent = (FlatComponent) this;
if (!flatComponent.promoteToView(true) && !(flatComponent
.getOrCreateFlatWidget() instanceof AndroidViewWidget)) {
flatComponent.getOrCreateFlatWidget().setBackgroundAndBorder(mBackgroundDrawable);
}
}
}
/**
* Apply styles and attributes.
* @param key name of argument
* @param param value of argument
* @return true means that the property is consumed
*/
protected boolean setProperty(String key, Object param) {
switch (key) {
case Constants.Name.PREVENT_MOVE_EVENT:
if(mGesture != null){
mGesture.setPreventMoveEvent(WXUtils.getBoolean(param,false));
}
return true;
case Constants.Name.DISABLED:
Boolean disabled = WXUtils.getBoolean(param,null);
if (disabled != null) {
setDisabled(disabled);
setPseudoClassStatus(Constants.PSEUDO.DISABLED, disabled);
}
return true;
case Constants.Name.POSITION:
String position = WXUtils.getString(param,null);
if (position != null)
setSticky(position);
return true;
case Constants.Name.BACKGROUND_COLOR:
String bgColor = WXUtils.getString(param,null);
if (bgColor != null)
setBackgroundColor(bgColor);
return true;
case Constants.Name.BACKGROUND_IMAGE:
String bgImage = WXUtils.getString(param, null);
if (bgImage != null) {
setBackgroundImage(bgImage);
}
return true;
case Constants.Name.OPACITY:
Float opacity = WXUtils.getFloat(param,null);
if (opacity != null)
setOpacity(opacity);
return true;
case Constants.Name.BORDER_RADIUS:
case Constants.Name.BORDER_TOP_LEFT_RADIUS:
case Constants.Name.BORDER_TOP_RIGHT_RADIUS:
case Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS:
case Constants.Name.BORDER_BOTTOM_LEFT_RADIUS:
Float radius = WXUtils.getFloat(param,null);
if (radius != null) {
setBorderRadius(key, radius);
}
return true;
case Constants.Name.BORDER_WIDTH:
case Constants.Name.BORDER_TOP_WIDTH:
case Constants.Name.BORDER_RIGHT_WIDTH:
case Constants.Name.BORDER_BOTTOM_WIDTH:
case Constants.Name.BORDER_LEFT_WIDTH:
Float width = WXUtils.getFloat(param,null);
if (width != null)
setBorderWidth(key,width);
return true;
case Constants.Name.BORDER_STYLE:
case Constants.Name.BORDER_RIGHT_STYLE:
case Constants.Name.BORDER_BOTTOM_STYLE:
case Constants.Name.BORDER_LEFT_STYLE:
case Constants.Name.BORDER_TOP_STYLE:
String border_style = WXUtils.getString(param,null);
if (border_style != null)
setBorderStyle(key, border_style);
return true;
case Constants.Name.BORDER_COLOR:
case Constants.Name.BORDER_TOP_COLOR:
case Constants.Name.BORDER_RIGHT_COLOR:
case Constants.Name.BORDER_BOTTOM_COLOR:
case Constants.Name.BORDER_LEFT_COLOR:
String border_color = WXUtils.getString(param,null);
if (border_color != null)
setBorderColor(key, border_color);
return true;
case Constants.Name.VISIBILITY:
String visibility = WXUtils.getString(param,null);
if (visibility != null)
setVisibility(visibility);
return true;
case Constants.Name.ELEVATION:
if(param!=null) {
updateElevation();
}
return true;
case PROP_FIXED_SIZE:
String fixedSize = WXUtils.getString(param, PROP_FS_MATCH_PARENT);
setFixedSize(fixedSize);
return true;
case Constants.Name.ARIA_LABEL:
String label = WXUtils.getString(param,"");
setAriaLabel(label);
return true;
case Constants.Name.ARIA_HIDDEN:
boolean isHidden = WXUtils.getBoolean(param,false);
setAriaHidden(isHidden);
return true;
case Constants.Name.WIDTH:
case Constants.Name.MIN_WIDTH:
case Constants.Name.MAX_WIDTH:
case Constants.Name.HEIGHT:
case Constants.Name.MIN_HEIGHT:
case Constants.Name.MAX_HEIGHT:
case Constants.Name.ALIGN_ITEMS:
case Constants.Name.ALIGN_SELF:
case Constants.Name.FLEX:
case Constants.Name.FLEX_DIRECTION:
case Constants.Name.JUSTIFY_CONTENT:
case Constants.Name.FLEX_WRAP:
case Constants.Name.MARGIN:
case Constants.Name.MARGIN_TOP:
case Constants.Name.MARGIN_LEFT:
case Constants.Name.MARGIN_RIGHT:
case Constants.Name.MARGIN_BOTTOM:
case Constants.Name.PADDING:
case Constants.Name.PADDING_TOP:
case Constants.Name.PADDING_LEFT:
case Constants.Name.PADDING_RIGHT:
case Constants.Name.PADDING_BOTTOM:
case Constants.Name.LEFT:
case Constants.Name.TOP:
case Constants.Name.RIGHT:
case Constants.Name.BOTTOM:
return true;
case Constants.Name.BOX_SHADOW:
try {
updateBoxShadow();
} catch (Throwable t) {
t.printStackTrace();
}
return true;
default:
return false;
}
}
protected void updateBoxShadow() {
if (getDomObject() != null && getDomObject().getStyles() != null) {
Object boxShadow = getDomObject().getStyles().get(Constants.Name.BOX_SHADOW);
if (boxShadow == null) {
return;
}
View target = mHost;
if (this instanceof WXVContainer) {
target = ((WXVContainer) this).getBoxShadowHost();
}
if (target == null) {
return;
}
float[] radii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
WXStyle style = getDomObject().getStyles();
if (style != null) {
float tl = WXUtils.getFloat(style.get(Constants.Name.BORDER_TOP_LEFT_RADIUS), 0f);
radii[0] = tl;
radii[1] = tl;
float tr = WXUtils.getFloat(style.get(Constants.Name.BORDER_TOP_RIGHT_RADIUS), 0f);
radii[2] = tr;
radii[3] = tr;
float br = WXUtils.getFloat(style.get(Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS), 0f);
radii[4] = br;
radii[5] = br;
float bl = WXUtils.getFloat(style.get(Constants.Name.BORDER_BOTTOM_LEFT_RADIUS), 0f);
radii[6] = bl;
radii[7] = bl;
if (style.containsKey(Constants.Name.BORDER_RADIUS)) {
float radius = WXUtils.getFloat(style.get(Constants.Name.BORDER_RADIUS), 0f);
for (int i = 0; i < radii.length; i++) {
radii[i] = radius;
}
}
}
BoxShadowUtil.setBoxShadow(target, boxShadow.toString(), radii, getInstance().getInstanceViewPortWidth());
} else {
WXLogUtils.w("Can not resolve styles");
}
}
protected void clearBoxShadow() {
View target = mHost;
if (this instanceof WXVContainer) {
target = ((WXVContainer) this).getBoxShadowHost();
}
if (target != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
ViewOverlay overlay = target.getOverlay();
if (overlay != null) {
overlay.clear();
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
protected void setAriaHidden(boolean isHidden) {
View host = getHostView();
if(host != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){
host.setImportantForAccessibility(isHidden?View.IMPORTANT_FOR_ACCESSIBILITY_NO:View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
}
protected void setAriaLabel(String label) {
View host = getHostView();
if(host != null){
host.setContentDescription(label);
}
}
/**
* Avoid large size view fail in GPU-Animation.
* @param fixedSize
*/
private void setFixedSize(String fixedSize) {
if(PROP_FS_MATCH_PARENT.equals(fixedSize)){
mFixedProp = ViewGroup.LayoutParams.MATCH_PARENT;
}else if(PROP_FS_WRAP_CONTENT.equals(fixedSize)){
mFixedProp = ViewGroup.LayoutParams.WRAP_CONTENT;
}else{
mFixedProp = 0;
return;
}
if(mHost != null){
ViewGroup.LayoutParams layoutParams = mHost.getLayoutParams();
if(layoutParams != null){
layoutParams.height = mFixedProp;
layoutParams.width = mFixedProp;
mHost.setLayoutParams(layoutParams);
}
}
}
/**
* Add new event to component,this will post a task to DOM thread to add event.
* @param type
*/
protected void appendEventToDOM(String type){
WXSDKManager.getInstance().getWXDomManager().postAction(getInstanceId(), Actions.getAddEvent(getRef(),type),false);
}
public void addEvent(Object type) {
if(type instanceof CharSequence){
addEvent(type.toString());
}else if(type instanceof JSONObject){
JSONObject bindings = (JSONObject) type;
String eventName = bindings.getString(WXEvent.EVENT_KEY_TYPE);
if(eventName != null){
addEvent(eventName);
}
}
}
/**
* Do not use this method to add event, this only apply event already add to DomObject.
* @param type
*/
public void addEvent(String type) {
if (TextUtils.isEmpty(type)
|| mAppendEvents.contains(type)
|| getRealView() == null) {
return;
}
mAppendEvents.add(type);
View view = getRealView();
if (type.equals(Constants.Event.CLICK) && view != null) {
addClickListener(mClickEventListener);
} else if ((type.equals( Constants.Event.FOCUS) || type.equals( Constants.Event.BLUR)) ) {
addFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(boolean hasFocus) {
Map<String, Object> params = new HashMap<>();
params.put("timeStamp", System.currentTimeMillis());
fireEvent(hasFocus ? Constants.Event.FOCUS : Constants.Event.BLUR, params);
}
});
} else if (view != null &&
needGestureDetector(type)) {
if (view instanceof WXGestureObservable) {
if (mGesture == null) {
mGesture = new WXGesture(this, mContext);
boolean isPreventMove = WXUtils.getBoolean(getDomObject().getAttrs().get(Constants.Name.PREVENT_MOVE_EVENT),false);
mGesture.setPreventMoveEvent(isPreventMove);
}
mGestureType.add(type);
((WXGestureObservable) view).registerGestureListener(mGesture);
} else {
WXLogUtils.e(view.getClass().getSimpleName() + " don't implement " +
"WXGestureObservable, so no gesture is supported.");
}
} else {
Scrollable scroller = getParentScroller();
if (type.equals(Constants.Event.APPEAR) && scroller != null) {
scroller.bindAppearEvent(this);
}
if (type.equals(Constants.Event.DISAPPEAR) && scroller != null) {
scroller.bindDisappearEvent(this);
}
}
}
public View getRealView() {
return mHost;
}
/**
* Judge whether need to set an onTouchListener.<br>
* As there is only one onTouchListener in each view, so all the gesture that use onTouchListener should put there.
*
* @param type eventType {@link com.taobao.weex.common.Constants.Event}
* @return true for set an onTouchListener, otherwise false
*/
private boolean needGestureDetector(String type) {
if (mHost != null) {
for (WXGestureType gesture : WXGestureType.LowLevelGesture.values()) {
if (type.equals(gesture.toString())) {
return true;
}
}
for (WXGestureType gesture : WXGestureType.HighLevelGesture.values()) {
if (type.equals(gesture.toString())) {
return true;
}
}
}
return false;
}
/**
* get Scroller components
*/
public Scrollable getParentScroller() {
WXComponent component = this;
WXVContainer container;
Scrollable scroller;
for (; ; ) {
container = component.getParent();
if (container == null) {
return null;
}
if (container instanceof Scrollable) {
scroller = (Scrollable) container;
return scroller;
}
if (container.getRef().equals(WXDomObject.ROOT)) {
return null;
}
component = container;
}
}
/**
* get Scroller components
*/
@Nullable
public Scrollable getFirstScroller() {
if(this instanceof Scrollable){
return (Scrollable)this;
}
return null;
}
public WXVContainer getParent() {
return mParent;
}
public String getRef() {
if (mDomObj == null) {
return null;
}
return mCurrentRef;
}
/**
* create view
*
*/
public final void createView() {
long startNanos = System.nanoTime();
if(!isLazy()) {
createViewImpl();
}
mTraceInfo.uiThreadStart = System.currentTimeMillis();
mTraceInfo.uiThreadNanos += (System.nanoTime() - startNanos);
}
protected void createViewImpl() {
if (mContext != null) {
mHost = initComponentHostView(mContext);
if (mHost == null && !isVirtualComponent()) {
//compatible
initView();
}
if(mHost != null){
mHost.setId(WXViewUtils.generateViewId());
ComponentObserver observer;
if ((observer = getInstance().getComponentObserver()) != null) {
observer.onViewCreated(this, mHost);
}
}
onHostViewInitialized(mHost);
}else{
WXLogUtils.e("createViewImpl","Context is null");
}
}
/**
* Use {@link #initComponentHostView(Context context)} instead.
*/
@Deprecated
protected void initView() {
if (mContext != null)
mHost = initComponentHostView(mContext);
}
/**
* Create corresponding view for this component.
* @param context
* @return
*/
protected T initComponentHostView(@NonNull Context context){
/**
* compatible old initView
* TODO: change to abstract method in next V1.0 .
*/
return null;
}
/**
* Called after host view init. <br>
* Any overriding methods should invoke this method at the right time, to ensure the cached animation can be triggered correctly.
* (the animation will be cached when {@link #isLazy()} is true)
* @param host the host view
*/
@CallSuper
protected void onHostViewInitialized(T host){
if (mAnimationHolder != null) {
//Performs cached animation
mAnimationHolder.execute(mInstance, this);
}
setActiveTouchListener();
}
public T getHostView() {
return mHost;
}
/**
* use {@link #getHostView()} instead
* @return
*/
@Deprecated
public View getView(){
return mHost;
}
public int getAbsoluteY() {
return mAbsoluteY;
}
public int getAbsoluteX() {
return mAbsoluteX;
}
public void updateDom(WXDomObject dom) {
if (dom == null) {
return;
}
mDomObj = dom.clone();
}
public final void removeEvent(String type) {
if (TextUtils.isEmpty(type)) {
return;
}
mAppendEvents.remove(type);//only clean append events, not dom's events.
mGestureType.remove(type);
removeEventFromView(type);
}
protected void removeEventFromView(String type) {
if (type.equals(Constants.Event.CLICK) && getRealView() != null && mHostClickListeners != null) {
mHostClickListeners.remove(mClickEventListener);
//click event only remove from listener array
}
Scrollable scroller = getParentScroller();
if (type.equals(Constants.Event.APPEAR) && scroller != null) {
scroller.unbindAppearEvent(this);
}
if (type.equals(Constants.Event.DISAPPEAR) && scroller != null) {
scroller.unbindDisappearEvent(this);
}
}
public final void removeAllEvent() {
if (mDomObj == null || mDomObj.getEvents().size() < 1) {
return;
}
for (String event : mDomObj.getEvents()) {
if(event == null){
continue;
}
removeEventFromView(event);
}
mAppendEvents.clear();//only clean append events, not dom's events.
mGestureType.clear();
mGesture = null;
if (getRealView() != null &&
getRealView() instanceof WXGestureObservable) {
((WXGestureObservable) getRealView()).registerGestureListener(null);
}
if(mHost != null) {
mHost.setOnFocusChangeListener(null);
if (mHostClickListeners != null && mHostClickListeners.size() > 0) {
mHostClickListeners.clear();
mHost.setOnClickListener(null);
}
}
}
public final void removeStickyStyle() {
if (mDomObj == null ) {
return;
}
if (isSticky()) {
Scrollable scroller = getParentScroller();
if (scroller != null) {
scroller.unbindStickStyle(this);
}
}
}
public boolean isSticky() {
return mDomObj.getStyles().isSticky();
}
public boolean isFixed() {
return mDomObj.getStyles().isFixed();
}
public void setDisabled(boolean disabled) {
mIsDisabled = disabled;
if (mHost == null) {
return;
}
mHost.setEnabled(!disabled);
}
public boolean isDisabled(){
return mIsDisabled;
}
public void setSticky(String sticky) {
if (!TextUtils.isEmpty(sticky) && sticky.equals(Constants.Value.STICKY)) {
Scrollable waScroller = getParentScroller();
if (waScroller != null) {
waScroller.bindStickStyle(this);
}
}
}
public void setBackgroundColor(String color) {
if (!TextUtils.isEmpty(color)) {
int colorInt = WXResourceUtils.getColor(color);
if (!(colorInt == Color.TRANSPARENT && mBackgroundDrawable == null)){
getOrCreateBorder().setColor(colorInt);
}
}
}
public void setBackgroundImage(@NonNull String bgImage) {
if ("".equals(bgImage.trim())) {
getOrCreateBorder().setImage(null);
} else {
Shader shader = WXResourceUtils.getShader(bgImage, mDomObj.getLayoutWidth(), mDomObj.getLayoutHeight());
getOrCreateBorder().setImage(shader);
}
}
public void setOpacity(float opacity) {
if (opacity >= 0 && opacity <= 1 && mHost.getAlpha() != opacity) {
if (isLayerTypeEnabled()) {
mHost.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
mHost.setAlpha(opacity);
}
}
public void setBorderRadius(String key, float borderRadius) {
if (borderRadius >= 0) {
switch (key) {
case Constants.Name.BORDER_RADIUS:
getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_RADIUS_ALL, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_TOP_LEFT_RADIUS:
getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_TOP_LEFT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_TOP_RIGHT_RADIUS:
getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_TOP_RIGHT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS:
getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_BOTTOM_RIGHT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_BOTTOM_LEFT_RADIUS:
getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_BOTTOM_LEFT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getInstanceViewPortWidth()));
break;
}
}
}
public void setBorderWidth(String key, float borderWidth) {
if (borderWidth >= 0) {
switch (key) {
case Constants.Name.BORDER_WIDTH:
getOrCreateBorder().setBorderWidth(Spacing.ALL, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_TOP_WIDTH:
getOrCreateBorder().setBorderWidth(Spacing.TOP, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_RIGHT_WIDTH:
getOrCreateBorder().setBorderWidth(Spacing.RIGHT, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_BOTTOM_WIDTH:
getOrCreateBorder().setBorderWidth(Spacing.BOTTOM, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getInstanceViewPortWidth()));
break;
case Constants.Name.BORDER_LEFT_WIDTH:
getOrCreateBorder().setBorderWidth(Spacing.LEFT, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getInstanceViewPortWidth()));
break;
}
}
}
public void setBorderStyle(String key, String borderStyle) {
if(!TextUtils.isEmpty(borderStyle)){
switch (key){
case Constants.Name.BORDER_STYLE:
getOrCreateBorder().setBorderStyle(Spacing.ALL,borderStyle);
break;
case Constants.Name.BORDER_RIGHT_STYLE:
getOrCreateBorder().setBorderStyle(Spacing.RIGHT,borderStyle);
break;
case Constants.Name.BORDER_BOTTOM_STYLE:
getOrCreateBorder().setBorderStyle(Spacing.BOTTOM,borderStyle);
break;
case Constants.Name.BORDER_LEFT_STYLE:
getOrCreateBorder().setBorderStyle(Spacing.LEFT,borderStyle);
break;
case Constants.Name.BORDER_TOP_STYLE:
getOrCreateBorder().setBorderStyle(Spacing.TOP,borderStyle);
break;
}
}
}
public void setBorderColor(String key, String borderColor) {
if (!TextUtils.isEmpty(borderColor)) {
int colorInt = WXResourceUtils.getColor(borderColor);
if (colorInt != Integer.MIN_VALUE) {
switch (key) {
case Constants.Name.BORDER_COLOR:
getOrCreateBorder().setBorderColor(Spacing.ALL, colorInt);
break;
case Constants.Name.BORDER_TOP_COLOR:
getOrCreateBorder().setBorderColor(Spacing.TOP, colorInt);
break;
case Constants.Name.BORDER_RIGHT_COLOR:
getOrCreateBorder().setBorderColor(Spacing.RIGHT, colorInt);
break;
case Constants.Name.BORDER_BOTTOM_COLOR:
getOrCreateBorder().setBorderColor(Spacing.BOTTOM, colorInt);
break;
case Constants.Name.BORDER_LEFT_COLOR:
getOrCreateBorder().setBorderColor(Spacing.LEFT, colorInt);
break;
}
}
}
}
public
@Nullable
String getVisibility() {
try {
return (String) getDomObject().getStyles().get(Constants.Name.VISIBILITY);
} catch (Exception e) {
return Constants.Value.VISIBLE;
}
}
public void setVisibility(String visibility) {
View view;
if ((view = getRealView()) != null) {
if (TextUtils.equals(visibility, Constants.Value.VISIBLE)) {
view.setVisibility(View.VISIBLE);
} else if (TextUtils.equals(visibility, Constants.Value.HIDDEN)) {
view.setVisibility(View.GONE);
}
}
}
/**
* This is an experimental feature for elevation of material design.
*/
private void updateElevation() {
float elevation = getDomObject().getAttrs().getElevation(getInstance().getInstanceViewPortWidth());
if (!Float.isNaN(elevation)) {
ViewCompat.setElevation(getHostView(), elevation);
}
}
@Deprecated
public void registerActivityStateListener() {
}
/********************************
* begin hook Activity life cycle callback
********************************************************/
public void onActivityCreate() {
}
public void onActivityStart() {
}
public void onActivityPause() {
}
public void onActivityResume() {
}
public void onActivityStop() {
}
public void onActivityDestroy() {
}
public boolean onActivityBack() {
return false;
}
public void onActivityResult(int requestCode, int resultCode, Intent data){
}
public boolean onCreateOptionsMenu(Menu menu){return false;}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
}
/********************************
* end hook Activity life cycle callback
********************************************************/
public void recycled() {
if(mDomObj.isFixed())
return;
}
public void destroy() {
ComponentObserver observer;
if ((observer = getInstance().getComponentObserver()) != null) {
observer.onPreDestory(this);
}
if (WXEnvironment.isApkDebugable() && !WXUtils.isUiThread()) {
throw new WXRuntimeException("[WXComponent] destroy can only be called in main thread");
}
if(mHost!= null && mHost.getLayerType()==View.LAYER_TYPE_HARDWARE && isLayerTypeEnabled()) {
mHost.setLayerType(View.LAYER_TYPE_NONE, null);
}
removeAllEvent();
removeStickyStyle();
View view;
if(mDomObj.isFixed() && (view = getHostView()) != null){
getInstance().removeFixedView(view);
}
mDomObj = ImmutableDomObject.DESTROYED;
mIsDestroyed = true;
}
public boolean isDestoryed() {
return mIsDestroyed;
}
/**
* Detach view from its component. Components,
* which have difference between getHostView and getRealView or have temp calculation results,
* must<strong> override</strong> this method with their own implementation.
*
* @return the original View
*/
public View detachViewAndClearPreInfo() {
View original = mHost;
mPreRealLeft = 0;
mPreRealWidth = 0;
mPreRealHeight = 0;
mPreRealTop = 0;
// mHost = null;
return original;
}
public void clearPreLayout(){
mPreRealLeft = 0;
mPreRealWidth = 0;
mPreRealHeight = 0;
mPreRealTop = 0;
}
/**
* This method computes user visible left-top point in view's coordinate.
* The default implementation uses the scrollX and scrollY of the view as the result,
* and put the value in the parameter pointer.
* Components with different computation algorithm
* <strong> should override </strong> this method.
*
* @param pointF the user visible left-top point in view's coordinate.
*/
public void computeVisiblePointInViewCoordinate(PointF pointF) {
View view = getRealView();
pointF.set(view.getScrollX(), view.getScrollY());
}
public boolean containsGesture(WXGestureType WXGestureType) {
return mGestureType != null && mGestureType.contains(WXGestureType.toString());
}
protected boolean containsEvent(String event){
return mDomObj.getEvents().contains(event) || mAppendEvents.contains(event);
}
public void notifyAppearStateChange(String wxEventType,String direction){
if(containsEvent(Constants.Event.APPEAR) || containsEvent(Constants.Event.DISAPPEAR)) {
Map<String, Object> params = new HashMap<>();
params.put("direction", direction);
fireEvent(wxEventType, params);
}
}
public void notifyWatchAppearDisappearEvent(String wxEventType,String direction){
if(containsEvent(wxEventType)) {
Map<String, Object> params = new HashMap<>();
params.put("direction", direction);
fireEvent(wxEventType, params);
}
}
public boolean isUsing() {
return isUsing;
}
public void setUsing(boolean using) {
isUsing = using;
}
public void readyToRender() {
if (mParent != null && getInstance().isTrackComponent()) {
mParent.readyToRender();
}
}
public static class MeasureOutput {
public int width;
public int height;
}
/**
* Determine whether the current component needs to be placed in the real View tree
* @return false component add subview
*/
public boolean isVirtualComponent(){
return mType == TYPE_VIRTUAL;
}
public void removeVirtualComponent() {
}
public void setType(int type) {
mType = type;
}
public boolean hasScrollParent(WXComponent component) {
if (component.getParent() == null) {
return true;
} else if (component.getParent() instanceof WXScroller) {
return false;
} else {
return hasScrollParent(component.getParent());
}
}
/**
* Called when property has empty value
* @param propName
*/
@CheckResult
protected Object convertEmptyProperty(String propName, Object originalValue) {
switch (propName) {
case Constants.Name.BACKGROUND_COLOR:
return "transparent";
case Constants.Name.BORDER_RADIUS:
case Constants.Name.BORDER_BOTTOM_LEFT_RADIUS:
case Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS:
case Constants.Name.BORDER_TOP_LEFT_RADIUS:
case Constants.Name.BORDER_TOP_RIGHT_RADIUS:
return 0;
case Constants.Name.BORDER_WIDTH:
case Constants.Name.BORDER_TOP_WIDTH:
case Constants.Name.BORDER_LEFT_WIDTH:
case Constants.Name.BORDER_RIGHT_WIDTH:
case Constants.Name.BORDER_BOTTOM_WIDTH:
return 0;
case Constants.Name.BORDER_COLOR:
case Constants.Name.BORDER_TOP_COLOR:
case Constants.Name.BORDER_LEFT_COLOR:
case Constants.Name.BORDER_RIGHT_COLOR:
case Constants.Name.BORDER_BOTTOM_COLOR:
return "black";
}
return originalValue;
}
private void setActiveTouchListener(){
boolean hasActivePesudo = mDomObj.getStyles().getPesudoStyles().containsKey(Constants.PSEUDO.ACTIVE);
View view;
if(hasActivePesudo && (view = getRealView()) != null) {
boolean hasTouchConsumer = isConsumeTouch();
view.setOnTouchListener(new TouchActivePseudoListener(this,!hasTouchConsumer));
}
}
protected boolean isConsumeTouch(){
return (mHostClickListeners != null && mHostClickListeners.size() > 0) || mGesture != null;
}
@Override
public void updateActivePseudo(boolean isSet) {
setPseudoClassStatus(Constants.PSEUDO.ACTIVE,isSet);
}
/**
*
* @param clzName like ':active' or ':active:enabled'
* @param status
*/
protected void setPseudoClassStatus(String clzName,boolean status){
WXStyle styles = getDomObject().getStyles();
Map<String, Map<String,Object>> pesudoStyles = styles.getPesudoStyles();
if(pesudoStyles == null || pesudoStyles.size() == 0){
return;
}
Map<String,Object> resultStyles = mPesudoStatus.updateStatusAndGetUpdateStyles(
clzName,
status,
pesudoStyles,
styles.getPesudoResetStyles());
updateStyleByPesudo(resultStyles);
}
private void updateStyleByPesudo(Map<String,Object> styles){
Message message = Message.obtain();
WXDomTask task = new WXDomTask();
task.instanceId = getInstanceId();
task.args = new ArrayList<>();
JSONObject styleJson = new JSONObject(styles);
task.args.add(getRef());
task.args.add(styleJson);
task.args.add(true);//flag pesudo
message.obj = task;
message.what = WXDomHandler.MsgType.WX_DOM_UPDATE_STYLE;
WXSDKManager.getInstance().getWXDomManager().sendMessage(message);
}
public int getStickyOffset() {
return mStickyOffset;
}
public boolean canRecycled(){
return (!isFixed() || !isSticky()) && mDomObj.getAttrs().canRecycled();
}
/**
* Sets the offset for the sticky
* @param stickyOffset child[y]-parent[y]
*/
public void setStickyOffset(int stickyOffset) {
mStickyOffset = stickyOffset;
}
/**
* For now, this method respect the result of {@link WXSDKInstance#isLayerTypeEnabled()}
* @return Refer {@link WXSDKInstance#isLayerTypeEnabled()}
*/
public boolean isLayerTypeEnabled() {
return getInstance().isLayerTypeEnabled();
}
/**
* Sets whether or not to relayout page during animation, default is false
*/
public void setNeedLayoutOnAnimation(boolean need) {
this.mNeedLayoutOnAnimation = need;
}
/**
* Trigger a updateStyle invoke to relayout current page
*/
public void notifyNativeSizeChanged(int w, int h) {
if (!mNeedLayoutOnAnimation) {
return;
}
Message message = Message.obtain();
WXDomTask task = new WXDomTask();
task.instanceId = getInstanceId();
if (task.args == null) {
task.args = new ArrayList<>();
}
JSONObject style = new JSONObject(2);
float webW = WXViewUtils.getWebPxByWidth(w);
float webH = WXViewUtils.getWebPxByWidth(h);
style.put("width", webW);
style.put("height", webH);
task.args.add(getRef());
task.args.add(style);
message.obj = task;
message.what = WXDomHandler.MsgType.WX_DOM_UPDATE_STYLE;
WXSDKManager.getInstance().getWXDomManager().sendMessage(message);
}
public static final int STATE_DOM_FINISH = 0;
public static final int STATE_UI_FINISH = 1;
public static final int STATE_ALL_FINISH = 2;
@IntDef({STATE_DOM_FINISH, STATE_UI_FINISH, STATE_ALL_FINISH})
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PARAMETER)
public @interface RenderState {}
@CallSuper
public void onRenderFinish(@RenderState int state) {
if (WXTracing.isAvailable()) {
double domTime = Stopwatch.nanosToMillis(((WXDomObject) mDomObj).mDomThreadNanos + mTraceInfo.domThreadNanos);
double uiTime = Stopwatch.nanosToMillis(mTraceInfo.uiThreadNanos);
if (state == STATE_ALL_FINISH || state == STATE_DOM_FINISH) {
WXTracing.TraceEvent domEvent = WXTracing.newEvent("DomExecute", getInstanceId(), mTraceInfo.rootEventId);
domEvent.ph = "X";
domEvent.duration = domTime;
domEvent.ts = mTraceInfo.domThreadStart;
domEvent.tname = "DOMThread";
domEvent.name = getDomObject().getType();
domEvent.classname = getClass().getSimpleName();
if (getParent() != null) {
domEvent.parentRef = getParent().getRef();
}
domEvent.submit();
}
if (state == STATE_ALL_FINISH || state == STATE_UI_FINISH) {
if (mTraceInfo.uiThreadStart != -1) {
WXTracing.TraceEvent uiEvent = WXTracing.newEvent("UIExecute", getInstanceId(), mTraceInfo.rootEventId);
uiEvent.ph = "X";
uiEvent.duration = uiTime;
uiEvent.ts = mTraceInfo.uiThreadStart;
uiEvent.name = getDomObject().getType();
uiEvent.classname = getClass().getSimpleName();
if (getParent() != null) {
uiEvent.parentRef = getParent().getRef();
}
uiEvent.submit();
} else {
if(WXEnvironment.isApkDebugable() && !isLazy()) {
WXLogUtils.w("onRenderFinish", "createView() not called");
}
}
}
}
}
public boolean isWaste() {
return waste;
}
public void setWaste(boolean waste) {
if(this.waste != waste){
this.waste = waste;
WXDomObject domObject = (WXDomObject) getDomObject();
if(waste){
getDomObject().getStyles().put(Constants.Name.VISIBILITY, Constants.Value.HIDDEN);
if(domObject.getAttrs().getStatement() == null) {
domObject.setVisible(false);
if (getHostView() != null) {
getHostView().setVisibility(View.GONE);
}
return;
}
if(Constants.Value.VISIBLE.equals(domObject.getAttrs().get(Constants.Name.VIF_FALSE))){
domObject.setVisible(true);
if(getHostView() != null){
getHostView().setVisibility(View.VISIBLE);
}
}else{
domObject.setVisible(false);
if(getHostView() != null){
getHostView().setVisibility(View.GONE);
}
}
}else{
domObject.setVisible(true);
if(getHostView() != null){
getHostView().setVisibility(View.VISIBLE);
}
getDomObject().getStyles().put(Constants.Name.VISIBILITY, Constants.Value.VISIBLE);
}
}
}
}