Java tutorial
/** * Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION * 1. Definitions. * "License" shall mean the terms and conditions for use, reproduction, and distribution as defined * by Sections 1 through 9 of this document. * "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is * granting the License. * "Legal Entity" shall mean the union of the acting entity and all other entities that control, are * controlled by, or are under common control with that entity. For the purposes of this definition, * "control" means (i) the power, direct or indirect, to cause the direction or management of such * entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the * outstanding shares, or (iii) beneficial ownership of such entity. * "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this * License. * "Source" form shall mean the preferred form for making modifications, including but not limited * to software source code, documentation source, and configuration files. * "Object" form shall mean any form resulting from mechanical transformation or translation of a * Source form, including but not limited to compiled object code, generated documentation, and * conversions to other media types. * "Work" shall mean the work of authorship, whether in Source or Object form, made available under * the License, as indicated by a copyright notice that is included in or attached to the work (an * example is provided in the Appendix below). * "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or * derived from) the Work and for which the editorial revisions, annotations, elaborations, or other * modifications represent, as a whole, an original work of authorship. For the purposes of this * License, Derivative Works shall not include works that remain separable from, or merely link (or * bind by name) to the interfaces of, the Work and Derivative Works thereof. * "Contribution" shall mean any work of authorship, including the original version of the Work and * any modifications or additions to that Work or Derivative Works thereof, that is intentionally * submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or * Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this * definition, "submitted" means any form of electronic, verbal, or written communication sent to * the Licensor or its representatives, including but not limited to communication on electronic * mailing lists, source code control systems, and issue tracking systems that are managed by, or on * behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding * communication that is conspicuously marked or otherwise designated in writing by the copyright * owner as "Not a Contribution." * "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a * Contribution has been received by Licensor and subsequently incorporated within the Work. * 2. Grant of Copyright License. Subject to the terms and conditions of this License, each * Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, * irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, * publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or * Object form. * 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor * hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable * (except as stated in this section) patent license to make, have made, use, offer to sell, sell, * import, and otherwise transfer the Work, where such license applies only to those patent claims * licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or * by combination of their Contribution(s) with the Work to which such Contribution(s) was * submitted. If You institute patent litigation against any entity (including a cross-claim or * counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work * constitutes direct or contributory patent infringement, then any patent licenses granted to You * under this License for that Work shall terminate as of the date such litigation is filed. * 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works * thereof in any medium, with or without modifications, and in Source or Object form, provided that * You meet the following conditions: * (a) You must give any other recipients of the Work or Derivative Works a copy of this License; * and * (b) You must cause any modified files to carry prominent notices stating that You changed the * files; and * (c) You must retain, in the Source form of any Derivative Works that You distribute, all * copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding * those notices that do not pertain to any part of the Derivative Works; and * (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative * Works that You distribute must include a readable copy of the attribution notices contained * within such NOTICE file, excluding those notices that do not pertain to any part of the * Derivative Works, in at least one of the following places: within a NOTICE text file distributed * as part of the Derivative Works; within the Source form or documentation, if provided along with * the Derivative Works; or, within a display generated by the Derivative Works, if and wherever * such third-party notices normally appear. The contents of the NOTICE file are for informational * purposes only and do not modify the License. You may add Your own attribution notices within * Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the * Work, provided that such additional attribution notices cannot be construed as modifying the * License. * You may add Your own copyright statement to Your modifications and may provide additional or * different license terms and conditions for use, reproduction, or distribution of Your * modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and * distribution of the Work otherwise complies with the conditions stated in this License. * 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution * intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms * and conditions of this License, without any additional terms or conditions. Notwithstanding the * above, nothing herein shall supersede or modify the terms of any separate license agreement you * may have executed with Licensor regarding such Contributions. * 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service * marks, or product names of the Licensor, except as required for reasonable and customary use in * describing the origin of the Work and reproducing the content of the NOTICE file. * 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor * provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, * any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A * PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or * redistributing the Work and assume any risks associated with Your exercise of permissions under * this License. * 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including * negligence), contract, or otherwise, unless required by applicable law (such as deliberate and * grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for * damages, including any direct, indirect, special, incidental, or consequential damages of any * character arising as a result of this License or out of the use or inability to use the Work * (including but not limited to damages for loss of goodwill, work stoppage, computer failure or * malfunction, or any and all other commercial damages or losses), even if such Contributor has * been advised of the possibility of such damages. * 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works * thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, * indemnity, or other liability obligations and/or rights consistent with this License. However, in * accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, * not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each * Contributor harmless for any liability incurred by, or claims asserted against, such Contributor * by reason of your accepting any such warranty or additional liability. * END OF TERMS AND CONDITIONS * APPENDIX: How to apply the Apache License to your work. * To apply the Apache License to your work, attach the following boilerplate notice, with the * fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include * the brackets!) The text should be enclosed in the appropriate comment syntax for the file * format. We also recommend that a file or class name and description of purpose be included on the * same "printed page" as the copyright notice for easier identification within third-party * archives. * Copyright 2016 Alibaba Group * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.taobao.weex.ui.component; import android.content.Context; import android.content.Intent; import android.graphics.Color; 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.Message; import android.support.annotation.CallSuper; import android.support.annotation.CheckResult; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.view.ViewCompat; import android.text.TextUtils; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; 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.WXStyle; import com.taobao.weex.dom.flex.Spacing; 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.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.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.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 mCanRecycled = true; //Holding the animation bean when component is uninitialized public void postAnimation(WXAnimationModule.AnimationHolder holder) { this.mAnimationHolder = holder; } 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.getViewPortWidth())); position.put("y", WXViewUtils.getWebPxByWidth(location[1], mInstance.getViewPortWidth())); position.put("width", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutWidth(), mInstance.getViewPortWidth())); position.put("height", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutHeight(), mInstance.getViewPortWidth())); 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) { mInstance = instance; mContext = mInstance.getContext(); mParent = parent; mDomObj = dom.clone(); mCurrentRef = mDomObj.getRef(); mCanRecycled = dom.canRecycled(); mGestureType = new HashSet<>(); ++mComponentNum; onCreate(); } 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) { mInstance.fireEvent(mCurrentRef, type, params, domChanges); } } /** * The view is created as needed * @return true for lazy */ public boolean isLazy() { return mParent != null && mParent.isLazy(); } public void applyLayoutAndEvent(WXComponent component) { if (!isLazy()) { if (component == null) { component = this; } setLayout(component.getDomObject()); setPadding(component.getDomObject().getPadding(), component.getDomObject().getBorder()); addEvents(); } } 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) { 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; } mCurrentRef = component.getDomObject().getRef(); updateProperties(component.getDomObject().getStyles()); updateProperties(component.getDomObject().getAttrs()); updateExtra(component.getDomObject().getExtra()); } } public void refreshData(WXComponent component) { } protected BorderDrawable getOrCreateBorder() { if (mBackgroundDrawable == null) { Drawable backgroundDrawable = mHost.getBackground(); WXViewUtils.setBackGround(mHost, null); mBackgroundDrawable = new BorderDrawable(); 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); 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(); } if (mHost == null) { return; } MeasureOutput measureOutput = measure(realWidth, realHeight); realWidth = measureOutput.width; realHeight = measureOutput.height; //fixed style 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(); } 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) { mHost.setLayoutParams(lp); } } private void setFixedHostLayoutParams(T host, int width, int height, int left, int right, int top, int bottom) { if (host.getParent() instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) host.getParent(); viewGroup.removeView(host); } 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.addFixedView(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() { } 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 (mHost == null) { return; } 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; } public void updateProperties(Map<String, Object> props) { if (props == null || mHost == null) { 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(); } /** * 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.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; default: return false; } } /** * 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) { Message message = Message.obtain(); WXDomTask task = new WXDomTask(); task.instanceId = getInstanceId(); task.args = new ArrayList<>(); task.args.add(getRef()); task.args.add(type); message.obj = task; message.what = WXDomHandler.MsgType.WX_DOM_ADD_EVENT; WXSDKManager.getInstance().getWXDomManager().sendMessage(message); } /** * 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)) { 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; } } public WXVContainer getParent() { return mParent; } public String getRef() { if (mDomObj == null) { return null; } return mCurrentRef; } /** * create view * */ public final void createView() { if (!isLazy()) { createViewImpl(); } } protected void createViewImpl() { if (mContext != null) { mHost = initComponentHostView(mContext); if (mHost == null) { //compatible initView(); } if (mHost != null) { mHost.setId(WXViewUtils.generateViewId()); } 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()) { 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); 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 void setDisabled(boolean disabled) { if (mHost == null) { return; } mHost.setEnabled(!disabled); } 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) && mHost != null) { int colorInt = WXResourceUtils.getColor(color); if (!(colorInt == Color.TRANSPARENT && mBackgroundDrawable == null)) { getOrCreateBorder().setColor(colorInt); } } } public void setBackgroundImage(String bgImage) { if (!TextUtils.isEmpty(bgImage) && mHost != null) { 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) { 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.getViewPortWidth())); break; case Constants.Name.BORDER_TOP_LEFT_RADIUS: getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_TOP_LEFT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getViewPortWidth())); break; case Constants.Name.BORDER_TOP_RIGHT_RADIUS: getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_TOP_RIGHT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getViewPortWidth())); break; case Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS: getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_BOTTOM_RIGHT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getViewPortWidth())); break; case Constants.Name.BORDER_BOTTOM_LEFT_RADIUS: getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_BOTTOM_LEFT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius, mInstance.getViewPortWidth())); 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().getViewPortWidth())); break; case Constants.Name.BORDER_TOP_WIDTH: getOrCreateBorder().setBorderWidth(Spacing.TOP, WXViewUtils.getRealSubPxByWidth(borderWidth, getInstance().getViewPortWidth())); break; case Constants.Name.BORDER_RIGHT_WIDTH: getOrCreateBorder().setBorderWidth(Spacing.RIGHT, WXViewUtils.getRealSubPxByWidth(borderWidth, getInstance().getViewPortWidth())); break; case Constants.Name.BORDER_BOTTOM_WIDTH: getOrCreateBorder().setBorderWidth(Spacing.BOTTOM, WXViewUtils.getRealSubPxByWidth(borderWidth, getInstance().getViewPortWidth())); break; case Constants.Name.BORDER_LEFT_WIDTH: getOrCreateBorder().setBorderWidth(Spacing.LEFT, WXViewUtils.getRealSubPxByWidth(borderWidth, getInstance().getViewPortWidth())); 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().getViewPortWidth()); 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 destroy() { 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) { mHost.setLayerType(View.LAYER_TYPE_NONE, null); } removeAllEvent(); removeStickyStyle(); if (mDomObj != null) { mDomObj = null; } 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; } /** * 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 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 false; } public void removeVirtualComponent() { } 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) { if (Constants.Name.BACKGROUND_COLOR.equals(propName)) { return "transparent"; } return originalValue; } private void setActiveTouchListener() { boolean hasActivePesudo = mDomObj.getStyles().getPesudoStyles().containsKey(Constants.PSEUDO.ACTIVE); View view; if (hasActivePesudo && (view = getRealView()) != null) { boolean hasTouchConsumer = (mHostClickListeners != null && mHostClickListeners.size() > 0) || mGesture != null; view.setOnTouchListener(new TouchActivePseudoListener(this, !hasTouchConsumer)); } } @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 mCanRecycled; } /** * Sets the offset for the sticky * @param stickyOffset child[y]-parent[y] */ public void setStickyOffset(int stickyOffset) { mStickyOffset = stickyOffset; } }