blob: ec2d73f8ef7ae0f71b5b9a1360f5390c9f056287 [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.support.annotation.NonNull;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.text.TextUtils;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.annotation.Component;
import com.taobao.weex.common.Constants;
import com.taobao.weex.dom.WXDomObject;
import com.taobao.weex.dom.WXEvent;
import com.taobao.weex.ui.ComponentCreator;
import com.taobao.weex.ui.view.WXCircleIndicator;
import com.taobao.weex.ui.view.WXCirclePageAdapter;
import com.taobao.weex.ui.view.WXCircleViewPager;
import com.taobao.weex.ui.view.gesture.WXGestureType;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.WXUtils;
import com.taobao.weex.utils.WXViewUtils;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
@Component(lazyload = false)
public class WXSlider extends WXVContainer<FrameLayout> {
public static final String INDEX = "index";
public static final String INFINITE = "infinite";
private boolean isInfinite = true;
Map<String, Object> params = new HashMap<>();
private float offsetXAccuracy = 0.1f;
private int initIndex = -1;
private boolean keepIndex = false;
public static class Creator implements ComponentCreator {
public WXComponent createInstance(WXSDKInstance instance, WXDomObject node, WXVContainer parent) throws IllegalAccessException, InvocationTargetException, InstantiationException {
return new WXSlider(instance, node, parent);
}
}
/**
* Scrollable sliderview
*/
/**
* package
**/
WXCircleViewPager mViewPager;
/**
* Circle indicator
*/
protected WXIndicator mIndicator;
/**
* Adapter for sliderview
*/
protected WXCirclePageAdapter mAdapter;
protected boolean mShowIndicators;
protected OnPageChangeListener mPageChangeListener = new SliderPageChangeListener();
@Deprecated
public WXSlider(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, String instanceId, boolean isLazy) {
this(instance, dom, parent);
}
public WXSlider(WXSDKInstance instance, WXDomObject node, WXVContainer parent) {
super(instance, node, parent);
}
@Override
protected FrameLayout initComponentHostView(@NonNull Context context) {
FrameLayout view = new FrameLayout(context);
// init view pager
if (getDomObject() != null && getDomObject().getAttrs() != null) {
Object obj = getDomObject().getAttrs().get(INFINITE);
isInfinite = WXUtils.getBoolean(obj, true);
}
FrameLayout.LayoutParams pagerParams = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mViewPager = new WXCircleViewPager(context);
mViewPager.setCircle(isInfinite);
mViewPager.setLayoutParams(pagerParams);
// init adapter
mAdapter = new WXCirclePageAdapter(isInfinite);
mViewPager.setAdapter(mAdapter);
// add to parent
view.addView(mViewPager);
mViewPager.addOnPageChangeListener(mPageChangeListener);
registerActivityStateListener();
return view;
}
/**
* Slider is not a regular container,top/left/right/bottom not apply to view,expect indicator.
*/
@Override
public LayoutParams getChildLayoutParams(WXComponent child,View childView, int width, int height, int left, int right, int top, int bottom) {
ViewGroup.LayoutParams lp = childView == null ? null : childView.getLayoutParams();
if (lp == null) {
lp = new FrameLayout.LayoutParams(width, height);
} else {
lp.width = width;
lp.height = height;
}
if (lp instanceof ViewGroup.MarginLayoutParams) {
//expect indicator .
if (child instanceof WXIndicator) {
((ViewGroup.MarginLayoutParams) lp).setMargins(left, top, right, bottom);
} else {
((ViewGroup.MarginLayoutParams) lp).setMargins(0, 0, 0, 0);
}
}
return lp;
}
@Override
public void addEvent(String type) {
super.addEvent(type);
if (Constants.Event.SCROLL.equals(type)) {
if (mViewPager == null) {
return;
}
mViewPager.addOnPageChangeListener(new SliderOnScrollListener(this));
}
}
@Override
public boolean containsGesture(WXGestureType WXGestureType) {
//Enable gesture for slider
return super.containsGesture(WXGestureType);
}
@Override
public ViewGroup getRealView() {
return mViewPager;
}
@Override
public void addSubView(View view, int index) {
if (view == null || mAdapter == null) {
return;
}
if (view instanceof WXCircleIndicator) {
return;
}
mAdapter.addPageView(view);
hackTwoItemsInfiniteScroll();
if (initIndex != -1 && mAdapter.getRealCount() > initIndex) {
mViewPager.setCurrentItem(initIndex);
initIndex = -1;
} else {
if (!keepIndex) {
mViewPager.setCurrentItem(0);
}
}
if (mIndicator != null) {
mIndicator.getHostView().forceLayout();
mIndicator.getHostView().requestLayout();
}
}
@Override
public void remove(WXComponent child, boolean destroy) {
if (child == null || child.getHostView() == null || mAdapter == null) {
return;
}
mAdapter.removePageView(child.getHostView());
hackTwoItemsInfiniteScroll();
super.remove(child,destroy);
}
@Override
public void destroy() {
super.destroy();
if (mViewPager != null) {
mViewPager.stopAutoScroll();
mViewPager.removeAllViews();
mViewPager.destory();
}
}
@Override
public void onActivityResume() {
if (mViewPager != null && mViewPager.isAutoScroll()) {
mViewPager.startAutoScroll();
}
}
@Override
public void onActivityStop() {
if (mViewPager != null) {
mViewPager.pauseAutoScroll();
}
}
public void addIndicator(WXIndicator indicator) {
FrameLayout root = getHostView();
if (root == null) {
return;
}
mIndicator = indicator;
WXCircleIndicator indicatorView = indicator.getHostView();
if (indicatorView != null) {
indicatorView.setCircleViewPager(mViewPager);
// indicatorView.setOnPageChangeListener(mPageChangeListener); // commented for twice onChange() called when do slide.
root.addView(indicatorView);
}
}
@Override
protected boolean setProperty(String key, Object param) {
switch (key) {
case Constants.Name.VALUE:
String value = WXUtils.getString(param, null);
if (value != null) {
setValue(value);
}
return true;
case Constants.Name.AUTO_PLAY:
String aotu_play = WXUtils.getString(param, null);
if (aotu_play != null) {
setAutoPlay(aotu_play);
}
return true;
case Constants.Name.SHOW_INDICATORS:
String indicators = WXUtils.getString(param, null);
if (indicators != null) {
setShowIndicators(indicators);
}
return true;
case Constants.Name.INTERVAL:
Integer interval = WXUtils.getInteger(param, null);
if (interval != null) {
setInterval(interval);
}
return true;
case Constants.Name.INDEX:
Integer index = WXUtils.getInteger(param, null);
if (index != null) {
setIndex(index);
}
return true;
case Constants.Name.OFFSET_X_ACCURACY:
Float accuracy = WXUtils.getFloat(param, 0.1f);
if (accuracy != 0) {
setOffsetXAccuracy(accuracy);
}
return true;
case Constants.Name.SCROLLABLE:
boolean scrollable = WXUtils.getBoolean(param, true);
setScrollable(scrollable);
return true;
case Constants.Name.KEEP_INDEX:
this.keepIndex = WXUtils.getBoolean(param, false);
return true;
}
return super.setProperty(key, param);
}
@Deprecated
@WXComponentProp(name = Constants.Name.VALUE)
public void setValue(String value) {
if (value == null || getHostView() == null) {
return;
}
int i;
try {
i = Integer.parseInt(value);
} catch (NumberFormatException e) {
WXLogUtils.e("", e);
return;
}
setIndex(i);
}
@WXComponentProp(name = Constants.Name.AUTO_PLAY)
public void setAutoPlay(String autoPlay) {
if (TextUtils.isEmpty(autoPlay) || autoPlay.equals("false")) {
mViewPager.stopAutoScroll();
} else {
mViewPager.stopAutoScroll();
mViewPager.startAutoScroll();
}
}
@WXComponentProp(name = Constants.Name.SHOW_INDICATORS)
public void setShowIndicators(String show) {
if (TextUtils.isEmpty(show) || show.equals("false")) {
mShowIndicators = false;
} else {
mShowIndicators = true;
}
if (mIndicator == null) {
return;
}
mIndicator.setShowIndicators(mShowIndicators);
}
@WXComponentProp(name = Constants.Name.INTERVAL)
public void setInterval(int intervalMS) {
if (mViewPager != null && intervalMS > 0) {
mViewPager.setIntervalTime(intervalMS);
}
}
@WXComponentProp(name = Constants.Name.INDEX)
public void setIndex(int index) {
if (mViewPager != null && mAdapter != null) {
if (index >= mAdapter.getRealCount() || index < 0) {
initIndex = index;
return;
}
mViewPager.setCurrentItem(index);
if (mIndicator != null && mIndicator.getHostView() != null
&& mIndicator.getHostView().getRealCurrentItem() != index) {
//OnPageChangeListener not triggered
WXLogUtils.d("setIndex >>>> correction indicator to " + index);
mIndicator.getHostView().setRealCurrentItem(index);
mIndicator.getHostView().invalidate();
if (mPageChangeListener != null && mAdapter != null) {
mPageChangeListener.onPageSelected(mAdapter.getFirst() + index);
}
}
}
}
@WXComponentProp(name = Constants.Name.SCROLLABLE)
public void setScrollable(boolean scrollable) {
if (mViewPager != null && mAdapter != null) {
if(mAdapter.getRealCount() > 0){
mViewPager.setScrollable(scrollable);
}
}
}
@WXComponentProp(name = Constants.Name.OFFSET_X_ACCURACY)
public void setOffsetXAccuracy(float accuracy) {
this.offsetXAccuracy = accuracy;
}
protected class SliderPageChangeListener implements OnPageChangeListener {
private int lastPos = -1;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int pos) {
if (mAdapter.getRealPosition(pos) == lastPos) {
return;
}
if (WXEnvironment.isApkDebugable()) {
WXLogUtils.d("onPageSelected >>>>" + mAdapter.getRealPosition(pos) + " lastPos: " + lastPos);
}
if (mAdapter == null || mAdapter.getRealCount() == 0) {
return;
}
int realPosition = mAdapter.getRealPosition(pos);
if (mChildren == null || realPosition >= mChildren.size()) {
return;
}
if (getDomObject().getEvents().size() == 0) {
return;
}
WXEvent event = getDomObject().getEvents();
String ref = getDomObject().getRef();
if (event.contains(Constants.Event.CHANGE) && WXViewUtils.onScreenArea(getHostView())) {
params.put(INDEX, realPosition);
Map<String, Object> domChanges = new HashMap<>();
Map<String, Object> attrsChanges = new HashMap<>();
attrsChanges.put(INDEX, realPosition);
domChanges.put("attrs", attrsChanges);
WXSDKManager.getInstance().fireEvent(getInstanceId(), ref,
Constants.Event.CHANGE, params, domChanges);
}
mViewPager.requestLayout();
getHostView().invalidate();
lastPos = mAdapter.getRealPosition(pos);
}
@Override
public void onPageScrollStateChanged(int arg0) {
FrameLayout root = getHostView();
if (null != root) {
root.invalidate();
}
}
}
protected static class SliderOnScrollListener implements OnPageChangeListener {
private float lastPositionOffset = 99f;
private int selectedPosition;
private WXSlider target;
public SliderOnScrollListener(WXSlider target) {
this.target = target;
this.selectedPosition = target.mViewPager.superGetCurrentItem();
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (lastPositionOffset == 99f) {
lastPositionOffset = positionOffset;
return;
}
float offset = positionOffset - lastPositionOffset;
if (Math.abs(offset) >= target.offsetXAccuracy) {
if (position == selectedPosition) {
//slide to left. positionOffset[0 -> 1]
Map<String,Object> event = new HashMap<>(1);
event.put(Constants.Name.OFFSET_X_RATIO, -positionOffset);
target.fireEvent(Constants.Event.SCROLL, event);
} else if (position < selectedPosition) {
//slide to right. positionOffset[1 -> 0]
Map<String,Object> event = new HashMap<>(1);
event.put(Constants.Name.OFFSET_X_RATIO, (1f - positionOffset));
target.fireEvent(Constants.Event.SCROLL, event);
}
lastPositionOffset = positionOffset;
}
}
@Override
public void onPageSelected(int position) {
selectedPosition = position;
}
@Override
public void onPageScrollStateChanged(int state) {
/**
* @homeblog@vip.qq.com
*
* add scrollstart & scrollend event
*
*/
switch (state) {
case ViewPager.SCROLL_STATE_IDLE:
lastPositionOffset = 99f;
target.fireEvent("scrollend");
break;
case ViewPager.SCROLL_STATE_DRAGGING:
target.fireEvent("scrollstart");
break;
case ViewPager.SCROLL_STATE_SETTLING:
break;
}
}
}
private void hackTwoItemsInfiniteScroll() {
if (mViewPager == null || mAdapter == null) {
return;
}
if (isInfinite) {
if (mAdapter.getRealCount() == 2) {
final GestureDetector gestureDetector = new GestureDetector(getContext(), new FlingGestureListener(mViewPager));
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
} else {
mViewPager.setOnTouchListener(null);
}
}
}
private static class FlingGestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_MIN_DISTANCE = WXViewUtils.dip2px(50);
private static final int SWIPE_MAX_OFF_PATH = WXViewUtils.dip2px(250);
private static final int SWIPE_THRESHOLD_VELOCITY = WXViewUtils.dip2px(200);
private WeakReference<WXCircleViewPager> pagerRef;
FlingGestureListener(WXCircleViewPager pager) {
this.pagerRef = new WeakReference<>(pager);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
WXCircleViewPager mViewPager = pagerRef.get();
if (mViewPager == null) {
return false;
}
try {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
return false;
}
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY
&& mViewPager.superGetCurrentItem() == 1) {
// right to left swipe
mViewPager.setCurrentItem(0, false);
return true;
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY
&& mViewPager.superGetCurrentItem() == 0) {
// left to right swipe
mViewPager.setCurrentItem(1, false);
return true;
}
} catch (Exception e) {
// ignore
}
return false;
}
}
}