Last active
June 12, 2018 05:34
-
-
Save imandaliya/7d198d790ddb2c7da67afd3e15ec3a97 to your computer and use it in GitHub Desktop.
AutoScrollview with Dot indicator using TabLayout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
android:layout_width="match_parent" | |
android:layout_height="120dp" | |
android:orientation="vertical"> | |
<com.rbm.myviewpager.AutoScrollViewPager | |
android:id="@+id/view_pager_promotion" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" /> | |
<android.support.design.widget.TabLayout | |
android:id="@+id/tab_layout" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_gravity="bottom|center_horizontal" | |
app:tabBackground="@drawable/viewpager_tab_selector" | |
app:tabGravity="center" | |
app:tabIndicatorHeight="0dp" /> | |
</FrameLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.os.Handler; | |
import android.os.Message; | |
import android.support.v4.view.MotionEventCompat; | |
import android.support.v4.view.PagerAdapter; | |
import android.support.v4.view.ViewPager; | |
import android.util.AttributeSet; | |
import android.view.MotionEvent; | |
import android.view.animation.Interpolator; | |
import android.widget.Scroller; | |
import java.lang.ref.WeakReference; | |
import java.lang.reflect.Field; | |
public class AutoScrollViewPager extends ViewPager { | |
public static final int DEFAULT_INTERVAL = 1500; | |
public static final int LEFT = 0; | |
public static final int RIGHT = 1; | |
/** | |
* do nothing when sliding at the last or first item | |
**/ | |
public static final int SLIDE_BORDER_MODE_NONE = 0; | |
/** | |
* cycle when sliding at the last or first item | |
**/ | |
public static final int SLIDE_BORDER_MODE_CYCLE = 1; | |
/** | |
* deliver event to parent when sliding at the last or first item | |
**/ | |
public static final int SLIDE_BORDER_MODE_TO_PARENT = 2; | |
/** | |
* auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL} | |
**/ | |
private long interval = DEFAULT_INTERVAL; | |
/** | |
* auto scroll direction, default is {@link #RIGHT} | |
**/ | |
private int direction = RIGHT; | |
/** | |
* whether automatic cycle when auto scroll reaching the last or first item, default is true | |
**/ | |
private boolean isCycle = true; | |
/** | |
* whether stop auto scroll when touching, default is true | |
**/ | |
private boolean stopScrollWhenTouch = true; | |
/** | |
* how to process when sliding at the last or first item, default is {@link #SLIDE_BORDER_MODE_NONE} | |
**/ | |
private int slideBorderMode = SLIDE_BORDER_MODE_NONE; | |
/** | |
* whether animating when auto scroll at the last or first item | |
**/ | |
private boolean isBorderAnimation = true; | |
/** | |
* scroll factor for auto scroll animation, default is 1.0 | |
**/ | |
private double autoScrollFactor = 1.0; | |
/** | |
* scroll factor for swipe scroll animation, default is 1.0 | |
**/ | |
private double swipeScrollFactor = 1.0; | |
private Handler handler; | |
private boolean isAutoScroll = false; | |
private boolean isStopByTouch = false; | |
private float touchX = 0f, downX = 0f; | |
private CustomDurationScroller scroller = null; | |
public static final int SCROLL_WHAT = 0; | |
public AutoScrollViewPager(Context paramContext) { | |
super(paramContext); | |
init(); | |
} | |
public AutoScrollViewPager(Context paramContext, AttributeSet paramAttributeSet) { | |
super(paramContext, paramAttributeSet); | |
init(); | |
} | |
private void init() { | |
handler = new MyHandler(this); | |
setViewPagerScroller(); | |
} | |
/** | |
* start auto scroll, first scroll delay time is {@link #getInterval()} | |
*/ | |
public void startAutoScroll() { | |
isAutoScroll = true; | |
sendScrollMessage((long) (interval + scroller.getDuration() / autoScrollFactor * swipeScrollFactor)); | |
} | |
/** | |
* start auto scroll | |
* | |
* @param delayTimeInMills first scroll delay time | |
*/ | |
public void startAutoScroll(int delayTimeInMills) { | |
isAutoScroll = true; | |
sendScrollMessage(delayTimeInMills); | |
} | |
/** | |
* stop auto scroll | |
*/ | |
public void stopAutoScroll() { | |
isAutoScroll = false; | |
handler.removeMessages(SCROLL_WHAT); | |
} | |
/** | |
* set the factor by which the duration of sliding animation will change while swiping | |
*/ | |
public void setSwipeScrollDurationFactor(double scrollFactor) { | |
swipeScrollFactor = scrollFactor; | |
} | |
/** | |
* set the factor by which the duration of sliding animation will change while auto scrolling | |
*/ | |
public void setAutoScrollDurationFactor(double scrollFactor) { | |
autoScrollFactor = scrollFactor; | |
} | |
private void sendScrollMessage(long delayTimeInMills) { | |
/** remove messages before, keeps one message is running at most **/ | |
handler.removeMessages(SCROLL_WHAT); | |
handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills); | |
} | |
/** | |
* set ViewPager scroller to change animation duration when sliding | |
*/ | |
private void setViewPagerScroller() { | |
try { | |
Field scrollerField = ViewPager.class.getDeclaredField("mScroller"); | |
scrollerField.setAccessible(true); | |
Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator"); | |
interpolatorField.setAccessible(true); | |
scroller = new CustomDurationScroller(getContext(), (Interpolator) interpolatorField.get(null)); | |
scrollerField.set(this, scroller); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
/** | |
* scroll only once | |
*/ | |
public void scrollOnce() { | |
PagerAdapter adapter = getAdapter(); | |
int currentItem = getCurrentItem(); | |
int totalCount; | |
if (adapter == null || (totalCount = adapter.getCount()) <= 1) { | |
return; | |
} | |
int nextItem = (direction == LEFT) ? --currentItem : ++currentItem; | |
if (nextItem < 0) { | |
if (isCycle) { | |
setCurrentItem(totalCount - 1, isBorderAnimation); | |
} | |
} else if (nextItem == totalCount) { | |
if (isCycle) { | |
setCurrentItem(0, isBorderAnimation); | |
} | |
} else { | |
setCurrentItem(nextItem, true); | |
} | |
} | |
/** | |
* <ul> | |
* if stopScrollWhenTouch is true | |
* <li>if event is down, stop auto scroll.</li> | |
* <li>if event is up, start auto scroll again.</li> | |
* </ul> | |
*/ | |
@Override | |
public boolean dispatchTouchEvent(MotionEvent ev) { | |
int action = MotionEventCompat.getActionMasked(ev); | |
if (stopScrollWhenTouch) { | |
if ((action == MotionEvent.ACTION_DOWN) && isAutoScroll) { | |
isStopByTouch = true; | |
stopAutoScroll(); | |
} else if (ev.getAction() == MotionEvent.ACTION_UP && isStopByTouch) { | |
startAutoScroll(); | |
} | |
} | |
if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT || slideBorderMode == SLIDE_BORDER_MODE_CYCLE) { | |
touchX = ev.getX(); | |
if (ev.getAction() == MotionEvent.ACTION_DOWN) { | |
downX = touchX; | |
} | |
int currentItem = getCurrentItem(); | |
PagerAdapter adapter = getAdapter(); | |
int pageCount = adapter == null ? 0 : adapter.getCount(); | |
/** | |
* current index is first one and slide to right or current index is last one and slide to left.<br/> | |
* if slide border mode is to parent, then requestDisallowInterceptTouchEvent false.<br/> | |
* else scroll to last one when current item is first one, scroll to first one when current item is last | |
* one. | |
*/ | |
if ((currentItem == 0 && downX <= touchX) || (currentItem == pageCount - 1 && downX >= touchX)) { | |
if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT) { | |
getParent().requestDisallowInterceptTouchEvent(false); | |
} else { | |
if (pageCount > 1) { | |
setCurrentItem(pageCount - currentItem - 1, isBorderAnimation); | |
} | |
getParent().requestDisallowInterceptTouchEvent(true); | |
} | |
return super.dispatchTouchEvent(ev); | |
} | |
} | |
getParent().requestDisallowInterceptTouchEvent(true); | |
return super.dispatchTouchEvent(ev); | |
} | |
private static class MyHandler extends Handler { | |
private final WeakReference<AutoScrollViewPager> autoScrollViewPager; | |
public MyHandler(AutoScrollViewPager autoScrollViewPager) { | |
this.autoScrollViewPager = new WeakReference<AutoScrollViewPager>(autoScrollViewPager); | |
} | |
@Override | |
public void handleMessage(Message msg) { | |
super.handleMessage(msg); | |
switch (msg.what) { | |
case SCROLL_WHAT: | |
AutoScrollViewPager pager = this.autoScrollViewPager.get(); | |
if (pager != null) { | |
pager.scroller.setScrollDurationFactor(pager.autoScrollFactor); | |
pager.scrollOnce(); | |
pager.scroller.setScrollDurationFactor(pager.swipeScrollFactor); | |
pager.sendScrollMessage(pager.interval + pager.scroller.getDuration()); | |
} | |
default: | |
break; | |
} | |
} | |
} | |
/** | |
* get auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL} | |
* | |
* @return the interval | |
*/ | |
public long getInterval() { | |
return interval; | |
} | |
/** | |
* set auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL} | |
* | |
* @param interval the interval to set | |
*/ | |
public void setInterval(long interval) { | |
this.interval = interval; | |
} | |
/** | |
* get auto scroll direction | |
* | |
* @return {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT} | |
*/ | |
public int getDirection() { | |
return (direction == LEFT) ? LEFT : RIGHT; | |
} | |
/** | |
* set auto scroll direction | |
* | |
* @param direction {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT} | |
*/ | |
public void setDirection(int direction) { | |
this.direction = direction; | |
} | |
/** | |
* whether automatic cycle when auto scroll reaching the last or first item, default is true | |
* | |
* @return the isCycle | |
*/ | |
public boolean isCycle() { | |
return isCycle; | |
} | |
/** | |
* set whether automatic cycle when auto scroll reaching the last or first item, default is true | |
* | |
* @param isCycle the isCycle to set | |
*/ | |
public void setCycle(boolean isCycle) { | |
this.isCycle = isCycle; | |
} | |
/** | |
* whether stop auto scroll when touching, default is true | |
* | |
* @return the stopScrollWhenTouch | |
*/ | |
public boolean isStopScrollWhenTouch() { | |
return stopScrollWhenTouch; | |
} | |
/** | |
* set whether stop auto scroll when touching, default is true | |
* | |
* @param stopScrollWhenTouch | |
*/ | |
public void setStopScrollWhenTouch(boolean stopScrollWhenTouch) { | |
this.stopScrollWhenTouch = stopScrollWhenTouch; | |
} | |
/** | |
* get how to process when sliding at the last or first item | |
* | |
* @return the slideBorderMode {@link #SLIDE_BORDER_MODE_NONE}, {@link #SLIDE_BORDER_MODE_TO_PARENT}, | |
* {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE} | |
*/ | |
public int getSlideBorderMode() { | |
return slideBorderMode; | |
} | |
/** | |
* set how to process when sliding at the last or first item | |
* | |
* @param slideBorderMode {@link #SLIDE_BORDER_MODE_NONE}, {@link #SLIDE_BORDER_MODE_TO_PARENT}, | |
* {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE} | |
*/ | |
public void setSlideBorderMode(int slideBorderMode) { | |
this.slideBorderMode = slideBorderMode; | |
} | |
/** | |
* whether animating when auto scroll at the last or first item, default is true | |
* | |
* @return | |
*/ | |
public boolean isBorderAnimation() { | |
return isBorderAnimation; | |
} | |
/** | |
* set whether animating when auto scroll at the last or first item, default is true | |
* | |
* @param isBorderAnimation | |
*/ | |
public void setBorderAnimation(boolean isBorderAnimation) { | |
this.isBorderAnimation = isBorderAnimation; | |
} | |
public class CustomDurationScroller extends Scroller { | |
private double scrollFactor = 1; | |
public CustomDurationScroller(Context context) { | |
super(context); | |
} | |
public CustomDurationScroller(Context context, Interpolator interpolator) { | |
super(context, interpolator); | |
} | |
public CustomDurationScroller(Context context, Interpolator interpolator, boolean flywheel) { | |
super(context, interpolator, flywheel); | |
} | |
public void setScrollDurationFactor(double scrollFactor) { | |
this.scrollFactor = scrollFactor; | |
} | |
@Override | |
public void startScroll(int startX, int startY, int dx, int dy, int duration) { | |
super.startScroll(startX, startY, dx, dy, (int) (duration * scrollFactor)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private final AutoScrollViewPager viewPager; | |
private final TabLayout tabLayout; | |
viewPager = findViewById(R.id.view_pager_promotion); | |
viewPager.startAutoScroll(); | |
viewPager.setInterval(3000); | |
viewPager.setCycle(true); | |
viewPager.setAutoScrollDurationFactor(10); | |
viewPager.setStopScrollWhenTouch(true); | |
tabLayout = findViewById(R.id.tab_layout); | |
PromotionItemAdapter promotionItemAdapter = new PromotionItemAdapter(itemView.getContext(), bean.getList()); | |
viewPager.setAdapter(promotionItemAdapter); | |
tabLayout.setupWithViewPager(viewPager, true); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item> | |
<shape | |
android:innerRadius="0dp" | |
android:shape="ring" | |
android:thickness="4dp" | |
android:useLevel="false"> | |
<solid android:color="@android:color/darker_gray"/> | |
</shape> | |
</item> | |
</layer-list> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item> | |
<shape | |
android:innerRadius="0dp" | |
android:shape="ring" | |
android:thickness="4dp" | |
android:useLevel="false"> | |
<solid android:color="@color/colorAccent"/> | |
</shape> | |
</item> | |
</layer-list> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item android:drawable="@drawable/viewpager_selected_dot" android:state_selected="true" /> | |
<item android:drawable="@drawable/viewpager_default_dot" /> | |
</selector> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment