diff --git a/README.md b/README.md index 12c65f8..6f1fa31 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ |指示器加标题模式
水平显示|![效果示例](https://raw.githubusercontent.com/youth5201314/banner/master/image/5.png) ### 联系方式 -![效果示例](https://raw.githubusercontent.com/youth5201314/banner/master/image/Android技术交流群二维码.png) +![效果示例](http://oceh51kku.bkt.clouddn.com/Android%E6%8A%80%E6%9C%AF%E4%BA%A4%E6%B5%81%E7%BE%A4%E4%BA%8C%E7%BB%B4%E7%A0%81.png) * 如果遇到问题和建议欢迎在给我发送邮件或者加入qq群,希望让这个工程越来越完善。 ## 常量 @@ -64,6 +64,7 @@ |startAutoPlay()|开始轮播|1.4开始,此方法只作用于banner加载完毕-->需要在start()后执行 |stopAutoPlay()|结束轮播|1.4开始,此方法只作用于banner加载完毕-->需要在start()后执行 |start()|开始进行banner渲染|1.4开始 +|setOffscreenPageLimit(int limit)|同viewpager的方法作用一样|1.4.2开始 |setBannerTitle(String[] titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.3.3结束 |setBannerTitleList(List titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.3.3结束 |setBannerTitles(List titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.4开始 @@ -103,9 +104,9 @@ Gradle ```groovy dependencies{ - compile 'com.youth.banner:banner:1.4.1' //最新版本 + compile 'com.youth.banner:banner:1.4.2' //最新版本 or - compile 'com.youth.banner:banner:1.3.3' //旧版本 + compile 'com.youth.banner:banner:1.3.3' //旧版本,旧版本用法下面有跳转链接 } ``` 或者引用本地lib @@ -135,12 +136,11 @@ compile project(':banner') #### Step 4.重写图片加载器 ```java -public class GlideImageLoader implements ImageLoader { +public class GlideImageLoader extends ImageLoader { @Override public void displayImage(Context context, Object path, ImageView imageView) { /** 常用的图片加载库: - Universal Image Loader:一个强大的图片加载库,包含各种各样的配置,最老牌,使用也最广泛。 Picasso: Square出品,必属精品。和OkHttp搭配起来更配呦! Volley ImageLoader:Google官方出品,可惜不能加载本地图片~ @@ -148,10 +148,21 @@ public class GlideImageLoader implements ImageLoader { Glide:Google推荐的图片加载库,专注于流畅的滚动。 */ + //Glide 加载图片简单用法 Glide.with(context).load(path).into(imageView); - or + + //Picasso 加载图片简单用法 Picasso.with(context).load(path).into(imageView) - ...... + + //用fresco加载图片简单用法 + Uri uri = Uri.parse((String) path); + imageView.setImageURI(uri); + } + //提供createImageView 方法,如果不用可以不重写这个方法,方便fresco自定义ImageView + @Override + public ImageView createImageView(Context context) { + SimpleDraweeView simpleDraweeView=new SimpleDraweeView(context); + return simpleDraweeView; } } ``` @@ -264,6 +275,15 @@ protected void onCreate(Bundle savedInstanceState) { ## 更新说明 +#### v1.4.2 + banner优化更新<感谢 694551594,FeverCombo3,MIkeeJY > + * !!!注意!!ImageLoader已从接口改变成抽象类,请调整下代码哦! + * ImageLoader中增加ImageView控件创建方法createImageView(),可以满足fresco加载图片时扩展ImageView需求 + * 修改关于banner刷新时需要第二轮才会更新图片问题(同title更新图片不更新问题),具体看demo + * 开放viewpager的setOffscreenPageLimit(int limit)方法 + * 优化banner在开始0s~20s之间会出现的内存泄漏问题 + * 优化最后一张到第一张之间滑动卡顿现象 + #### v1.4.1 bug修改<感谢深圳-放飞,台北-Tom> * 第一次加载一张图片(不能滑动[正常])-->刷新-->第二次加载多张图片(不能滑动[bug]) diff --git a/app/app.iml b/app/app.iml index c4ebea3..6969eae 100644 --- a/app/app.iml +++ b/app/app.iml @@ -65,14 +65,6 @@ - - - - - - - - @@ -81,6 +73,14 @@ + + + + + + + + @@ -90,6 +90,11 @@ + + + + + @@ -105,7 +110,13 @@ + + + + + + @@ -113,6 +124,7 @@ + diff --git a/app/build.gradle b/app/build.gradle index bf93735..c45b874 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,7 @@ dependencies { compile 'com.android.support:appcompat-v7:23+' compile 'com.android.support:recyclerview-v7:23+' compile "com.github.bumptech.glide:glide:3.7.0" + compile 'com.facebook.fresco:fresco:0.12.0' compile project(':banner') // compile 'com.youth.banner:banner:+' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a65a28e..fe5fe43 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/com/test/banner/App.java b/app/src/main/java/com/test/banner/App.java new file mode 100644 index 0000000..4c34b65 --- /dev/null +++ b/app/src/main/java/com/test/banner/App.java @@ -0,0 +1,16 @@ +package com.test.banner; + +import android.app.Application; +import android.content.Context; + +import com.facebook.drawee.backends.pipeline.Fresco; + + +public class App extends Application { + @Override + public void onCreate() { + super.onCreate(); + Fresco.initialize(this); + + } +} diff --git a/app/src/main/java/com/test/banner/CustomImageLoader.java b/app/src/main/java/com/test/banner/CustomImageLoader.java new file mode 100644 index 0000000..1402f87 --- /dev/null +++ b/app/src/main/java/com/test/banner/CustomImageLoader.java @@ -0,0 +1,29 @@ +package com.test.banner; + +import android.content.Context; +import android.net.Uri; +import android.widget.ImageView; + +import com.bumptech.glide.Glide; +import com.facebook.drawee.view.SimpleDraweeView; +import com.youth.banner.loader.ImageLoader; + + +public class CustomImageLoader extends ImageLoader { + @Override + public void displayImage(Context context, Object path, ImageView imageView) { + //具体方法内容自己去选择,次方法是为了减少banner过多的依赖第三方包,所以将这个权限开放给使用者去选择 +// Glide.with(context).load(path).into(imageView); + + //用fresco加载图片 + Uri uri = Uri.parse((String) path); + imageView.setImageURI(uri); + + } + //提供createImageView 方法,方便fresco自定义ImageView + @Override + public ImageView createImageView(Context context) { + SimpleDraweeView simpleDraweeView=new SimpleDraweeView(context); + return simpleDraweeView; + } +} diff --git a/app/src/main/java/com/test/banner/GlideImageLoader.java b/app/src/main/java/com/test/banner/GlideImageLoader.java deleted file mode 100644 index 9904262..0000000 --- a/app/src/main/java/com/test/banner/GlideImageLoader.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.test.banner; - -import android.content.Context; -import android.widget.ImageView; - -import com.bumptech.glide.Glide; -import com.youth.banner.loader.ImageViewImageLoader; - - -public class GlideImageLoader extends ImageViewImageLoader { - @Override - public void displayImage(Context context, Object path, ImageView imageView) { - //具体方法内容自己去选择,次方法是为了减少banner过多的依赖第三方包,所以将这个权限开放给使用者去选择 - Glide.with(context).load(path).into(imageView); - } -} diff --git a/app/src/main/java/com/test/banner/MainActivity.java b/app/src/main/java/com/test/banner/MainActivity.java index 8047696..7d60155 100644 --- a/app/src/main/java/com/test/banner/MainActivity.java +++ b/app/src/main/java/com/test/banner/MainActivity.java @@ -1,5 +1,6 @@ package com.test.banner; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.v4.widget.SwipeRefreshLayout; @@ -14,6 +15,8 @@ import com.test.banner.common.BaseRecyclerAdapter; import com.youth.banner.Banner; +import com.youth.banner.BannerConfig; +import com.youth.banner.Transformer; import com.youth.banner.listener.OnBannerClickListener; import java.util.Arrays; @@ -29,7 +32,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou public void handleMessage(android.os.Message msg) { switch (msg.what) { case REFRESH_COMPLETE: - images = getResources().getStringArray(R.array.url2); + images = getResources().getStringArray(R.array.url4); banner.setImages(Arrays.asList(images)).start(); mSwipeLayout.setRefreshing(false); break; @@ -68,22 +71,22 @@ protected void onCreate(Bundle savedInstanceState) { */ //简单使用 - banner.setImages(Arrays.asList(images)).setImageLoader(new GlideImageLoader()).start(); - - /*//设置banner样式 - banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE); + banner.setImages(Arrays.asList(images)).setImageLoader(new CustomImageLoader()).start(); +/* + //设置banner样式 + banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE); //设置图片加载器 - banner.setImageLoader(new GlideImageLoader()); + banner.setImageLoader(new CustomImageLoader()); //设置图片集合 banner.setImages(Arrays.asList(images)); //设置banner动画效果 - banner.setBannerAnimation(Transformer.DepthPage); + banner.setBannerAnimation(Transformer.CubeOut); //设置标题集合(当banner样式有显示title时) banner.setBannerTitles(Arrays.asList(titles)); //设置自动轮播,默认为true banner.isAutoPlay(true); //设置轮播时间 - banner.setDelayTime(1500); + banner.setDelayTime(3000); //设置指示器位置(当banner模式中有指示器时) banner.setIndicatorGravity(BannerConfig.CENTER); //banner设置方法全部调用完毕时最后调用 @@ -92,6 +95,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void OnBannerClick(int position) { Toast.makeText(getApplicationContext(), "点击:" + position, Toast.LENGTH_SHORT).show(); + Log.e("--",position+""); } }); } diff --git a/app/src/main/res/layout/activity_test.xml b/app/src/main/res/layout/activity_test.xml new file mode 100644 index 0000000..1e3f385 --- /dev/null +++ b/app/src/main/res/layout/activity_test.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/layout/header.xml b/app/src/main/res/layout/header.xml index 157b323..1ceff69 100644 --- a/app/src/main/res/layout/header.xml +++ b/app/src/main/res/layout/header.xml @@ -2,5 +2,9 @@ + android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto" + app:image_scale_type="center_crop" + app:indicator_drawable_selected="@drawable/selected_radius" + app:title_background="#555CB85C"/> diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 173d401..79a4ee7 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -9,15 +9,19 @@ http://img.zcool.cn/community/01fd2756e142716ac72531cbf8bbbf.jpg http://img.zcool.cn/community/0192a557a352ef0000012e7ed104e4.jpg + + http://img.zcool.cn/community/01700557a7f42f0000018c1bd6eb23.jpg + + http://img.zcool.cn/community/01d28457d621800000018c1bb7877e.jpg + http://img.zcool.cn/community/01ae5656e1427f6ac72531cb72bac5.jpg + + http://img.zcool.cn/community/01b72057a7e0790000018c1bf4fce0.png http://img.zcool.cn/community/01fca557a7f5f90000012e7e9feea8.jpg http://img.zcool.cn/community/01996b57a7f6020000018c1bedef97.jpg http://img.zcool.cn/community/01700557a7f42f0000018c1bd6eb23.jpg - - http://img.zcool.cn/community/01700557a7f42f0000018c1bd6eb23.jpg - 51巅峰钜惠 十大星级品牌联盟,全场2折起 diff --git a/banner/banner-banner.iml b/banner/banner-banner.iml index d4e58f6..b8aea74 100644 --- a/banner/banner-banner.iml +++ b/banner/banner-banner.iml @@ -66,14 +66,6 @@ - - - - - - - - @@ -82,6 +74,14 @@ + + + + + + + + diff --git a/banner/build.gradle b/banner/build.gradle index 2b4f189..ec3bab7 100644 --- a/banner/build.gradle +++ b/banner/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' -version = "1.4.1" +version = "1.4.2" android { compileSdkVersion 23 @@ -10,8 +10,8 @@ android { defaultConfig { minSdkVersion 11 targetSdkVersion 23 - versionCode 32 - versionName "1.4.1" + versionCode 33 + versionName version } buildTypes { release { @@ -19,6 +19,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + lintOptions { + abortOnError false + } } dependencies { @@ -78,7 +81,7 @@ task javadocJar(type: Jar, dependsOn: javadoc) { } artifacts { - archives javadocJar +// archives javadocJar archives sourcesJar } diff --git a/banner/src/main/java/com/youth/banner/Banner.java b/banner/src/main/java/com/youth/banner/Banner.java index b1c0e53..7f0ba86 100644 --- a/banner/src/main/java/com/youth/banner/Banner.java +++ b/banner/src/main/java/com/youth/banner/Banner.java @@ -21,7 +21,7 @@ import android.widget.TextView; import com.youth.banner.listener.OnBannerClickListener; -import com.youth.banner.loader.ImageLoader; +import com.youth.banner.loader.ImageLoaderInterface; import com.youth.banner.view.BannerViewPager; import java.lang.reflect.Field; @@ -58,12 +58,13 @@ public class Banner extends FrameLayout implements OnPageChangeListener { private BannerViewPager viewPager; private TextView bannerTitle, numIndicatorInside, numIndicator; private LinearLayout indicator, indicatorInside, titleView; - private ImageLoader imageLoader; + private ImageLoaderInterface imageLoader; private BannerPagerAdapter adapter; private OnPageChangeListener mOnPageChangeListener; private BannerScroller mScroller; private OnBannerClickListener listener; - private Handler handler = new Handler(); + private WeakHandler handler = new WeakHandler(); + public Banner(Context context) { this(context, null); } @@ -131,7 +132,7 @@ public Banner isAutoPlay(boolean isAutoPlay) { return this; } - public Banner setImageLoader(ImageLoader imageLoader) { + public Banner setImageLoader(ImageLoaderInterface imageLoader) { this.imageLoader = imageLoader; return this; } @@ -163,7 +164,30 @@ public Banner setBannerAnimation(Class transformer) { } return this; } - + /** + * Set the number of pages that should be retained to either side of the + * current page in the view hierarchy in an idle state. Pages beyond this + * limit will be recreated from the adapter when needed. + * + * @param limit How many pages will be kept offscreen in an idle state. + * @return Banner + */ + public Banner setOffscreenPageLimit(int limit){ + if (viewPager!=null){ + viewPager.setOffscreenPageLimit(limit); + } + return this; + } + /** + * Set a {@link PageTransformer} that will be called for each attached page whenever + * the scroll position is changed. This allows the application to apply custom property + * transformations to each page, overriding the default sliding look and feel. + * + * @param reverseDrawingOrder true if the supplied PageTransformer requires page views + * to be drawn from last to first instead of first to last. + * @param transformer PageTransformer that will modify each page's animation properties + * @return Banner + */ public Banner setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) { viewPager.setPageTransformer(reverseDrawingOrder, transformer); return this; @@ -316,10 +340,8 @@ private void setData() { currentItem = 1; if (adapter == null) { adapter = new BannerPagerAdapter(); - viewPager.setAdapter(adapter); - } else { - adapter.notifyDataSetChanged(); } + viewPager.setAdapter(adapter); viewPager.setFocusable(true); viewPager.setCurrentItem(1); viewPager.addOnPageChangeListener(this); @@ -415,13 +437,22 @@ public void onPageScrollStateChanged(int state) { } currentItem = viewPager.getCurrentItem(); switch (state) { - case 0: + case 0://No operation if (currentItem == 0) { viewPager.setCurrentItem(count, false); } else if (currentItem == count + 1) { viewPager.setCurrentItem(1, false); } break; + case 1://start Sliding + if (currentItem == count + 1) { + viewPager.setCurrentItem(1, false); + }else if(currentItem == 0){ + viewPager.setCurrentItem(count, false); + } + break; + case 2://end Sliding + break; } } @@ -484,13 +515,4 @@ public void setOnBannerClickListener(OnBannerClickListener listener) { public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) { mOnPageChangeListener = onPageChangeListener; } -// public void releaseBanner(){ -// stopAutoPlay(); -// new ArrayList(imageUrls).clear(); -// new ArrayList(titles).clear(); -// imageViews.clear(); -// indicatorImages.clear(); -// viewPager.removeAllViews(); -// adapter=null; -// } } diff --git a/banner/src/main/java/com/youth/banner/WeakHandler.java b/banner/src/main/java/com/youth/banner/WeakHandler.java new file mode 100644 index 0000000..be676f5 --- /dev/null +++ b/banner/src/main/java/com/youth/banner/WeakHandler.java @@ -0,0 +1,477 @@ +package com.youth.banner; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; + +import java.lang.ref.WeakReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +@SuppressWarnings("unused") +public class WeakHandler { + private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory + private final ExecHandler mExec; + private Lock mLock = new ReentrantLock(); + @SuppressWarnings("ConstantConditions") + @VisibleForTesting + final ChainedRef mRunnables = new ChainedRef(mLock, null); + + /** + * Default constructor associates this handler with the {@link Looper} for the + * current thread. + * + * If this thread does not have a looper, this handler won't be able to receive messages + * so an exception is thrown. + */ + public WeakHandler() { + mCallback = null; + mExec = new ExecHandler(); + } + + /** + * Constructor associates this handler with the {@link Looper} for the + * current thread and takes a callback interface in which you can handle + * messages. + * + * If this thread does not have a looper, this handler won't be able to receive messages + * so an exception is thrown. + * + * @param callback The callback interface in which to handle messages, or null. + */ + public WeakHandler(@Nullable Handler.Callback callback) { + mCallback = callback; // Hard referencing body + mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler + } + + /** + * Use the provided {@link Looper} instead of the default one. + * + * @param looper The looper, must not be null. + */ + public WeakHandler(@NonNull Looper looper) { + mCallback = null; + mExec = new ExecHandler(looper); + } + + /** + * Use the provided {@link Looper} instead of the default one and take a callback + * interface in which to handle messages. + * + * @param looper The looper, must not be null. + * @param callback The callback interface in which to handle messages, or null. + */ + public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) { + mCallback = callback; + mExec = new ExecHandler(looper, new WeakReference<>(callback)); + } + + /** + * Causes the Runnable r to be added to the message queue. + * The runnable will be run on the thread to which this handler is + * attached. + * + * @param r The Runnable that will be executed. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean post(@NonNull Runnable r) { + return mExec.post(wrapRunnable(r)); + } + + /** + * Causes the Runnable r to be added to the message queue, to be run + * at a specific time given by uptimeMillis. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * The runnable will be run on the thread to which this handler is attached. + * + * @param r The Runnable that will be executed. + * @param uptimeMillis The absolute time at which the callback should run, + * using the {@link android.os.SystemClock#uptimeMillis} time-base. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { + return mExec.postAtTime(wrapRunnable(r), uptimeMillis); + } + + /** + * Causes the Runnable r to be added to the message queue, to be run + * at a specific time given by uptimeMillis. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * The runnable will be run on the thread to which this handler is attached. + * + * @param r The Runnable that will be executed. + * @param uptimeMillis The absolute time at which the callback should run, + * using the {@link android.os.SystemClock#uptimeMillis} time-base. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + * + * @see android.os.SystemClock#uptimeMillis + */ + public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { + return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis); + } + + /** + * Causes the Runnable r to be added to the message queue, to be run + * after the specified amount of time elapses. + * The runnable will be run on the thread to which this handler + * is attached. + * + * @param r The Runnable that will be executed. + * @param delayMillis The delay (in milliseconds) until the Runnable + * will be executed. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- + * if the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean postDelayed(Runnable r, long delayMillis) { + return mExec.postDelayed(wrapRunnable(r), delayMillis); + } + + /** + * Posts a message to an object that implements Runnable. + * Causes the Runnable r to executed on the next iteration through the + * message queue. The runnable will be run on the thread to which this + * handler is attached. + * This method is only for use in very special circumstances -- it + * can easily starve the message queue, cause ordering problems, or have + * other unexpected side-effects. + * + * @param r The Runnable that will be executed. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean postAtFrontOfQueue(Runnable r) { + return mExec.postAtFrontOfQueue(wrapRunnable(r)); + } + + /** + * Remove any pending posts of Runnable r that are in the message queue. + */ + public final void removeCallbacks(Runnable r) { + final WeakRunnable runnable = mRunnables.remove(r); + if (runnable != null) { + mExec.removeCallbacks(runnable); + } + } + + /** + * Remove any pending posts of Runnable r with Object + * token that are in the message queue. If token is null, + * all callbacks will be removed. + */ + public final void removeCallbacks(Runnable r, Object token) { + final WeakRunnable runnable = mRunnables.remove(r); + if (runnable != null) { + mExec.removeCallbacks(runnable, token); + } + } + + /** + * Pushes a message onto the end of the message queue after all pending messages + * before the current time. It will be received in callback, + * in the thread attached to this handler. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendMessage(Message msg) { + return mExec.sendMessage(msg); + } + + /** + * Sends a Message containing only the what value. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendEmptyMessage(int what) { + return mExec.sendEmptyMessage(what); + } + + /** + * Sends a Message containing only the what value, to be delivered + * after the specified amount of time elapses. + * @see #sendMessageDelayed(android.os.Message, long) + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { + return mExec.sendEmptyMessageDelayed(what, delayMillis); + } + + /** + * Sends a Message containing only the what value, to be delivered + * at a specific time. + * @see #sendMessageAtTime(android.os.Message, long) + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { + return mExec.sendEmptyMessageAtTime(what, uptimeMillis); + } + + /** + * Enqueue a message into the message queue after all pending messages + * before (current time + delayMillis). You will receive it in + * callback, in the thread attached to this handler. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the message will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean sendMessageDelayed(Message msg, long delayMillis) { + return mExec.sendMessageDelayed(msg, delayMillis); + } + + /** + * Enqueue a message into the message queue after all pending messages + * before the absolute time (in milliseconds) uptimeMillis. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * You will receive it in callback, in the thread attached + * to this handler. + * + * @param uptimeMillis The absolute time at which the message should be + * delivered, using the + * {@link android.os.SystemClock#uptimeMillis} time-base. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the message will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + return mExec.sendMessageAtTime(msg, uptimeMillis); + } + + /** + * Enqueue a message at the front of the message queue, to be processed on + * the next iteration of the message loop. You will receive it in + * callback, in the thread attached to this handler. + * This method is only for use in very special circumstances -- it + * can easily starve the message queue, cause ordering problems, or have + * other unexpected side-effects. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendMessageAtFrontOfQueue(Message msg) { + return mExec.sendMessageAtFrontOfQueue(msg); + } + + /** + * Remove any pending posts of messages with code 'what' that are in the + * message queue. + */ + public final void removeMessages(int what) { + mExec.removeMessages(what); + } + + /** + * Remove any pending posts of messages with code 'what' and whose obj is + * 'object' that are in the message queue. If object is null, + * all messages will be removed. + */ + public final void removeMessages(int what, Object object) { + mExec.removeMessages(what, object); + } + + /** + * Remove any pending posts of callbacks and sent messages whose + * obj is token. If token is null, + * all callbacks and messages will be removed. + */ + public final void removeCallbacksAndMessages(Object token) { + mExec.removeCallbacksAndMessages(token); + } + + /** + * Check if there are any pending posts of messages with code 'what' in + * the message queue. + */ + public final boolean hasMessages(int what) { + return mExec.hasMessages(what); + } + + /** + * Check if there are any pending posts of messages with code 'what' and + * whose obj is 'object' in the message queue. + */ + public final boolean hasMessages(int what, Object object) { + return mExec.hasMessages(what, object); + } + + public final Looper getLooper() { + return mExec.getLooper(); + } + + private WeakRunnable wrapRunnable(@NonNull Runnable r) { + //noinspection ConstantConditions + if (r == null) { + throw new NullPointerException("Runnable can't be null"); + } + final ChainedRef hardRef = new ChainedRef(mLock, r); + mRunnables.insertAfter(hardRef); + return hardRef.wrapper; + } + + private static class ExecHandler extends Handler { + private final WeakReference mCallback; + + ExecHandler() { + mCallback = null; + } + + ExecHandler(WeakReference callback) { + mCallback = callback; + } + + ExecHandler(Looper looper) { + super(looper); + mCallback = null; + } + + ExecHandler(Looper looper, WeakReference callback) { + super(looper); + mCallback = callback; + } + + @Override + public void handleMessage(@NonNull Message msg) { + if (mCallback == null) { + return; + } + final Handler.Callback callback = mCallback.get(); + if (callback == null) { // Already disposed + return; + } + callback.handleMessage(msg); + } + } + + static class WeakRunnable implements Runnable { + private final WeakReference mDelegate; + private final WeakReference mReference; + + WeakRunnable(WeakReference delegate, WeakReference reference) { + mDelegate = delegate; + mReference = reference; + } + + @Override + public void run() { + final Runnable delegate = mDelegate.get(); + final ChainedRef reference = mReference.get(); + if (reference != null) { + reference.remove(); + } + if (delegate != null) { + delegate.run(); + } + } + } + + static class ChainedRef { + @Nullable + ChainedRef next; + @Nullable + ChainedRef prev; + @NonNull + final Runnable runnable; + @NonNull + final WeakRunnable wrapper; + + @NonNull + Lock lock; + + public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) { + this.runnable = r; + this.lock = lock; + this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this)); + } + + public WeakRunnable remove() { + lock.lock(); + try { + if (prev != null) { + prev.next = next; + } + if (next != null) { + next.prev = prev; + } + prev = null; + next = null; + } finally { + lock.unlock(); + } + return wrapper; + } + + public void insertAfter(@NonNull ChainedRef candidate) { + lock.lock(); + try { + if (this.next != null) { + this.next.prev = candidate; + } + + candidate.next = this.next; + this.next = candidate; + candidate.prev = this; + } finally { + lock.unlock(); + } + } + + @Nullable + public WeakRunnable remove(Runnable obj) { + lock.lock(); + try { + ChainedRef curr = this.next; // Skipping head + while (curr != null) { + if (curr.runnable == obj) { // We do comparison exactly how Handler does inside + return curr.remove(); + } + curr = curr.next; + } + } finally { + lock.unlock(); + } + return null; + } + } +} \ No newline at end of file diff --git a/banner/src/main/java/com/youth/banner/loader/ImageLoader.java b/banner/src/main/java/com/youth/banner/loader/ImageLoader.java index 311af4e..f153dfb 100644 --- a/banner/src/main/java/com/youth/banner/loader/ImageLoader.java +++ b/banner/src/main/java/com/youth/banner/loader/ImageLoader.java @@ -1,14 +1,15 @@ package com.youth.banner.loader; import android.content.Context; -import android.view.View; +import android.widget.ImageView; -import java.io.Serializable; +public abstract class ImageLoader implements ImageLoaderInterface { -public interface ImageLoader extends Serializable { + @Override + public ImageView createImageView(Context context) { + ImageView imageView = new ImageView(context); + return imageView; + } - void displayImage(Context context, Object path, T imageView); - - T createImageView(Context context); } diff --git a/banner/src/main/java/com/youth/banner/loader/ImageLoaderInterface.java b/banner/src/main/java/com/youth/banner/loader/ImageLoaderInterface.java new file mode 100644 index 0000000..0066531 --- /dev/null +++ b/banner/src/main/java/com/youth/banner/loader/ImageLoaderInterface.java @@ -0,0 +1,14 @@ +package com.youth.banner.loader; + +import android.content.Context; +import android.view.View; + +import java.io.Serializable; + + +public interface ImageLoaderInterface extends Serializable { + + void displayImage(Context context, Object path, T imageView); + + T createImageView(Context context); +} diff --git a/banner/src/main/java/com/youth/banner/loader/ImageViewImageLoader.java b/banner/src/main/java/com/youth/banner/loader/ImageViewImageLoader.java deleted file mode 100644 index 2246b81..0000000 --- a/banner/src/main/java/com/youth/banner/loader/ImageViewImageLoader.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.youth.banner.loader; - -import android.content.Context; -import android.widget.ImageView; - - -public abstract class ImageViewImageLoader implements ImageLoader { - - @Override - public ImageView createImageView(Context context) { - ImageView imageView = new ImageView(context); - return imageView; - } - -} diff --git a/build.gradle b/build.gradle index 430c4ba..0c758cf 100644 --- a/build.gradle +++ b/build.gradle @@ -5,9 +5,9 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' + classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.1' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' } }