blob: a7e95d0f46615cb7b3f093bd6cc773d489fb127a [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.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.common.WXThread;
import com.taobao.weex.ui.ComponentCreator;
import com.taobao.weex.ui.action.BasicComponentData;
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.utils.WXUtils;
import com.taobao.weex.utils.WXViewUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
/**
* Known Issus: In auto play mode, neighbor view not scaled or aplhaed rarely.
*
* Created by xingjiu on 16/8/18.
*/
public class WXSliderNeighbor extends WXSlider {
public static final String NEIGHBOR_SCALE = "neighborScale"; // the init scale of neighbor page
public static final String NEIGHBOR_ALPHA = "neighborAlpha"; // the init alpha of neighbor page
public static final String NEIGHBOR_SPACE = "neighborSpace"; // the init space of neighbor page
public static final String CURRENT_ITEM_SCALE = "currentItemScale"; // the scale of middle item
private static final int DEFAULT_NEIGHBOR_SPACE = 25;
private static final float DEFAULT_NEIGHBOR_SCALE = 0.8F;
private static final float DEFAULT_NEIGHBOR_ALPHA = 0.6F;
private static final float DEFAULT_CURRENT_ITEM_SCALE = 0.9F;
private float mNeighborScale = DEFAULT_NEIGHBOR_SCALE;
private float mNeighborAlpha = DEFAULT_NEIGHBOR_ALPHA;
private float mNeighborSpace = DEFAULT_NEIGHBOR_SPACE;
private float mCurrentItemScale = DEFAULT_CURRENT_ITEM_SCALE;
private ZoomTransformer mCachedTransformer;
public WXSliderNeighbor(WXSDKInstance instance, WXVContainer parent, BasicComponentData basicComponentData) {
super(instance, parent, basicComponentData);
}
public static class Creator implements ComponentCreator {
public WXComponent createInstance(WXSDKInstance instance, WXVContainer parent, BasicComponentData basicComponentData) throws IllegalAccessException, InvocationTargetException, InstantiationException {
return new WXSliderNeighbor(instance, parent, basicComponentData);
}
}
@Override
public void bindData(WXComponent component) {
super.bindData(component);
}
@Override
protected FrameLayout initComponentHostView(@NonNull Context context) {
FrameLayout view = new FrameLayout(context);
// init view pager
FrameLayout.LayoutParams pagerParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
pagerParams.gravity = Gravity.CENTER;
mViewPager = new WXCircleViewPager(getContext());
mViewPager.setLayoutParams(pagerParams);
// init adapter
mAdapter = new WXCirclePageAdapter();
mViewPager.setAdapter(mAdapter);
// add to parent
view.addView(mViewPager);
mViewPager.addOnPageChangeListener(mPageChangeListener);
mViewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
registerActivityStateListener();
mViewPager.setPageTransformer(false, createTransformer());
return view;
}
ZoomTransformer createTransformer() {
if(mCachedTransformer == null) {
mCachedTransformer = new ZoomTransformer();
}
return mCachedTransformer;
}
@Override
public void addSubView(View view, final int index) {
if (view == null || mAdapter == null) {
return;
}
if (view instanceof WXCircleIndicator) {
return;
}
FrameLayout wrapper = new FrameLayout(getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER;
view.setLayoutParams(params);
wrapper.addView(view);
super.addSubView(wrapper,index);
updateAdapterScaleAndAlpha(mNeighborAlpha, mNeighborScale); // we need to set neighbor view status when added.
mViewPager.postDelayed(WXThread.secure(new Runnable() {
@Override
public void run() {
try {
if(mViewPager.getRealCount() > 0 && index > 2) { // index > 2 mean more than two times, then need a fake drag
// prevent a bug of init status. ZoomTransformer no called as excepted.
mViewPager.beginFakeDrag();
mViewPager.fakeDragBy(1); // must be 1
}
}catch (IndexOutOfBoundsException e){
// do nothing
} finally {
try {
mViewPager.endFakeDrag();
}catch (Exception e) {
// do nothing
}
}
}
}), 50);
}
private void updateScaleAndAlpha(View view, float alpha, float scale) {
if(null == view) {
return;
}
if(alpha >= 0 && view.getAlpha() != alpha) {
view.setAlpha(alpha);
}
if(scale >= 0 && view.getScaleX() != scale) {
view.setScaleX(scale);
view.setScaleY(scale);
}
}
private void updateAdapterScaleAndAlpha(final float alpha, final float scale) {
final List<View> pageViews = mAdapter.getViews();
final int curPos = mViewPager.getCurrentItem();
if(pageViews.size() > 0) {
final View currentPage = pageViews.get(curPos);
updateScaleAndAlpha(((ViewGroup)currentPage).getChildAt(0), 1.0F, mCurrentItemScale);
if(pageViews.size() < 2) {
return;
}
//make sure View's width & height are measured.
currentPage.postDelayed(WXThread.secure(new Runnable() {
@Override
public void run() {
//change left and right page's translation
updateNeighbor(currentPage, alpha, scale);
}
}), 17);
// make sure only display view current, left, right.
int left = (curPos == 0) ? pageViews.size()-1 : curPos-1;
int right = (curPos == pageViews.size()-1) ? 0 : curPos+1;
for(int i =0; i<mAdapter.getRealCount(); i++) {
if(i != left && i != curPos && i != right) {
((ViewGroup)pageViews.get(i)).getChildAt(0).setAlpha(0F);
}
}
}
}
private void updateNeighbor(View currentPage, final float alpha, final float scale) {
final List<View> pageViews = mAdapter.getViews();
final int curPos = mViewPager.getCurrentItem();
float translation = calculateTranslation(currentPage);
int left = (curPos == 0) ? pageViews.size()-1 : curPos-1;
View leftPage = pageViews.get(left);
int right = (curPos == pageViews.size()-1) ? 0 : curPos+1;
View rightPage = pageViews.get(right);
if(pageViews.size() == 2) {
if(curPos == 0) {
moveRight(rightPage, translation, alpha, scale);
}else if(curPos == 1) {
moveLeft(leftPage, translation, alpha, scale);
}
} else {
moveLeft(leftPage, translation, alpha, scale);
moveRight(rightPage, translation, alpha, scale);
}
}
private void moveLeft(View page, float translation, float alpha, float scale) {
updateScaleAndAlpha(((ViewGroup)page).getChildAt(0), alpha, scale);
page.setTranslationX(translation);
((ViewGroup)page).getChildAt(0).setTranslationX(translation);
}
private void moveRight(View page, float translation, float alpha, float scale) {
moveLeft(page, -translation, alpha, scale);
}
@WXComponentProp(name = NEIGHBOR_SCALE)
public void setNeighborScale(String input) {
float neighborScale = DEFAULT_NEIGHBOR_SCALE;
if (!TextUtils.isEmpty(input)) {
try {
neighborScale = Float.parseFloat(input);
} catch (NumberFormatException e) {
}
}
// addSubView is called before setProperty, so we need to modify the neighbor view in mAdapter.
if(this.mNeighborScale != neighborScale) {
this.mNeighborScale = neighborScale;
updateAdapterScaleAndAlpha(-1, neighborScale);
}
}
@WXComponentProp(name = NEIGHBOR_ALPHA)
public void setNeighborAlpha(String input) {
float neighborAlpha = DEFAULT_NEIGHBOR_ALPHA;
if (!TextUtils.isEmpty(input)) {
try {
neighborAlpha = Float.parseFloat(input);
} catch (NumberFormatException e) {
}
}
// The same work as setNeighborScale()
if(this.mNeighborAlpha != neighborAlpha) {
this.mNeighborAlpha = neighborAlpha;
updateAdapterScaleAndAlpha(neighborAlpha, -1);
}
}
@WXComponentProp(name = NEIGHBOR_SPACE)
@SuppressWarnings("unused")
public void setNeighborSpace(String input) {
float neighborSpace = DEFAULT_NEIGHBOR_SPACE;
if (!TextUtils.isEmpty(input)) {
try {
neighborSpace = Float.parseFloat(input);
} catch (NumberFormatException e) {
}
}
if(this.mNeighborSpace != neighborSpace) {
this.mNeighborSpace = neighborSpace;
}
}
@WXComponentProp(name = CURRENT_ITEM_SCALE)
@SuppressWarnings("unused")
public void setCurrentItemScale(String input) {
float currentItemScale = DEFAULT_CURRENT_ITEM_SCALE;
if (!TextUtils.isEmpty(input)) {
try {
currentItemScale = Float.parseFloat(input);
} catch (NumberFormatException e) {
}
}
if(this.mCurrentItemScale != currentItemScale) {
this.mCurrentItemScale = currentItemScale;
updateAdapterScaleAndAlpha(-1, -1);
}
}
@Override
protected boolean setProperty(String key, Object param) {
String input;
switch (key) {
case NEIGHBOR_SCALE:
input = WXUtils.getString(param, null);
if (input != null) {
setNeighborScale(input);
}
return true;
case NEIGHBOR_ALPHA:
input = WXUtils.getString(param, null);
if (input != null) {
setNeighborAlpha(input);
}
return true;
case NEIGHBOR_SPACE:
input = WXUtils.getString(param, null);
if (input != null) {
setNeighborSpace(input);
}
return true;
case CURRENT_ITEM_SCALE:
input = WXUtils.getString(param, null);
if (input != null) {
setCurrentItemScale(input);
}
return true;
}
return super.setProperty(key, param);
}
/**
* we need add translation for left and right card view.
* */
private float calculateTranslation(@NonNull View hostPage) {
if(!(hostPage instanceof ViewGroup)) {
return 0;
}
View realView = ((ViewGroup)hostPage).getChildAt(0);
float translation = (hostPage.getMeasuredWidth()-realView.getMeasuredWidth()*mNeighborScale)/4;
translation += ((hostPage.getMeasuredWidth()-realView.getMeasuredWidth() * mCurrentItemScale)/2 - WXViewUtils.getRealPxByWidth(mNeighborSpace, getInstance().getInstanceViewPortWidth()))/2 ;
return translation;
}
// Here is the trick.
class ZoomTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
int pagePosition = mAdapter.getPagePosition(page);
int curPosition = mViewPager.getCurrentItem();
int realCount = mAdapter.getRealCount();
boolean ignore = false;
if(curPosition != 0 && curPosition != realCount - 1 && Math.abs(pagePosition - curPosition) > 1) {
ignore = true;
}
if(curPosition == 0 && pagePosition < realCount - 1 && pagePosition > 1) {
ignore = true;
}
if(curPosition == realCount - 1 && pagePosition < realCount - 2 && pagePosition > 0) {
ignore = true;
}
// just transfer the neighbor(left & right) page.
if(ignore) {
return;
}
View realView = ((ViewGroup)page).getChildAt(0);
if(realView == null){
return;
}
float alpha, scale;
if(position <= (-realCount + 1)) {
position = position + realCount;
}
if(position >= realCount - 1) {
position = position - realCount;
}
if (position >= -1 && position <= 1) {
float factor = Math.abs(Math.abs(position) - 1);
scale = mNeighborScale + factor * (mCurrentItemScale - mNeighborScale);
alpha = (1- mNeighborAlpha) * factor + mNeighborAlpha;
float translation = calculateTranslation(page);
if(position > 0){
translation = (position*translation);
realView.setTranslationX(-translation);
page.setTranslationX(-translation);
}else if(position == 0){
page.setTranslationX(0);
realView.setTranslationX(0);
updateAdapterScaleAndAlpha(mNeighborAlpha, mNeighborScale);
}else{
if(realCount == 2 && Math.abs(position) == 1) {
return;
}
translation = (-position*translation);
realView.setTranslationX(translation);
page.setTranslationX(translation);
}
realView.setScaleX(scale);
realView.setScaleY(scale);
realView.setAlpha(alpha);
}
}
}
}