| /* |
| * 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.content.res.ColorStateList; |
| import android.graphics.Canvas; |
| import android.graphics.Color; |
| import android.graphics.Path; |
| import android.graphics.Point; |
| import android.graphics.PointF; |
| import android.graphics.Rect; |
| import android.graphics.RectF; |
| import android.graphics.Shader; |
| import android.graphics.drawable.ColorDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.LayerDrawable; |
| import android.graphics.drawable.RippleDrawable; |
| import android.os.Build; |
| 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.v4.view.AccessibilityDelegateCompat; |
| import android.support.v4.view.ViewCompat; |
| import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; |
| import android.text.TextUtils; |
| import android.util.Pair; |
| import android.view.Menu; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewOverlay; |
| import android.widget.FrameLayout; |
| |
| import com.alibaba.fastjson.JSONArray; |
| 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.adapter.IWXAccessibilityRoleAdapter; |
| import com.taobao.weex.bridge.EventResult; |
| import com.taobao.weex.bridge.Invoker; |
| import com.taobao.weex.bridge.WXBridgeManager; |
| import com.taobao.weex.common.Constants; |
| import com.taobao.weex.common.IWXObject; |
| import com.taobao.weex.common.WXErrorCode; |
| import com.taobao.weex.common.WXPerformance; |
| import com.taobao.weex.common.WXRuntimeException; |
| import com.taobao.weex.dom.CSSShorthand; |
| import com.taobao.weex.dom.CSSShorthand.CORNER; |
| import com.taobao.weex.dom.WXEvent; |
| import com.taobao.weex.dom.WXStyle; |
| import com.taobao.weex.dom.transition.WXTransition; |
| import com.taobao.weex.layout.ContentBoxMeasurement; |
| import com.taobao.weex.tracing.Stopwatch; |
| import com.taobao.weex.tracing.WXTracing; |
| import com.taobao.weex.ui.IFComponentHolder; |
| import com.taobao.weex.ui.action.BasicComponentData; |
| import com.taobao.weex.ui.action.GraphicActionAnimation; |
| import com.taobao.weex.ui.action.GraphicActionUpdateStyle; |
| import com.taobao.weex.ui.action.GraphicPosition; |
| import com.taobao.weex.ui.action.GraphicSize; |
| import com.taobao.weex.ui.animation.WXAnimationBean; |
| import com.taobao.weex.ui.animation.WXAnimationModule; |
| import com.taobao.weex.ui.component.basic.WXBasicComponent; |
| import com.taobao.weex.ui.component.binding.Statements; |
| import com.taobao.weex.ui.component.list.template.jni.NativeRenderObjectUtils; |
| 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.WXExceptionUtils; |
| 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.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentLinkedQueue; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * abstract component |
| */ |
| public abstract class WXComponent<T extends View> extends WXBasicComponent 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"; |
| public static final String TYPE = "type"; |
| public static final String ROOT = "_root"; |
| |
| private int mFixedProp = 0; |
| /** package **/ T mHost; |
| |
| private volatile WXVContainer mParent; |
| private WXSDKInstance mInstance; |
| private Context mContext; |
| |
| private int mAbsoluteY = 0; |
| private int mAbsoluteX = 0; |
| @Nullable |
| private Set<String> mGestureType; |
| |
| private BorderDrawable mBackgroundDrawable; |
| private Drawable mRippleBackground; |
| private int mPreRealWidth = 0; |
| private int mPreRealHeight = 0; |
| private int mPreRealLeft = 0; |
| private int mPreRealTop = 0; |
| private int mStickyOffset = 0; |
| protected WXGesture mGesture; |
| private IFComponentHolder mHolder; |
| private boolean isUsing = false; |
| private List<OnClickListener> mHostClickListeners; |
| private List<OnFocusChangeListener> mFocusChangeListeners; |
| private Set<String> mAppendEvents; |
| private WXAnimationModule.AnimationHolder mAnimationHolder; |
| private PesudoStatus mPesudoStatus; |
| private boolean mIsDestroyed = false; |
| private boolean mIsDisabled = false; |
| private int mType = TYPE_COMMON; |
| private boolean mNeedLayoutOnAnimation = false; |
| private String mLastBoxShadowId; |
| public int deepInComponentTree = 0; |
| |
| 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; |
| |
| private ContentBoxMeasurement contentBoxMeasurement; |
| private WXTransition mTransition; |
| private GraphicSize mPseudoResetGraphicSize; |
| @Nullable |
| private ConcurrentLinkedQueue<Pair<String, Map<String, Object>>> animations; |
| |
| @Deprecated |
| public WXComponent(WXSDKInstance instance, WXVContainer parent, String instanceId, boolean isLazy, BasicComponentData basicComponentData) { |
| this(instance, parent, isLazy, basicComponentData); |
| } |
| |
| @Deprecated |
| public WXComponent(WXSDKInstance instance, WXVContainer parent, boolean isLazy, BasicComponentData basicComponentData) { |
| this(instance, parent, basicComponentData); |
| } |
| |
| public WXComponent(WXSDKInstance instance, WXVContainer parent, BasicComponentData basicComponentData) { |
| this(instance, parent, TYPE_COMMON, basicComponentData); |
| } |
| |
| public WXComponent(WXSDKInstance instance, WXVContainer parent, int type, BasicComponentData basicComponentData) { |
| super(basicComponentData); |
| mInstance = instance; |
| mContext = mInstance.getContext(); |
| mParent = parent; |
| mType = type; |
| |
| if (instance != null) |
| setViewPortWidth(instance.getInstanceViewPortWidth()); |
| |
| onCreate(); |
| ComponentObserver observer; |
| if ((observer = getInstance().getComponentObserver()) != null) { |
| observer.onCreate(this); |
| } |
| } |
| |
| |
| |
| @Override |
| protected final void bindComponent(WXComponent component) { |
| super.bindComponent(component); |
| if (getInstance() != null) { |
| setViewPortWidth(getInstance().getInstanceViewPortWidth()); |
| } |
| mParent = component.getParent(); |
| mType = component.getType(); |
| } |
| |
| protected final void setContentBoxMeasurement(final ContentBoxMeasurement contentBoxMeasurement) { |
| this.contentBoxMeasurement = contentBoxMeasurement; |
| mInstance.addContentBoxMeasurement(getRenderObjectPtr(), contentBoxMeasurement); |
| WXBridgeManager.getInstance().bindMeasurementToRenderObject(getRenderObjectPtr()); |
| } |
| |
| public void updateStyles(WXComponent component) { |
| if (component != null) { |
| updateProperties(component.getStyles()); |
| applyBorder(component); |
| } |
| } |
| |
| public void updateStyles(Map<String, Object> styles) { |
| if (styles != null) { |
| updateProperties(styles); |
| applyBorder(this); |
| } |
| } |
| |
| public void updateAttrs(WXComponent component) { |
| if (component != null) { |
| updateProperties(component.getAttrs()); |
| } |
| } |
| |
| public void updateAttrs(Map<String, Object> attrs) { |
| if (attrs != null) { |
| updateProperties(attrs); |
| } |
| } |
| |
| private void applyBorder(WXComponent component) { |
| CSSShorthand border = component.getBorder(); |
| float left = border.get(CSSShorthand.EDGE.LEFT); |
| float top = border.get(CSSShorthand.EDGE.TOP); |
| float right = border.get(CSSShorthand.EDGE.RIGHT); |
| float bottom = border.get(CSSShorthand.EDGE.BOTTOM); |
| |
| if (mHost == null) { |
| return; |
| } |
| |
| setBorderWidth(Constants.Name.BORDER_LEFT_WIDTH, left); |
| setBorderWidth(Constants.Name.BORDER_TOP_WIDTH, top); |
| setBorderWidth(Constants.Name.BORDER_RIGHT_WIDTH, right); |
| setBorderWidth(Constants.Name.BORDER_BOTTOM_WIDTH, bottom); |
| } |
| |
| public void setPadding(CSSShorthand padding, CSSShorthand border) { |
| int left = (int) (padding.get(CSSShorthand.EDGE.LEFT) + border.get(CSSShorthand.EDGE.LEFT)); |
| int top = (int) (padding.get(CSSShorthand.EDGE.TOP) + border.get(CSSShorthand.EDGE.TOP)); |
| int right = (int) (padding.get(CSSShorthand.EDGE.RIGHT) + border.get(CSSShorthand.EDGE.RIGHT)); |
| int bottom = (int) (padding.get(CSSShorthand.EDGE.BOTTOM) + border.get(CSSShorthand.EDGE.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 applyEvents() { |
| if (getEvents() == null || getEvents().isEmpty()) |
| return; |
| WXEvent event = getEvents(); |
| int size = event.size(); |
| for (int i=0; i<size; i++) { |
| if(i >= event.size()){ |
| break; |
| } |
| String type = event.get(i); |
| addEvent(type); |
| } |
| setActiveTouchListener(); |
| } |
| |
| /** |
| * Do not use this method to add event, this only apply event already add to DomObject. |
| * |
| * @param type |
| */ |
| public void addEvent(final String type) { |
| if (mAppendEvents == null) { |
| mAppendEvents = new HashSet<>(); |
| } |
| if (TextUtils.isEmpty(type) || mAppendEvents.contains(type)) { |
| return; |
| } |
| final View view = getRealView(); |
| |
| if (type.equals(Constants.Event.LAYEROVERFLOW)) |
| addLayerOverFlowListener(getRef()); |
| |
| if (type.equals(Constants.Event.CLICK)) { |
| if (view == null) { |
| // wait next time to add. |
| return; |
| } |
| if(mClickEventListener == null){ |
| mClickEventListener = new OnClickListenerImp(); |
| } |
| addClickListener(mClickEventListener); |
| } else if ((type.equals(Constants.Event.FOCUS) || type.equals(Constants.Event.BLUR))) { |
| addFocusChangeListener(new OnFocusChangeListener() { |
| @Override |
| 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 (needGestureDetector(type)) { |
| if (null == view) { |
| // wait next time to add. |
| return; |
| } |
| if (view instanceof WXGestureObservable) { |
| if (mGesture == null) { |
| mGesture = new WXGesture(this, mContext); |
| boolean isPreventMove = WXUtils.getBoolean(getAttrs().get(Constants.Name.PREVENT_MOVE_EVENT), false); |
| mGesture.setPreventMoveEvent(isPreventMove); |
| } |
| if (mGestureType == null) { |
| mGestureType = new HashSet<>(); |
| } |
| mGestureType.add(type); |
| ((WXGestureObservable)view).registerGestureListener(mGesture); |
| } else { |
| WXLogUtils.e(view.getClass().getSimpleName() + " don't implement " + |
| "WXGestureObservable, so no gesture is supported."); |
| } |
| } else { |
| final Scrollable scroller = getParentScroller(); |
| if (scroller == null) { |
| // wait next time to add. |
| return; |
| } |
| if (type.equals(Constants.Event.APPEAR)) { |
| scroller.bindAppearEvent(this); |
| } else if (type.equals(Constants.Event.DISAPPEAR)) { |
| scroller.bindDisappearEvent(this); |
| } |
| } |
| // Final add to mAppendEvents. |
| mAppendEvents.add(type); |
| } |
| |
| 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 String getAttrByKey(String key) { |
| return "default"; |
| } |
| |
| //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(RestrictTo.Scope.LIBRARY) |
| public boolean isFlatUIEnabled() { |
| return mParent != null && mParent.isFlatUIEnabled(); |
| } |
| |
| private class OnClickListenerImp implements 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(getLayoutWidth(), mInstance.getInstanceViewPortWidth())); |
| position.put("height", WXViewUtils.getWebPxByWidth(getLayoutHeight(), mInstance.getInstanceViewPortWidth())); |
| param.put(Constants.Name.POSITION, position); |
| fireEvent(Constants.Event.CLICK, param); |
| } |
| }; |
| private OnClickListenerImp mClickEventListener; |
| |
| 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) getLayoutWidth(); |
| int height = (int) 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); |
| } |
| |
| public final void fireEvent(String type){ |
| fireEvent(type,null); |
| } |
| |
| public final void fireEvent(String type, Map<String, Object> params){ |
| if(WXUtils.getBoolean(getAttrs().get("fireEventSyn"), false)){ |
| fireEventWait(type, params); |
| }else{ |
| fireEvent(type, params,null, null); |
| } |
| } |
| |
| public final EventResult fireEventWait(String type, Map<String, Object> params){ |
| final CountDownLatch waitLatch = new CountDownLatch(1); |
| EventResult callback = new EventResult(){ |
| @Override |
| public void onCallback(Object result) { |
| super.onCallback(result); |
| waitLatch.countDown(); |
| } |
| }; |
| try{ |
| fireEvent(type, params, null, callback); |
| waitLatch.await(10, TimeUnit.MILLISECONDS); |
| return callback; |
| }catch (Exception e){ |
| if(WXEnvironment.isApkDebugable()){ |
| WXLogUtils.e("fireEventWait", e); |
| } |
| return callback; |
| } |
| } |
| |
| protected final void fireEvent(String type, Map<String, Object> params,Map<String, Object> domChanges){ |
| fireEvent(type, params, domChanges, null); |
| } |
| |
| |
| private final void fireEvent(String type, Map<String, Object> params,Map<String, Object> domChanges, EventResult callback){ |
| if(mInstance != null) { |
| List<Object> eventArgsValues = null; |
| if(getEvents() != null && getEvents().getEventBindingArgsValues() != null){ |
| eventArgsValues = getEvents().getEventBindingArgsValues().get(type); |
| } |
| if(params != null){ |
| String componentId = Statements.getComponentId(this); |
| if(componentId != null) { |
| params.put("componentId", componentId); |
| } |
| } |
| mInstance.fireEvent(getRef(), type, params,domChanges, eventArgsValues, callback); |
| } |
| } |
| |
| /** |
| * 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() { |
| if(mLazy){ |
| return true; |
| } |
| return mParent != null && mParent.isLazy(); |
| } |
| |
| 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) { |
| if (!isLazy()) { |
| if (component == null) { |
| component = this; |
| } |
| bindComponent(component); |
| updateStyles(component); |
| updateAttrs(component); |
| updateExtra(component.getExtra()); |
| } |
| } |
| |
| public void applyLayoutAndEvent(WXComponent component) { |
| if (!isLazy()) { |
| if (component == null) { |
| component = this; |
| } |
| bindComponent(component); |
| setLayout(component); |
| setPadding(component.getPadding(), component.getBorder()); |
| applyEvents(); |
| } |
| } |
| |
| public void setDemission(GraphicSize size, GraphicPosition position) { |
| setLayoutPosition(position); |
| setLayoutSize(size); |
| } |
| |
| public void updateDemission(float top, float bottom, float left, float right, float height, float width) { |
| getLayoutPosition().update(top, bottom, left, right); |
| getLayoutSize().update(width, height); |
| } |
| |
| |
| public void applyLayoutOnly(){ |
| if(!isLazy()) { |
| setLayout(this); |
| setPadding(this.getPadding(), this.getBorder()); |
| } |
| } |
| |
| |
| public void refreshData(WXComponent component) { |
| |
| } |
| |
| @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 (key == null) { |
| WXExceptionUtils.commitCriticalExceptionRT(getInstanceId(), |
| WXErrorCode.WX_RENDER_ERR_NULL_KEY, "updateProperties", |
| WXErrorCode.WX_RENDER_ERR_NULL_KEY.getErrorMsg(), null); |
| } else { |
| 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) { |
| if(key == null){ |
| return true; |
| } |
| 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 && mHost != 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_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.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: |
| 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; |
| case Constants.Name.ROLE: |
| setRole(WXUtils.getString(param, "")); |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| protected BorderDrawable getOrCreateBorder() { |
| if (mBackgroundDrawable == null) { |
| mBackgroundDrawable = new BorderDrawable(); |
| if (mHost != null) { |
| WXViewUtils.setBackGround(mHost, null); |
| if (mRippleBackground == null) { |
| WXViewUtils.setBackGround(mHost, mBackgroundDrawable); |
| } else { |
| //TODO Not strictly clip according to background-clip:border-box |
| WXViewUtils.setBackGround(mHost, new LayerDrawable(new Drawable[]{ |
| mRippleBackground, mBackgroundDrawable})); |
| } |
| } |
| } |
| return mBackgroundDrawable; |
| } |
| |
| /** |
| * layout view |
| */ |
| public final void setLayout(WXComponent component) { |
| |
| if (TextUtils.isEmpty(component.getComponentType()) |
| || TextUtils.isEmpty(component.getRef()) || component.getLayoutPosition() == null |
| || component.getLayoutSize() == null) { |
| return; |
| } |
| |
| setLayoutSize(component.getLayoutSize()); |
| setLayoutPosition(component.getLayoutPosition()); |
| setPaddings(component.getPadding()); |
| setMargins(component.getMargin()); |
| setBorders(component.getBorder()); |
| |
| parseAnimation(); |
| |
| boolean nullParent = mParent == null;//parent is nullable |
| |
| //offset by sibling |
| int siblingOffset = nullParent ? 0 : mParent.getChildrenLayoutTopOffset(); |
| |
| CSSShorthand parentPadding = (nullParent ? new CSSShorthand() : mParent.getPadding()); |
| CSSShorthand parentBorder = (nullParent ? new CSSShorthand() : mParent.getBorder()); |
| |
| int realWidth = (int) getLayoutSize().getWidth(); |
| int realHeight = (int) getLayoutSize().getHeight(); |
| |
| int realLeft = 0; |
| int realTop = 0; |
| |
| if (isFixed()) { |
| realLeft = (int) (getLayoutPosition().getLeft() - getInstance().getRenderContainerPaddingLeft()); |
| realTop = (int) (getLayoutPosition().getTop() - getInstance().getRenderContainerPaddingTop()) + siblingOffset; |
| } else { |
| realLeft = (int) (getLayoutPosition().getLeft() - |
| parentPadding.get(CSSShorthand.EDGE.LEFT) - parentBorder.get(CSSShorthand.EDGE.LEFT)); |
| realTop = (int) (getLayoutPosition().getTop() - |
| parentPadding.get(CSSShorthand.EDGE.TOP) - parentBorder.get(CSSShorthand.EDGE.TOP)) + siblingOffset; |
| } |
| |
| int realRight = (int) getMargin().get(CSSShorthand.EDGE.RIGHT); |
| int realBottom = (int) getMargin().get(CSSShorthand.EDGE.BOTTOM); |
| |
| Point rawOffset = new Point( |
| (int) getLayoutPosition().getLeft(), |
| (int) getLayoutPosition().getTop()); |
| |
| if (mPreRealWidth == realWidth && mPreRealHeight == realHeight && mPreRealLeft == realLeft && mPreRealTop == realTop) { |
| return; |
| } |
| |
| if (realHeight >= WXPerformance.VIEW_LIMIT_HEIGHT && realWidth>=WXPerformance.VIEW_LIMIT_WIDTH){ |
| mInstance.getWXPerformance().cellExceedNum++; |
| } |
| |
| mAbsoluteY = (int) (nullParent ? 0 : mParent.getAbsoluteY() + getCSSLayoutTop()); |
| mAbsoluteX = (int) (nullParent ? 0 : mParent.getAbsoluteX() + getCSSLayoutLeft()); |
| |
| if (mHost == null) { |
| return; |
| } |
| |
| //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) { |
| if(getInstance() == null || getInstance().isDestroy()){ |
| return; |
| } |
| |
| FlatGUIContext UIImp = getInstance().getFlatUIContext(); |
| WidgetContainer ancestor; |
| Widget widget; |
| if (UIImp != null && (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 (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 ViewGroup.MarginLayoutParams) { |
| width = lp.width; |
| height = lp.height; |
| left = ((ViewGroup.MarginLayoutParams) lp).leftMargin; |
| right = ((ViewGroup.MarginLayoutParams) lp).rightMargin; |
| top = ((ViewGroup.MarginLayoutParams) lp).topMargin; |
| bottom = ((ViewGroup.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); |
| } |
| } |
| |
| public int getLayoutTopOffsetForSibling() { |
| return 0; |
| } |
| |
| 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) { |
| host.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:" + getStyles().getLeft() + " " + (int) getStyles().getTop()); |
| } |
| } |
| |
| protected void updateBoxShadow() { |
| if (!BoxShadowUtil.isBoxShadowEnabled()) { |
| // WXLogUtils.w("BoxShadow", "box-shadow disabled"); |
| return; |
| } |
| |
| if (getStyles() != null) { |
| Object boxShadow = getStyles().get(Constants.Name.BOX_SHADOW); |
| Object shadowQuality = getAttrs().get(Constants.Name.SHADOW_QUALITY); |
| if (boxShadow == null) { |
| return; |
| } |
| |
| View target = mHost; |
| if (this instanceof WXVContainer) { |
| target = ((WXVContainer) this).getBoxShadowHost(false); |
| } |
| |
| if (target == null) { |
| return; |
| } |
| |
| float quality = WXUtils.getFloat(shadowQuality, 0.5f); |
| int viewPort = getInstance().getInstanceViewPortWidth(); |
| String token = new StringBuilder(boxShadow.toString()).append(" / [") |
| .append(target.getMeasuredWidth()).append(",") |
| .append(target.getMeasuredHeight()).append("] / ") |
| .append(quality).toString(); |
| |
| if (mLastBoxShadowId != null && mLastBoxShadowId.equals(token)) { |
| WXLogUtils.d("BoxShadow", "box-shadow style was not modified. " + token); |
| return; |
| } |
| |
| float[] radii = new float[]{0, 0, 0, 0, 0, 0, 0, 0}; |
| WXStyle style = 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, viewPort, quality); |
| mLastBoxShadowId = token; |
| } else { |
| WXLogUtils.w("Can not resolve styles"); |
| } |
| } |
| |
| protected void clearBoxShadow() { |
| if (!BoxShadowUtil.isBoxShadowEnabled()) { |
| // WXLogUtils.w("BoxShadow", "box-shadow disabled"); |
| return; |
| } |
| |
| if (getStyles() != null) { |
| Object obj = getStyles().get(Constants.Name.BOX_SHADOW); |
| if (obj == null) { |
| return; |
| } |
| } |
| |
| View target = mHost; |
| if (this instanceof WXVContainer) { |
| target = ((WXVContainer) this).getBoxShadowHost(true); |
| } |
| |
| if (target != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { |
| ViewOverlay overlay = target.getOverlay(); |
| if (overlay != null) { |
| overlay.clear(); |
| } |
| } |
| mLastBoxShadowId = null; |
| } |
| |
| /** |
| * 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 = getStyles() != null ? getStyles().get(Constants.Name.BACKGROUND_IMAGE) : null; |
| if (param != null) { |
| setBackgroundImage(param.toString()); |
| } |
| } |
| |
| /** |
| * 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; |
| } |
| |
| @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); |
| } |
| } |
| |
| protected void setRole(String roleKey) { |
| View host = getHostView(); |
| String role = roleKey; |
| if (host != null && !TextUtils.isEmpty(roleKey)) { |
| IWXAccessibilityRoleAdapter roleAdapter = WXSDKManager.getInstance().getAccessibilityRoleAdapter(); |
| if (roleAdapter != null) { |
| role = roleAdapter.getRole(roleKey); |
| } |
| final String finalRole = role; |
| AccessibilityDelegateCompat delegate = new AccessibilityDelegateCompat() { |
| @Override |
| public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { |
| try { |
| super.onInitializeAccessibilityNodeInfo(host, info); |
| info.setRoleDescription(finalRole); |
| } catch (Throwable e) { |
| WXLogUtils.e("SetRole failed!"); |
| } |
| } |
| }; |
| ViewCompat.setAccessibilityDelegate(host, delegate); |
| } |
| } |
| |
| /** |
| * 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 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; |
| } |
| } |
| } |
| if(WXGesture.isStopPropagation(type)){ |
| 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(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; |
| } |
| |
| /** |
| * create view |
| */ |
| public final void createView() { |
| if (!isLazy()) { |
| createViewImpl(); |
| } |
| } |
| |
| 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 |
| */ |
| 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 removeEvent(String type) { |
| if (TextUtils.isEmpty(type)) { |
| return; |
| } |
| if (getEvents() == null || mAppendEvents == null || mGestureType == null) { |
| return; |
| } |
| |
| if (type.equals(Constants.Event.LAYEROVERFLOW)) |
| removeLayerOverFlowListener(getRef()); |
| |
| getEvents().remove(type); |
| 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) { |
| if(mClickEventListener == null){ |
| mClickEventListener = new OnClickListenerImp(); |
| } |
| 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 void removeAllEvent() { |
| if (getEvents().size() < 1) { |
| return; |
| } |
| WXEvent events = getEvents(); |
| int size = events.size(); |
| for (int i=0; i<size; i++) { |
| if(i >= events.size()){ |
| break; |
| } |
| String event = events.get(i); |
| if (event == null) { |
| continue; |
| } |
| removeEventFromView(event); |
| } |
| if(mAppendEvents!=null) { |
| mAppendEvents.clear();//only clean append events, not dom's events. |
| } |
| if(mGestureType != null){ |
| 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 (isSticky()) { |
| Scrollable scroller = getParentScroller(); |
| if (scroller != null) { |
| scroller.unbindStickStyle(this); |
| } |
| } |
| } |
| |
| public boolean isSticky() { |
| return getStyles().isSticky(); |
| } |
| |
| public boolean isFixed() { |
| return 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 (isRippleEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
| mRippleBackground = prepareBackgroundRipple(); |
| if (mRippleBackground != null) { |
| if (mBackgroundDrawable == null) { |
| WXViewUtils.setBackGround(mHost, mRippleBackground); |
| } else { |
| LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{mRippleBackground, mBackgroundDrawable}); |
| WXViewUtils.setBackGround(mHost, layerDrawable); |
| } |
| return; |
| } |
| } |
| if (!(colorInt == Color.TRANSPARENT && mBackgroundDrawable == null)) { |
| getOrCreateBorder().setColor(colorInt); |
| } |
| } |
| } |
| |
| private Drawable prepareBackgroundRipple() { |
| try { |
| if (getStyles() != null && getStyles().getPesudoResetStyles() != null) { |
| Map<String, Object> resetStyles = getStyles().getPesudoResetStyles(); |
| |
| Object bgColor = resetStyles.get(Constants.Name.BACKGROUND_COLOR); |
| int colorInt = Color.TRANSPARENT; |
| if (bgColor != null) { |
| colorInt = WXResourceUtils.getColor(bgColor.toString(), Color.TRANSPARENT); |
| if (colorInt == Color.TRANSPARENT) { |
| return null; |
| } |
| } |
| |
| Object bg = resetStyles.get(Constants.Name.BACKGROUND_COLOR + Constants.PSEUDO.ACTIVE); |
| if (bg == null) { |
| return null; |
| } |
| int rippleColor = WXResourceUtils.getColor(bg.toString(), colorInt); |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
| ColorStateList colorStateList = new ColorStateList( |
| new int[][]{new int[]{}}, |
| new int[]{rippleColor}); |
| return new RippleDrawable(colorStateList, new ColorDrawable(colorInt), null) { |
| @Override |
| public void draw(@NonNull Canvas canvas) { |
| if (mBackgroundDrawable != null) { |
| Path border = mBackgroundDrawable.getContentPath(new RectF(0, 0, canvas.getWidth(), canvas.getHeight())); |
| canvas.clipPath(border); |
| } |
| super.draw(canvas); |
| } |
| }; |
| } |
| } |
| } catch (Throwable t) { |
| WXLogUtils.w("Exception on create ripple: ", t); |
| } |
| return null; |
| } |
| |
| public void setBackgroundImage(@NonNull String bgImage) { |
| if ("".equals(bgImage.trim())) { |
| getOrCreateBorder().setImage(null); |
| } else { |
| Shader shader = WXResourceUtils.getShader(bgImage, getLayoutSize().getWidth(), getLayoutSize().getHeight()); |
| 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( |
| CORNER.ALL, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getInstanceViewPortWidth())); |
| break; |
| case Constants.Name.BORDER_TOP_LEFT_RADIUS: |
| getOrCreateBorder().setBorderRadius(CORNER.BORDER_TOP_LEFT, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getInstanceViewPortWidth())); |
| break; |
| case Constants.Name.BORDER_TOP_RIGHT_RADIUS: |
| getOrCreateBorder().setBorderRadius(CORNER.BORDER_TOP_RIGHT, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getInstanceViewPortWidth())); |
| break; |
| case Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS: |
| getOrCreateBorder().setBorderRadius(CORNER.BORDER_BOTTOM_RIGHT, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getInstanceViewPortWidth())); |
| break; |
| case Constants.Name.BORDER_BOTTOM_LEFT_RADIUS: |
| getOrCreateBorder().setBorderRadius(CORNER.BORDER_BOTTOM_LEFT, 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(CSSShorthand.EDGE.ALL, borderWidth); |
| break; |
| case Constants.Name.BORDER_TOP_WIDTH: |
| getOrCreateBorder().setBorderWidth(CSSShorthand.EDGE.TOP, borderWidth); |
| break; |
| case Constants.Name.BORDER_RIGHT_WIDTH: |
| getOrCreateBorder().setBorderWidth(CSSShorthand.EDGE.RIGHT, borderWidth); |
| break; |
| case Constants.Name.BORDER_BOTTOM_WIDTH: |
| getOrCreateBorder().setBorderWidth(CSSShorthand.EDGE.BOTTOM, borderWidth); |
| break; |
| case Constants.Name.BORDER_LEFT_WIDTH: |
| getOrCreateBorder().setBorderWidth(CSSShorthand.EDGE.LEFT, borderWidth); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| public void setBorderStyle(String key, String borderStyle) { |
| if (!TextUtils.isEmpty(borderStyle)) { |
| switch (key) { |
| case Constants.Name.BORDER_STYLE: |
| getOrCreateBorder().setBorderStyle(CSSShorthand.EDGE.ALL, borderStyle); |
| break; |
| case Constants.Name.BORDER_RIGHT_STYLE: |
| getOrCreateBorder().setBorderStyle(CSSShorthand.EDGE.RIGHT, borderStyle); |
| break; |
| case Constants.Name.BORDER_BOTTOM_STYLE: |
| getOrCreateBorder().setBorderStyle(CSSShorthand.EDGE.BOTTOM, borderStyle); |
| break; |
| case Constants.Name.BORDER_LEFT_STYLE: |
| getOrCreateBorder().setBorderStyle(CSSShorthand.EDGE.LEFT, borderStyle); |
| break; |
| case Constants.Name.BORDER_TOP_STYLE: |
| getOrCreateBorder().setBorderStyle(CSSShorthand.EDGE.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(CSSShorthand.EDGE.ALL, colorInt); |
| break; |
| case Constants.Name.BORDER_TOP_COLOR: |
| getOrCreateBorder().setBorderColor(CSSShorthand.EDGE.TOP, colorInt); |
| break; |
| case Constants.Name.BORDER_RIGHT_COLOR: |
| getOrCreateBorder().setBorderColor(CSSShorthand.EDGE.RIGHT, colorInt); |
| break; |
| case Constants.Name.BORDER_BOTTOM_COLOR: |
| getOrCreateBorder().setBorderColor(CSSShorthand.EDGE.BOTTOM, colorInt); |
| break; |
| case Constants.Name.BORDER_LEFT_COLOR: |
| getOrCreateBorder().setBorderColor(CSSShorthand.EDGE.LEFT, colorInt); |
| break; |
| } |
| } |
| } |
| } |
| |
| public |
| @Nullable |
| String getVisibility() { |
| try { |
| return (String) 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 = 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 (isFixed()) |
| return; |
| clearBoxShadow(); |
| } |
| |
| 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 (isFixed() && (view = getHostView()) != null) { |
| getInstance().removeFixedView(view); |
| } |
| |
| if(contentBoxMeasurement!=null){ |
| contentBoxMeasurement.destroy(); |
| contentBoxMeasurement = null; |
| } |
| mIsDestroyed = true; |
| if(animations!=null) { |
| animations.clear(); |
| } |
| } |
| |
| 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()); |
| } |
| |
| public boolean containsEvent(String event) { |
| return getEvents().contains(event) || (mAppendEvents!=null && 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 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 int getType() { |
| return mType; |
| } |
| |
| 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 = 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 = getStyles(); |
| Map<String, Map<String, Object>> pesudoStyles = styles.getPesudoStyles(); |
| |
| if (pesudoStyles == null || pesudoStyles.size() == 0) { |
| return; |
| } |
| if(mPesudoStatus == null){ |
| mPesudoStatus = new PesudoStatus(); |
| } |
| Map<String, Object> resultStyles = mPesudoStatus.updateStatusAndGetUpdateStyles( |
| clzName, |
| status, |
| pesudoStyles, |
| styles.getPesudoResetStyles()); |
| |
| if (null != resultStyles) { |
| if (status) { |
| mPseudoResetGraphicSize = new GraphicSize(getLayoutSize().getWidth(), getLayoutSize().getHeight()); |
| if (resultStyles.keySet().contains(Constants.Name.WIDTH)) { |
| getLayoutSize().setWidth(WXViewUtils.getRealPxByWidth(WXUtils.parseFloat(styles.getPesudoResetStyles().get(Constants.Name.WIDTH + Constants.PSEUDO.ACTIVE)), getViewPortWidth())); |
| } else if (resultStyles.keySet().contains(Constants.Name.HEIGHT)){ |
| getLayoutSize().setHeight(WXViewUtils.getRealPxByWidth(WXUtils.parseFloat(styles.getPesudoResetStyles().get(Constants.Name.HEIGHT + Constants.PSEUDO.ACTIVE)), getViewPortWidth())); |
| } |
| } else { |
| if (null != mPseudoResetGraphicSize) { |
| setLayoutSize(mPseudoResetGraphicSize); |
| } |
| } |
| } |
| |
| updateStyleByPesudo(resultStyles); |
| } |
| |
| private void updateStyleByPesudo(Map<String, Object> styles) { |
| new GraphicActionUpdateStyle(getInstanceId(), getRef(), styles, getPadding(), getMargin(), getBorder(), true) |
| .executeActionOnRender(); |
| if (getLayoutWidth() == 0 && getLayoutWidth() == 0) { |
| } else { |
| WXBridgeManager.getInstance().setStyleWidth(getInstanceId(), getRef(), getLayoutWidth()); |
| WXBridgeManager.getInstance().setStyleHeight(getInstanceId(), getRef(), getLayoutHeight()); |
| // WXBridgeManager.getInstance().calculateLayout(getInstanceId(), getRef(), false); |
| } |
| } |
| |
| public int getStickyOffset() { |
| return mStickyOffset; |
| } |
| |
| public boolean canRecycled() { |
| return (!isFixed() || !isSticky()) && 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 updateStyles invoke to relayout current page |
| */ |
| public void notifyNativeSizeChanged(int w, int h) { |
| if (!mNeedLayoutOnAnimation) { |
| return; |
| } |
| |
| final WXBridgeManager manager = WXBridgeManager.getInstance(); |
| manager.setStyleWidth(getInstanceId(), getRef(), w); |
| manager.setStyleHeight(getInstanceId(), getRef(), h); |
| } |
| |
| 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 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.ts = mTraceInfo.domThreadStart; |
| domEvent.tname = "DOMThread"; |
| domEvent.name = getComponentType(); |
| 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 = getComponentType(); |
| uiEvent.classname = getClass().getSimpleName(); |
| if (getParent() != null) { |
| uiEvent.parentRef = getParent().getRef(); |
| } |
| uiEvent.submit(); |
| } else { |
| if (WXEnvironment.isApkDebugable() && !isLazy()) { |
| // WXLogUtils.w("onRenderFinish", "createView() not called"); |
| } |
| } |
| } |
| } |
| } |
| |
| protected boolean isRippleEnabled() { |
| try { |
| Object obj = getAttrs().get(Constants.Name.RIPPLE_ENABLED); |
| return WXUtils.getBoolean(obj, false); |
| } catch (Throwable t) { |
| //ignore |
| } |
| return false; |
| } |
| |
| public boolean isWaste() { |
| return waste; |
| } |
| |
| /** |
| * mark node waste, |
| * if node is waster should hidden, and dom tree should allow not show |
| * */ |
| public void setWaste(boolean waste) { |
| if(this.waste != waste){ |
| this.waste = waste; |
| if(!WXBasicComponentType.RECYCLE_LIST.equals(getParent().getComponentType())){ |
| NativeRenderObjectUtils.nativeRenderObjectChildWaste(getRenderObjectPtr(), waste); |
| } |
| |
| if(waste){ |
| //update dom not show, and put style to hidden |
| getStyles().put(Constants.Name.VISIBILITY, Constants.Value.HIDDEN); |
| //if component not init, mark lazy init when use, reduce view count |
| if(getHostView() == null){ |
| if(!mLazy){ |
| lazy(true); |
| } |
| }else{ |
| getHostView().setVisibility(View.GONE); |
| } |
| }else{ |
| getStyles().put(Constants.Name.VISIBILITY, Constants.Value.VISIBLE); |
| if(getHostView() == null){ |
| if(mLazy) { // when parent is lazy just mark node lazy false |
| if(mParent != null && mParent.isLazy()){ |
| lazy(false); |
| }else{ |
| Statements.initLazyComponent(this, mParent); |
| } |
| } |
| }else{ |
| getHostView().setVisibility(View.VISIBLE); |
| } |
| } |
| } |
| } |
| |
| |
| /** component key id in native, |
| * differ with ref, ref + position |
| * */ |
| public String getViewTreeKey(){ |
| if(mViewTreeKey == null){ |
| if(getParent() == null){ |
| mViewTreeKey = getRef(); |
| }else{ |
| mViewTreeKey = getRef() + "_" + getParent().indexOf(this); |
| } |
| } |
| return mViewTreeKey; |
| } |
| |
| private String mViewTreeKey; |
| |
| public WXTransition getTransition() { |
| return mTransition; |
| } |
| |
| public void setTransition(WXTransition transition) { |
| this.mTransition = transition; |
| } |
| |
| public void addAnimationForElement(Map<String, Object> animMap) { |
| if(animMap!=null && !animMap.isEmpty()){ |
| if(animations == null){ |
| animations = new ConcurrentLinkedQueue<>(); |
| } |
| animations.add(new Pair<>(getRef(),animMap)); |
| } |
| } |
| |
| private void parseAnimation() { |
| if (null == animations) { |
| return; |
| } |
| for (final Pair<String, Map<String, Object>> pair : animations) { |
| if (!TextUtils.isEmpty(pair.first)) { |
| final WXAnimationBean animationBean = createAnimationBean(pair.first, pair.second); |
| if (animationBean != null) { |
| GraphicActionAnimation action = new GraphicActionAnimation(getInstanceId(), getRef(), animationBean); |
| action.executeAction(); |
| } |
| } |
| } |
| animations.clear(); |
| } |
| |
| |
| private WXAnimationBean createAnimationBean(String ref,Map<String, Object> style){ |
| if (style != null) { |
| try { |
| Object transform = style.get(Constants.Name.TRANSFORM); |
| if (transform instanceof String && !TextUtils.isEmpty((String) transform)) { |
| String transformOrigin = (String) style.get(Constants.Name.TRANSFORM_ORIGIN); |
| WXAnimationBean animationBean = new WXAnimationBean(); |
| int width = (int) getLayoutWidth(); |
| int height = (int) getLayoutHeight(); |
| animationBean.styles = new WXAnimationBean.Style(); |
| animationBean.styles.init(transformOrigin, (String) transform, width, height,WXSDKManager.getInstanceViewPortWidth(getInstanceId())); |
| return animationBean; |
| } |
| }catch (RuntimeException e){ |
| WXLogUtils.e("", e); |
| return null; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * node is lazy |
| * */ |
| private boolean mLazy = false; |
| |
| /***/ |
| public void lazy(boolean lazy) { |
| mLazy = lazy; |
| } |
| |
| public long getRenderObjectPtr(){ |
| if(getBasicComponentData().isRenderPtrEmpty()){ |
| getBasicComponentData().setRenderObjectPr(NativeRenderObjectUtils.nativeGetRenderObject(getInstanceId(), getRef())); |
| } |
| return getBasicComponentData().getRenderObjectPr(); |
| } |
| |
| |
| public void updateNativeAttr(String key, Object value){ |
| if(key == null){ |
| return; |
| } |
| if(value == null){ |
| value = ""; |
| } |
| getBasicComponentData().getAttrs().put(key, value); |
| NativeRenderObjectUtils.nativeUpdateRenderObjectAttr(getRenderObjectPtr(), key, value.toString()); |
| } |
| |
| public void nativeUpdateAttrs(Map<String, Object> dynamic){ |
| Set<Map.Entry<String, Object>> entries = dynamic.entrySet(); |
| /** |
| * diff attrs, see attrs has update, remove none update attrs |
| * */ |
| Iterator<Map.Entry<String, Object>> iterator = entries.iterator(); |
| while (iterator.hasNext()){ |
| Map.Entry<String, Object> objectEntry = iterator.next(); |
| if(objectEntry.getKey() == null){ |
| continue; |
| } |
| updateNativeAttr(objectEntry.getKey(), objectEntry.getValue()); |
| } |
| } |
| |
| |
| public void updateNativeStyle(String key, Object value){ |
| if(key == null){ |
| return; |
| } |
| if(value == null){ |
| value = ""; |
| } |
| getBasicComponentData().getStyles().put(key, value); |
| NativeRenderObjectUtils.nativeUpdateRenderObjectStyle(getRenderObjectPtr(), key, value.toString()); |
| } |
| |
| public void updateNativeStyles(Map<String, Object> dynamic){ |
| Set<Map.Entry<String, Object>> entries = dynamic.entrySet(); |
| /** |
| * diff attrs, see attrs has update, remove none update attrs |
| * */ |
| Iterator<Map.Entry<String, Object>> iterator = entries.iterator(); |
| while (iterator.hasNext()){ |
| Map.Entry<String, Object> objectEntry = iterator.next(); |
| if(objectEntry.getKey() == null){ |
| continue; |
| } |
| updateNativeStyle(objectEntry.getKey(), objectEntry.getValue()); |
| } |
| } |
| |
| public void addLayerOverFlowListener(String ref) { |
| if (getInstance() != null) |
| getInstance().addLayerOverFlowListener(ref); |
| } |
| |
| public void removeLayerOverFlowListener(String ref) { |
| if (getInstance() != null) |
| getInstance().removeLayerOverFlowListener(ref); |
| } |
| } |