| /* |
| * 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); |
| } |
| } |
| } |
| } |