| /** |
| * 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 org.apache.weex.extend.component; |
| |
| import android.graphics.Color; |
| import android.support.annotation.Nullable; |
| import android.support.v4.view.ViewCompat; |
| import android.support.v7.widget.RecyclerView; |
| import android.text.TextUtils; |
| import android.view.View; |
| import android.view.animation.Animation; |
| import android.view.animation.AnimationSet; |
| import android.view.animation.RotateAnimation; |
| import android.view.animation.ScaleAnimation; |
| import android.view.animation.TranslateAnimation; |
| |
| import com.alibaba.fastjson.JSONArray; |
| import com.alibaba.fastjson.JSONObject; |
| import com.taobao.weex.WXEnvironment; |
| import com.taobao.weex.WXSDKInstance; |
| import com.taobao.weex.common.Constants; |
| import com.taobao.weex.common.ICheckBindingScroller; |
| import com.taobao.weex.common.OnWXScrollListener; |
| import com.taobao.weex.ui.action.BasicComponentData; |
| import com.taobao.weex.ui.animation.WXAnimationBean; |
| import com.taobao.weex.ui.component.Scrollable; |
| import com.taobao.weex.ui.component.WXComponent; |
| import com.taobao.weex.ui.component.WXDiv; |
| import com.taobao.weex.ui.component.WXVContainer; |
| import com.taobao.weex.ui.component.list.BasicListComponent; |
| import com.taobao.weex.ui.component.list.template.WXRecyclerTemplateList; |
| import com.taobao.weex.utils.WXLogUtils; |
| import com.taobao.weex.utils.WXResourceUtils; |
| |
| import java.util.ArrayList; |
| |
| |
| /** |
| * Created by zhengshihan on 2017/5/3. |
| */ |
| |
| public class WXParallax extends WXDiv implements OnWXScrollListener, ICheckBindingScroller { |
| |
| public final static String WX_TRANSFORM = "transform"; |
| public final static String WX_OPACITY = "opacity"; |
| public static final String PARALLAX = "parallax"; |
| public static final String BINDING_SCROLLER = "bindingScroller"; |
| |
| |
| ArrayList<TransformCreator> mTransformPropArrayList = new ArrayList<>(); |
| BackgroundColorCreator mBackgroundColor; |
| String mBindingRef = ""; |
| WXComponent mBindingComponent; |
| |
| private int mBackGroundColor = 0; |
| private float mOffsetY = 0; |
| |
| public WXParallax(WXSDKInstance instance, WXVContainer parent, BasicComponentData basicComponentData) { |
| super(instance, parent, basicComponentData); |
| initTransform(getAttrs().get(WX_TRANSFORM)); |
| initOpacity(getAttrs().get(Constants.Name.OPACITY)); |
| initBackgroundColor(getAttrs().get(Constants.Name.BACKGROUND_COLOR)); |
| |
| mBindingRef = (String) (getAttrs().get(BINDING_SCROLLER)); |
| instance.registerOnWXScrollListener(this); |
| } |
| |
| private void initBackgroundColor(Object obj) { |
| if (obj == null) |
| return; |
| |
| if (obj instanceof JSONObject) { |
| mBackgroundColor = new BackgroundColorCreator(); |
| JSONObject object = (JSONObject) obj; |
| |
| JSONArray in = object.getJSONArray("in"); |
| mBackgroundColor.input = new int[in.size()]; |
| for (int i = 0; i < in.size(); i++) { |
| mBackgroundColor.input[i] = in.getInteger(i); |
| } |
| |
| JSONArray out = object.getJSONArray("out"); |
| mBackgroundColor.output = new int[out.size()]; |
| for (int i = 0; i < out.size(); i++) { |
| String colorStr = out.getString(i); |
| mBackgroundColor.output[i] = WXResourceUtils.getColor(colorStr); |
| } |
| } |
| } |
| |
| private void initOpacity(Object obj) { |
| if (obj == null) |
| return; |
| |
| if (obj instanceof JSONObject) { |
| mTransformPropArrayList.add(new TransformCreator(WX_OPACITY, (JSONObject) obj)); |
| } |
| |
| } |
| |
| private void initTransform(Object propStr) { |
| |
| if (propStr == null) { |
| WXLogUtils.w("WXParallax initAnimation propStr ==null"); |
| return; |
| } |
| JSONArray jsonObject; |
| if (propStr instanceof JSONArray) { |
| jsonObject = (JSONArray) propStr; |
| for (int i = 0; i < jsonObject.size(); i++) { |
| JSONObject object = jsonObject.getJSONObject(i); |
| mTransformPropArrayList.add(i, |
| new TransformCreator(object.getString("type"), object)); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isNeedScroller(String ref, Object option) { |
| |
| mBindingRef = (String) (getAttrs().get(BINDING_SCROLLER)); |
| if (TextUtils.isEmpty(mBindingRef)) { |
| WXComponent root = getInstance().getRootComponent(); |
| if (root != null && root instanceof WXVContainer) { |
| Scrollable scrollable = root.getFirstScroller(); |
| if (scrollable != null) { |
| mBindingRef = scrollable.getRef(); |
| } |
| } |
| } |
| if (!TextUtils.isEmpty(mBindingRef) |
| && !TextUtils.isEmpty(ref) |
| && ref.equals(mBindingRef)) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void onScrolled(View view, int dx, int dy) { |
| if(ViewCompat.isInLayout(view)){ |
| if(mBindingComponent == null && mBindingRef != null){ |
| mBindingComponent = findComponent(mBindingRef); |
| } |
| if(mBindingComponent instanceof BasicListComponent |
| && view instanceof RecyclerView){ |
| BasicListComponent listComponent = (BasicListComponent) mBindingComponent; |
| mOffsetY = Math.abs(listComponent.calcContentOffset((RecyclerView) view)); |
| }else if(mBindingComponent instanceof WXRecyclerTemplateList |
| && view instanceof RecyclerView){ |
| WXRecyclerTemplateList listComponent = (WXRecyclerTemplateList) mBindingComponent; |
| mOffsetY = Math.abs(listComponent.calcContentOffset((RecyclerView) view)); |
| } |
| }else{ |
| mOffsetY = mOffsetY + dy; |
| } |
| |
| AnimationSet animationSet = new AnimationSet(true); |
| boolean hasAnimation = false; |
| for (int i = 0; i < mTransformPropArrayList.size(); i++) { |
| TransformCreator creator = mTransformPropArrayList.get(i); |
| Animation animation = creator.getAnimation(dx, dy); |
| if (animation != null) { |
| animationSet.addAnimation(animation); |
| hasAnimation = true; |
| } |
| } |
| |
| if (hasAnimation) { |
| animationSet.setFillAfter(true); |
| if (getHostView() != null) { |
| getHostView().startAnimation(animationSet); |
| } |
| } |
| |
| if (mBackgroundColor != null) { |
| int color = mBackgroundColor.getColor(dx, dy); |
| if (mBackGroundColor != color) { |
| getHostView().setBackgroundColor(color); |
| mBackGroundColor = color; |
| } |
| } |
| } |
| |
| @Override |
| public void onScrollStateChanged(View view, int x, int y, int newState) { |
| |
| } |
| |
| |
| private class BackgroundColorCreator { |
| |
| int[] input; |
| int[] output; |
| |
| int getColor(int XDelta, int YDelta) { |
| if (WXEnvironment.isApkDebugable()) { |
| WXLogUtils.d("WXParallax:getColor: XDelta" + XDelta + " YDelta:" + YDelta + " mOffsetY" + mOffsetY); |
| } |
| int r1, g1, b1; |
| |
| if (mOffsetY > input[1]) { |
| return output[1]; |
| } |
| |
| if (mOffsetY < input[0]) { |
| return output[0]; |
| } |
| |
| r1 = Color.red(output[0]) + (int) (mOffsetY - input[0]) * (Color.red(output[1]) - Color.red(output[0])) / (input[1] - input[0]); |
| g1 = Color.green(output[0]) + (int) (mOffsetY - input[0]) * (Color.green(output[1]) - Color.green(output[0])) / (input[1] - input[0]); |
| b1 = Color.blue(output[0]) + (int) (mOffsetY - input[0]) * (Color.blue(output[1]) - Color.blue(output[0])) / (input[1] - input[0]); |
| |
| if (WXEnvironment.isApkDebugable()) { |
| WXLogUtils.d("WXParallax:getColor: r1" + r1 + " g1:" + g1 + " b1:" + b1); |
| } |
| return Color.rgb(r1, g1, b1); |
| } |
| |
| } |
| |
| private class TransformCreator { |
| |
| String transformType; |
| float[] input, output; |
| float fromTranslateX, fromTranslateY; |
| float fromScaleX, fromScaleY; |
| float fromRotate, fromOpacity; |
| |
| TransformCreator(String type, JSONObject object) { |
| transformType = type; |
| JSONArray in = object.getJSONArray("in"); |
| input = parseParamArray(in); |
| JSONArray out = object.getJSONArray("out"); |
| output = parseParamArray(out); |
| |
| switch (transformType) { |
| case WXAnimationBean.Style.WX_TRANSLATE: |
| fromTranslateX = output[0]; |
| fromTranslateY = output[1]; |
| break; |
| case WXAnimationBean.Style.WX_SCALE: |
| fromScaleX = output[0]; |
| fromScaleY = output[1]; |
| break; |
| case WXAnimationBean.Style.WX_ROTATE: |
| fromRotate = output[0]; |
| break; |
| case WX_OPACITY: |
| fromOpacity = output[0]; |
| break; |
| } |
| } |
| |
| float[] parseParamArray(JSONArray in) { |
| |
| int size = in.size(); |
| float[] param = new float[size]; |
| for (int j = 0; j < size; j++) { |
| param[j] = in.getFloatValue(j); |
| } |
| return param; |
| } |
| |
| |
| @Nullable |
| Animation getAnimation(float XDelta, float YDelta) { |
| |
| float offset = mOffsetY; |
| if (offset > input[1]) { |
| offset = input[1]; |
| } |
| if (offset < input[0]) { |
| offset = input[0]; |
| } |
| float toTranslateX, toTranslateY, toOpacity, toScaleX, toScaleY, toRotate; |
| Animation animation = null; |
| |
| if (WXEnvironment.isApkDebugable()) { |
| WXLogUtils.d("WXParallax", "type:" + transformType + " XDelta:" + XDelta + " YDelta:" + YDelta); |
| } |
| switch (transformType) { |
| |
| case WXAnimationBean.Style.WX_TRANSLATE: |
| toTranslateX = output[0] + (output[2] - output[0]) * (offset - input[0]) / (input[1] - input[0]); |
| toTranslateY = output[1] + (output[3] - output[1]) * (offset - input[0]) / (input[1] - input[0]); |
| |
| if (fromTranslateX == toTranslateX && fromTranslateY == toTranslateY) { |
| return null; |
| } |
| animation = new TranslateAnimation(fromTranslateX, toTranslateX, fromTranslateY, toTranslateY); |
| |
| fromTranslateX = toTranslateX; |
| fromTranslateY = toTranslateY; |
| |
| if (WXEnvironment.isApkDebugable()) { |
| WXLogUtils.d("WXParallax", "XDelta:" + XDelta + " YDelta:" + YDelta); |
| WXLogUtils.d("WXParallax", " fromTranslateX:" + fromTranslateX + " toTranslateX:" + toTranslateX + " fromTranslateY:" + fromTranslateY + " toTranslateY:" + toTranslateY); |
| } |
| |
| break; |
| case WXAnimationBean.Style.WX_SCALE: |
| toScaleX = output[0] + (output[2] - output[0]) * (offset - input[0]) / (input[1] - input[0]); |
| toScaleY = output[1] + (output[3] - output[1]) * (offset - input[0]) / (input[1] - input[0]); |
| |
| if (fromScaleX == toScaleX && fromScaleY == toScaleY) { |
| return null; |
| } |
| animation = new ScaleAnimation(fromScaleX, toScaleX, fromScaleY, toScaleY); |
| |
| if (WXEnvironment.isApkDebugable()) { |
| WXLogUtils.d("WXParallax", " fromScaleX:" + fromScaleX + " toScaleX:" + toScaleX + " fromScaleY:" + fromScaleY + " toScaleY:" + toScaleY); |
| } |
| |
| fromScaleX = toScaleX; |
| fromScaleY = toScaleY; |
| break; |
| |
| case WXAnimationBean.Style.WX_ROTATE: |
| toRotate = output[0] + (output[1] - output[0]) * (offset - input[0]) / (input[1] - input[0]); |
| if (fromRotate == toRotate) { |
| return null; |
| } |
| animation = new RotateAnimation(fromRotate, toRotate, output[2], output[3]); |
| fromRotate = toRotate; |
| break; |
| |
| case WX_OPACITY: |
| toOpacity = output[0] + (output[1] - output[0]) * (offset - input[0]) / (input[1] - input[0]); |
| if (fromOpacity == toOpacity) { |
| return null; |
| } |
| setOpacity(toOpacity); |
| // animation = new AlphaAnimation(fromOpacity, toOpacity); |
| |
| if (WXEnvironment.isApkDebugable()) { |
| WXLogUtils.d("WXParallax", "opacity fromOpacity:" + fromOpacity + " toOpacity:" + toOpacity); |
| } |
| |
| fromOpacity = toOpacity; |
| break; |
| } |
| |
| return animation; |
| } |
| } |
| } |