blob: ee64b98c5cea72638556aae048c091b546f8ad19 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.taobao.weex.ui.component;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.annotation.JSMethod;
import com.taobao.weex.common.Constants;
import com.taobao.weex.dom.CSSShorthand;
import com.taobao.weex.ui.action.BasicComponentData;
import com.taobao.weex.ui.view.WXImageView;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.WXUtils;
import com.taobao.weex.utils.WXViewUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* All container components must implement this class
*/
public abstract class WXVContainer<T extends ViewGroup> extends WXComponent<T> {
private static final String TAG = "WXVContainer";
protected ArrayList<WXComponent> mChildren = new ArrayList<>();
private BoxShadowHost mBoxShadowHost;
private boolean requestDisallowInterceptTouchEvent = false;
@Deprecated
public WXVContainer(WXSDKInstance instance, WXVContainer parent, String instanceId, boolean isLazy, BasicComponentData basicComponentData) {
this(instance, parent, isLazy, basicComponentData);
}
@Deprecated
public WXVContainer(WXSDKInstance instance, WXVContainer parent, boolean lazy, BasicComponentData basicComponentData) {
super(instance, parent, basicComponentData);
}
public WXVContainer(WXSDKInstance instance, WXVContainer parent, BasicComponentData basicComponentData) {
super(instance, parent, basicComponentData);
}
/**
* Container will get focus before any of its descendants.
*/
public void interceptFocus() {
T host = getHostView();
if (host != null) {
host.setFocusable(true);
host.setFocusableInTouchMode(true);
host.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
host.requestFocus();
}
}
/**
* Container will can not receive focus
*/
public void ignoreFocus() {
T host = getHostView();
if (host != null) {
host.setFocusable(false);
host.setFocusableInTouchMode(false);
host.clearFocus();
}
}
/**
* Offset top for children layout.
*/
protected int getChildrenLayoutTopOffset() {
return 0;
}
/**
* use {@link #getHostView()} instead
*/
@Deprecated
public ViewGroup getView() {
return getHostView();
}
@Override
public void applyLayoutAndEvent(WXComponent component) {
if (!isLazy()) {
if (component == null) {
component = this;
}
super.applyLayoutAndEvent(component);
int count = childCount();
for (int i = 0; i < count; i++) {
WXComponent child = getChild(i);
child.applyLayoutAndEvent(((WXVContainer) component).getChild(i));
}
}
}
/**
* Get or generate new layout parameter for child view
*/
public ViewGroup.LayoutParams getChildLayoutParams(WXComponent child, View childView, int width, int height, int left, int right, int top, int bottom) {
ViewGroup.LayoutParams lp = null;
if (childView != null) {
lp = childView.getLayoutParams();
}
if(lp == null) {
lp = new ViewGroup.LayoutParams(width,height);
}else{
lp.width = width;
lp.height = height;
if(lp instanceof ViewGroup.MarginLayoutParams){
((ViewGroup.MarginLayoutParams) lp).setMargins(left,top,right,bottom);
}
}
return lp;
}
public Scrollable getFirstScroller() {
if (this instanceof Scrollable) {
return (Scrollable) this;
} else {
for (int i = 0; i < getChildCount(); i++) {
Scrollable scrollable = getChild(i).getFirstScroller();
if (scrollable != null) {
return scrollable;
}
}
}
return null;
}
@Override
public void bindData(WXComponent component) {
if (!isLazy()) {
if (component == null) {
component = this;
}
super.bindData(component);
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).bindData(((WXVContainer) component).getChild(i));
}
}
}
@Override
public void refreshData(WXComponent component) {
if (component == null) {
component = this;
}
super.refreshData(component);
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).refreshData(((WXVContainer) component).getChild(i));
}
}
/**
* return real View
*/
@Override
public ViewGroup getRealView() {
return (ViewGroup) super.getRealView();
}
@Override
public void createViewImpl() {
super.createViewImpl();
int count = childCount();
for (int i = 0; i < count; ++i) {
createChildViewAt(i);
}
if (getHostView() != null) {
getHostView().setClipToPadding(false);
}
}
@Override
public void destroy() {
if (mChildren != null) {
int count = mChildren.size();
for (int i = 0; i < count; ++i) {
mChildren.get(i).destroy();
}
mChildren.clear();
}
super.destroy();
}
/**
* recycle component resources
*/
public void recycled() {
if (mChildren != null && !isFixed() && getAttrs().canRecycled()) {
int count = mChildren.size();
for (int i = 0; i < count; ++i) {
mChildren.get(i).recycled();
}
}
super.recycled();
}
@Override
public View detachViewAndClearPreInfo() {
View original = super.detachViewAndClearPreInfo();
if (mChildren != null) {
int count = childCount();
for (int i = 0; i < count; ++i) {
mChildren.get(i).detachViewAndClearPreInfo();
}
}
return original;
}
public int childCount() {
return mChildren == null ? 0 : mChildren.size();
}
public WXComponent getChild(int index) {
if (mChildren == null || index < 0 || index >= mChildren.size()) {
//To avoid index out of bounds
return null;
}
return mChildren.get(index);
}
public int getChildCount() {
return childCount();
}
public void addChild(WXComponent child) {
addChild(child, -1);
}
public void addChild(WXComponent child, int index) {
if (child == null || index < -1) {
return;
}
child.deepInComponentTree = this.deepInComponentTree+1;
getInstance().setMaxDomDeep(child.deepInComponentTree);
int count = mChildren.size();
index = index >= count ? -1 : index;
if (index == -1) {
mChildren.add(child);
} else {
mChildren.add(index, child);
}
}
public final int indexOf(WXComponent comp) {
return mChildren.indexOf(comp);
}
public void createChildViewAt(int index) {
Pair<WXComponent, Integer> ret = rearrangeIndexAndGetChild(index);
if (ret.first != null) {
WXComponent child = ret.first;
child.createView();
if (!child.isVirtualComponent()) {
addSubView(child.getHostView(), ret.second);
}
}
}
protected Pair<WXComponent, Integer> rearrangeIndexAndGetChild(int index) {
int indexToCreate = index;
if (indexToCreate < 0) {
indexToCreate = childCount() - 1;
}
if (indexToCreate < 0) {
return new Pair<>(null, indexToCreate);
} else {
return new Pair<>(getChild(indexToCreate), indexToCreate);
}
}
public void addSubView(View child, int index) {
if (child == null || getRealView() == null) {
return;
}
int count = getRealView().getChildCount();
index = index >= count ? -1 : index;
if (index == -1) {
getRealView().addView(child);
} else {
getRealView().addView(child, index);
}
}
public void remove(WXComponent child, boolean destroy) {
if (child == null || mChildren == null || mChildren.size() == 0) {
return;
}
mChildren.remove(child);
if (getInstance() != null
&& getInstance().getRootView() != null
&& child.isFixed()) {
getInstance().removeFixedView(child.getHostView());
} else if (getRealView() != null) {
if (!child.isVirtualComponent()) {
getRealView().removeView(child.getHostView());
} else {
child.removeVirtualComponent();
}
}
if (destroy) {
child.destroy();
}
}
@Override
public void notifyAppearStateChange(String wxEventType, String direction) {
super.notifyAppearStateChange(wxEventType, direction);
if (getHostView() == null || mChildren == null) {
return;
}
for (WXComponent component : mChildren) {
if (component.getHostView() != null && !(component.getHostView().getVisibility() == View.VISIBLE)) {
wxEventType = Constants.Event.DISAPPEAR;
}
component.notifyAppearStateChange(wxEventType, direction);
}
}
/********************************************************
* begin hook Activity life cycle callback *
********************************************************/
@Override
public void onActivityCreate() {
super.onActivityCreate();
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityCreate();
}
}
@Override
public void onActivityStart() {
super.onActivityStart();
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityStart();
}
}
@Override
public void onActivityPause() {
super.onActivityPause();
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityPause();
}
}
@Override
public void onActivityResume() {
super.onActivityResume();
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityResume();
}
}
@Override
public void onActivityStop() {
super.onActivityStop();
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityStop();
}
}
@Override
public void onActivityDestroy() {
super.onActivityDestroy();
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityDestroy();
}
}
@Override
public boolean onActivityBack() {
super.onActivityBack();
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityBack();
}
return false;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onActivityResult(requestCode, resultCode, data);
}
}
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onCreateOptionsMenu(menu);
}
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
int count = childCount();
for (int i = 0; i < count; i++) {
getChild(i).onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
public void onRenderFinish(@RenderState int state) {
for (int i = 0; i < getChildCount(); i++) {
WXComponent child = getChild(i);
child.mTraceInfo.uiQueueTime = mTraceInfo.uiQueueTime;
child.onRenderFinish(state);
}
super.onRenderFinish(state);
}
@JSMethod
public void releaseImageList(String viewTreeRecycle){
if(getHostView() == null
|| !ViewCompat.isAttachedToWindow(getHostView())
|| !(getHostView() instanceof ViewGroup)){
return;
}
boolean isViewTree = WXUtils.getBoolean(viewTreeRecycle, false);
if(isViewTree){
doViewTreeRecycleImageView(getHostView(), true);
}else{
int count = getChildCount();
for(int i=0; i<count; i++){
WXComponent component = getChild(i);
if(component instanceof WXImage && ((WXImage) component).getHostView() instanceof WXImageView){
WXImageView imageView = (WXImageView) component.getHostView();
if(imageView != null && ViewCompat.isAttachedToWindow(imageView)){
imageView.autoReleaseImage();
}
}else if(component instanceof WXVContainer){
((WXVContainer) component).releaseImageList(viewTreeRecycle);
}
}
}
}
@JSMethod
public void recoverImageList(String viewTreeRecycle){
if(getHostView() == null
|| !ViewCompat.isAttachedToWindow(getHostView())
|| !(getHostView() instanceof ViewGroup)){
return;
}
boolean isViewTree = WXUtils.getBoolean(viewTreeRecycle, false);
if(isViewTree){
doViewTreeRecycleImageView(getHostView(), false);
}else{
int count = getChildCount();
for(int i=0; i<count; i++){
WXComponent component = getChild(i);
if(component instanceof WXImage && ((WXImage) component).getHostView() instanceof WXImageView){
WXImageView imageView = (WXImageView) component.getHostView();
if(imageView != null && ViewCompat.isAttachedToWindow(imageView)){
imageView.autoRecoverImage();
}
}else if(component instanceof WXVContainer){
((WXVContainer) component).recoverImageList(viewTreeRecycle);
}
}
}
}
/**
* transverse view tree, and recycle wximageview in container
* */
private void doViewTreeRecycleImageView(ViewGroup viewGroup, boolean isRelease){
int count = viewGroup.getChildCount();
for(int i=0; i<count; i++){
View view = viewGroup.getChildAt(i);
if(view instanceof WXImageView){
if(isRelease){
((WXImageView) view).autoReleaseImage();
}else{
((WXImageView) view).autoRecoverImage();
}
}else if(view instanceof ViewGroup){
doViewTreeRecycleImageView((ViewGroup) view, isRelease);
}
}
}
public void requestDisallowInterceptTouchEvent(boolean requestDisallowInterceptTouchEvent) {
if(this.requestDisallowInterceptTouchEvent != requestDisallowInterceptTouchEvent){
this.requestDisallowInterceptTouchEvent = requestDisallowInterceptTouchEvent;
if(mGesture != null){
mGesture.setRequestDisallowInterceptTouchEvent(requestDisallowInterceptTouchEvent);
}
if(getParent() != null){
getParent().requestDisallowInterceptTouchEvent(requestDisallowInterceptTouchEvent);
}
}
}
/********************************
* end hook Activity life cycle callback
********************************************************/
public @Nullable
View getBoxShadowHost(boolean isClear) {
if (isClear) {
// Return existed host if want clear shadow
return mBoxShadowHost;
}
ViewGroup hostView = getHostView();
if (hostView == null) {
return null;
}
try {
String type = getComponentType();
if (WXBasicComponentType.DIV.equals(type)) {
WXLogUtils.d("BoxShadow", "Draw box-shadow with BoxShadowHost on div: " + toString());
if (mBoxShadowHost == null) {
mBoxShadowHost = new BoxShadowHost(getContext());
WXViewUtils.setBackGround(mBoxShadowHost, null);
CSSShorthand padding = this.getPadding();
CSSShorthand border = this.getBorder();
int left = (int) (padding.get(CSSShorthand.EDGE.LEFT) + border.get(CSSShorthand.EDGE.LEFT));
int top = (int) (padding.get(CSSShorthand.EDGE.TOP) + border.get(CSSShorthand.EDGE.TOP));
int right = (int) (padding.get(CSSShorthand.EDGE.RIGHT) + border.get(CSSShorthand.EDGE.RIGHT));
int bottom = (int) (padding.get(CSSShorthand.EDGE.BOTTOM) + border.get(CSSShorthand.EDGE.BOTTOM));
ViewGroup.MarginLayoutParams layoutParams = new ViewGroup.MarginLayoutParams(hostView.getLayoutParams()) ;
layoutParams.setMargins(-left, -top, -right, -bottom);
mBoxShadowHost.setLayoutParams(layoutParams);
hostView.addView(mBoxShadowHost);
}
hostView.removeView(mBoxShadowHost);
hostView.addView(mBoxShadowHost);
return mBoxShadowHost;
}
} catch (Throwable t) {
WXLogUtils.w("BoxShadow", t);
}
return hostView;
}
private class BoxShadowHost extends View {
public BoxShadowHost(Context context) {
super(context);
}
}
public void appendTreeCreateFinish() {
}
}