mViewCache;
592 |
593 | BannerPagerAdapter() {
594 | this.mViewCache = new LinkedList<>();
595 | }
596 |
597 | @Override
598 | public int getCount() {
599 | if (mDatas == null) {
600 | return 0;
601 | }
602 | if (mDatas.size() == 1) {
603 | return mDatas.size();
604 | } else if (mDatas.size() < 1) {
605 | return 0;
606 | } else {
607 | if (isBackLoop) {
608 | // 返回播放
609 | return mDatas.size();
610 | } else {
611 | // 循环播放
612 | if (isLoop) {
613 | return NUM;
614 | } else {
615 | return mDatas.size();
616 | }
617 | }
618 | }
619 | }
620 |
621 | @Override
622 | public boolean isViewFromObject(View view, Object object) {
623 | return view == object;
624 | }
625 |
626 | @Override
627 | public Object instantiateItem(ViewGroup container, final int position) {
628 | if (creator == null) {
629 | throw new RuntimeException("[Banner] --> The layout is not specified,请指定 holder");
630 | }
631 | ByBannerViewHolder holder;
632 | View view;
633 | if (mViewCache.size() == 0) {
634 | holder = creator.createViewHolder();
635 | view = holder.createView(container.getContext());
636 | view.setTag(holder);
637 | } else {
638 | view = mViewCache.removeFirst();
639 | holder = (ByBannerViewHolder) view.getTag();
640 | }
641 |
642 | // 设置点击事件,在onBind设置点击事件可将此点击事件覆盖
643 | view.setOnClickListener(new OnClickListener() {
644 | @Override
645 | public void onClick(View v) {
646 | if (listener != null) {
647 | if (isBackLoop) {
648 | listener.onBannerClick(position);
649 | } else {
650 | listener.onBannerClick(toRealPosition(position));
651 | }
652 | }
653 | if (isClipChildrenMode() && isCanClickSideRoll) {
654 | // 一屏多页且点击的不是当前page,则滚动到对应的page
655 | setClipCurrentItem(position);
656 | }
657 | }
658 | });
659 |
660 | if (mDatas != null && mDatas.size() > 0) {
661 | if (isBackLoop) {
662 | holder.onBind(container.getContext(), position, mDatas.get(position));
663 | } else {
664 | holder.onBind(container.getContext(), toRealPosition(position), mDatas.get(toRealPosition(position)));
665 | }
666 | }
667 | container.addView(view);
668 | return view;
669 | }
670 |
671 | @Override
672 | public void destroyItem(ViewGroup container, int position, Object object) {
673 | container.removeView((View) object);
674 | this.mViewCache.add((View) object);
675 | }
676 |
677 | }
678 |
679 | @Override
680 | public void onPageScrollStateChanged(int state) {
681 | if (mOnPageChangeListener != null) {
682 | mOnPageChangeListener.onPageScrollStateChanged(state);
683 | }
684 | }
685 |
686 | @Override
687 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
688 | if (mOnPageChangeListener != null) {
689 | if (isBackLoop) {
690 | mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
691 | } else {
692 | mOnPageChangeListener.onPageScrolled(toRealPosition(position), positionOffset, positionOffsetPixels);
693 | }
694 | }
695 | }
696 |
697 | public int getCurrentItem() {
698 | if (isBackLoop) {
699 | return viewPager.getCurrentItem();
700 | } else {
701 | return toRealPosition(viewPager.getCurrentItem());
702 | }
703 | }
704 |
705 | @Override
706 | public void onPageSelected(int position) {
707 | currentItem = position;
708 | if (mOnPageChangeListener != null) {
709 | if (isBackLoop) {
710 | mOnPageChangeListener.onPageSelected(position);
711 | } else {
712 | mOnPageChangeListener.onPageSelected(toRealPosition(position));
713 | }
714 | }
715 | if (bannerStyle == BannerConfig.CIRCLE_INDICATOR || bannerStyle == BannerConfig.CUSTOM_INDICATOR) {
716 | if (isLoop) {
717 | if (isBackLoop) {
718 | // 返回播放
719 | if (mIndicatorSelectedDrawable != null && mIndicatorUnselectedDrawable != null) {
720 | // 未选择的图片
721 | indicatorImages.get(lastPosition).setImageDrawable(mIndicatorUnselectedDrawable);
722 | // 选择的图片
723 | indicatorImages.get(position).setImageDrawable(mIndicatorSelectedDrawable);
724 | } else {
725 | indicatorImages.get(lastPosition).setImageResource(mIndicatorUnselectedResId);
726 | indicatorImages.get(position).setImageResource(mIndicatorSelectedResId);
727 | }
728 | } else {
729 | if (mIndicatorSelectedDrawable != null && mIndicatorUnselectedDrawable != null) {
730 | indicatorImages.get((lastPosition - 1 + count) % count).setImageDrawable(mIndicatorUnselectedDrawable);
731 | indicatorImages.get((position - 1 + count) % count).setImageDrawable(mIndicatorSelectedDrawable);
732 | } else {
733 | indicatorImages.get((lastPosition - 1 + count) % count).setImageResource(mIndicatorUnselectedResId);
734 | indicatorImages.get((position - 1 + count) % count).setImageResource(mIndicatorSelectedResId);
735 | }
736 | }
737 | } else {
738 | if (isBackLoop) {
739 | // 返回播放
740 | if (mIndicatorSelectedDrawable != null && mIndicatorUnselectedDrawable != null) {
741 | indicatorImages.get(lastPosition).setImageDrawable(mIndicatorUnselectedDrawable);
742 | indicatorImages.get(position).setImageDrawable(mIndicatorSelectedDrawable);
743 | } else {
744 | indicatorImages.get(lastPosition).setImageResource(mIndicatorUnselectedResId);
745 | indicatorImages.get(position).setImageResource(mIndicatorSelectedResId);
746 | }
747 | } else {
748 | if (mIndicatorSelectedDrawable != null && mIndicatorUnselectedDrawable != null) {
749 | indicatorImages.get((lastPosition + count) % count).setImageDrawable(mIndicatorUnselectedDrawable);
750 | indicatorImages.get((toRealPosition(position) + count) % count).setImageDrawable(mIndicatorSelectedDrawable);
751 | } else {
752 | indicatorImages.get((lastPosition + count) % count).setImageResource(mIndicatorUnselectedResId);
753 | indicatorImages.get((toRealPosition(position) + count) % count).setImageResource(mIndicatorSelectedResId);
754 | }
755 | }
756 | }
757 | lastPosition = position;
758 | }
759 | }
760 |
761 | /**
762 | * 是否是一屏多页状态
763 | */
764 | private boolean isClipChildrenMode() {
765 | return (mPageLeftMargin > 0 || mPageRightMargin > 0);
766 | }
767 |
768 | private int toRealPosition(int position) {
769 | //int realPosition = (position - 1) % count;
770 | int realPosition;
771 | if (isLoop) {
772 | realPosition = (position - 1 + count) % count;
773 | } else {
774 | realPosition = (position + count) % count;
775 | }
776 | if (realPosition < 0) {
777 | realPosition += count;
778 | }
779 | return realPosition;
780 | }
781 |
782 | public void setOnBannerClickListener(OnBannerClickListener listener) {
783 | this.listener = listener;
784 | }
785 |
786 | public void setOnPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener) {
787 | mOnPageChangeListener = onPageChangeListener;
788 | }
789 |
790 | public void releaseBanner() {
791 | if (handler != null) {
792 | handler.removeCallbacksAndMessages(null);
793 | handler = null;
794 | }
795 | if (mDatas != null) {
796 | mDatas.clear();
797 | }
798 | if (indicatorImages != null) {
799 | indicatorImages.clear();
800 | }
801 | }
802 | }
803 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/config/BannerConfig.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.config;
2 |
3 | public class BannerConfig {
4 |
5 | /**
6 | * indicator style
7 | * NOT_INDICATOR: 取消指示器
8 | * CIRCLE_INDICATOR: 自带的指示器
9 | * CUSTOM_INDICATOR: 手动设置的指示器
10 | */
11 | public static final int NOT_INDICATOR = 0;
12 | public static final int CIRCLE_INDICATOR = 1;
13 | public static final int CUSTOM_INDICATOR = 2;
14 |
15 | /**
16 | * indicator gravity
17 | */
18 | public static final int LEFT = 5;
19 | public static final int CENTER = 6;
20 | public static final int RIGHT = 7;
21 |
22 | /**
23 | * layout_jbanner
24 | * PADDING_SIZE: 指示器大小
25 | * MARGIN_BOTTOM: 指示器距底部的距离
26 | * TIME: 滚动时间间隔
27 | * DURATION: ViewPager切换滑动速度 时间越大速度越慢
28 | * IS_AUTO_PLAY: 是否自动循环
29 | * IS_SCROLL: ViewPager是否能手动滑动
30 | * IS_LOOP: 是否循环播放,false则循环一轮后停止
31 | * IS_BACK_LOOP: 滑到到最后一个时,是否返回滑动,false则循环播放
32 | */
33 | public static final int PADDING_SIZE = 5;
34 | public static final int MARGIN_BOTTOM = 10;
35 | public static final int TIME = 2000;
36 | public static final int DURATION = 800;
37 | public static final boolean IS_AUTO_PLAY = true;
38 | public static final boolean IS_SCROLL = true;
39 | public static final boolean IS_LOOP = true;
40 | public static final boolean IS_BACK_LOOP = false;
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/config/BannerScroller.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.config;
2 |
3 | import android.content.Context;
4 | import android.view.animation.Interpolator;
5 | import android.widget.Scroller;
6 |
7 | public class BannerScroller extends Scroller {
8 |
9 | private int mDuration = BannerConfig.DURATION;
10 |
11 | public BannerScroller(Context context) {
12 | super(context);
13 | }
14 |
15 | public BannerScroller(Context context, Interpolator interpolator) {
16 | super(context, interpolator);
17 | }
18 |
19 | public BannerScroller(Context context, Interpolator interpolator, boolean flywheel) {
20 | super(context, interpolator, flywheel);
21 | }
22 |
23 | @Override
24 | public void startScroll(int startX, int startY, int dx, int dy, int duration) {
25 | super.startScroll(startX, startY, dx, dy, mDuration);
26 | }
27 |
28 | @Override
29 | public void startScroll(int startX, int startY, int dx, int dy) {
30 | super.startScroll(startX, startY, dx, dy, mDuration);
31 | }
32 |
33 | public void setDuration(int time) {
34 | mDuration = time;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/config/BannerViewPager.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.config;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 |
7 | import androidx.viewpager.widget.ViewPager;
8 |
9 | import java.lang.reflect.Field;
10 |
11 | public class BannerViewPager extends ViewPager {
12 |
13 | // 是否能手动滑动
14 | private boolean scrollable = true;
15 | private boolean mHandleAttach = true;
16 |
17 | public BannerViewPager(Context context) {
18 | super(context);
19 | }
20 |
21 | public BannerViewPager(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | @Override
26 | public boolean onTouchEvent(MotionEvent ev) {
27 | try {
28 | return this.scrollable && super.onTouchEvent(ev);
29 | } catch (IllegalArgumentException ex) {
30 | ex.printStackTrace();
31 | }
32 | return false;
33 | }
34 |
35 | @Override
36 | public boolean onInterceptTouchEvent(MotionEvent ev) {
37 | try {
38 | return this.scrollable && super.onInterceptTouchEvent(ev);
39 | } catch (IllegalArgumentException ex) {
40 | ex.printStackTrace();
41 | }
42 | return false;
43 | }
44 |
45 | public void setScrollable(boolean scrollable) {
46 | this.scrollable = scrollable;
47 | }
48 |
49 | @Override
50 | protected void onAttachedToWindow() {
51 | super.onAttachedToWindow();
52 | // 解决完全隐藏ViewPager,再回来时,第一次滑动时没有动画效果,并且,经常出现view没有加载的情况的bug
53 | if (mHandleAttach) {
54 | try {
55 | Field mFirstLayout = ViewPager.class.getDeclaredField("mFirstLayout");
56 | mFirstLayout.setAccessible(true);
57 | mFirstLayout.set(this, false);
58 | setCurrentItem(getCurrentItem());
59 | } catch (Exception e) {
60 | e.printStackTrace();
61 | }
62 | }
63 | }
64 |
65 | @Override
66 | protected void onDetachedFromWindow() {
67 | super.onDetachedFromWindow();
68 | // 如果设置左右有间距的banner样式时,会初次进去会黏在一起的bug。
69 | // 这样会有另一个问题,在banner不可见刷新banner时左右间距会出现与异常
70 | // if (!mHandleAttach) {
71 | // mHandleAttach = true;
72 | // }
73 | }
74 |
75 | public void setHandleAttached(boolean handleAttach) {
76 | if (mHandleAttach != handleAttach) {
77 | mHandleAttach = handleAttach;
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/config/OnBannerClickListener.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.config;
2 |
3 | public interface OnBannerClickListener {
4 |
5 | /**
6 | * 点击事件
7 | */
8 | void onBannerClick(int position);
9 | }
10 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/config/OnBannerFilterClickListener.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.config;
2 |
3 | /**
4 | * banner防止重复点击
5 | *
6 | * @author jingbin
7 | */
8 | public abstract class OnBannerFilterClickListener implements OnBannerClickListener {
9 |
10 | private long mLastClickTime = 0L;
11 | private long mTimeInterval = 1000L;
12 |
13 | public OnBannerFilterClickListener() {
14 | }
15 |
16 | public OnBannerFilterClickListener(long interval) {
17 | this.mTimeInterval = interval;
18 | }
19 |
20 | @Override
21 | public void onBannerClick(int position) {
22 | long nowTime = System.currentTimeMillis();
23 | if (nowTime - this.mLastClickTime > this.mTimeInterval) {
24 | this.mLastClickTime = nowTime;
25 | this.onSingleClick(position);
26 | }
27 | }
28 |
29 | protected abstract void onSingleClick(int position);
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/config/ScaleRightTransformer.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.config;
2 |
3 | import android.view.View;
4 |
5 | import androidx.viewpager.widget.ViewPager;
6 |
7 | public class ScaleRightTransformer implements ViewPager.PageTransformer {
8 |
9 | private ViewPager viewPager;
10 | private static final float SCALE_X = 0.08f;
11 |
12 | @Override
13 | public void transformPage(View view, float position) {
14 | if (viewPager == null) {
15 | viewPager = (ViewPager) view.getParent();
16 | }
17 | int leftInScreen = view.getLeft() - viewPager.getScrollX();
18 | int centerXInViewPager = leftInScreen + view.getMeasuredWidth() / 2;
19 | int offsetX = centerXInViewPager - viewPager.getMeasuredWidth() / 2;
20 | float offsetRate = (float) offsetX * SCALE_X / viewPager.getMeasuredWidth();
21 | float scaleFactor = 1 - Math.abs(offsetRate);
22 | if (scaleFactor > 0) {
23 | view.setScaleX(scaleFactor);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/config/WeakHandler.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.config;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 | import android.os.Message;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 | import androidx.annotation.VisibleForTesting;
10 |
11 | import java.lang.ref.WeakReference;
12 | import java.util.concurrent.locks.Lock;
13 | import java.util.concurrent.locks.ReentrantLock;
14 |
15 | public class WeakHandler {
16 |
17 | private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory
18 | private final ExecHandler mExec;
19 | private Lock mLock = new ReentrantLock();
20 | @SuppressWarnings("ConstantConditions")
21 | @VisibleForTesting
22 | final ChainedRef mRunnables = new ChainedRef(mLock, null);
23 |
24 | /**
25 | * Default constructor associates this handler with the {@link Looper} for the
26 | * current thread.
27 | *
28 | * If this thread does not have a looper, this handler won't be able to receive messages
29 | * so an exception is thrown.
30 | */
31 | public WeakHandler() {
32 | mCallback = null;
33 | mExec = new ExecHandler();
34 | }
35 |
36 | /**
37 | * Constructor associates this handler with the {@link Looper} for the
38 | * current thread and takes a callback interface in which you can handle
39 | * messages.
40 | *
41 | * If this thread does not have a looper, this handler won't be able to receive messages
42 | * so an exception is thrown.
43 | *
44 | * @param callback The callback interface in which to handle messages, or null.
45 | */
46 | public WeakHandler(@Nullable Handler.Callback callback) {
47 | mCallback = callback; // Hard referencing body
48 | mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler
49 | }
50 |
51 | /**
52 | * Use the provided {@link Looper} instead of the default one.
53 | *
54 | * @param looper The looper, must not be null.
55 | */
56 | public WeakHandler(@NonNull Looper looper) {
57 | mCallback = null;
58 | mExec = new ExecHandler(looper);
59 | }
60 |
61 | /**
62 | * Use the provided {@link Looper} instead of the default one and take a callback
63 | * interface in which to handle messages.
64 | *
65 | * @param looper The looper, must not be null.
66 | * @param callback The callback interface in which to handle messages, or null.
67 | */
68 | public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) {
69 | mCallback = callback;
70 | mExec = new ExecHandler(looper, new WeakReference<>(callback));
71 | }
72 |
73 | /**
74 | * Causes the Runnable r to be added to the message queue.
75 | * The runnable will be run on the thread to which this handler is
76 | * attached.
77 | *
78 | * @param r The Runnable that will be executed.
79 | * @return Returns true if the Runnable was successfully placed in to the
80 | * message queue. Returns false on failure, usually because the
81 | * looper processing the message queue is exiting.
82 | */
83 | public final boolean post(@NonNull Runnable r) {
84 | return mExec.post(wrapRunnable(r));
85 | }
86 |
87 | /**
88 | * Causes the Runnable r to be added to the message queue, to be run
89 | * at a specific time given by uptimeMillis.
90 | * The time-base is {@link android.os.SystemClock#uptimeMillis}.
91 | * The runnable will be run on the thread to which this handler is attached.
92 | *
93 | * @param r The Runnable that will be executed.
94 | * @param uptimeMillis The absolute time at which the callback should run,
95 | * using the {@link android.os.SystemClock#uptimeMillis} time-base.
96 | * @return Returns true if the Runnable was successfully placed in to the
97 | * message queue. Returns false on failure, usually because the
98 | * looper processing the message queue is exiting. Note that a
99 | * result of true does not mean the Runnable will be processed -- if
100 | * the looper is quit before the delivery time of the message
101 | * occurs then the message will be dropped.
102 | */
103 | public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
104 | return mExec.postAtTime(wrapRunnable(r), uptimeMillis);
105 | }
106 |
107 | /**
108 | * Causes the Runnable r to be added to the message queue, to be run
109 | * at a specific time given by uptimeMillis.
110 | * The time-base is {@link android.os.SystemClock#uptimeMillis}.
111 | * The runnable will be run on the thread to which this handler is attached.
112 | *
113 | * @param r The Runnable that will be executed.
114 | * @param uptimeMillis The absolute time at which the callback should run,
115 | * using the {@link android.os.SystemClock#uptimeMillis} time-base.
116 | * @return Returns true if the Runnable was successfully placed in to the
117 | * message queue. Returns false on failure, usually because the
118 | * looper processing the message queue is exiting. Note that a
119 | * result of true does not mean the Runnable will be processed -- if
120 | * the looper is quit before the delivery time of the message
121 | * occurs then the message will be dropped.
122 | * @see android.os.SystemClock#uptimeMillis
123 | */
124 | public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
125 | return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis);
126 | }
127 |
128 | /**
129 | * Causes the Runnable r to be added to the message queue, to be run
130 | * after the specified amount of time elapses.
131 | * The runnable will be run on the thread to which this handler
132 | * is attached.
133 | *
134 | * @param r The Runnable that will be executed.
135 | * @param delayMillis The delay (in milliseconds) until the Runnable
136 | * will be executed.
137 | * @return Returns true if the Runnable was successfully placed in to the
138 | * message queue. Returns false on failure, usually because the
139 | * looper processing the message queue is exiting. Note that a
140 | * result of true does not mean the Runnable will be processed --
141 | * if the looper is quit before the delivery time of the message
142 | * occurs then the message will be dropped.
143 | */
144 | public final boolean postDelayed(Runnable r, long delayMillis) {
145 | return mExec.postDelayed(wrapRunnable(r), delayMillis);
146 | }
147 |
148 | /**
149 | * Posts a message to an object that implements Runnable.
150 | * Causes the Runnable r to executed on the next iteration through the
151 | * message queue. The runnable will be run on the thread to which this
152 | * handler is attached.
153 | * This method is only for use in very special circumstances -- it
154 | * can easily starve the message queue, cause ordering problems, or have
155 | * other unexpected side-effects.
156 | *
157 | * @param r The Runnable that will be executed.
158 | * @return Returns true if the message was successfully placed in to the
159 | * message queue. Returns false on failure, usually because the
160 | * looper processing the message queue is exiting.
161 | */
162 | public final boolean postAtFrontOfQueue(Runnable r) {
163 | return mExec.postAtFrontOfQueue(wrapRunnable(r));
164 | }
165 |
166 | /**
167 | * Remove any pending posts of Runnable r that are in the message queue.
168 | */
169 | public final void removeCallbacks(Runnable r) {
170 | final WeakRunnable runnable = mRunnables.remove(r);
171 | if (runnable != null) {
172 | mExec.removeCallbacks(runnable);
173 | }
174 | }
175 |
176 | /**
177 | * Remove any pending posts of Runnable r with Object
178 | * token that are in the message queue. If token is null,
179 | * all callbacks will be removed.
180 | */
181 | public final void removeCallbacks(Runnable r, Object token) {
182 | final WeakRunnable runnable = mRunnables.remove(r);
183 | if (runnable != null) {
184 | mExec.removeCallbacks(runnable, token);
185 | }
186 | }
187 |
188 | /**
189 | * Pushes a message onto the end of the message queue after all pending messages
190 | * before the current time. It will be received in callback,
191 | * in the thread attached to this handler.
192 | *
193 | * @return Returns true if the message was successfully placed in to the
194 | * message queue. Returns false on failure, usually because the
195 | * looper processing the message queue is exiting.
196 | */
197 | public final boolean sendMessage(Message msg) {
198 | return mExec.sendMessage(msg);
199 | }
200 |
201 | /**
202 | * Sends a Message containing only the what value.
203 | *
204 | * @return Returns true if the message was successfully placed in to the
205 | * message queue. Returns false on failure, usually because the
206 | * looper processing the message queue is exiting.
207 | */
208 | public final boolean sendEmptyMessage(int what) {
209 | return mExec.sendEmptyMessage(what);
210 | }
211 |
212 | /**
213 | * Sends a Message containing only the what value, to be delivered
214 | * after the specified amount of time elapses.
215 | *
216 | * @return Returns true if the message was successfully placed in to the
217 | * message queue. Returns false on failure, usually because the
218 | * looper processing the message queue is exiting.
219 | * @see #sendMessageDelayed(Message, long)
220 | */
221 | public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
222 | return mExec.sendEmptyMessageDelayed(what, delayMillis);
223 | }
224 |
225 | /**
226 | * Sends a Message containing only the what value, to be delivered
227 | * at a specific time.
228 | *
229 | * @return Returns true if the message was successfully placed in to the
230 | * message queue. Returns false on failure, usually because the
231 | * looper processing the message queue is exiting.
232 | * @see #sendMessageAtTime(Message, long)
233 | */
234 | public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
235 | return mExec.sendEmptyMessageAtTime(what, uptimeMillis);
236 | }
237 |
238 | /**
239 | * Enqueue a message into the message queue after all pending messages
240 | * before (current time + delayMillis). You will receive it in
241 | * callback, in the thread attached to this handler.
242 | *
243 | * @return Returns true if the message was successfully placed in to the
244 | * message queue. Returns false on failure, usually because the
245 | * looper processing the message queue is exiting. Note that a
246 | * result of true does not mean the message will be processed -- if
247 | * the looper is quit before the delivery time of the message
248 | * occurs then the message will be dropped.
249 | */
250 | public final boolean sendMessageDelayed(Message msg, long delayMillis) {
251 | return mExec.sendMessageDelayed(msg, delayMillis);
252 | }
253 |
254 | /**
255 | * Enqueue a message into the message queue after all pending messages
256 | * before the absolute time (in milliseconds) uptimeMillis.
257 | * The time-base is {@link android.os.SystemClock#uptimeMillis}.
258 | * You will receive it in callback, in the thread attached
259 | * to this handler.
260 | *
261 | * @param uptimeMillis The absolute time at which the message should be
262 | * delivered, using the
263 | * {@link android.os.SystemClock#uptimeMillis} time-base.
264 | * @return Returns true if the message was successfully placed in to the
265 | * message queue. Returns false on failure, usually because the
266 | * looper processing the message queue is exiting. Note that a
267 | * result of true does not mean the message will be processed -- if
268 | * the looper is quit before the delivery time of the message
269 | * occurs then the message will be dropped.
270 | */
271 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
272 | return mExec.sendMessageAtTime(msg, uptimeMillis);
273 | }
274 |
275 | /**
276 | * Enqueue a message at the front of the message queue, to be processed on
277 | * the next iteration of the message loop. You will receive it in
278 | * callback, in the thread attached to this handler.
279 | * This method is only for use in very special circumstances -- it
280 | * can easily starve the message queue, cause ordering problems, or have
281 | * other unexpected side-effects.
282 | *
283 | * @return Returns true if the message was successfully placed in to the
284 | * message queue. Returns false on failure, usually because the
285 | * looper processing the message queue is exiting.
286 | */
287 | public final boolean sendMessageAtFrontOfQueue(Message msg) {
288 | return mExec.sendMessageAtFrontOfQueue(msg);
289 | }
290 |
291 | /**
292 | * Remove any pending posts of messages with code 'what' that are in the
293 | * message queue.
294 | */
295 | public final void removeMessages(int what) {
296 | mExec.removeMessages(what);
297 | }
298 |
299 | /**
300 | * Remove any pending posts of messages with code 'what' and whose obj is
301 | * 'object' that are in the message queue. If object is null,
302 | * all messages will be removed.
303 | */
304 | public final void removeMessages(int what, Object object) {
305 | mExec.removeMessages(what, object);
306 | }
307 |
308 | /**
309 | * Remove any pending posts of callbacks and sent messages whose
310 | * obj is token. If token is null,
311 | * all callbacks and messages will be removed.
312 | */
313 | public final void removeCallbacksAndMessages(Object token) {
314 | mExec.removeCallbacksAndMessages(token);
315 | }
316 |
317 | /**
318 | * Check if there are any pending posts of messages with code 'what' in
319 | * the message queue.
320 | */
321 | public final boolean hasMessages(int what) {
322 | return mExec.hasMessages(what);
323 | }
324 |
325 | /**
326 | * Check if there are any pending posts of messages with code 'what' and
327 | * whose obj is 'object' in the message queue.
328 | */
329 | public final boolean hasMessages(int what, Object object) {
330 | return mExec.hasMessages(what, object);
331 | }
332 |
333 | public final Looper getLooper() {
334 | return mExec.getLooper();
335 | }
336 |
337 | private WeakRunnable wrapRunnable(@NonNull Runnable r) {
338 | //noinspection ConstantConditions
339 | if (r == null) {
340 | throw new NullPointerException("Runnable can't be null");
341 | }
342 | final ChainedRef hardRef = new ChainedRef(mLock, r);
343 | mRunnables.insertAfter(hardRef);
344 | return hardRef.wrapper;
345 | }
346 |
347 | private static class ExecHandler extends Handler {
348 | private final WeakReference mCallback;
349 |
350 | ExecHandler() {
351 | mCallback = null;
352 | }
353 |
354 | ExecHandler(WeakReference callback) {
355 | mCallback = callback;
356 | }
357 |
358 | ExecHandler(Looper looper) {
359 | super(looper);
360 | mCallback = null;
361 | }
362 |
363 | ExecHandler(Looper looper, WeakReference callback) {
364 | super(looper);
365 | mCallback = callback;
366 | }
367 |
368 | @Override
369 | public void handleMessage(@NonNull Message msg) {
370 | if (mCallback == null) {
371 | return;
372 | }
373 | final Callback callback = mCallback.get();
374 | if (callback == null) { // Already disposed
375 | return;
376 | }
377 | callback.handleMessage(msg);
378 | }
379 | }
380 |
381 | static class WeakRunnable implements Runnable {
382 | private final WeakReference mDelegate;
383 | private final WeakReference mReference;
384 |
385 | WeakRunnable(WeakReference delegate, WeakReference reference) {
386 | mDelegate = delegate;
387 | mReference = reference;
388 | }
389 |
390 | @Override
391 | public void run() {
392 | final Runnable delegate = mDelegate.get();
393 | final ChainedRef reference = mReference.get();
394 | if (reference != null) {
395 | reference.remove();
396 | }
397 | if (delegate != null) {
398 | delegate.run();
399 | }
400 | }
401 | }
402 |
403 | static class ChainedRef {
404 | @Nullable
405 | ChainedRef next;
406 | @Nullable
407 | ChainedRef prev;
408 | @NonNull
409 | final Runnable runnable;
410 | @NonNull
411 | final WeakRunnable wrapper;
412 |
413 | @NonNull
414 | Lock lock;
415 |
416 | public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) {
417 | this.runnable = r;
418 | this.lock = lock;
419 | this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this));
420 | }
421 |
422 | public WeakRunnable remove() {
423 | lock.lock();
424 | try {
425 | if (prev != null) {
426 | prev.next = next;
427 | }
428 | if (next != null) {
429 | next.prev = prev;
430 | }
431 | prev = null;
432 | next = null;
433 | } finally {
434 | lock.unlock();
435 | }
436 | return wrapper;
437 | }
438 |
439 | public void insertAfter(@NonNull ChainedRef candidate) {
440 | lock.lock();
441 | try {
442 | if (this.next != null) {
443 | this.next.prev = candidate;
444 | }
445 |
446 | candidate.next = this.next;
447 | this.next = candidate;
448 | candidate.prev = this;
449 | } finally {
450 | lock.unlock();
451 | }
452 | }
453 |
454 | @Nullable
455 | public WeakRunnable remove(Runnable obj) {
456 | lock.lock();
457 | try {
458 | ChainedRef curr = this.next; // Skipping head
459 | while (curr != null) {
460 | if (curr.runnable == obj) { // We do comparison exactly how Handler does inside
461 | return curr.remove();
462 | }
463 | curr = curr.next;
464 | }
465 | } finally {
466 | lock.unlock();
467 | }
468 | return null;
469 | }
470 | }
471 | }
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/holder/ByBannerViewHolder.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.holder;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | public interface ByBannerViewHolder {
7 |
8 | /**
9 | * 创建View
10 | */
11 | View createView(Context context);
12 |
13 | /**
14 | * 绑定数据
15 | */
16 | void onBind(Context context, int position, T data);
17 | }
18 |
--------------------------------------------------------------------------------
/bybanner/src/main/java/me/jingbin/banner/holder/HolderCreator.java:
--------------------------------------------------------------------------------
1 | package me.jingbin.banner.holder;
2 |
3 | /**
4 | * Created by songwenchao
5 | * on 2018/5/16 0016.
6 | *
7 | * 类名
8 | * 需要 --
9 | * 可以 --
10 | */
11 | public interface HolderCreator {
12 |
13 | /**
14 | * 创建ViewHolder
15 | */
16 | VH createViewHolder();
17 | }
18 |
--------------------------------------------------------------------------------
/bybanner/src/main/res/drawable/by_gray_radius.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/bybanner/src/main/res/drawable/by_white_radius.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/bybanner/src/main/res/layout/layout_bybanner.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
18 |
19 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bybanner/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/bybanner/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/ByBannerView/2eb949d5f419c106b08bbb462a40dedd0fc6e2dc/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Mar 01 16:09:04 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/sbannerview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/ByBannerView/2eb949d5f419c106b08bbb462a40dedd0fc6e2dc/sbannerview.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':bybanner'
2 |
--------------------------------------------------------------------------------