package com.marshalchen.common.uimodule.slider; import android.content.Context; import android.content.res.TypedArray; import android.os.Message; import android.support.v4.view.PagerAdapter; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; import android.widget.RelativeLayout; import com.marshalchen.common.uimodule.R; import com.marshalchen.common.uimodule.slider.Animations.BaseAnimationInterface; import com.marshalchen.common.uimodule.slider.Indicators.PagerIndicator; import com.marshalchen.common.uimodule.slider.SliderTypes.BaseSliderView; import com.marshalchen.common.uimodule.slider.Transformers.AccordionTransformer; import com.marshalchen.common.uimodule.slider.Transformers.BackgroundToForegroundTransformer; import com.marshalchen.common.uimodule.slider.Transformers.BaseTransformer; import com.marshalchen.common.uimodule.slider.Transformers.CubeInTransformer; import com.marshalchen.common.uimodule.slider.Transformers.DefaultTransformer; import com.marshalchen.common.uimodule.slider.Transformers.DepthPageTransformer; import com.marshalchen.common.uimodule.slider.Transformers.FadeTransformer; import com.marshalchen.common.uimodule.slider.Transformers.FlipHorizontalTransformer; import com.marshalchen.common.uimodule.slider.Transformers.FlipPageViewTransformer; import com.marshalchen.common.uimodule.slider.Transformers.ForegroundToBackgroundTransformer; import com.marshalchen.common.uimodule.slider.Transformers.RotateDownTransformer; import com.marshalchen.common.uimodule.slider.Transformers.RotateUpTransformer; import com.marshalchen.common.uimodule.slider.Transformers.StackTransformer; import com.marshalchen.common.uimodule.slider.Transformers.TabletTransformer; import com.marshalchen.common.uimodule.slider.Transformers.ZoomInTransformer; import com.marshalchen.common.uimodule.slider.Transformers.ZoomOutSlideTransformer; import com.marshalchen.common.uimodule.slider.Transformers.ZoomOutTransformer; import com.marshalchen.common.uimodule.slider.Tricks.FixedSpeedScroller; import com.marshalchen.common.uimodule.slider.Tricks.InfinitePagerAdapter; import com.marshalchen.common.uimodule.slider.Tricks.InfiniteViewPager; import com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx; import java.lang.reflect.Field; import java.util.Timer; import java.util.TimerTask; /** * SliderLayout is compound layout. This is combined with {@link com.marshalchen.common.uimodule.slider.Indicators.PagerIndicator} * and {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx} . * * There is some properties you can set in XML: * * indicator_visibility * visible * invisible * * indicator_shape * oval * rect * * indicator_selected_color * * indicator_unselected_color * * indicator_selected_drawable * * indicator_unselected_drawable * * pager_animation * Default * Accordion * Background2Foreground * CubeIn * DepthPage * Fade * FlipHorizontal * FlipPage * Foreground2Background * RotateDown * RotateUp * Stack * Tablet * ZoomIn * ZoomOutSlide * ZoomOut * * pager_animation_span * * */ public class SliderLayout extends RelativeLayout{ private Context mContext; /** * InfiniteViewPager is extended from ViewPagerEx. As the name says, it can scroll without bounder. */ private InfiniteViewPager mViewPager; /** * InfiniteViewPager adapter. */ private SliderAdapter mSliderAdapter; /** * {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx} indicator. */ private PagerIndicator mIndicator; /** * A timer and a TimerTask using to cycle the {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx}. */ private Timer mCycleTimer; private TimerTask mCycleTask; /** * For resuming the cycle, after user touch or click the {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx}. */ private Timer mResumingTimer; private TimerTask mResumingTask; /** * If {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx} is Cycling */ private boolean mCycling; /** * Determine if auto recover after user touch the {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx} */ private boolean mAutoRecover = true; private int mTransformerId; /** * {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx} transformer time span. */ private int mTransformerSpan = 1100; private boolean mAutoCycle; /** * the duration between animation. */ private long mSliderDuration = 4000; /** * Visibility of {@link com.marshalchen.common.uimodule.slider.Indicators.PagerIndicator} */ private PagerIndicator.IndicatorVisibility mIndicatorVisibility = PagerIndicator.IndicatorVisibility.Visible; /** * {@link com.marshalchen.common.uimodule.slider.Tricks.ViewPagerEx} 's transformer */ private BaseTransformer mViewPagerTransformer; /** * @see com.marshalchen.common.uimodule.slider.Animations.BaseAnimationInterface */ private BaseAnimationInterface mCustomAnimation; /** * {@link com.marshalchen.common.uimodule.slider.Indicators.PagerIndicator} shape, rect or oval. */ public SliderLayout(Context context) { this(context,null); } public SliderLayout(Context context, AttributeSet attrs) { this(context,attrs, R.attr.SliderStyle); } public SliderLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContext = context; LayoutInflater.from(context).inflate(R.layout.slider_layout, this, true); final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs,R.styleable.SliderLayout, defStyle,0); mTransformerSpan = attributes.getInteger(R.styleable.SliderLayout_pager_animation_span, 1100); mTransformerId = attributes.getInt(R.styleable.SliderLayout_pager_animation, Transformer.Default.ordinal()); mAutoCycle = attributes.getBoolean(R.styleable.SliderLayout_auto_cycle,true); int visibility = attributes.getInt(R.styleable.SliderLayout_indicator_visibility,0); for(PagerIndicator.IndicatorVisibility v: PagerIndicator.IndicatorVisibility.values()){ if(v.ordinal() == visibility){ mIndicatorVisibility = v; break; } } mSliderAdapter = new SliderAdapter(mContext); PagerAdapter wrappedAdapter = new InfinitePagerAdapter(mSliderAdapter); mViewPager = (InfiniteViewPager)findViewById(R.id.daimajia_slider_viewpager); mViewPager.setAdapter(wrappedAdapter); mViewPager.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_UP: recoverCycle(); break; } return false; } }); attributes.recycle(); setPresetIndicator(PresetIndicators.Center_Bottom); setPresetTransformer(mTransformerId); setSliderTransformDuration(mTransformerSpan,null); setIndicatorVisibility(mIndicatorVisibility); if(mAutoCycle){ startAutoCycle(); } } public void setCustomIndicator(PagerIndicator indicator){ if(mIndicator != null){ mIndicator.destroySelf(); } mIndicator = indicator; mIndicator.setIndicatorVisibility(mIndicatorVisibility); mIndicator.setViewPager(mViewPager); mIndicator.redraw(); } public <T extends BaseSliderView> void addSlider(T imageContent){ mSliderAdapter.addSlider(imageContent); } private android.os.Handler mh = new android.os.Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); mViewPager.nextItem(); } }; public void startAutoCycle(){ startAutoCycle(1000, mSliderDuration, mAutoRecover); } /** * start auto cycle. * @param delay delay time * @param duration animation duration time. * @param autoRecover if recover after user touches the slider. */ public void startAutoCycle(long delay,long duration,boolean autoRecover){ if(mCycleTimer != null) mCycleTimer.cancel(); if(mCycleTask != null) mCycleTask.cancel(); if(mResumingTask != null) mResumingTask.cancel(); if(mResumingTimer != null) mResumingTimer.cancel(); mSliderDuration = duration; mCycleTimer = new Timer(); mAutoRecover = autoRecover; mCycleTask = new TimerTask() { @Override public void run() { mh.sendEmptyMessage(0); } }; mCycleTimer.schedule(mCycleTask,delay,mSliderDuration); mCycling = true; mAutoCycle = true; } /** * pause auto cycle. */ private void pauseAutoCycle(){ if(mCycling){ mCycleTimer.cancel(); mCycleTask.cancel(); mCycling = false; }else{ if(mResumingTimer != null && mResumingTask != null){ recoverCycle(); } } } /** * set the duration between two slider changes. the duration value must >= 500 * @param duration */ public void setDuration(long duration){ if(duration >= 500){ mSliderDuration = duration; if(mAutoCycle && mCycling){ startAutoCycle(); } } } /** * stop the auto circle */ public void stopAutoCycle(){ if(mCycleTask!=null){ mCycleTask.cancel(); } if(mCycleTimer!= null){ mCycleTimer.cancel(); } if(mResumingTimer!= null){ mResumingTimer.cancel(); } if(mResumingTask!=null){ mResumingTask.cancel(); } mAutoCycle = false; mCycling = false; } /** * when paused cycle, this method can weak it up. */ private void recoverCycle(){ if(!mAutoRecover || !mAutoCycle){ return; } if(!mCycling){ if(mResumingTask != null && mResumingTimer!= null){ mResumingTimer.cancel(); mResumingTask.cancel(); } mResumingTimer = new Timer(); mResumingTask = new TimerTask() { @Override public void run() { startAutoCycle(); } }; mResumingTimer.schedule(mResumingTask, 6000); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: pauseAutoCycle(); break; } return false; } /** * set ViewPager transformer. * @param reverseDrawingOrder * @param transformer */ public void setPagerTransformer(boolean reverseDrawingOrder,BaseTransformer transformer){ mViewPagerTransformer = transformer; mViewPagerTransformer.setCustomAnimationInterface(mCustomAnimation); mViewPager.setPageTransformer(reverseDrawingOrder,mViewPagerTransformer); } /** * set the duration between two slider changes. * @param period * @param interpolator */ public void setSliderTransformDuration(int period,Interpolator interpolator){ try{ Field mScroller = ViewPagerEx.class.getDeclaredField("mScroller"); mScroller.setAccessible(true); FixedSpeedScroller scroller = new FixedSpeedScroller(mViewPager.getContext(),interpolator, period); mScroller.set(mViewPager,scroller); }catch (Exception e){ } } /** * preset transformers and their names */ public enum Transformer{ Default("Default"), Accordion("Accordion"), Background2Foreground("Background2Foreground"), CubeIn("CubeIn"), DepthPage("DepthPage"), Fade("Fade"), FlipHorizontal("FlipHorizontal"), FlipPage("FlipPage"), Foreground2Background("Foreground2Background"), RotateDown("RotateDown"), RotateUp("RotateUp"), Stack("Stack"), Tablet("Tablet"), ZoomIn("ZoomIn"), ZoomOutSlide("ZoomOutSlide"), ZoomOut("ZoomOut"); private final String name; private Transformer(String s){ name = s; } public String toString(){ return name; } public boolean equals(String other){ return (other == null)? false:name.equals(other); } }; /** * set a preset viewpager transformer by id. * @param transformerId */ public void setPresetTransformer(int transformerId){ for(Transformer t : Transformer.values()){ if(t.ordinal() == transformerId){ setPresetTransformer(t); break; } } } /** * set preset PagerTransformer via the name of transforemer. * @param transformerName */ public void setPresetTransformer(String transformerName){ for(Transformer t : Transformer.values()){ if(t.equals(transformerName)){ setPresetTransformer(t); return; } } } /** * Inject your custom animation into PageTransformer, you can know more details in * {@link com.marshalchen.common.uimodule.slider.Animations.BaseAnimationInterface}, * and you can see a example in {@link com.marshalchen.common.uimodule.slider.Animations.DescriptionAnimation} * @param animation */ public void setCustomAnimation(BaseAnimationInterface animation){ mCustomAnimation = animation; if(mViewPagerTransformer != null){ mViewPagerTransformer.setCustomAnimationInterface(mCustomAnimation); } } /** * pretty much right? enjoy it. :-D * * @param ts */ public void setPresetTransformer(Transformer ts){ // // special thanks to https://github.com/ToxicBakery/ViewPagerTransforms // BaseTransformer t = null; switch (ts){ case Default: t = new DefaultTransformer(); break; case Accordion: t = new AccordionTransformer(); break; case Background2Foreground: t = new BackgroundToForegroundTransformer(); break; case CubeIn: t = new CubeInTransformer(); break; case DepthPage: t = new DepthPageTransformer(); break; case Fade: t = new FadeTransformer(); break; case FlipHorizontal: t = new FlipHorizontalTransformer(); break; case FlipPage: t = new FlipPageViewTransformer(); break; case Foreground2Background: t = new ForegroundToBackgroundTransformer(); break; case RotateDown: t = new RotateDownTransformer(); break; case RotateUp: t = new RotateUpTransformer(); break; case Stack: t = new StackTransformer(); break; case Tablet: t = new TabletTransformer(); break; case ZoomIn: t = new ZoomInTransformer(); break; case ZoomOutSlide: t = new ZoomOutSlideTransformer(); break; case ZoomOut: t = new ZoomOutTransformer(); break; } setPagerTransformer(true,t); } /** * Set the visibility of the indicators. * @param visibility */ public void setIndicatorVisibility(PagerIndicator.IndicatorVisibility visibility){ if(mIndicator == null){ return; } mIndicator.setIndicatorVisibility(visibility); } public PagerIndicator.IndicatorVisibility getIndicatorVisibility(){ if(mIndicator == null){ return mIndicator.getIndicatorVisibility(); } return PagerIndicator.IndicatorVisibility.Invisible; } /** * get the {@link com.marshalchen.common.uimodule.slider.Indicators.PagerIndicator} instance. * You can manipulate the properties of the indicator. * @return */ public PagerIndicator getPagerIndicator(){ return mIndicator; } public enum PresetIndicators{ Center_Bottom("Center_Bottom",R.id.default_center_bottom_indicator), Right_Bottom("Right_Bottom",R.id.default_bottom_right_indicator), Left_Bottom("Left_Bottom",R.id.default_bottom_left_indicator), Center_Top("Center_Top",R.id.default_center_top_indicator), Right_Top("Right_Top",R.id.default_center_top_right_indicator), Left_Top("Left_Top",R.id.default_center_top_left_indicator); private final String name; private final int id; private PresetIndicators(String name,int id){ this.name = name; this.id = id; } public String toString(){ return name; } public int getResourceId(){ return id; } } public void setPresetIndicator(PresetIndicators presetIndicator){ PagerIndicator pagerIndicator = (PagerIndicator)findViewById(presetIndicator.getResourceId()); setCustomIndicator(pagerIndicator); } private InfinitePagerAdapter getWrapperAdapter(){ PagerAdapter adapter = mViewPager.getAdapter(); if(adapter!=null){ return (InfinitePagerAdapter)adapter; }else{ return null; } } private SliderAdapter getRealAdapter(){ PagerAdapter adapter = mViewPager.getAdapter(); if(adapter!=null){ return ((InfinitePagerAdapter)adapter).getRealAdapter(); } return null; } /** * get the current item position * @return */ public int getCurrentPosition(){ if(getRealAdapter() == null) throw new IllegalStateException("You did not set a slider adapter"); return mViewPager.getCurrentItem() % getRealAdapter().getCount(); } /** * get current slider. * @return */ public BaseSliderView getCurrentSlider(){ if(getRealAdapter() == null) throw new IllegalStateException("You did not set a slider adapter"); int count = getRealAdapter().getCount(); int realCount = mViewPager.getCurrentItem() % count; return getRealAdapter().getSliderView(realCount); } /** * remove the slider at the position. Notice: It's a not perfect method, a very small bug still exists. */ public void removeSliderAt(int position){ if(getRealAdapter()!=null){ getRealAdapter().removeSliderAt(position); mViewPager.setCurrentItem(mViewPager.getCurrentItem(),false); } } /** * remove all the sliders. Notice: It's a not perfect method, a very small bug still exists. */ public void removeAllSliders(){ if(getRealAdapter()!=null){ int count = getRealAdapter().getCount(); getRealAdapter().removeAllSliders(); //a small bug, but fixed by this trick. //bug: when remove adapter's all the sliders.some caching slider still alive. mViewPager.setCurrentItem(mViewPager.getCurrentItem() + count,false); } } }