├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── think │ │ └── uiloader │ │ ├── App.java │ │ ├── data │ │ ├── cache │ │ │ ├── AbstractCache.java │ │ │ ├── Cache.java │ │ │ ├── ImageCache.java │ │ │ ├── internal │ │ │ │ ├── BitmapByteMapper.java │ │ │ │ ├── ByteMapper.java │ │ │ │ ├── BytesMapper.java │ │ │ │ ├── Cache.java │ │ │ │ ├── CacheManager.java │ │ │ │ ├── DiskCacheManager.java │ │ │ │ ├── FileManager.java │ │ │ │ ├── MemoryCacheManager.java │ │ │ │ ├── Optional.java │ │ │ │ ├── SerializableByteMapper.java │ │ │ │ └── TCache.java │ │ │ └── serializer │ │ │ │ └── JsonSerializer.java │ │ ├── datasource │ │ │ ├── AbstractStore.java │ │ │ └── ImageStore.java │ │ ├── entity │ │ │ ├── ImageEntity.java │ │ │ └── mapper │ │ │ │ ├── DtoEntityMapper.java │ │ │ │ └── EntityDtoMapper.java │ │ ├── exception │ │ │ ├── NetworkConnectionException.java │ │ │ ├── NotFoundException.java │ │ │ └── NotFoundFromWebException.java │ │ ├── executor │ │ │ ├── JobExecutor.java │ │ │ ├── PostExecutionThread.java │ │ │ ├── ThreadExecutor.java │ │ │ └── UIThread.java │ │ ├── net │ │ │ ├── Api.java │ │ │ ├── ApiClient.java │ │ │ └── ApiConnection.java │ │ └── store │ │ │ └── internal │ │ │ ├── Config.java │ │ │ ├── ConfigImpl.java │ │ │ ├── Obj.java │ │ │ ├── ObjImpl.java │ │ │ ├── Preference.java │ │ │ ├── PreferenceImpl.java │ │ │ ├── Store.java │ │ │ └── TStore.java │ │ ├── domain │ │ ├── Case.java │ │ ├── DefaultObserver.java │ │ └── ImageCase.java │ │ └── ui │ │ ├── GlideImageLoader.java │ │ ├── ItemDecorationVerticalDivider.java │ │ ├── LListViewActivity.java │ │ ├── ListImageAdapter.java │ │ ├── MainActivity.java │ │ ├── RAutoRefreshActivity.java │ │ ├── RCannotMoveHeadByTLRActivity.java │ │ ├── RGridViewActivity.java │ │ ├── RKeepContentActivity.java │ │ ├── RLListViewActivity.java │ │ ├── RListViewActivity.java │ │ ├── RMaterialHeadActivity.java │ │ ├── RMaterialHeadKeepContentActivity.java │ │ ├── RNotKeepHeadActivity.java │ │ ├── ROtherLibraryActivity.java │ │ ├── RRecyclerViewActivity.java │ │ ├── RRefreshMaxMoveDistanceActivity.java │ │ ├── RScrollViewActivity.java │ │ ├── RTextViewActivity.java │ │ ├── RViewGroupActivity.java │ │ ├── RViewPagerActivity.java │ │ ├── RWebViewActivity.java │ │ ├── TLRMultiContentActivity.java │ │ ├── TLRSunofOtherActivity.java │ │ ├── di │ │ ├── AScope.java │ │ ├── HasComponent.java │ │ ├── components │ │ │ ├── ActivityComponent.java │ │ │ ├── ApplicationComponent.java │ │ │ └── FragmentComponent.java │ │ └── modules │ │ │ └── ApplicationModule.java │ │ └── mvp │ │ ├── BasePresenter.java │ │ ├── BaseView.java │ │ ├── contract │ │ └── ImageContract.java │ │ └── presenter │ │ └── ImagePresenter.java │ └── res │ ├── drawable-xxhdpi │ ├── demo_refresh_text_bg.jpg │ ├── demo_refresh_viewgroup_img.jpg │ └── list_top_image.jpg │ ├── layout │ ├── activity_cannot_movehead_bytlr.xml │ ├── activity_main.xml │ ├── activity_tlr_r_l_listview.xml │ ├── activity_tlrautorefresh.xml │ ├── activity_tlrgridview.xml │ ├── activity_tlrkeepcontent.xml │ ├── activity_tlrlistview.xml │ ├── activity_tlrlistview_load.xml │ ├── activity_tlrmaterialhead.xml │ ├── activity_tlrmaterialhead_keepcontent.xml │ ├── activity_tlrmulticontent.xml │ ├── activity_tlrnotkeephead.xml │ ├── activity_tlrotherlibrary.xml │ ├── activity_tlrrecyclerview.xml │ ├── activity_tlrrefresh_max_move_distance.xml │ ├── activity_tlrscrollview.xml │ ├── activity_tlrsunofother.xml │ ├── activity_tlrtextview.xml │ ├── activity_tlrviewgroup.xml │ ├── activity_tlrviewpager.xml │ ├── activity_tlrwebview.xml │ ├── fragment_pager_layout.xml │ ├── grid_item.xml │ ├── layout.xml │ ├── list_item.xml │ └── test.xml │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── buildsystem └── dependencies.gradle ├── debug.sh ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── think │ │ └── tlr │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── think │ │ │ └── tlr │ │ │ ├── CircleImageView.java │ │ │ ├── MaterialHeaderView.java │ │ │ ├── MaterialProgressDrawable.java │ │ │ ├── TLRCalculator.java │ │ │ ├── TLRDefFootView.java │ │ │ ├── TLRDefHeadView.java │ │ │ ├── TLRLinearLayout.java │ │ │ ├── TLRLog.java │ │ │ ├── TLRNestedLinearLayout.java │ │ │ ├── TLRStatusController.java │ │ │ ├── TLRUIHandler.java │ │ │ ├── TLRUIHandlerAdapter.java │ │ │ └── TLRUIHandlerHook.java │ └── res │ │ ├── drawable-xxhdpi │ │ ├── tlr_def_load.png │ │ ├── tlr_def_refresh.png │ │ ├── tlr_def_refresh_loading01.png │ │ ├── tlr_def_refresh_loading02.png │ │ ├── tlr_def_refresh_loading03.png │ │ ├── tlr_def_refresh_loading04.png │ │ ├── tlr_def_refresh_loading05.png │ │ ├── tlr_def_refresh_loading06.png │ │ ├── tlr_def_refresh_loading07.png │ │ ├── tlr_def_refresh_loading08.png │ │ ├── tlr_def_refresh_loading09.png │ │ ├── tlr_def_refresh_loading10.png │ │ ├── tlr_def_refresh_loading11.png │ │ └── tlr_def_refresh_loading12.png │ │ ├── drawable │ │ └── tlr_def_refresh_loading.xml │ │ ├── layout │ │ ├── tlr_def_foot_layout.xml │ │ └── tlr_def_head_layout.xml │ │ └── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── think │ └── tlr │ └── ExampleUnitTest.java ├── local.properties ├── png ├── 1.gif ├── 2.gif ├── 3.gif └── 4.gif └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | # TLRLoadRefresh 2 | 3 | TLRLoadRefresh是一个支持ListView,RecycleView,ViewGroup等下拉刷新和上拉加载的UI组件,使用具有很大的灵活性。可以根据自己的不同需求来定义不同UI设计,可以灵活的自定义下拉刷新的Head和上拉加载的Foot,下面将介绍该控件的使用方法和设计思想,首先先看下效果。 4 | 5 | ![1](png/1.gif) ![2](png/2.gif) ![3](png/3.gif) ![4](png/4.gif) 6 | 7 | # 控件属性介绍 8 | 9 | 下面对控件的属性坐下介绍,如果描述有不清楚的地方,大家可以自行运行demo查看,demo中也用到我自己写的一个缓存框架,[TCache](https://github.com/borneywpf/TCache),也欢迎大家star。 10 | 11 | ## TLRLinearLayout属性介绍,如下: 12 | 13 | ```xml 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ``` 36 | - **enableRefresh** 是否可以刷新 37 | - **enableLoad** 是否可以加载 38 | - **refreshThreshold** 可刷新系数,以Head View的高度为基础,默认是1.0f 39 | - **loadThreshold** 可加载系数,以Foot View的高度为基础,默认是1.0f 40 | - **refreshMaxMoveDistance** 刷新时,可下拉的最大高度,这个必须要大于加载系数计算的加载高度,否则无法触发刷新 41 | - **loadMaxMoveDistance** 加载时,可上拉的最大高度,同上 42 | - **closeAnimDuration** 刷新结束的动画时间 43 | - **openAnimDuration** 自动刷新,打开Head的动画时间 44 | - **resistance** 触摸滑动阻尼系数,系数越大,触摸滑动的越吃力 45 | - **releaseRefresh** 是否释放刷新,默认是 46 | - **releaseLoad** 是否释放加载,默认是 47 | - **keepHeadRefreshing** 刷新时,是否保持Head View 48 | - **keepFootLoading** 加载时,是否保持Foot View 49 | - **keepContentLayout** 加载或刷新时,是否保持内容体部不移动 50 | - **canMoveHeadByTLR** 刷新时,是否可以让Head View一起移动 51 | - **canMoveFootByTLR** 加载时,是否可以让Foot View一起移动 52 | 53 | ## TLRLinearLayout子控件label设置 54 | ```xml 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ``` 63 | 64 | # TLRLinearLayout子类TLRNestedLinearLayout 65 | 66 | TLRNestedLinearLayout子类是支持android的嵌套滑动,可以支持RecycleView,NestedScrollView等嵌套滑动view的下拉刷新和上拉加载,具体可以查看demo 67 | 68 | # 回调接口TLRUIHandler介绍 69 | 70 | ```java 71 | public interface TLRUIHandler { 72 | /** 73 | * 指定view刷新状态的回调 74 | */ 75 | void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status); 76 | 77 | /** 78 | * 指定view加载状态的回调 79 | */ 80 | void onLoadStatusChanged(View target, TLRLinearLayout.LoadStatus status); 81 | 82 | /** 83 | * 指定view坐标信息的回调 84 | * @param target 哪个view触发的位置变化 85 | * @param isRefresh 是否时刷新 86 | * @param totalOffsetY touch Y方向移动的距离 87 | * @param totalThresholdY Y方向通过刷新系数计算的移动距离 88 | * @param offsetY Y方向相对移动的距离 89 | * @param threshOffset Y方向移动变化系数 90 | */ 91 | void onOffsetChanged(View target, boolean isRefresh, int totalOffsetY, int totalThresholdY, 92 | int offsetY, float threshOffset); 93 | 94 | /** 95 | * 刷新或加载完成时状态的回调 96 | */ 97 | void onFinish(View target, boolean isRefresh, boolean isSuccess, int errorCode); 98 | } 99 | ``` 100 | 101 | # 刷新或加载完成时hook keepview接口TLRUIHandlerHook 102 | 103 | 在加载或刷新完成时,head或foot view有时想完成一些自己的动画,比如类似原生的[MaterialHeaderView](https://github.com/borneywpf/TLRLoadRefresh/blob/master/library/src/main/java/com/think/tlr/MaterialHeaderView.java)在加载完成时,想先做一个缩小动画,然后在让TLRLinearLayout完成剩下的动画,这时候就用到了TLRUIHandlerHook,具体可参考MaterialHeaderView实现。这种设计思想源自于[android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh),感谢大牛们的贡献。 104 | 105 | # 实现原理介绍 106 | 107 | 暂略 108 | 109 | # 总结 110 | 111 | 在实现过程中我参考了一些优秀的开源库,感谢这些无私的大牛们做出的贡献;这个库目前没有进一步封装,因为我还没有做过全面的测试,所以如果有问题欢迎大家指正,并提[issues](https://github.com/borneywpf/TLRLoadRefresh/issues) 112 | 113 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.1" 7 | defaultConfig { 8 | applicationId "com.think.uiloader" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | lintOptions { 22 | abortOnError false 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | compile project(':library') 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | 31 | def appDependencies = rootProject.ext.appDependencies 32 | 33 | compile appDependencies.okhttp 34 | compile appDependencies.loggingInterceptor 35 | compile appDependencies.retrofit 36 | compile appDependencies.rxJava 37 | compile appDependencies.rxAndroid 38 | compile appDependencies.adapterRxJava 39 | compile appDependencies.gson 40 | compile appDependencies.converterGson 41 | compile appDependencies.dagger 42 | apt appDependencies.daggerCompiler 43 | compile appDependencies.androidAnnotations 44 | compile appDependencies.appcompat 45 | compile appDependencies.design 46 | compile appDependencies.recyclerview 47 | compile appDependencies.viewServer 48 | // compile appDependencies.butterknife 49 | // annotationProcessor appDependencies.butterknifecompiler 50 | compile appDependencies.glide 51 | compile appDependencies.guava 52 | compile appDependencies.banner 53 | } 54 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/borney/data/opt/android-sdk-linux/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/App.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader; 2 | 3 | import android.app.Application; 4 | 5 | import com.think.uiloader.ui.di.components.ApplicationComponent; 6 | import com.think.uiloader.ui.di.components.DaggerApplicationComponent; 7 | import com.think.uiloader.ui.di.modules.ApplicationModule; 8 | 9 | /** 10 | * Created by borney on 5/8/17. 11 | */ 12 | public class App extends Application { 13 | private ApplicationComponent applicationComponent; 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | initializeInjector(); 19 | } 20 | 21 | private void initializeInjector() { 22 | applicationComponent = DaggerApplicationComponent.builder() 23 | .applicationModule(new ApplicationModule(this)) 24 | .build(); 25 | } 26 | 27 | public ApplicationComponent getApplicationComponent() { 28 | return applicationComponent; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/AbstractCache.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache; 2 | 3 | import android.content.Context; 4 | 5 | import com.think.uiloader.data.cache.internal.TCache; 6 | import com.think.uiloader.data.cache.serializer.JsonSerializer; 7 | import com.think.uiloader.data.executor.ThreadExecutor; 8 | 9 | /** 10 | * Created by borney on 2/13/17. 11 | */ 12 | 13 | abstract class AbstractCache implements Cache { 14 | Context context; 15 | ThreadExecutor threadExecutor; 16 | JsonSerializer jsonSerializer; 17 | TCache cache; 18 | 19 | AbstractCache(Context context, ThreadExecutor threadExecutor, JsonSerializer jsonSerializer) { 20 | this.context = context; 21 | this.threadExecutor = threadExecutor; 22 | this.jsonSerializer = jsonSerializer; 23 | this.cache = TCache.get(context); 24 | } 25 | 26 | void executeAsynchronously(Runnable runnable) { 27 | threadExecutor.execute(runnable); 28 | } 29 | 30 | static class CacheWriter implements Runnable { 31 | private TCache cache; 32 | private String key; 33 | private String content; 34 | 35 | CacheWriter(TCache cache, String key, String content) { 36 | this.cache = cache; 37 | this.key = key; 38 | this.content = content; 39 | } 40 | 41 | @Override 42 | public void run() { 43 | cache.putSerializable(key, content); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/Cache.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache; 2 | 3 | 4 | import io.reactivex.Observable; 5 | 6 | /** 7 | * Created by borney on 2/13/17. 8 | */ 9 | 10 | public interface Cache { 11 | 12 | Observable get(String key); 13 | 14 | void put(String key, V value); 15 | 16 | boolean isExpired(String key); 17 | 18 | void evict(String key); 19 | 20 | boolean isCached(String key); 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/ImageCache.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache; 2 | 3 | import android.content.Context; 4 | 5 | import com.think.uiloader.data.cache.serializer.JsonSerializer; 6 | import com.think.uiloader.data.entity.ImageEntity; 7 | import com.think.uiloader.data.exception.NotFoundException; 8 | import com.think.uiloader.data.executor.ThreadExecutor; 9 | 10 | import javax.inject.Inject; 11 | 12 | import io.reactivex.Observable; 13 | import io.reactivex.ObservableEmitter; 14 | import io.reactivex.ObservableOnSubscribe; 15 | 16 | /** 17 | * Created by borney on 5/8/17. 18 | */ 19 | public class ImageCache extends AbstractCache { 20 | 21 | @Inject 22 | ImageCache(Context context, ThreadExecutor threadExecutor, JsonSerializer jsonSerializer) { 23 | super(context, threadExecutor, jsonSerializer); 24 | } 25 | 26 | @Override 27 | public Observable get(final String key) { 28 | return Observable.create(new ObservableOnSubscribe() { 29 | @Override 30 | public void subscribe(ObservableEmitter e) throws Exception { 31 | String json = cache.getSerializable(key); 32 | ImageEntity entity = jsonSerializer.deserialize(json, ImageEntity.class); 33 | if (entity != null) { 34 | e.onNext(entity); 35 | e.onComplete(); 36 | } else { 37 | e.onError(new NotFoundException("not found " + key + " cache")); 38 | } 39 | } 40 | }); 41 | } 42 | 43 | @Override 44 | public void put(String key, ImageEntity value) { 45 | String json = jsonSerializer.serialize(value, ImageEntity.class); 46 | executeAsynchronously(new CacheWriter(cache, key, json)); 47 | } 48 | 49 | @Override 50 | public boolean isExpired(String key) { 51 | return cache.isExpired(key); 52 | } 53 | 54 | @Override 55 | public void evict(String key) { 56 | cache.evict(key); 57 | } 58 | 59 | @Override 60 | public boolean isCached(String key) { 61 | return cache.isCached(key); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/BitmapByteMapper.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | 8 | /** 9 | * Created by borney on 3/7/17. 10 | */ 11 | 12 | public class BitmapByteMapper implements ByteMapper { 13 | 14 | BitmapByteMapper() { 15 | 16 | } 17 | 18 | @Override 19 | public byte[] getBytes(Bitmap bitmap) { 20 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 21 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); 22 | return bos.toByteArray(); 23 | } 24 | 25 | @Override 26 | public Bitmap getObject(byte[] bytes) { 27 | return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/ByteMapper.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | /** 4 | * ByteMapper is a object convert to byte array or byte array to object mapper, It is used to 5 | * complete the conversion of objects and byte arrays 6 | * 7 | * Created by borney on 3/7/17. 8 | */ 9 | 10 | public interface ByteMapper { 11 | /** 12 | * The byte array of objects 13 | * 14 | * @param obj 15 | * @return 16 | */ 17 | byte[] getBytes(T obj); 18 | 19 | /** 20 | * The byte array is converted to an object 21 | * 22 | * @param bytes 23 | * @return 24 | */ 25 | T getObject(byte[] bytes); 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/BytesMapper.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | /** 4 | * Created by borney on 3/8/17. 5 | */ 6 | 7 | public class BytesMapper implements ByteMapper { 8 | 9 | BytesMapper() { 10 | 11 | } 12 | 13 | @Override 14 | public byte[] getBytes(byte[] obj) { 15 | return obj; 16 | } 17 | 18 | @Override 19 | public byte[] getObject(byte[] bytes) { 20 | return bytes; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/Cache.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | /** 4 | * Created by borney on 3/7/17. 5 | */ 6 | 7 | interface Cache { 8 | /** 9 | * putByteMapper a object to cache 10 | * 11 | * @param key 存储对象文件的相对名称,可以是目录树 12 | * @param mapper 存储的对象的转换器 13 | * @param 要存储的对象 14 | */ 15 | void putByteMapper(String key, T obj, ByteMapper mapper); 16 | 17 | /** 18 | * get a object from cache 19 | * 20 | * @param key 存储对象文件的相对名称,可以是目录树 21 | * @param 存储的对象的转换器 22 | * @return 存储的对象 23 | */ 24 | T getByteMapper(String key, ByteMapper mapper); 25 | 26 | /** 27 | * cache data is expired or not by key 28 | * 29 | * @param key 存储对象文件的相对名称,可以是目录树 30 | */ 31 | boolean isExpired(String key); 32 | 33 | /** 34 | * 根据 {@param age} 判断缓存是否过期 35 | * 36 | * @param key 存储对象文件的相对名称,可以是目录树 37 | * @param age 过期指数 38 | */ 39 | boolean isExpired(String key, long age); 40 | 41 | /** 42 | * 清除 key 对应的缓存 43 | * 44 | * @param key 存储对象文件的相对名称,可以是目录树 45 | */ 46 | void evict(String key); 47 | 48 | /** 49 | * 清除所有缓存 50 | */ 51 | void evictAll(); 52 | 53 | /** 54 | * 是否缓存了key对应的数据 55 | * 56 | * @param key 57 | * @return 58 | */ 59 | boolean isCached(String key); 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/CacheManager.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | import org.json.JSONException; 6 | import org.json.JSONObject; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author borney 12 | * @date 3/1/17 13 | */ 14 | 15 | interface CacheManager extends Cache { 16 | 17 | /** 18 | * cache byte array 19 | * 20 | * @param key 21 | * @param bytes 22 | */ 23 | void putBytes(String key, byte[] bytes); 24 | 25 | /** 26 | * get byte array from cache 27 | * 28 | * @param key 29 | * @return 30 | */ 31 | byte[] getBytes(String key); 32 | 33 | /** 34 | * cache bitmap 35 | * 36 | * @param key 37 | * @param bitmap 38 | */ 39 | void putBitmap(String key, Bitmap bitmap); 40 | 41 | /** 42 | * get bitmap from cache 43 | * 44 | * @param key 45 | * @return 46 | */ 47 | Bitmap getBitmap(String key); 48 | 49 | /** 50 | * cache Serializable object 51 | * 52 | * @param key 53 | * @param obj which extends Serializable {@link Serializable} 54 | * @param 55 | */ 56 | void putSerializable(String key, T obj); 57 | 58 | /** 59 | * get Serializable object from cache 60 | * @param key 61 | * @param 62 | * @return 63 | */ 64 | T getSerializable(String key); 65 | 66 | /** 67 | * cache JSONObject 68 | * 69 | * @param key 70 | * @param obj 71 | */ 72 | void putJSONObject(String key, JSONObject obj); 73 | 74 | /** 75 | * get JSONObject from cache 76 | * 77 | * @param key 78 | * @return 79 | * @throws JSONException 80 | */ 81 | JSONObject getJSONObject(String key) throws JSONException; 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/DiskCacheManager.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | import android.annotation.SuppressLint; 4 | 5 | import java.io.File; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by borney on 3/1/17. 10 | */ 11 | @SuppressLint("NewApi") 12 | class DiskCacheManager implements Cache { 13 | private FileManager fileManager; 14 | private String cacheDir; 15 | private int maxCount; 16 | private int maxSpace; 17 | private long age; 18 | 19 | DiskCacheManager(FileManager fileManager, String cacheDir, int maxCount, int maxSpace, 20 | long age) { 21 | this.fileManager = fileManager; 22 | this.cacheDir = cacheDir; 23 | this.maxCount = maxCount; 24 | this.maxSpace = maxSpace; 25 | this.age = age; 26 | } 27 | 28 | @Override 29 | public void putByteMapper(String key, T obj, ByteMapper mapper) { 30 | //ensure total file count and space 31 | byte[] bytes = mapper.getBytes(obj); 32 | ensureTotalCount(); 33 | ensureTotalSpace(bytes); 34 | 35 | //create cache file 36 | File file = buildFile(key); 37 | createNotExistsParent(file); 38 | 39 | //write data to cache file 40 | fileManager.writeBytes(file, bytes); 41 | 42 | //update file and it's parent list modify time 43 | updateLastModified(file, System.currentTimeMillis()); 44 | } 45 | 46 | @Override 47 | public T getByteMapper(String key, ByteMapper mapper) { 48 | File file = buildFile(key); 49 | if (file.exists()) { 50 | byte[] bytes = fileManager.readBytes(file); 51 | return mapper.getObject(bytes); 52 | } else { 53 | return null; 54 | } 55 | } 56 | 57 | @Override 58 | public boolean isExpired(String key) { 59 | return isExpired(key, age); 60 | } 61 | 62 | @Override 63 | public boolean isExpired(String key, long age) { 64 | File file = buildFile(key); 65 | return System.currentTimeMillis() - file.lastModified() > age; 66 | } 67 | 68 | @Override 69 | public void evict(String key) { 70 | File file = buildFile(key); 71 | fileManager.deleFile(file); 72 | } 73 | 74 | @Override 75 | public void evictAll() { 76 | fileManager.deleFile(new File(cacheDir)); 77 | } 78 | 79 | @Override 80 | public boolean isCached(String key) { 81 | File file = buildFile(key); 82 | return file.exists() && fileManager.calFileSize(file) != 0; 83 | } 84 | 85 | private void ensureTotalSpace(byte[] bytes) { 86 | int objSize = bytes.length; 87 | File file = new File(cacheDir); 88 | long cacheSize = fileManager.calFileSize(file); 89 | while (cacheSize + objSize > maxSpace) { 90 | long removeSize = removeLastModifiedFile(file); 91 | cacheSize -= removeSize; 92 | } 93 | } 94 | 95 | private void ensureTotalCount() { 96 | File file = new File(cacheDir); 97 | int size = fileManager.calFileCount(file); 98 | while (size > maxCount) { 99 | removeLastModifiedFile(file); 100 | size--; 101 | } 102 | } 103 | 104 | private File buildFile(String key) { 105 | return new File(key); 106 | } 107 | 108 | private void createNotExistsParent(File file) { 109 | File parentFile = file.getParentFile(); 110 | if (!parentFile.exists()) { 111 | parentFile.mkdirs(); 112 | } 113 | } 114 | 115 | private void updateLastModified(File file, long time) { 116 | if (file.getPath().equals(cacheDir)) { 117 | return; 118 | } 119 | file.setLastModified(time); 120 | updateLastModified(file.getParentFile(), time); 121 | } 122 | 123 | private long removeLastModifiedFile(File parent) { 124 | List files = fileManager.allFiles(parent); 125 | if (!files.isEmpty()) { 126 | File lastModifyFile = files.get(0); 127 | long removedSize = lastModifyFile.length(); 128 | for (File f : files) { 129 | if (f.lastModified() < lastModifyFile.lastModified()) { 130 | lastModifyFile = f; 131 | removedSize = lastModifyFile.length(); 132 | } 133 | } 134 | lastModifyFile.delete(); 135 | return removedSize; 136 | } 137 | return 0; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/FileManager.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.util.Log; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.nio.ByteBuffer; 11 | import java.nio.channels.FileChannel; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * Created by borney on 2/27/17. 17 | */ 18 | @SuppressLint("NewApi") 19 | @SuppressWarnings("unused") 20 | class FileManager { 21 | 22 | FileManager() { 23 | 24 | } 25 | 26 | /** 27 | * write byte array to file 28 | */ 29 | public void writeBytes(File file, byte[] content) { 30 | checkNotNull(file); 31 | checkNotNull(content); 32 | Log.d("TCache", "content:" + content.length); 33 | try (FileOutputStream fos = new FileOutputStream(file)) { 34 | ByteBuffer buffer = ByteBuffer.allocate(content.length); 35 | FileChannel channel = fos.getChannel(); 36 | buffer.put(content); 37 | buffer.flip(); 38 | channel.write(buffer); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | /** 45 | * return byte array from file 46 | * 47 | * @param file which provide content 48 | * @return byte array of file content 49 | */ 50 | public byte[] readBytes(File file) { 51 | checkNotNull(file); 52 | if (!file.exists()) { 53 | return null; 54 | } 55 | try (FileInputStream fis = new FileInputStream(file)) { 56 | FileChannel fisChannel = fis.getChannel(); 57 | ByteBuffer buffer = ByteBuffer.allocate(1024); 58 | int j = 0; 59 | byte[] result = new byte[fis.available()]; 60 | while (fisChannel.read(buffer) > 0) { 61 | buffer.flip(); 62 | for (int i = 0; i < buffer.limit(); i++) { 63 | result[j++] = buffer.get(); 64 | } 65 | buffer.clear(); 66 | } 67 | return result; 68 | } catch (IOException e) { 69 | e.printStackTrace(); 70 | } 71 | return null; 72 | } 73 | 74 | public void deleFile(File file) { 75 | checkNotNull(file); 76 | if (file.isDirectory()) { 77 | File[] files = file.listFiles(); 78 | for (File f : files) { 79 | deleFile(f); 80 | } 81 | 82 | } 83 | file.delete(); 84 | } 85 | 86 | public long calFileSize(File file) { 87 | if (file.isDirectory()) { 88 | long length = 0; 89 | File[] files = file.listFiles(); 90 | for (File f : files) { 91 | if (f.isFile()) { 92 | length += f.length(); 93 | } else { 94 | length += calFileSize(f); 95 | } 96 | } 97 | return length; 98 | } else { 99 | return file.length(); 100 | } 101 | 102 | } 103 | 104 | public int calFileCount(File file) { 105 | return allFiles(file).size(); 106 | } 107 | 108 | public List allFiles(File file) { 109 | ArrayList files = new ArrayList<>(); 110 | allFiles(file, files); 111 | return files; 112 | } 113 | 114 | private void allFiles(File file, List files) { 115 | if (file.isDirectory()) { 116 | File[] listFiles = file.listFiles(); 117 | for (File f : listFiles) { 118 | allFiles(f, files); 119 | } 120 | } else { 121 | files.add(file); 122 | } 123 | } 124 | 125 | private Object checkNotNull(Object object) { 126 | if (object == null) { 127 | throw new NullPointerException("error:object is null"); 128 | } 129 | return object; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/MemoryCacheManager.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.util.ArrayMap; 5 | 6 | /** 7 | * Created by borney on 3/1/17. 8 | */ 9 | @SuppressLint("NewApi") 10 | class MemoryCacheManager implements Cache { 11 | private ArrayMap bytesMap; 12 | 13 | MemoryCacheManager() { 14 | bytesMap = new ArrayMap<>(); 15 | } 16 | 17 | @Override 18 | public void putByteMapper(String key, T obj, ByteMapper mapper) { 19 | bytesMap.put(key, mapper.getBytes(obj)); 20 | } 21 | 22 | @Override 23 | public T getByteMapper(String key, ByteMapper mapper) { 24 | if (bytesMap.containsKey(key)) { 25 | return mapper.getObject(bytesMap.get(key)); 26 | } 27 | return null; 28 | } 29 | 30 | @Override 31 | public boolean isExpired(String key) { 32 | throw new UnsupportedOperationException("not support isExpired(key, age) operation!!!"); 33 | } 34 | 35 | @Override 36 | public boolean isExpired(String key, long age) { 37 | throw new UnsupportedOperationException("not support isExpired(key, age) operation!!!"); 38 | } 39 | 40 | @Override 41 | public void evict(String key) { 42 | if (bytesMap.containsKey(key)) { 43 | bytesMap.remove(key); 44 | } 45 | } 46 | 47 | @Override 48 | public void evictAll() { 49 | bytesMap.clear(); 50 | } 51 | 52 | @Override 53 | public boolean isCached(String key) { 54 | return bytesMap.containsKey(key); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/Optional.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | /** 4 | * Created by borney on 3/9/17. 5 | */ 6 | 7 | public final class Optional { 8 | 9 | public static T checkNotNull(T ref) { 10 | return checkNotNull(ref, "ref is null!!!"); 11 | } 12 | 13 | public static T checkNotNull(T ref, String errorMsg) { 14 | if(ref == null) { 15 | throw new NullPointerException(errorMsg); 16 | } 17 | return ref; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/internal/SerializableByteMapper.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.cache.internal; 2 | 3 | import android.annotation.SuppressLint; 4 | 5 | import java.io.ByteArrayInputStream; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.io.ObjectInputStream; 9 | import java.io.ObjectOutput; 10 | import java.io.ObjectOutputStream; 11 | import java.io.Serializable; 12 | 13 | /** 14 | * Created by borney on 3/7/17. 15 | */ 16 | 17 | @SuppressLint("NewApi") 18 | public class SerializableByteMapper implements ByteMapper { 19 | 20 | 21 | SerializableByteMapper() { 22 | } 23 | 24 | @Override 25 | public byte[] getBytes(Serializable obj) { 26 | try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); 27 | ObjectOutput out = new ObjectOutputStream(bos)) { 28 | out.writeObject(obj); 29 | return bos.toByteArray(); 30 | } catch (IOException e) { 31 | e.printStackTrace(); 32 | } 33 | return new byte[0]; 34 | } 35 | 36 | @Override 37 | public Serializable getObject(byte[] bytes) { 38 | try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); 39 | ObjectInputStream ois = new ObjectInputStream(bis)) { 40 | return (Serializable) ois.readObject(); 41 | } catch (IOException e) { 42 | e.printStackTrace(); 43 | } catch (ClassNotFoundException e) { 44 | e.printStackTrace(); 45 | } 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/cache/serializer/JsonSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.data.cache.serializer; 17 | 18 | import com.google.gson.Gson; 19 | import com.google.gson.GsonBuilder; 20 | 21 | import javax.inject.Inject; 22 | 23 | /** 24 | * Class user as Serializer/Deserializer for user entities. 25 | */ 26 | public class JsonSerializer { 27 | 28 | private final Gson gson = new GsonBuilder().setLenient().create(); 29 | 30 | @Inject 31 | public JsonSerializer() { 32 | } 33 | 34 | public String serialize(T entity, Class clazz) { 35 | String jsonString = gson.toJson(entity, clazz); 36 | return jsonString; 37 | } 38 | 39 | /** 40 | * Deserialize a json representation of an object. 41 | * 42 | * @param jsonString A json string to deserialize. 43 | */ 44 | public T deserialize(String jsonString, Class clazz) { 45 | T userEntity = gson.fromJson(jsonString, clazz); 46 | return userEntity; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/datasource/AbstractStore.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.datasource; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | import android.text.TextUtils; 7 | 8 | import com.think.uiloader.data.net.ApiConnection; 9 | import com.think.uiloader.data.store.internal.Store; 10 | import com.think.uiloader.data.store.internal.TStore; 11 | 12 | import io.reactivex.Observable; 13 | import io.reactivex.ObservableSource; 14 | import io.reactivex.functions.Function; 15 | 16 | /** 17 | * Created by borney on 2/17/17. 18 | */ 19 | 20 | abstract class AbstractStore { 21 | private final String TAG = getClass().getSimpleName(); 22 | Context context; 23 | ApiConnection api; 24 | Store store; 25 | 26 | AbstractStore(Context context, ApiConnection api) { 27 | this.context = context; 28 | this.api = api; 29 | this.store = TStore.get(context); 30 | } 31 | 32 | boolean isThereInternetConnection() { 33 | boolean isConnected; 34 | 35 | ConnectivityManager connectivityManager = 36 | (ConnectivityManager) this.context.getSystemService(Context.CONNECTIVITY_SERVICE); 37 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 38 | isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting()); 39 | 40 | return isConnected; 41 | } 42 | 43 | Function> errorResumeNext () { 44 | return new Function>() { 45 | @Override 46 | public ObservableSource apply( 47 | Throwable throwable) throws Exception { 48 | return (ObservableSource) Observable.empty(); 49 | } 50 | }; 51 | } 52 | 53 | boolean isEmpty(String s) { 54 | return TextUtils.isEmpty(s); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/datasource/ImageStore.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.datasource; 2 | 3 | import android.content.Context; 4 | 5 | import com.think.uiloader.data.cache.ImageCache; 6 | import com.think.uiloader.data.entity.ImageEntity; 7 | import com.think.uiloader.data.net.ApiConnection; 8 | 9 | import java.io.File; 10 | 11 | import javax.inject.Inject; 12 | 13 | import io.reactivex.Observable; 14 | import io.reactivex.functions.Consumer; 15 | import io.reactivex.functions.Predicate; 16 | 17 | /** 18 | * Created by borney on 5/8/17. 19 | */ 20 | public class ImageStore extends AbstractStore { 21 | private ImageCache mCache; 22 | 23 | @Inject 24 | ImageStore(Context context, ApiConnection api, ImageCache cache) { 25 | super(context, api); 26 | mCache = cache; 27 | } 28 | 29 | public Observable images(int fromIndex, int returnNum) { 30 | final String key = absKey(fromIndex, returnNum); 31 | 32 | if (!isThereInternetConnection()) { 33 | return mCache.get(key); 34 | } 35 | 36 | return api.images(fromIndex, returnNum) 37 | .filter(new Predicate() { 38 | @Override 39 | public boolean test(ImageEntity entity) throws Exception { 40 | return entity != null && entity.getImgs().size() > 0; 41 | } 42 | }) 43 | .doOnNext(new Consumer() { 44 | @Override 45 | public void accept(ImageEntity entity) throws Exception { 46 | mCache.put(key, entity); 47 | } 48 | }) 49 | .onErrorResumeNext(this.errorResumeNext()); 50 | } 51 | 52 | private String absKey(int index, int num) { 53 | StringBuilder sb = new StringBuilder(); 54 | sb.append("images"); 55 | sb.append(File.separator); 56 | sb.append(index); 57 | sb.append(File.separator); 58 | sb.append(num); 59 | return sb.toString(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/entity/mapper/DtoEntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.entity.mapper; 2 | 3 | /** 4 | * Created by borney on 3/27/17. 5 | */ 6 | 7 | public class DtoEntityMapper { 8 | private DtoEntityMapper() { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/entity/mapper/EntityDtoMapper.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.entity.mapper; 2 | 3 | /** 4 | * Created by borney on 2/14/17. 5 | */ 6 | 7 | public final class EntityDtoMapper { 8 | private EntityDtoMapper() { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/exception/NetworkConnectionException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.data.exception; 17 | 18 | /** 19 | * Exception throw by the application when a there is a network connection exception. 20 | */ 21 | public class NetworkConnectionException extends Exception { 22 | 23 | public NetworkConnectionException() { 24 | super(); 25 | } 26 | 27 | public NetworkConnectionException(final String message) { 28 | super(message); 29 | } 30 | 31 | public NetworkConnectionException(final String message, final Throwable cause) { 32 | super(message, cause); 33 | } 34 | 35 | public NetworkConnectionException(final Throwable cause) { 36 | super(cause); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.exception; 2 | 3 | /** 4 | * Created by borney on 2/23/17. 5 | */ 6 | 7 | public class NotFoundException extends RuntimeException { 8 | public NotFoundException(String msg) { 9 | super(msg); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/exception/NotFoundFromWebException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.data.exception; 17 | 18 | /** 19 | * Exception throw by the application when a User search can't return a valid result. 20 | */ 21 | public class NotFoundFromWebException extends RuntimeException { 22 | 23 | public NotFoundFromWebException() { 24 | super(); 25 | } 26 | 27 | public NotFoundFromWebException(final String message) { 28 | super(message); 29 | } 30 | 31 | public NotFoundFromWebException(final String message, final Throwable cause) { 32 | super(message, cause); 33 | } 34 | 35 | public NotFoundFromWebException(final Throwable cause) { 36 | super(cause); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/executor/JobExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.data.executor; 17 | 18 | 19 | import java.util.concurrent.BlockingQueue; 20 | import java.util.concurrent.LinkedBlockingQueue; 21 | import java.util.concurrent.ThreadFactory; 22 | import java.util.concurrent.ThreadPoolExecutor; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | import javax.inject.Inject; 26 | import javax.inject.Singleton; 27 | 28 | /** 29 | * Decorated {@link ThreadPoolExecutor} 30 | */ 31 | @Singleton 32 | public class JobExecutor implements ThreadExecutor { 33 | 34 | private static final int INITIAL_POOL_SIZE = 3; 35 | private static final int MAX_POOL_SIZE = 5; 36 | 37 | // Sets the amount of time an idle thread waits before terminating 38 | private static final int KEEP_ALIVE_TIME = 10; 39 | 40 | // Sets the Time Unit to seconds 41 | private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; 42 | 43 | private final BlockingQueue workQueue; 44 | 45 | private final ThreadPoolExecutor threadPoolExecutor; 46 | 47 | private final ThreadFactory threadFactory; 48 | 49 | @Inject 50 | JobExecutor() { 51 | this.workQueue = new LinkedBlockingQueue<>(); 52 | this.threadFactory = new JobThreadFactory(); 53 | this.threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE, 54 | KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, this.workQueue, this.threadFactory); 55 | } 56 | 57 | @Override 58 | public void execute(Runnable runnable) { 59 | if (runnable == null) { 60 | throw new IllegalArgumentException("Runnable to execute cannot be null"); 61 | } 62 | this.threadPoolExecutor.execute(runnable); 63 | } 64 | 65 | private static class JobThreadFactory implements ThreadFactory { 66 | private static final String THREAD_NAME = "android_"; 67 | private int counter = 0; 68 | 69 | @Override 70 | public Thread newThread(Runnable runnable) { 71 | return new Thread(runnable, THREAD_NAME + counter); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/executor/PostExecutionThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.data.executor; 17 | 18 | 19 | import io.reactivex.Scheduler; 20 | 21 | /** 22 | * Thread abstraction created to change the execution context from any thread to any other thread. 23 | * Useful to encapsulate a UI Thread for example, since some job will be done in background, an 24 | * implementation of this interface will change context and update the UI. 25 | */ 26 | public interface PostExecutionThread { 27 | Scheduler getScheduler(); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/executor/ThreadExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.data.executor; 17 | 18 | import java.util.concurrent.Executor; 19 | 20 | public interface ThreadExecutor extends Executor { 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/executor/UIThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.data.executor; 17 | 18 | 19 | import javax.inject.Inject; 20 | 21 | import io.reactivex.Scheduler; 22 | import io.reactivex.android.schedulers.AndroidSchedulers; 23 | 24 | ; 25 | 26 | /** 27 | * MainThread (UI Thread) implementation based on a {@link Scheduler} 28 | * which will execute actions on the Android UI thread 29 | */ 30 | public class UIThread implements PostExecutionThread { 31 | 32 | @Inject 33 | public UIThread() { 34 | } 35 | 36 | @Override 37 | public Scheduler getScheduler() { 38 | return AndroidSchedulers.mainThread(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/net/Api.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.net; 2 | 3 | /** 4 | * Created by borney on 2/15/17. 5 | */ 6 | 7 | 8 | public interface Api { 9 | String BASE_URL = "http://image.baidu.com/"; 10 | 11 | String IMAGE_URL = "data/imgs?col=美女&tag=小清新&sort=0&p=channel&from=1"; 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/net/ApiClient.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.net; 2 | 3 | import com.think.uiloader.data.entity.ImageEntity; 4 | 5 | import io.reactivex.Observable; 6 | import retrofit2.http.GET; 7 | import retrofit2.http.Query; 8 | 9 | /** 10 | * Created by borney on 2/14/17. 11 | */ 12 | 13 | public interface ApiClient { 14 | @GET(Api.IMAGE_URL) 15 | Observable images(@Query("pn")int fromIndex, @Query("rn") int returnNum); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/net/ApiConnection.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.net; 2 | 3 | import com.think.uiloader.data.entity.ImageEntity; 4 | 5 | import javax.inject.Inject; 6 | 7 | import io.reactivex.Observable; 8 | import retrofit2.Retrofit; 9 | 10 | /** 11 | * Created by borney on 2/15/17. 12 | */ 13 | 14 | public class ApiConnection implements ApiClient, Api { 15 | private ApiClient apiClient; 16 | 17 | @Inject 18 | ApiConnection(Retrofit retrofit) { 19 | apiClient = retrofit.create(ApiClient.class); 20 | } 21 | 22 | @Override 23 | public Observable images(int fromIndex, int returnNum) { 24 | return apiClient.images(fromIndex, returnNum); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/Config.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | import java.util.Properties; 4 | 5 | /** 6 | * Created by borney on 3/21/17. 7 | */ 8 | 9 | public interface Config { 10 | void setProperty(String key, String value); 11 | 12 | String getProperty(String key); 13 | 14 | void setProps(Properties properties); 15 | 16 | Properties getProps(); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/ConfigImpl.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.util.Properties; 12 | 13 | /** 14 | * Created by borney on 3/21/17. 15 | */ 16 | 17 | @SuppressLint("NewApi") 18 | class ConfigImpl implements Config { 19 | private static final String APP_CONFIG = "app_config"; 20 | private Context mContext; 21 | private File mConfigFile; 22 | 23 | ConfigImpl(Context context, String configFileName) { 24 | mContext = context; 25 | mConfigFile = new File(mContext.getDir(APP_CONFIG, Context.MODE_PRIVATE), configFileName); 26 | } 27 | 28 | @Override 29 | public void setProperty(String key, String value) { 30 | Properties props = getProps(); 31 | props.setProperty(key, value); 32 | setProps(props); 33 | } 34 | 35 | @Override 36 | public String getProperty(String key) { 37 | Properties props = getProps(); 38 | return props.getProperty(key); 39 | } 40 | 41 | @Override 42 | public void setProps(Properties properties) { 43 | 44 | try (FileOutputStream fos = new FileOutputStream(mConfigFile)) { 45 | properties.store(fos, null); 46 | fos.flush(); 47 | } catch (FileNotFoundException e) { 48 | e.printStackTrace(); 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | 54 | @Override 55 | public Properties getProps() { 56 | Properties properties = new Properties(); 57 | try (FileInputStream fis = new FileInputStream(mConfigFile)) { 58 | properties.load(fis); 59 | } catch (FileNotFoundException e) { 60 | e.printStackTrace(); 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | } 64 | return properties; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/Obj.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by borney on 3/21/17. 7 | */ 8 | 9 | public interface Obj { 10 | void set(T obj); 11 | 12 | T get(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/ObjImpl.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.io.InvalidClassException; 12 | import java.io.ObjectInputStream; 13 | import java.io.ObjectOutputStream; 14 | import java.io.Serializable; 15 | 16 | /** 17 | * Created by borney on 3/21/17. 18 | */ 19 | 20 | @SuppressLint("NewApi") 21 | class ObjImpl implements Obj { 22 | private static final String APP_OBJECT = "app_obj"; 23 | private Context mContext; 24 | private File mObjFile; 25 | 26 | ObjImpl(Context context, String objFileName) { 27 | mContext = context; 28 | mObjFile = new File(mContext.getDir(APP_OBJECT, Context.MODE_PRIVATE), objFileName); 29 | } 30 | 31 | @Override 32 | public void set(T obj) { 33 | try (FileOutputStream fos = new FileOutputStream(mObjFile); 34 | ObjectOutputStream oos = new ObjectOutputStream(fos)) { 35 | oos.writeObject(obj); 36 | oos.flush(); 37 | } catch (FileNotFoundException e) { 38 | e.printStackTrace(); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | @Override 45 | public T get() { 46 | try (FileInputStream fis = new FileInputStream(mObjFile); 47 | ObjectInputStream ois = new ObjectInputStream(fis)) { 48 | return (T) ois.readObject(); 49 | } catch (FileNotFoundException e) { 50 | e.printStackTrace(); 51 | } catch (IOException e) { 52 | e.printStackTrace(); 53 | } catch (Exception e) { 54 | if (e instanceof InvalidClassException) { 55 | mObjFile.delete(); 56 | } 57 | } 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/Preference.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | /** 4 | * Created by borney on 3/21/17. 5 | */ 6 | 7 | public interface Preference { 8 | void set(String key, int value); 9 | 10 | int get(String key, int def); 11 | 12 | void set(String key, float value); 13 | 14 | float get(String key, float def); 15 | 16 | void set(String key, long value); 17 | 18 | long get(String key, long def); 19 | 20 | void set(String key, boolean value); 21 | 22 | boolean get(String key, boolean def); 23 | 24 | void set(String key, String value); 25 | 26 | String get(String key, String def); 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/PreferenceImpl.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | /** 7 | * Created by borney on 3/21/17. 8 | */ 9 | 10 | class PreferenceImpl implements Preference { 11 | private SharedPreferences mPreferences; 12 | 13 | PreferenceImpl(Context context, String name) { 14 | mPreferences = context.getSharedPreferences(name, Context.MODE_PRIVATE); 15 | } 16 | 17 | @Override 18 | public void set(String key, int value) { 19 | SharedPreferences.Editor edit = mPreferences.edit(); 20 | edit.putInt(key, value); 21 | edit.apply(); 22 | } 23 | 24 | @Override 25 | public int get(String key, int def) { 26 | return mPreferences.getInt(key, def); 27 | } 28 | 29 | @Override 30 | public void set(String key, float value) { 31 | SharedPreferences.Editor edit = mPreferences.edit(); 32 | edit.putFloat(key, value); 33 | edit.apply(); 34 | } 35 | 36 | @Override 37 | public float get(String key, float def) { 38 | return mPreferences.getFloat(key, def); 39 | } 40 | 41 | @Override 42 | public void set(String key, long value) { 43 | SharedPreferences.Editor edit = mPreferences.edit(); 44 | edit.putLong(key, value); 45 | edit.apply(); 46 | } 47 | 48 | @Override 49 | public long get(String key, long def) { 50 | return mPreferences.getLong(key, def); 51 | } 52 | 53 | @Override 54 | public void set(String key, boolean value) { 55 | SharedPreferences.Editor edit = mPreferences.edit(); 56 | edit.putBoolean(key, value); 57 | edit.apply(); 58 | } 59 | 60 | @Override 61 | public boolean get(String key, boolean def) { 62 | return mPreferences.getBoolean(key, def); 63 | } 64 | 65 | @Override 66 | public void set(String key, String value) { 67 | SharedPreferences.Editor edit = mPreferences.edit(); 68 | edit.putString(key, value); 69 | edit.apply(); 70 | } 71 | 72 | @Override 73 | public String get(String key, String def) { 74 | return mPreferences.getString(key, def); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/Store.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by borney on 3/21/17. 7 | */ 8 | 9 | public interface Store extends Preference, Config { 10 | Preference getPreference(String name); 11 | 12 | Config getConfig(String name); 13 | 14 | Obj getObject(String name); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/data/store/internal/TStore.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.data.store.internal; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.Serializable; 6 | import java.util.Map; 7 | import java.util.Properties; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * Created by borney on 3/21/17. 12 | */ 13 | 14 | public class TStore implements Store { 15 | private static final String DEF_PREFERENCE_NAME = "def_preference"; 16 | private static final String DEF_CONFIG_NAME = "def_config"; 17 | private static final Map PREFERENCE_MAP = new ConcurrentHashMap<>(); 18 | private static final Map CONFIG_MAP = new ConcurrentHashMap<>(); 19 | private static final Map OBJECT_MAP = new ConcurrentHashMap<>(); 20 | private static Store STORE; 21 | 22 | private TStore(Context context) { 23 | mContext = context.getApplicationContext(); 24 | } 25 | 26 | public static Store get(Context context) { 27 | if (STORE == null) { 28 | synchronized (TStore.class) { 29 | if (STORE == null) { 30 | STORE = new TStore(context); 31 | } 32 | } 33 | } 34 | return STORE; 35 | } 36 | 37 | private Context mContext; 38 | 39 | @Override 40 | public void set(String key, int value) { 41 | getPreference(DEF_PREFERENCE_NAME).set(key, value); 42 | } 43 | 44 | @Override 45 | public int get(String key, int def) { 46 | return getPreference(DEF_PREFERENCE_NAME).get(key, def); 47 | } 48 | 49 | @Override 50 | public void set(String key, float value) { 51 | getPreference(DEF_PREFERENCE_NAME).set(key, value); 52 | } 53 | 54 | @Override 55 | public float get(String key, float def) { 56 | return getPreference(DEF_PREFERENCE_NAME).get(key, def); 57 | } 58 | 59 | @Override 60 | public void set(String key, long value) { 61 | getPreference(DEF_PREFERENCE_NAME).set(key, value); 62 | } 63 | 64 | @Override 65 | public long get(String key, long def) { 66 | return getPreference(DEF_PREFERENCE_NAME).get(key, def); 67 | } 68 | 69 | @Override 70 | public void set(String key, boolean value) { 71 | getPreference(DEF_PREFERENCE_NAME).set(key, value); 72 | } 73 | 74 | @Override 75 | public boolean get(String key, boolean def) { 76 | return getPreference(DEF_PREFERENCE_NAME).get(key, def); 77 | } 78 | 79 | @Override 80 | public void set(String key, String value) { 81 | getPreference(DEF_PREFERENCE_NAME).set(key, value); 82 | } 83 | 84 | @Override 85 | public String get(String key, String def) { 86 | return getPreference(DEF_PREFERENCE_NAME).get(key, def); 87 | } 88 | 89 | @Override 90 | public void setProperty(String key, String value) { 91 | getConfig(DEF_CONFIG_NAME).setProperty(key, value); 92 | } 93 | 94 | @Override 95 | public String getProperty(String key) { 96 | return getConfig(DEF_CONFIG_NAME).getProperty(key); 97 | } 98 | 99 | @Override 100 | public void setProps(Properties properties) { 101 | getConfig(DEF_CONFIG_NAME).setProps(properties); 102 | } 103 | 104 | @Override 105 | public Properties getProps() { 106 | return getConfig(DEF_CONFIG_NAME).getProps(); 107 | } 108 | 109 | @Override 110 | public Preference getPreference(String name) { 111 | Preference preference = PREFERENCE_MAP.get(name); 112 | if (preference == null) { 113 | preference = new PreferenceImpl(mContext, name); 114 | PREFERENCE_MAP.put(name, preference); 115 | } 116 | return preference; 117 | } 118 | 119 | @Override 120 | public Config getConfig(String name) { 121 | Config config = CONFIG_MAP.get(name); 122 | if (config == null) { 123 | config = new ConfigImpl(mContext, name); 124 | CONFIG_MAP.put(name, config); 125 | } 126 | return config; 127 | } 128 | 129 | @Override 130 | public Obj getObject(String name) { 131 | Obj obj = OBJECT_MAP.get(name); 132 | if (obj == null) { 133 | obj = new ObjImpl(mContext, name); 134 | OBJECT_MAP.put(name, obj); 135 | } 136 | return obj; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/domain/Case.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.domain; 2 | 3 | 4 | import com.think.uiloader.data.executor.PostExecutionThread; 5 | import com.think.uiloader.data.executor.ThreadExecutor; 6 | 7 | import dagger.internal.Preconditions; 8 | import io.reactivex.Observable; 9 | import io.reactivex.Scheduler; 10 | import io.reactivex.disposables.CompositeDisposable; 11 | import io.reactivex.disposables.Disposable; 12 | import io.reactivex.observers.DisposableObserver; 13 | import io.reactivex.schedulers.Schedulers; 14 | 15 | /** 16 | * Created by borney on 2/15/17. 17 | */ 18 | 19 | public class Case { 20 | 21 | private final ThreadExecutor threadExecutor; 22 | private final PostExecutionThread postExecutionThread; 23 | private final CompositeDisposable disposables; 24 | 25 | protected Case(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { 26 | this.threadExecutor = threadExecutor; 27 | this.postExecutionThread = postExecutionThread; 28 | this.disposables = new CompositeDisposable(); 29 | } 30 | 31 | /** 32 | * Executes the current use case. 33 | */ 34 | void execute(Observable observable, DisposableObserver observer) { 35 | Preconditions.checkNotNull(observer); 36 | Preconditions.checkNotNull(observable); 37 | Scheduler postExecutionThreadScheduler = postExecutionThread.getScheduler(); 38 | Scheduler scheduler = Schedulers.from(threadExecutor); 39 | DisposableObserver disposableObserver = observable 40 | .subscribeOn(scheduler) 41 | .observeOn(postExecutionThreadScheduler) 42 | .subscribeWith(observer); 43 | addDisposable(disposableObserver); 44 | } 45 | 46 | /** 47 | * Dispose from current {@link CompositeDisposable}. 48 | */ 49 | public void dispose() { 50 | if (!disposables.isDisposed()) { 51 | disposables.dispose(); 52 | } 53 | } 54 | 55 | /** 56 | * Dispose from current {@link CompositeDisposable}. 57 | */ 58 | private void addDisposable(Disposable disposable) { 59 | Preconditions.checkNotNull(disposable); 60 | Preconditions.checkNotNull(disposables); 61 | disposables.add(disposable); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/domain/DefaultObserver.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.domain; 17 | 18 | import io.reactivex.observers.DisposableObserver; 19 | 20 | /** 21 | * Default subscriber base class to be used whenever you want default error handling. 22 | */ 23 | public class DefaultObserver extends DisposableObserver { 24 | @Override 25 | public void onNext(T t) { 26 | // no-op by default. 27 | } 28 | 29 | @Override 30 | public void onComplete() { 31 | // no-op by default. 32 | } 33 | 34 | @Override 35 | public void onError(Throwable exception) { 36 | // no-op by default. 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/domain/ImageCase.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.domain; 2 | 3 | import com.think.uiloader.data.datasource.ImageStore; 4 | import com.think.uiloader.data.entity.ImageEntity; 5 | import com.think.uiloader.data.executor.PostExecutionThread; 6 | import com.think.uiloader.data.executor.ThreadExecutor; 7 | 8 | import javax.inject.Inject; 9 | 10 | import io.reactivex.observers.DisposableObserver; 11 | 12 | /** 13 | * Created by borney on 5/8/17. 14 | */ 15 | public class ImageCase extends Case { 16 | private ImageStore mImageStore; 17 | 18 | @Inject 19 | protected ImageCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread, ImageStore store) { 20 | super(threadExecutor, postExecutionThread); 21 | mImageStore = store; 22 | } 23 | 24 | public void images(int fromIndex, int returnNum, DisposableObserver observer) { 25 | execute(mImageStore.images(fromIndex, returnNum), observer); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/GlideImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.widget.ImageView; 6 | 7 | import com.bumptech.glide.Glide; 8 | import com.youth.banner.loader.ImageLoader; 9 | 10 | /** 11 | * Created by borney on 5/8/17. 12 | */ 13 | public class GlideImageLoader extends ImageLoader { 14 | @Override 15 | public void displayImage(Context context, Object path, ImageView imageView) { 16 | /** 17 | 注意: 18 | 1.图片加载器由自己选择,这里不限制,只是提供几种使用方法 19 | 2.返回的图片路径为Object类型,由于不能确定你到底使用的那种图片加载器, 20 | 传输的到的是什么格式,那么这种就使用Object接收和返回,你只需要强转成你传输的类型就行, 21 | 切记不要胡乱强转! 22 | */ 23 | 24 | //Glide 加载图片简单用法 25 | Glide.with(context).load(path).into(imageView); 26 | 27 | //用fresco加载图片简单用法,记得要写下面的createImageView方法 28 | Uri uri = Uri.parse((String) path); 29 | imageView.setImageURI(uri); 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/ItemDecorationVerticalDivider.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.View; 10 | 11 | /** 12 | * Created by borney on 8/16/16. 13 | */ 14 | public class ItemDecorationVerticalDivider extends RecyclerView.ItemDecoration { 15 | private Drawable mDivider; 16 | private final int[] ATTRS = new int[]{ 17 | android.R.attr.listDivider 18 | }; 19 | 20 | public ItemDecorationVerticalDivider(Context context) { 21 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 22 | mDivider = a.getDrawable(0); 23 | a.recycle(); 24 | } 25 | 26 | @Override 27 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 28 | //super.getItemOffsets(outRect, view, parent, state); 29 | 30 | // if (parent.getChildAdapterPosition(view) == 0) { 31 | // return; 32 | // } 33 | 34 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 35 | } 36 | 37 | @Override 38 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 39 | int dividerLeft = 0; 40 | int dividerRight = parent.getWidth(); 41 | 42 | final int childCount = parent.getChildCount(); 43 | for (int i = 0; i < childCount; i++) { 44 | final View child = parent.getChildAt(i); 45 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 46 | .getLayoutParams(); 47 | final int top = child.getBottom() + params.bottomMargin; 48 | final int bottom = top + mDivider.getIntrinsicHeight(); 49 | mDivider.setBounds(dividerLeft, top, dividerRight, bottom); 50 | mDivider.draw(c); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/LListViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.TLRLinearLayout; 13 | import com.think.tlr.TLRUIHandlerAdapter; 14 | import com.think.uiloader.App; 15 | import com.think.uiloader.R; 16 | import com.think.uiloader.data.entity.ImageEntity; 17 | import com.think.uiloader.ui.di.components.ActivityComponent; 18 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 19 | import com.think.uiloader.ui.mvp.contract.ImageContract; 20 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import javax.inject.Inject; 26 | 27 | /** 28 | * Created by borney on 5/11/17. 29 | */ 30 | public class LListViewActivity extends AppCompatActivity implements ImageContract.View { 31 | private ListView mListView; 32 | private TLRLinearLayout mTLRLinearLayout; 33 | private ListImageAdapter mAdapter; 34 | private List mImageList = new ArrayList<>(); 35 | private App mApp; 36 | private int curIndex = 0; 37 | private Handler mHandler = new Handler(); 38 | 39 | @Inject 40 | ImagePresenter mPresenter; 41 | 42 | @Override 43 | protected void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | mApp = (App) getApplication(); 46 | initActivityComponent(); 47 | setContentView(R.layout.activity_tlrlistview_load); 48 | mListView = (ListView) findViewById(R.id.content); 49 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 50 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 51 | 52 | @Override 53 | public void onLoadStatusChanged(View target, TLRLinearLayout.LoadStatus status) { 54 | if (status == TLRLinearLayout.LoadStatus.LOADING) { 55 | mPresenter.images(curIndex, 10); 56 | } 57 | } 58 | }); 59 | mAdapter = new ListImageAdapter(); 60 | mListView.setAdapter(mAdapter); 61 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 62 | @Override 63 | public void onItemClick(AdapterView parent, View view, int position, long id) { 64 | Toast.makeText(LListViewActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 65 | } 66 | }); 67 | } 68 | 69 | private void initActivityComponent() { 70 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 71 | mApp.getApplicationComponent()).build(); 72 | component.inject(this); 73 | mPresenter.setView(this); 74 | } 75 | 76 | @Override 77 | protected void onResume() { 78 | super.onResume(); 79 | } 80 | 81 | @Override 82 | public void startImages() { 83 | 84 | } 85 | 86 | @Override 87 | public void imagesSuccess(final List images) { 88 | mHandler.postDelayed(new Runnable() { 89 | @Override 90 | public void run() { 91 | mImageList.addAll(images); 92 | curIndex += images.size(); 93 | mAdapter.notifyImages(mImageList); 94 | mTLRLinearLayout.finishLoad(true); 95 | } 96 | }, 1500); 97 | } 98 | 99 | @Override 100 | public void endImages() { 101 | 102 | } 103 | 104 | @Override 105 | public void error(int errorCode) { 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/ListImageAdapter.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.BaseAdapter; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.bumptech.glide.Glide; 11 | import com.think.uiloader.R; 12 | import com.think.uiloader.data.entity.ImageEntity; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Created by borney on 5/8/17. 19 | */ 20 | public class ListImageAdapter extends BaseAdapter { 21 | private final List mList = new ArrayList<>(); 22 | 23 | public ListImageAdapter() { 24 | } 25 | 26 | public void notifyImages(List list) { 27 | mList.clear(); 28 | mList.addAll(list); 29 | notifyDataSetChanged(); 30 | } 31 | 32 | @Override 33 | public int getCount() { 34 | return mList != null ? mList.size() : 0; 35 | } 36 | 37 | @Override 38 | public ImageEntity.Image getItem(int position) { 39 | if (mList == null) { 40 | return null; 41 | } 42 | if (position < 0 || position >= getCount()) { 43 | return null; 44 | } 45 | return mList.get(position); 46 | } 47 | 48 | @Override 49 | public long getItemId(int position) { 50 | return position; 51 | } 52 | 53 | @Override 54 | public View getView(int position, View convertView, ViewGroup parent) { 55 | if (convertView == null) { 56 | convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false); 57 | } 58 | ImageEntity.Image item = getItem(position); 59 | ImageView imageView = (ImageView) convertView.findViewById(R.id.image); 60 | Glide.with(parent.getContext()).load(item.getThumbnailUrl()).into(imageView); 61 | TextView textView = (TextView) convertView.findViewById(R.id.text); 62 | textView.setText(item.getDesc()); 63 | return convertView; 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RAutoRefreshActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.TLRLinearLayout; 13 | import com.think.tlr.TLRUIHandlerAdapter; 14 | import com.think.uiloader.App; 15 | import com.think.uiloader.R; 16 | import com.think.uiloader.data.entity.ImageEntity; 17 | import com.think.uiloader.ui.di.components.ActivityComponent; 18 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 19 | import com.think.uiloader.ui.mvp.contract.ImageContract; 20 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import javax.inject.Inject; 26 | 27 | /** 28 | * Created by borney on 4/28/17. 29 | */ 30 | public class RAutoRefreshActivity extends AppCompatActivity implements ImageContract.View { 31 | private ListView mListView; 32 | private TLRLinearLayout mTLRLinearLayout; 33 | private ListImageAdapter mAdapter; 34 | private List mImageList = new ArrayList<>(); 35 | private App mApp; 36 | private int curIndex = 0; 37 | private Handler mHandler = new Handler(); 38 | 39 | @Inject 40 | ImagePresenter mPresenter; 41 | 42 | @Override 43 | protected void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | mApp = (App) getApplication(); 46 | initActivityComponent(); 47 | setContentView(R.layout.activity_tlrautorefresh); 48 | mListView = (ListView) findViewById(R.id.content); 49 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 50 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 51 | @Override 52 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 53 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 54 | mPresenter.images(curIndex, 10); 55 | } 56 | } 57 | }); 58 | mAdapter = new ListImageAdapter(); 59 | mListView.setAdapter(mAdapter); 60 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 61 | @Override 62 | public void onItemClick(AdapterView parent, View view, int position, long id) { 63 | Toast.makeText(RAutoRefreshActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 64 | } 65 | }); 66 | mTLRLinearLayout.autoRefresh(); 67 | } 68 | 69 | private void initActivityComponent() { 70 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 71 | mApp.getApplicationComponent()).build(); 72 | component.inject(this); 73 | mPresenter.setView(this); 74 | } 75 | 76 | @Override 77 | protected void onResume() { 78 | super.onResume(); 79 | } 80 | 81 | @Override 82 | public void startImages() { 83 | 84 | } 85 | 86 | @Override 87 | public void imagesSuccess(final List images) { 88 | if (images != null) { 89 | mHandler.postDelayed(new Runnable() { 90 | @Override 91 | public void run() { 92 | mImageList.addAll(0, images); 93 | curIndex += images.size(); 94 | mAdapter.notifyImages(mImageList); 95 | mTLRLinearLayout.finishRefresh(true); 96 | } 97 | }, 1500); 98 | } 99 | } 100 | 101 | @Override 102 | public void endImages() { 103 | 104 | } 105 | 106 | @Override 107 | public void error(int errorCode) { 108 | 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RCannotMoveHeadByTLRActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.AdapterView; 8 | import android.widget.ListView; 9 | import android.widget.Toast; 10 | 11 | import com.think.tlr.TLRLinearLayout; 12 | import com.think.tlr.TLRUIHandlerAdapter; 13 | import com.think.uiloader.App; 14 | import com.think.uiloader.R; 15 | import com.think.uiloader.data.entity.ImageEntity; 16 | import com.think.uiloader.ui.di.components.ActivityComponent; 17 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 18 | import com.think.uiloader.ui.mvp.contract.ImageContract; 19 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import javax.inject.Inject; 25 | 26 | /** 27 | * Created by borney on 4/28/17. 28 | */ 29 | public class RCannotMoveHeadByTLRActivity extends AppCompatActivity implements ImageContract.View { 30 | private ListView mListView; 31 | private TLRLinearLayout mTLRLinearLayout; 32 | private ListImageAdapter mAdapter; 33 | private List mImageList = new ArrayList<>(); 34 | private App mApp; 35 | private int curIndex = 0; 36 | 37 | @Inject 38 | ImagePresenter mPresenter; 39 | 40 | @Override 41 | protected void onCreate(@Nullable Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | mApp = (App) getApplication(); 44 | initActivityComponent(); 45 | setContentView(R.layout.activity_cannot_movehead_bytlr); 46 | mListView = (ListView) findViewById(R.id.content); 47 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 48 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 49 | @Override 50 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 51 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 52 | mPresenter.images(curIndex, 10); 53 | } 54 | } 55 | }); 56 | mAdapter = new ListImageAdapter(); 57 | mListView.setAdapter(mAdapter); 58 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 59 | @Override 60 | public void onItemClick(AdapterView parent, View view, int position, long id) { 61 | Toast.makeText(RCannotMoveHeadByTLRActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 62 | } 63 | }); 64 | } 65 | 66 | private void initActivityComponent() { 67 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 68 | mApp.getApplicationComponent()).build(); 69 | component.inject(this); 70 | mPresenter.setView(this); 71 | } 72 | 73 | @Override 74 | protected void onResume() { 75 | super.onResume(); 76 | } 77 | 78 | @Override 79 | public void startImages() { 80 | 81 | } 82 | 83 | @Override 84 | public void imagesSuccess(List images) { 85 | if (images != null) { 86 | mImageList.addAll(0, images); 87 | curIndex += images.size(); 88 | mAdapter.notifyImages(mImageList); 89 | mTLRLinearLayout.finishRefresh(true); 90 | } 91 | } 92 | 93 | @Override 94 | public void endImages() { 95 | 96 | } 97 | 98 | @Override 99 | public void error(int errorCode) { 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RKeepContentActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.graphics.Color; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.View; 10 | import android.view.ViewTreeObserver; 11 | import android.widget.AdapterView; 12 | import android.widget.ListView; 13 | import android.widget.Toast; 14 | 15 | import com.think.tlr.TLRLinearLayout; 16 | import com.think.tlr.TLRUIHandlerAdapter; 17 | import com.think.uiloader.App; 18 | import com.think.uiloader.R; 19 | import com.think.uiloader.data.entity.ImageEntity; 20 | import com.think.uiloader.ui.di.components.ActivityComponent; 21 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 22 | import com.think.uiloader.ui.mvp.contract.ImageContract; 23 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import javax.inject.Inject; 29 | 30 | /** 31 | * Created by borney on 4/28/17. 32 | */ 33 | public class RKeepContentActivity extends AppCompatActivity implements ImageContract.View { 34 | private ListView mListView; 35 | private TLRLinearLayout mTLRLinearLayout; 36 | private ListImageAdapter mAdapter; 37 | private List mImageList = new ArrayList<>(); 38 | private App mApp; 39 | private int curIndex = 0; 40 | private Handler mHandler = new Handler(); 41 | 42 | @Inject 43 | ImagePresenter mPresenter; 44 | 45 | @Override 46 | protected void onCreate(@Nullable Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | mApp = (App) getApplication(); 49 | initActivityComponent(); 50 | setContentView(R.layout.activity_tlrkeepcontent); 51 | mListView = (ListView) findViewById(R.id.content); 52 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 53 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 54 | @Override 55 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 56 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 57 | mPresenter.images(curIndex, 10); 58 | } 59 | } 60 | }); 61 | mTLRLinearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 62 | @Override 63 | public void onGlobalLayout() { 64 | mTLRLinearLayout.getHeaderView().setBackgroundColor(Color.WHITE); 65 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 66 | mTLRLinearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); 67 | } else { 68 | mTLRLinearLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); 69 | } 70 | } 71 | }); 72 | mAdapter = new ListImageAdapter(); 73 | mListView.setAdapter(mAdapter); 74 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 75 | @Override 76 | public void onItemClick(AdapterView parent, View view, int position, long id) { 77 | Toast.makeText(RKeepContentActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 78 | } 79 | }); 80 | } 81 | 82 | private void initActivityComponent() { 83 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 84 | mApp.getApplicationComponent()).build(); 85 | component.inject(this); 86 | mPresenter.setView(this); 87 | } 88 | 89 | @Override 90 | protected void onResume() { 91 | super.onResume(); 92 | } 93 | 94 | @Override 95 | public void startImages() { 96 | 97 | } 98 | 99 | @Override 100 | public void imagesSuccess(final List images) { 101 | if (images != null) { 102 | mHandler.postDelayed(new Runnable() { 103 | @Override 104 | public void run() { 105 | mImageList.addAll(0, images); 106 | curIndex += images.size(); 107 | mAdapter.notifyImages(mImageList); 108 | mTLRLinearLayout.finishRefresh(true); 109 | } 110 | }, 1500); 111 | } 112 | } 113 | 114 | @Override 115 | public void endImages() { 116 | 117 | } 118 | 119 | @Override 120 | public void error(int errorCode) { 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RLListViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.AdapterView; 10 | import android.widget.ListView; 11 | import android.widget.Toast; 12 | 13 | import com.think.tlr.TLRLinearLayout; 14 | import com.think.tlr.TLRUIHandlerAdapter; 15 | import com.think.uiloader.App; 16 | import com.think.uiloader.R; 17 | import com.think.uiloader.data.entity.ImageEntity; 18 | import com.think.uiloader.ui.di.components.ActivityComponent; 19 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 20 | import com.think.uiloader.ui.mvp.contract.ImageContract; 21 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | import javax.inject.Inject; 27 | 28 | /** 29 | * Created by borney on 4/28/17. 30 | */ 31 | public class RLListViewActivity extends AppCompatActivity implements ImageContract.View { 32 | private ListView mListView; 33 | private TLRLinearLayout mTLRLinearLayout; 34 | private ListImageAdapter mAdapter; 35 | private List mImageList = new ArrayList<>(); 36 | private App mApp; 37 | private int curIndex = 0; 38 | private Handler mHandler = new Handler(); 39 | private boolean isRefresh = true; 40 | 41 | @Inject 42 | ImagePresenter mPresenter; 43 | 44 | @Override 45 | protected void onCreate(@Nullable Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | mApp = (App) getApplication(); 48 | initActivityComponent(); 49 | setContentView(R.layout.activity_tlr_r_l_listview); 50 | mListView = (ListView) findViewById(R.id.content); 51 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 52 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 53 | @Override 54 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 55 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 56 | isRefresh = true; 57 | mPresenter.images(curIndex, 10); 58 | } 59 | } 60 | 61 | @Override 62 | public void onLoadStatusChanged(View target, TLRLinearLayout.LoadStatus status) { 63 | if (status == TLRLinearLayout.LoadStatus.LOADING) { 64 | isRefresh = false; 65 | mPresenter.images(curIndex, 10); 66 | } 67 | } 68 | }); 69 | mTLRLinearLayout.getHeaderView().setBackgroundColor(Color.WHITE); 70 | mAdapter = new ListImageAdapter(); 71 | mListView.setAdapter(mAdapter); 72 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 73 | @Override 74 | public void onItemClick(AdapterView parent, View view, int position, long id) { 75 | Toast.makeText(RLListViewActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 76 | } 77 | }); 78 | } 79 | 80 | private void initActivityComponent() { 81 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 82 | mApp.getApplicationComponent()).build(); 83 | component.inject(this); 84 | mPresenter.setView(this); 85 | } 86 | 87 | @Override 88 | protected void onResume() { 89 | super.onResume(); 90 | } 91 | 92 | @Override 93 | public void startImages() { 94 | 95 | } 96 | 97 | @Override 98 | public void imagesSuccess(final List images) { 99 | mHandler.postDelayed(new Runnable() { 100 | @Override 101 | public void run() { 102 | if (isRefresh) { 103 | mImageList.addAll(0, images); 104 | curIndex += images.size(); 105 | mAdapter.notifyImages(mImageList); 106 | mTLRLinearLayout.finishRefresh(true); 107 | } else { 108 | mImageList.addAll(images); 109 | curIndex += images.size(); 110 | mAdapter.notifyImages(mImageList); 111 | mTLRLinearLayout.finishLoad(true); 112 | } 113 | } 114 | }, 1500); 115 | } 116 | 117 | @Override 118 | public void endImages() { 119 | 120 | } 121 | 122 | @Override 123 | public void error(int errorCode) { 124 | 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RListViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.TLRLinearLayout; 13 | import com.think.tlr.TLRUIHandlerAdapter; 14 | import com.think.uiloader.App; 15 | import com.think.uiloader.R; 16 | import com.think.uiloader.data.entity.ImageEntity; 17 | import com.think.uiloader.ui.di.components.ActivityComponent; 18 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 19 | import com.think.uiloader.ui.mvp.contract.ImageContract; 20 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import javax.inject.Inject; 26 | 27 | /** 28 | * Created by borney on 4/28/17. 29 | */ 30 | public class RListViewActivity extends AppCompatActivity implements ImageContract.View { 31 | private ListView mListView; 32 | private TLRLinearLayout mTLRLinearLayout; 33 | private ListImageAdapter mAdapter; 34 | private List mImageList = new ArrayList<>(); 35 | private App mApp; 36 | private int curIndex = 0; 37 | private Handler mHandler = new Handler(); 38 | 39 | @Inject 40 | ImagePresenter mPresenter; 41 | 42 | @Override 43 | protected void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | mApp = (App) getApplication(); 46 | initActivityComponent(); 47 | setContentView(R.layout.activity_tlrlistview); 48 | mListView = (ListView) findViewById(R.id.content); 49 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 50 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 51 | @Override 52 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 53 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 54 | mPresenter.images(curIndex, 10); 55 | } 56 | } 57 | }); 58 | mAdapter = new ListImageAdapter(); 59 | mListView.setAdapter(mAdapter); 60 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 61 | @Override 62 | public void onItemClick(AdapterView parent, View view, int position, long id) { 63 | Toast.makeText(RListViewActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 64 | } 65 | }); 66 | } 67 | 68 | private void initActivityComponent() { 69 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 70 | mApp.getApplicationComponent()).build(); 71 | component.inject(this); 72 | mPresenter.setView(this); 73 | } 74 | 75 | @Override 76 | protected void onResume() { 77 | super.onResume(); 78 | } 79 | 80 | @Override 81 | public void startImages() { 82 | 83 | } 84 | 85 | @Override 86 | public void imagesSuccess(final List images) { 87 | mHandler.postDelayed(new Runnable() { 88 | @Override 89 | public void run() { 90 | mImageList.addAll(0, images); 91 | curIndex += images.size(); 92 | mAdapter.notifyImages(mImageList); 93 | mTLRLinearLayout.finishRefresh(true); 94 | } 95 | }, 1500); 96 | } 97 | 98 | @Override 99 | public void endImages() { 100 | 101 | } 102 | 103 | @Override 104 | public void error(int errorCode) { 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RMaterialHeadActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.MaterialHeaderView; 13 | import com.think.tlr.TLRLinearLayout; 14 | import com.think.tlr.TLRUIHandlerAdapter; 15 | import com.think.uiloader.App; 16 | import com.think.uiloader.R; 17 | import com.think.uiloader.data.entity.ImageEntity; 18 | import com.think.uiloader.ui.di.components.ActivityComponent; 19 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 20 | import com.think.uiloader.ui.mvp.contract.ImageContract; 21 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | import javax.inject.Inject; 27 | 28 | /** 29 | * Created by borney on 4/28/17. 30 | */ 31 | public class RMaterialHeadActivity extends AppCompatActivity implements ImageContract.View { 32 | private ListView mListView; 33 | private MaterialHeaderView mHeaderView; 34 | private TLRLinearLayout mTLRLinearLayout; 35 | private ListImageAdapter mAdapter; 36 | private List mImageList = new ArrayList<>(); 37 | private App mApp; 38 | private int curIndex = 0; 39 | private Handler mHandler = new Handler(); 40 | 41 | @Inject 42 | ImagePresenter mPresenter; 43 | 44 | @Override 45 | protected void onCreate(@Nullable Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | mApp = (App) getApplication(); 48 | initActivityComponent(); 49 | setContentView(R.layout.activity_tlrmaterialhead); 50 | mListView = (ListView) findViewById(R.id.content); 51 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 52 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 53 | @Override 54 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 55 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 56 | mPresenter.images(curIndex, 10); 57 | } 58 | } 59 | }); 60 | mHeaderView = (MaterialHeaderView) findViewById(R.id.head); 61 | mHeaderView.setTLRLinearLayout(mTLRLinearLayout); 62 | mAdapter = new ListImageAdapter(); 63 | mListView.setAdapter(mAdapter); 64 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 65 | @Override 66 | public void onItemClick(AdapterView parent, View view, int position, long id) { 67 | Toast.makeText(RMaterialHeadActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 68 | } 69 | }); 70 | } 71 | 72 | private void initActivityComponent() { 73 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 74 | mApp.getApplicationComponent()).build(); 75 | component.inject(this); 76 | mPresenter.setView(this); 77 | } 78 | 79 | @Override 80 | protected void onResume() { 81 | super.onResume(); 82 | } 83 | 84 | @Override 85 | public void startImages() { 86 | 87 | } 88 | 89 | @Override 90 | public void imagesSuccess(final List images) { 91 | mHandler.postDelayed(new Runnable() { 92 | @Override 93 | public void run() { 94 | mImageList.addAll(0, images); 95 | curIndex += images.size(); 96 | mAdapter.notifyImages(mImageList); 97 | mTLRLinearLayout.finishRefresh(true); 98 | } 99 | }, 1500); 100 | } 101 | 102 | @Override 103 | public void endImages() { 104 | 105 | } 106 | 107 | @Override 108 | public void error(int errorCode) { 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RMaterialHeadKeepContentActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.MaterialHeaderView; 13 | import com.think.tlr.TLRLinearLayout; 14 | import com.think.tlr.TLRUIHandlerAdapter; 15 | import com.think.uiloader.App; 16 | import com.think.uiloader.R; 17 | import com.think.uiloader.data.entity.ImageEntity; 18 | import com.think.uiloader.ui.di.components.ActivityComponent; 19 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 20 | import com.think.uiloader.ui.mvp.contract.ImageContract; 21 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | import javax.inject.Inject; 27 | 28 | /** 29 | * Created by borney on 4/28/17. 30 | */ 31 | public class RMaterialHeadKeepContentActivity extends AppCompatActivity implements ImageContract.View { 32 | private ListView mListView; 33 | private MaterialHeaderView mHeaderView; 34 | private TLRLinearLayout mTLRLinearLayout; 35 | private ListImageAdapter mAdapter; 36 | private List mImageList = new ArrayList<>(); 37 | private App mApp; 38 | private int curIndex = 0; 39 | private Handler mHandler = new Handler(); 40 | 41 | @Inject 42 | ImagePresenter mPresenter; 43 | 44 | @Override 45 | protected void onCreate(@Nullable Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | mApp = (App) getApplication(); 48 | initActivityComponent(); 49 | setContentView(R.layout.activity_tlrmaterialhead_keepcontent); 50 | mListView = (ListView) findViewById(R.id.content); 51 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 52 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 53 | @Override 54 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 55 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 56 | mPresenter.images(curIndex, 10); 57 | } 58 | } 59 | }); 60 | mHeaderView = (MaterialHeaderView) findViewById(R.id.head); 61 | mHeaderView.setTLRLinearLayout(mTLRLinearLayout); 62 | mAdapter = new ListImageAdapter(); 63 | mListView.setAdapter(mAdapter); 64 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 65 | @Override 66 | public void onItemClick(AdapterView parent, View view, int position, long id) { 67 | Toast.makeText(RMaterialHeadKeepContentActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 68 | } 69 | }); 70 | } 71 | 72 | private void initActivityComponent() { 73 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 74 | mApp.getApplicationComponent()).build(); 75 | component.inject(this); 76 | mPresenter.setView(this); 77 | } 78 | 79 | @Override 80 | protected void onResume() { 81 | super.onResume(); 82 | } 83 | 84 | @Override 85 | public void startImages() { 86 | 87 | } 88 | 89 | @Override 90 | public void imagesSuccess(final List images) { 91 | mHandler.postDelayed(new Runnable() { 92 | @Override 93 | public void run() { 94 | mImageList.addAll(0, images); 95 | curIndex += images.size(); 96 | mAdapter.notifyImages(mImageList); 97 | mTLRLinearLayout.finishRefresh(true); 98 | } 99 | }, 1500); 100 | } 101 | 102 | @Override 103 | public void endImages() { 104 | 105 | } 106 | 107 | @Override 108 | public void error(int errorCode) { 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RNotKeepHeadActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.TLRLinearLayout; 13 | import com.think.tlr.TLRUIHandlerAdapter; 14 | import com.think.uiloader.App; 15 | import com.think.uiloader.R; 16 | import com.think.uiloader.data.entity.ImageEntity; 17 | import com.think.uiloader.ui.di.components.ActivityComponent; 18 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 19 | import com.think.uiloader.ui.mvp.contract.ImageContract; 20 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import javax.inject.Inject; 26 | 27 | /** 28 | * Created by borney on 4/28/17. 29 | */ 30 | public class RNotKeepHeadActivity extends AppCompatActivity implements ImageContract.View { 31 | private ListView mListView; 32 | private TLRLinearLayout mTLRLinearLayout; 33 | private ListImageAdapter mAdapter; 34 | private List mImageList = new ArrayList<>(); 35 | private App mApp; 36 | private int curIndex = 0; 37 | private Handler mHandler = new Handler(); 38 | 39 | @Inject 40 | ImagePresenter mPresenter; 41 | 42 | @Override 43 | protected void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | mApp = (App) getApplication(); 46 | initActivityComponent(); 47 | setContentView(R.layout.activity_tlrnotkeephead); 48 | mListView = (ListView) findViewById(R.id.content); 49 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 50 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 51 | @Override 52 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 53 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 54 | mPresenter.images(curIndex, 10); 55 | } 56 | } 57 | }); 58 | mAdapter = new ListImageAdapter(); 59 | mListView.setAdapter(mAdapter); 60 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 61 | @Override 62 | public void onItemClick(AdapterView parent, View view, int position, long id) { 63 | Toast.makeText(RNotKeepHeadActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 64 | } 65 | }); 66 | } 67 | 68 | private void initActivityComponent() { 69 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 70 | mApp.getApplicationComponent()).build(); 71 | component.inject(this); 72 | mPresenter.setView(this); 73 | } 74 | 75 | @Override 76 | protected void onResume() { 77 | super.onResume(); 78 | } 79 | 80 | @Override 81 | public void startImages() { 82 | 83 | } 84 | 85 | @Override 86 | public void imagesSuccess(final List images) { 87 | if (images != null) { 88 | mImageList.addAll(0, images); 89 | curIndex += images.size(); 90 | mAdapter.notifyImages(mImageList); 91 | mTLRLinearLayout.finishRefresh(true); 92 | } 93 | } 94 | 95 | @Override 96 | public void endImages() { 97 | 98 | } 99 | 100 | @Override 101 | public void error(int errorCode) { 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RRefreshMaxMoveDistanceActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.TLRLinearLayout; 13 | import com.think.tlr.TLRUIHandlerAdapter; 14 | import com.think.uiloader.App; 15 | import com.think.uiloader.R; 16 | import com.think.uiloader.data.entity.ImageEntity; 17 | import com.think.uiloader.ui.di.components.ActivityComponent; 18 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 19 | import com.think.uiloader.ui.mvp.contract.ImageContract; 20 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import javax.inject.Inject; 26 | 27 | /** 28 | * Created by borney on 4/28/17. 29 | */ 30 | public class RRefreshMaxMoveDistanceActivity extends AppCompatActivity implements ImageContract.View { 31 | private ListView mListView; 32 | private TLRLinearLayout mTLRLinearLayout; 33 | private ListImageAdapter mAdapter; 34 | private List mImageList = new ArrayList<>(); 35 | private App mApp; 36 | private int curIndex = 0; 37 | private Handler mHandler = new Handler(); 38 | 39 | @Inject 40 | ImagePresenter mPresenter; 41 | 42 | @Override 43 | protected void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | mApp = (App) getApplication(); 46 | initActivityComponent(); 47 | setContentView(R.layout.activity_tlrrefresh_max_move_distance); 48 | mListView = (ListView) findViewById(R.id.content); 49 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 50 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 51 | @Override 52 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 53 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 54 | mPresenter.images(curIndex, 10); 55 | } 56 | } 57 | }); 58 | mAdapter = new ListImageAdapter(); 59 | mListView.setAdapter(mAdapter); 60 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 61 | @Override 62 | public void onItemClick(AdapterView parent, View view, int position, long id) { 63 | Toast.makeText(RRefreshMaxMoveDistanceActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 64 | } 65 | }); 66 | } 67 | 68 | private void initActivityComponent() { 69 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 70 | mApp.getApplicationComponent()).build(); 71 | component.inject(this); 72 | mPresenter.setView(this); 73 | } 74 | 75 | @Override 76 | protected void onResume() { 77 | super.onResume(); 78 | } 79 | 80 | @Override 81 | public void startImages() { 82 | 83 | } 84 | 85 | @Override 86 | public void imagesSuccess(final List images) { 87 | if (images != null) { 88 | mHandler.postDelayed(new Runnable() { 89 | @Override 90 | public void run() { 91 | mImageList.addAll(0, images); 92 | curIndex += images.size(); 93 | mAdapter.notifyImages(mImageList); 94 | mTLRLinearLayout.finishRefresh(true); 95 | } 96 | }, 1500); 97 | } 98 | } 99 | 100 | @Override 101 | public void endImages() { 102 | 103 | } 104 | 105 | @Override 106 | public void error(int errorCode) { 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RScrollViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import com.think.tlr.TLRLinearLayout; 11 | import com.think.tlr.TLRUIHandlerAdapter; 12 | import com.think.uiloader.R; 13 | 14 | /** 15 | * Created by borney on 5/11/17. 16 | */ 17 | public class RScrollViewActivity extends AppCompatActivity { 18 | private TLRLinearLayout mTLRLinearLayout; 19 | private TextView mTextView; 20 | private int refreshCount = 0; 21 | private Handler mHandler = new Handler(); 22 | 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_tlrscrollview); 27 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 28 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 29 | @Override 30 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 31 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 32 | refreshCount += 1; 33 | mTLRLinearLayout.postDelayed(new Runnable() { 34 | @Override 35 | public void run() { 36 | mTextView.setText("ScrollView中刷新了 " + refreshCount + " 次."); 37 | mTLRLinearLayout.finishRefresh(true); 38 | } 39 | }, 1500); 40 | } 41 | } 42 | }); 43 | mTextView = (TextView) findViewById(R.id.text); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RTextViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import com.think.tlr.TLRLinearLayout; 11 | import com.think.tlr.TLRUIHandlerAdapter; 12 | import com.think.uiloader.R; 13 | 14 | /** 15 | * Created by borney on 5/2/17. 16 | */ 17 | public class RTextViewActivity extends AppCompatActivity { 18 | private TLRLinearLayout mTLRLinearLayout; 19 | private TextView mTextView; 20 | private int refreshCount = 0; 21 | private Handler mHandler = new Handler(); 22 | 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_tlrtextview); 27 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 28 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 29 | @Override 30 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 31 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 32 | refreshCount += 1; 33 | mTLRLinearLayout.postDelayed(new Runnable() { 34 | @Override 35 | public void run() { 36 | mTextView.setText("刷新了 " + refreshCount + " 次."); 37 | mTLRLinearLayout.finishRefresh(true); 38 | } 39 | }, 1500); 40 | } 41 | } 42 | }); 43 | mTextView = (TextView) findViewById(R.id.text); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RViewGroupActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import com.think.tlr.TLRLinearLayout; 11 | import com.think.tlr.TLRUIHandlerAdapter; 12 | import com.think.uiloader.R; 13 | 14 | /** 15 | * Created by borney on 4/28/17. 16 | */ 17 | public class RViewGroupActivity extends AppCompatActivity { 18 | private TLRLinearLayout mTLRLinearLayout; 19 | private TextView mTextView; 20 | private int refreshCount = 0; 21 | private Handler mHandler = new Handler(); 22 | 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_tlrviewgroup); 27 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 28 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 29 | @Override 30 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 31 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 32 | refreshCount += 1; 33 | mTLRLinearLayout.postDelayed(new Runnable() { 34 | @Override 35 | public void run() { 36 | mTextView.setText("刷新了 " + refreshCount + " 次."); 37 | mTLRLinearLayout.finishRefresh(true); 38 | } 39 | }, 1500); 40 | } 41 | } 42 | }); 43 | mTextView = (TextView) findViewById(R.id.text); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/RWebViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.webkit.WebChromeClient; 9 | import android.webkit.WebResourceError; 10 | import android.webkit.WebResourceRequest; 11 | import android.webkit.WebView; 12 | import android.webkit.WebViewClient; 13 | import android.widget.ProgressBar; 14 | 15 | import com.think.tlr.TLRLinearLayout; 16 | import com.think.tlr.TLRUIHandlerAdapter; 17 | import com.think.uiloader.R; 18 | 19 | /** 20 | * Created by borney on 5/11/17. 21 | */ 22 | public class RWebViewActivity extends AppCompatActivity { 23 | private TLRLinearLayout mTLRLinearLayout; 24 | private ProgressBar mProgressBar; 25 | private WebView mWebView; 26 | private int refreshCount = 0; 27 | private Handler mHandler = new Handler(); 28 | 29 | @Override 30 | protected void onCreate(@Nullable Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_tlrwebview); 33 | mProgressBar = (ProgressBar) findViewById(R.id.progress); 34 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 35 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 36 | @Override 37 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 38 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 39 | if (refreshCount % 2 == 0) { 40 | mWebView.loadUrl("https://developers.google.cn/"); 41 | } else { 42 | mWebView.loadUrl("https://developer.android.google.cn/index.html"); 43 | } 44 | refreshCount++; 45 | } 46 | } 47 | }); 48 | mWebView = (WebView) findViewById(R.id.content); 49 | mWebView.setWebViewClient(new WebViewClient() { 50 | 51 | @Override 52 | public void onPageFinished(WebView view, String url) { 53 | super.onPageFinished(view, url); 54 | mHandler.postDelayed(new Runnable() { 55 | @Override 56 | public void run() { 57 | mTLRLinearLayout.finishRefresh(true); 58 | } 59 | }, 1500); 60 | } 61 | 62 | @Override 63 | public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { 64 | super.onReceivedError(view, request, error); 65 | mHandler.postDelayed(new Runnable() { 66 | @Override 67 | public void run() { 68 | mTLRLinearLayout.finishRefresh(false); 69 | } 70 | }, 1500); 71 | } 72 | }); 73 | 74 | mWebView.setWebChromeClient(new WebChromeClient() { 75 | @Override 76 | public void onProgressChanged(WebView view, int newProgress) { 77 | super.onProgressChanged(view, newProgress); 78 | if (newProgress == 0 || newProgress == 100) { 79 | mProgressBar.setVisibility(View.GONE); 80 | } else { 81 | mProgressBar.setVisibility(View.VISIBLE); 82 | } 83 | mProgressBar.setProgress(newProgress); 84 | } 85 | }); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/TLRMultiContentActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.RelativeLayout; 11 | import android.widget.TextView; 12 | import android.widget.Toast; 13 | 14 | import com.think.tlr.TLRLinearLayout; 15 | import com.think.tlr.TLRUIHandlerAdapter; 16 | import com.think.uiloader.App; 17 | import com.think.uiloader.R; 18 | import com.think.uiloader.data.entity.ImageEntity; 19 | import com.think.uiloader.ui.di.components.ActivityComponent; 20 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 21 | import com.think.uiloader.ui.mvp.contract.ImageContract; 22 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import javax.inject.Inject; 28 | 29 | /** 30 | * Created by borney on 5/11/17. 31 | */ 32 | public class TLRMultiContentActivity extends AppCompatActivity implements ImageContract.View { 33 | private ListView mListView; 34 | private TextView mTextView; 35 | private TLRLinearLayout mTLRLinearLayout; 36 | private ListImageAdapter mAdapter; 37 | private List mImageList = new ArrayList<>(); 38 | private App mApp; 39 | private int refreshCount = 0; 40 | private int curIndex = 0; 41 | private Handler mHandler = new Handler(); 42 | 43 | @Inject 44 | ImagePresenter mPresenter; 45 | 46 | @Override 47 | protected void onCreate(@Nullable Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | mApp = (App) getApplication(); 50 | initActivityComponent(); 51 | setContentView(R.layout.activity_tlrmulticontent); 52 | mListView = (ListView) findViewById(R.id.content); 53 | mTextView = (TextView) findViewById(R.id.text); 54 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 55 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 56 | @Override 57 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 58 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 59 | if (target instanceof RelativeLayout) { 60 | refreshCount += 1; 61 | mTLRLinearLayout.postDelayed(new Runnable() { 62 | @Override 63 | public void run() { 64 | mTextView.setText("刷新了 " + refreshCount + " 次."); 65 | mTLRLinearLayout.finishRefresh(true); 66 | } 67 | }, 1500); 68 | } else if (target instanceof ListView) { 69 | mPresenter.images(curIndex, 10); 70 | } 71 | } 72 | } 73 | }); 74 | mAdapter = new ListImageAdapter(); 75 | mListView.setAdapter(mAdapter); 76 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 77 | @Override 78 | public void onItemClick(AdapterView parent, View view, int position, long id) { 79 | Toast.makeText(TLRMultiContentActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 80 | } 81 | }); 82 | } 83 | 84 | private void initActivityComponent() { 85 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 86 | mApp.getApplicationComponent()).build(); 87 | component.inject(this); 88 | mPresenter.setView(this); 89 | } 90 | 91 | 92 | @Override 93 | public void startImages() { 94 | 95 | } 96 | 97 | @Override 98 | public void imagesSuccess(final List images) { 99 | mHandler.postDelayed(new Runnable() { 100 | @Override 101 | public void run() { 102 | mImageList.addAll(0, images); 103 | curIndex += images.size(); 104 | mAdapter.notifyImages(mImageList); 105 | mTLRLinearLayout.finishRefresh(true); 106 | } 107 | }, 1500); 108 | } 109 | 110 | @Override 111 | public void endImages() { 112 | 113 | } 114 | 115 | @Override 116 | public void error(int errorCode) { 117 | 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/TLRSunofOtherActivity.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ListView; 10 | import android.widget.Toast; 11 | 12 | import com.think.tlr.TLRLinearLayout; 13 | import com.think.tlr.TLRUIHandlerAdapter; 14 | import com.think.uiloader.App; 15 | import com.think.uiloader.R; 16 | import com.think.uiloader.data.entity.ImageEntity; 17 | import com.think.uiloader.ui.di.components.ActivityComponent; 18 | import com.think.uiloader.ui.di.components.DaggerActivityComponent; 19 | import com.think.uiloader.ui.mvp.contract.ImageContract; 20 | import com.think.uiloader.ui.mvp.presenter.ImagePresenter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import javax.inject.Inject; 26 | 27 | /** 28 | * Created by borney on 5/11/17. 29 | */ 30 | public class TLRSunofOtherActivity extends AppCompatActivity implements ImageContract.View { 31 | private ListView mListView; 32 | private TLRLinearLayout mTLRLinearLayout; 33 | private ListImageAdapter mAdapter; 34 | private List mImageList = new ArrayList<>(); 35 | private App mApp; 36 | private int curIndex = 0; 37 | private Handler mHandler = new Handler(); 38 | 39 | @Inject 40 | ImagePresenter mPresenter; 41 | 42 | @Override 43 | protected void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | mApp = (App) getApplication(); 46 | initActivityComponent(); 47 | setContentView(R.layout.activity_tlrsunofother); 48 | mListView = (ListView) findViewById(R.id.content); 49 | mTLRLinearLayout = (TLRLinearLayout) findViewById(R.id.tlrlayout); 50 | mTLRLinearLayout.addTLRUiHandler(new TLRUIHandlerAdapter() { 51 | @Override 52 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 53 | if (status == TLRLinearLayout.RefreshStatus.REFRESHING) { 54 | mPresenter.images(curIndex, 10); 55 | } 56 | } 57 | }); 58 | mAdapter = new ListImageAdapter(); 59 | mListView.setAdapter(mAdapter); 60 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 61 | @Override 62 | public void onItemClick(AdapterView parent, View view, int position, long id) { 63 | Toast.makeText(TLRSunofOtherActivity.this, "onclick " + position, Toast.LENGTH_SHORT).show(); 64 | } 65 | }); 66 | } 67 | 68 | private void initActivityComponent() { 69 | ActivityComponent component = DaggerActivityComponent.builder().applicationComponent( 70 | mApp.getApplicationComponent()).build(); 71 | component.inject(this); 72 | mPresenter.setView(this); 73 | } 74 | 75 | @Override 76 | protected void onResume() { 77 | super.onResume(); 78 | } 79 | 80 | @Override 81 | public void startImages() { 82 | 83 | } 84 | 85 | @Override 86 | public void imagesSuccess(final List images) { 87 | mHandler.postDelayed(new Runnable() { 88 | @Override 89 | public void run() { 90 | mImageList.addAll(0, images); 91 | curIndex += images.size(); 92 | mAdapter.notifyImages(mImageList); 93 | mTLRLinearLayout.finishRefresh(true); 94 | } 95 | }, 1500); 96 | } 97 | 98 | @Override 99 | public void endImages() { 100 | 101 | } 102 | 103 | @Override 104 | public void error(int errorCode) { 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/di/AScope.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.ui.di; 17 | 18 | import java.lang.annotation.Retention; 19 | 20 | import javax.inject.Scope; 21 | 22 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 23 | 24 | /** 25 | * A scoping annotation to permit objects whose lifetime should 26 | * conform to the life of the activity to be memorized in the 27 | * correct component. 28 | */ 29 | @Scope 30 | @Retention(RUNTIME) 31 | public @interface AScope {} 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/di/HasComponent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.ui.di; 17 | 18 | /** 19 | * Interface representing a contract for clients that contains a component for dependency injection. 20 | */ 21 | public interface HasComponent { 22 | C getComponent(); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/di/components/ActivityComponent.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui.di.components; 2 | 3 | 4 | import com.think.uiloader.ui.LListViewActivity; 5 | import com.think.uiloader.ui.RAutoRefreshActivity; 6 | import com.think.uiloader.ui.RCannotMoveHeadByTLRActivity; 7 | import com.think.uiloader.ui.RGridViewActivity; 8 | import com.think.uiloader.ui.RKeepContentActivity; 9 | import com.think.uiloader.ui.RLListViewActivity; 10 | import com.think.uiloader.ui.RListViewActivity; 11 | import com.think.uiloader.ui.RMaterialHeadActivity; 12 | import com.think.uiloader.ui.RMaterialHeadKeepContentActivity; 13 | import com.think.uiloader.ui.RNotKeepHeadActivity; 14 | import com.think.uiloader.ui.ROtherLibraryActivity; 15 | import com.think.uiloader.ui.RRecyclerViewActivity; 16 | import com.think.uiloader.ui.RRefreshMaxMoveDistanceActivity; 17 | import com.think.uiloader.ui.TLRMultiContentActivity; 18 | import com.think.uiloader.ui.TLRSunofOtherActivity; 19 | import com.think.uiloader.ui.di.AScope; 20 | 21 | import dagger.Component; 22 | 23 | /** 24 | * Created by borney on 3/13/17. 25 | */ 26 | 27 | @AScope 28 | @Component(dependencies = {ApplicationComponent.class}) 29 | public interface ActivityComponent { 30 | void inject(RListViewActivity activity); 31 | void inject(RRecyclerViewActivity activity); 32 | void inject(RNotKeepHeadActivity activity); 33 | void inject(RCannotMoveHeadByTLRActivity activity); 34 | void inject(RAutoRefreshActivity activity); 35 | void inject(RRefreshMaxMoveDistanceActivity activity); 36 | void inject(RGridViewActivity activity); 37 | void inject(ROtherLibraryActivity activity); 38 | void inject(TLRMultiContentActivity activity); 39 | void inject(TLRSunofOtherActivity activity); 40 | void inject(LListViewActivity activity); 41 | void inject(RLListViewActivity activity); 42 | void inject(RMaterialHeadActivity activity); 43 | void inject(RMaterialHeadKeepContentActivity activity); 44 | void inject(RKeepContentActivity activity); 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/di/components/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.think.uiloader.ui.di.components; 17 | 18 | import android.content.Context; 19 | 20 | import com.think.uiloader.data.executor.PostExecutionThread; 21 | import com.think.uiloader.data.executor.ThreadExecutor; 22 | import com.think.uiloader.ui.di.modules.ApplicationModule; 23 | 24 | import javax.inject.Singleton; 25 | 26 | import dagger.Component; 27 | import retrofit2.Retrofit; 28 | 29 | /** 30 | * A component whose lifetime is the life of the application. 31 | */ 32 | // Constraints this component to one-per-application or unscoped bindings. 33 | @Singleton 34 | @Component(modules = ApplicationModule.class) 35 | public interface ApplicationComponent { 36 | 37 | Context context(); 38 | 39 | ThreadExecutor threadExecutor(); 40 | 41 | PostExecutionThread postExecutionThread(); 42 | 43 | Retrofit retrofit(); 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/di/components/FragmentComponent.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui.di.components; 2 | 3 | 4 | import com.think.uiloader.ui.di.AScope; 5 | 6 | import dagger.Component; 7 | 8 | /** 9 | * Created by borney on 2/22/17. 10 | */ 11 | @AScope 12 | @Component(dependencies = ApplicationComponent.class) 13 | public interface FragmentComponent { 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/di/modules/ApplicationModule.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui.di.modules; 2 | 3 | 4 | import android.content.Context; 5 | 6 | import com.think.tlr.TLRLog; 7 | import com.think.uiloader.data.executor.JobExecutor; 8 | import com.think.uiloader.data.executor.PostExecutionThread; 9 | import com.think.uiloader.data.executor.ThreadExecutor; 10 | import com.think.uiloader.data.executor.UIThread; 11 | import com.think.uiloader.data.net.Api; 12 | 13 | import java.io.IOException; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | import javax.inject.Singleton; 17 | 18 | import dagger.Module; 19 | import dagger.Provides; 20 | import okhttp3.Interceptor; 21 | import okhttp3.OkHttpClient; 22 | import okhttp3.Request; 23 | import okhttp3.Response; 24 | import retrofit2.Retrofit; 25 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 26 | import retrofit2.converter.gson.GsonConverterFactory; 27 | 28 | ; 29 | 30 | /** 31 | * Created by borney on 2/22/17. 32 | */ 33 | @Module 34 | public class ApplicationModule { 35 | private static final String TAG = "ApplicationModule"; 36 | private final Context appContext; 37 | 38 | public ApplicationModule(Context context) { 39 | this.appContext = context.getApplicationContext(); 40 | } 41 | 42 | @Provides 43 | public Context provideContext() { 44 | return appContext; 45 | } 46 | 47 | @Provides 48 | public ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) { 49 | return jobExecutor; 50 | } 51 | 52 | @Provides 53 | public PostExecutionThread providePostExecutionThread(UIThread uiThread) { 54 | return uiThread; 55 | } 56 | 57 | @Provides 58 | @Singleton 59 | public OkHttpClient provideOkhttpClient() { 60 | OkHttpClient okHttpClient = new OkHttpClient.Builder() 61 | .connectTimeout(5, TimeUnit.SECONDS) 62 | .readTimeout(3, TimeUnit.SECONDS) 63 | .addInterceptor(new LoggingInterceptor()) 64 | .build(); 65 | return okHttpClient; 66 | } 67 | 68 | @Provides 69 | @Singleton 70 | public Retrofit provideRetrofit(OkHttpClient okHttpClient) { 71 | 72 | Retrofit retrofit = new Retrofit.Builder() 73 | .client(okHttpClient) 74 | .baseUrl(Api.BASE_URL) 75 | .addConverterFactory(GsonConverterFactory.create()) 76 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 77 | .build(); 78 | return retrofit; 79 | } 80 | 81 | class LoggingInterceptor implements Interceptor { 82 | @Override public Response intercept(Chain chain) throws IOException { 83 | Request request = chain.request(); 84 | 85 | long t1 = System.nanoTime(); 86 | TLRLog.i(TAG, String.format("Sending request %s on %s%n%s", 87 | request.url(), chain.connection(), request.headers())); 88 | 89 | Response response = chain.proceed(request); 90 | 91 | long t2 = System.nanoTime(); 92 | TLRLog.i(TAG, String.format("Received response for %s in %.1fms%n%s", 93 | response.request().url(), (t2 - t1) / 1e6d, response.headers())); 94 | 95 | return response; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/mvp/BasePresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.think.uiloader.ui.mvp; 18 | 19 | public interface BasePresenter { 20 | 21 | void setView(V view); 22 | 23 | /** 24 | * Method that control the lifecycle of the view. It should be called in the view's 25 | * (Activity or Fragment) onResume() method. 26 | */ 27 | void resume(); 28 | 29 | /** 30 | * Method that control the lifecycle of the view. It should be called in the view's 31 | * (Activity or Fragment) onPause() method. 32 | */ 33 | void pause(); 34 | 35 | /** 36 | * Method that control the lifecycle of the view. It should be called in the view's 37 | * (Activity or Fragment) onDestroy() method. 38 | */ 39 | void destroy(); 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/mvp/BaseView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.think.uiloader.ui.mvp; 18 | 19 | public interface BaseView { 20 | void error(int errorCode); 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/mvp/contract/ImageContract.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui.mvp.contract; 2 | 3 | import com.think.uiloader.data.entity.ImageEntity; 4 | import com.think.uiloader.ui.mvp.BasePresenter; 5 | import com.think.uiloader.ui.mvp.BaseView; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by borney on 5/8/17. 11 | */ 12 | public interface ImageContract { 13 | interface View extends BaseView { 14 | void startImages(); 15 | 16 | void imagesSuccess(List images); 17 | 18 | void endImages(); 19 | } 20 | 21 | interface Presenter extends BasePresenter { 22 | void images(int startIndex, int returnNum); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/think/uiloader/ui/mvp/presenter/ImagePresenter.java: -------------------------------------------------------------------------------- 1 | package com.think.uiloader.ui.mvp.presenter; 2 | 3 | import com.google.common.base.Optional; 4 | import com.think.tlr.TLRLog; 5 | import com.think.uiloader.data.entity.ImageEntity; 6 | import com.think.uiloader.domain.DefaultObserver; 7 | import com.think.uiloader.domain.ImageCase; 8 | import com.think.uiloader.ui.mvp.contract.ImageContract; 9 | 10 | import java.lang.ref.WeakReference; 11 | 12 | import javax.inject.Inject; 13 | 14 | /** 15 | * Created by borney on 5/8/17. 16 | */ 17 | public class ImagePresenter implements ImageContract.Presenter { 18 | private ImageCase mImageCase; 19 | private WeakReference mView; 20 | 21 | @Inject 22 | ImagePresenter(ImageCase imageCase) { 23 | mImageCase = imageCase; 24 | } 25 | 26 | 27 | @Override 28 | public void images(int startIndex, int returnNum) { 29 | if (getView().isPresent()) { 30 | getView().get().startImages(); 31 | } 32 | mImageCase.images(startIndex, returnNum, new DefaultObserver() { 33 | @Override 34 | public void onNext(ImageEntity entity) { 35 | TLRLog.v("-----------------------get images success!!!-------------------"); 36 | if (getView().isPresent()) { 37 | getView().get().imagesSuccess(entity.getImgs()); 38 | } 39 | } 40 | 41 | @Override 42 | public void onComplete() { 43 | if (getView().isPresent()) { 44 | getView().get().endImages(); 45 | } 46 | } 47 | 48 | @Override 49 | public void onError(Throwable exception) { 50 | if (getView().isPresent()) { 51 | getView().get().error(0); 52 | } 53 | } 54 | }); 55 | } 56 | 57 | @Override 58 | public void setView(ImageContract.View view) { 59 | mView = new WeakReference<>(view); 60 | } 61 | 62 | @Override 63 | public void resume() { 64 | 65 | } 66 | 67 | @Override 68 | public void pause() { 69 | 70 | } 71 | 72 | @Override 73 | public void destroy() { 74 | 75 | } 76 | 77 | private Optional getView() { 78 | return Optional.fromNullable(mView.get()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/demo_refresh_text_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/app/src/main/res/drawable-xxhdpi/demo_refresh_text_bg.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/demo_refresh_viewgroup_img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/app/src/main/res/drawable-xxhdpi/demo_refresh_viewgroup_img.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/list_top_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/app/src/main/res/drawable-xxhdpi/list_top_image.jpg -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_cannot_movehead_bytlr.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 13 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlr_r_l_listview.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrautorefresh.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrgridview.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrkeepcontent.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrlistview.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrlistview_load.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrmaterialhead.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrmaterialhead_keepcontent.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 19 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrmulticontent.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 25 | 26 | 37 | 38 | 39 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrnotkeephead.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrotherlibrary.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrrecyclerview.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrrefresh_max_move_distance.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrscrollview.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrsunofother.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrtextview.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrviewgroup.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 18 | 19 | 26 | 27 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrviewpager.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tlrwebview.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_pager_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/grid_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #00ae9d 4 | #00ae9d 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TLR 3 | 下拉刷新-ListView 4 | 下拉刷新-RecyclerView 5 | 下拉刷新-ViewGroup 6 | 下拉刷新-TextView 7 | 下拉刷新-WebView 8 | 下拉刷新-GridView 9 | 下拉刷新-ViewPager 10 | 下拉刷新-ScrollView 11 | 下拉刷新-三方库兼容(Banner) 12 | 刷新时不保持头部(HeadView) 13 | 刷新时不移动内容(Content)体 14 | 下拉刷新过程中不移动头部(HeadView) 15 | 自动刷新 16 | 刷新时,设置最大移动距离 17 | 多个刷新体 18 | TLR做为子控件被集成 19 | Tips:更多内容敬请期待... 20 | 上拉加载-ListView 21 | 上拉加载-下拉刷新-ListView 22 | 类似原生Material风格的Head 23 | 类似原生Material风格(内容体不移动) 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: 'buildsystem/dependencies.gradle' 3 | 4 | buildscript { 5 | repositories { 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:2.2.3' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | 14 | // the latest version of the android-apt plugin 15 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 16 | 17 | // Installing Realm as a Gradle plugin is a two step process. 18 | //classpath "io.realm:realm-gradle-plugin:2.3.1" 19 | classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1' 20 | } 21 | } 22 | 23 | allprojects { 24 | 25 | ext { 26 | androidApplicationId = "com.firm.books" 27 | androidVersionCode = 1 28 | androidVersionName = "1.0" 29 | testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" 30 | testApplicationId = 'com.firm.books.test' 31 | } 32 | 33 | repositories { 34 | jcenter() 35 | } 36 | } 37 | 38 | task clean(type: Delete) { 39 | delete rootProject.buildDir 40 | } 41 | -------------------------------------------------------------------------------- /buildsystem/dependencies.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | jcenter() 4 | } 5 | } 6 | 7 | ext { 8 | //Android 9 | androidBuildToolsVersion = "23.0.1" 10 | androidMinSdkVersion = 15 11 | androidTargetSdkVersion = 23 12 | androidCompileSdkVersion = 23 13 | 14 | //Testing 15 | jUnitVersion = '4.12' 16 | mockitoVersion = '1.9.5' 17 | robolectricVersion = '3.0' 18 | 19 | //http Libraries 20 | okHttpVersion = "3.6.0" 21 | loggingInterceptorVersion = "3.6.0" 22 | retrofitVersion = "2.1.0" 23 | 24 | //rxjava Libraries 25 | rxJavaVersion = "2.0.2" 26 | rxAndroidVersion = "2.0.1" 27 | adapterRxJavaVersion = "2.2.0" 28 | 29 | //gson Libraries 30 | gsonVersion = "2.8.0" 31 | converterGsonVersion = "2.1.0" 32 | 33 | //dagger Libraries 34 | daggerVersion = "2.6" 35 | daggerCompilerVersion = "2.6" 36 | 37 | //android support Libraries 38 | androidAnnotationsVersion = "23.0.1" 39 | androidAppcompatVersion = '23.0.1' 40 | designVersion = "23.0.1" 41 | recyclerviewVersion = "23.0.1" 42 | 43 | //other Libraries 44 | viewServerVersion = "1.0.3" 45 | butterknifeVersion = "8.5.1" 46 | butterknifeCompilerVersion = "8.5.1" 47 | glideVersion = "3.7.0" 48 | guavaVersion = "20.0" 49 | bannerVersion = "1.4.9" 50 | 51 | debugLeakcanaryVersion = "1.3.1" 52 | releaseLeakcanaryVersion = "1.3.1" 53 | 54 | appDependencies = [ 55 | okhttp : "com.squareup.okhttp3:okhttp:${okHttpVersion}", 56 | loggingInterceptor : "com.squareup.okhttp3:logging-interceptor:${loggingInterceptorVersion}", 57 | retrofit : "com.squareup.retrofit2:retrofit:${retrofitVersion}", 58 | rxJava : "io.reactivex.rxjava2:rxjava:${rxJavaVersion}", 59 | rxAndroid : "io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}", 60 | adapterRxJava : "com.squareup.retrofit2:adapter-rxjava2:${adapterRxJavaVersion}", 61 | gson : "com.google.code.gson:gson:${gsonVersion}", 62 | converterGson : "com.squareup.retrofit2:converter-gson:${converterGsonVersion}", 63 | dagger : "com.google.dagger:dagger:${daggerVersion}", 64 | daggerCompiler : "com.google.dagger:dagger-compiler:${daggerCompilerVersion}", 65 | androidAnnotations : "com.android.support:support-annotations:${androidAnnotationsVersion}", 66 | appcompat : "com.android.support:appcompat-v7:${androidAppcompatVersion}", 67 | design : "com.android.support:design:${designVersion}", 68 | recyclerview : "com.android.support:recyclerview-v7:${recyclerviewVersion}", 69 | viewServer : "com.hanhuy.android:viewserver:${viewServerVersion}", 70 | butterknife : "com.jakewharton:butterknife:${butterknifeVersion}", 71 | butterknifecompiler: "com.jakewharton:butterknife-compiler:${butterknifeCompilerVersion}", 72 | glide : "com.github.bumptech.glide:glide:${glideVersion}", 73 | guava : "com.google.guava:guava:${guavaVersion}", 74 | banner : "com.youth.banner:banner:${bannerVersion}" 75 | ] 76 | 77 | appTestDependencies = [ 78 | junit: "junit:junit:${jUnitVersion}", 79 | ] 80 | 81 | appDebugDependencies = [ 82 | leakcanaryDebug: "com.squareup.leakcanary:leakcanary-android:${debugLeakcanaryVersion}", 83 | ] 84 | 85 | appReleaseDependencies = [ 86 | leakcanaryRelease: "com.squareup.leakcanary:leakcanary-android-no-op:${releaseLeakcanaryVersion}", 87 | ] 88 | } -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ./gradlew app:assembleDebug && 3 | adb install -r -d app/build/outputs/apk/app-debug.apk && 4 | adb shell am start com.think.uiloader 5 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 18 15:49:28 HKT 2017 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-3.3-all.zip 7 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | lintOptions { 23 | abortOnError false 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.android.support:appcompat-v7:23.1.0' 33 | testCompile 'junit:junit:4.12' 34 | } 35 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/borney/data/opt/android-sdk-linux/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/think/tlr/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.think.tlr.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /library/src/main/java/com/think/tlr/TLRDefFootView.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.drawable.AnimationDrawable; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | import android.view.animation.LinearInterpolator; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | import android.widget.TextView; 12 | 13 | /** 14 | * @author borney 15 | * @date 4/28/17 16 | * @see TLRUIHandler 17 | */ 18 | public class TLRDefFootView extends LinearLayout implements TLRUIHandler { 19 | private boolean isMoveUp = false; 20 | private ImageView mImageView; 21 | private TextView mTextView; 22 | private ValueAnimator mReleaseAnimator; 23 | private AnimationDrawable mAnimationDrawable; 24 | 25 | public TLRDefFootView(Context context) { 26 | this(context, null); 27 | } 28 | 29 | public TLRDefFootView(Context context, AttributeSet attrs) { 30 | this(context, attrs, 0); 31 | } 32 | 33 | public TLRDefFootView(Context context, AttributeSet attrs, int defStyleAttr) { 34 | super(context, attrs, defStyleAttr); 35 | inflate(context, R.layout.tlr_def_foot_layout, this); 36 | setWillNotDraw(true); 37 | mImageView = (ImageView) findViewById(R.id.tlr_def_icon); 38 | mTextView = (TextView) findViewById(R.id.tlr_def_text); 39 | initReleaseAnimator(); 40 | } 41 | 42 | private void initReleaseAnimator() { 43 | mReleaseAnimator = ValueAnimator.ofFloat(0, 180); 44 | mReleaseAnimator.setDuration(210); 45 | mReleaseAnimator.setInterpolator(new LinearInterpolator()); 46 | mReleaseAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 47 | @Override 48 | public void onAnimationUpdate(ValueAnimator animation) { 49 | float value = (float) animation.getAnimatedValue(); 50 | mImageView.setRotation(value); 51 | } 52 | }); 53 | } 54 | 55 | public void setTextView(CharSequence sequence) { 56 | if (mTextView != null) { 57 | mTextView.setText(sequence); 58 | } 59 | } 60 | 61 | @Override 62 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 63 | 64 | } 65 | 66 | @Override 67 | public void onLoadStatusChanged(View target, TLRLinearLayout.LoadStatus status) { 68 | switch (status) { 69 | case PULL_UP: 70 | if (isMoveUp) { 71 | mTextView.setText(R.string.tlr_def_foot_pull_up); 72 | } 73 | break; 74 | case RELEASE_LOAD: 75 | mTextView.setText(R.string.tlr_def_foot_release_load); 76 | mReleaseAnimator.start(); 77 | break; 78 | case LOADING: 79 | if (mReleaseAnimator.isRunning()) { 80 | mReleaseAnimator.end(); 81 | } 82 | mImageView.setImageResource(R.drawable.tlr_def_refresh_loading); 83 | mAnimationDrawable = (AnimationDrawable) mImageView.getDrawable(); 84 | mAnimationDrawable.start(); 85 | mTextView.setText(R.string.tlr_def_foot_loading); 86 | break; 87 | case IDLE: 88 | break; 89 | } 90 | } 91 | 92 | @Override 93 | public void onOffsetChanged(View target, boolean isRefresh, int totalOffsetY, int totalThresholdY, int offsetY, float threshOffset) { 94 | isMoveUp = offsetY < 0; 95 | if (threshOffset == 0) { 96 | mImageView.setRotation(0); 97 | } 98 | } 99 | 100 | @Override 101 | public void onFinish(View target, boolean isRefresh, boolean isSuccess, int errorCode) { 102 | mTextView.setText(R.string.tlr_def_foot_load_complete); 103 | if (!isRefresh) { 104 | mAnimationDrawable.stop(); 105 | mImageView.setRotation(180); 106 | mImageView.setImageResource(R.drawable.tlr_def_load); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /library/src/main/java/com/think/tlr/TLRDefHeadView.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.drawable.AnimationDrawable; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | import android.view.animation.LinearInterpolator; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | import android.widget.TextView; 12 | 13 | /** 14 | * @author borney 15 | * @date 4/28/17 16 | * @see TLRUIHandler 17 | */ 18 | public class TLRDefHeadView extends LinearLayout implements TLRUIHandler { 19 | private boolean isMoveDown = false; 20 | private ImageView mImageView; 21 | private TextView mTextView; 22 | private ValueAnimator mReleaseAnimator; 23 | private AnimationDrawable mAnimationDrawable; 24 | 25 | public TLRDefHeadView(Context context) { 26 | this(context, null); 27 | } 28 | 29 | public TLRDefHeadView(Context context, AttributeSet attrs) { 30 | this(context, attrs, 0); 31 | } 32 | 33 | public TLRDefHeadView(Context context, AttributeSet attrs, int defStyleAttr) { 34 | super(context, attrs, defStyleAttr); 35 | inflate(context, R.layout.tlr_def_head_layout, this); 36 | setWillNotDraw(true); 37 | mImageView = (ImageView) findViewById(R.id.tlr_def_icon); 38 | mTextView = (TextView) findViewById(R.id.tlr_def_text); 39 | initReleaseAnimator(); 40 | } 41 | 42 | private void initReleaseAnimator() { 43 | mReleaseAnimator = ValueAnimator.ofFloat(0, 180); 44 | mReleaseAnimator.setDuration(210); 45 | mReleaseAnimator.setInterpolator(new LinearInterpolator()); 46 | mReleaseAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 47 | @Override 48 | public void onAnimationUpdate(ValueAnimator animation) { 49 | float value = (float) animation.getAnimatedValue(); 50 | mImageView.setRotation(value); 51 | } 52 | }); 53 | } 54 | 55 | @Override 56 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 57 | switch (status) { 58 | case PULL_DOWN: 59 | if (isMoveDown) { 60 | mTextView.setText(R.string.tlr_def_head_pull_down); 61 | } 62 | break; 63 | case RELEASE_REFRESH: 64 | mTextView.setText(R.string.tlr_def_head_release_refresh); 65 | mReleaseAnimator.start(); 66 | break; 67 | case REFRESHING: 68 | if (mReleaseAnimator.isRunning()) { 69 | mReleaseAnimator.end(); 70 | } 71 | mImageView.setImageResource(R.drawable.tlr_def_refresh_loading); 72 | mAnimationDrawable = (AnimationDrawable) mImageView.getDrawable(); 73 | mAnimationDrawable.start(); 74 | mTextView.setText(R.string.tlr_def_head_refreshing); 75 | break; 76 | case IDLE: 77 | break; 78 | } 79 | } 80 | 81 | public void setTextView(CharSequence sequence) { 82 | if (mTextView != null) { 83 | mTextView.setText(sequence); 84 | } 85 | } 86 | 87 | @Override 88 | public void onLoadStatusChanged(View target, TLRLinearLayout.LoadStatus status) { 89 | 90 | } 91 | 92 | @Override 93 | public void onOffsetChanged(View target, boolean isRefresh, int totalOffsetY, int totalThresholdY, int offsetY, float threshOffset) { 94 | isMoveDown = offsetY > 0; 95 | if (threshOffset == 0) { 96 | mImageView.setRotation(0); 97 | } 98 | } 99 | 100 | @Override 101 | public void onFinish(View target, boolean isRefresh, boolean isSuccess, int errorCode) { 102 | mTextView.setText(R.string.tlr_def_head_refresh_complete); 103 | if (isRefresh) { 104 | mAnimationDrawable.stop(); 105 | mImageView.setRotation(180); 106 | mImageView.setImageResource(R.drawable.tlr_def_refresh); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /library/src/main/java/com/think/tlr/TLRLog.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | /** 7 | * @author borney 8 | * @date 4/28/17 9 | * @see android.view.View 10 | * @see android.view.ViewGroup 11 | */ 12 | public class TLRLog { 13 | private static final String DEF_TAG = "TLRLoadRefresh"; 14 | 15 | public static void d(String msg) { 16 | d(DEF_TAG, msg); 17 | } 18 | 19 | public static void d(String tag, String msg) { 20 | android.util.Log.d(tag, msg); 21 | } 22 | 23 | public static void v(String msg) { 24 | v(DEF_TAG, msg); 25 | } 26 | 27 | public static void v(String tag, String msg) { 28 | android.util.Log.v(tag, msg); 29 | } 30 | 31 | public static void i(String msg) { 32 | i(DEF_TAG, msg); 33 | } 34 | 35 | public static void i(String tag, String msg) { 36 | android.util.Log.i(tag, msg); 37 | } 38 | 39 | public static void w(String msg) { 40 | w(DEF_TAG, msg); 41 | } 42 | 43 | public static void w(String tag, String msg) { 44 | android.util.Log.w(tag, msg); 45 | } 46 | 47 | public static void e(String msg) { 48 | e(DEF_TAG, msg); 49 | } 50 | 51 | public static void e(String tag, String msg) { 52 | android.util.Log.e(tag, msg); 53 | } 54 | 55 | public static void e(Throwable thx) { 56 | e(DEF_TAG, thx); 57 | } 58 | 59 | public static void e(String tag, Throwable thx) { 60 | StringWriter sw = new StringWriter(); 61 | PrintWriter pw = new PrintWriter(sw); 62 | thx.printStackTrace(pw); 63 | e(tag, sw.toString()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /library/src/main/java/com/think/tlr/TLRNestedLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.v4.view.NestedScrollingParent; 6 | import android.support.v4.view.NestedScrollingParentHelper; 7 | import android.support.v4.view.ViewCompat; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | 12 | /** 13 | * @author borney 14 | * @date 4/28/17 15 | * @see TLRLinearLayout 16 | */ 17 | public class TLRNestedLinearLayout extends TLRLinearLayout implements NestedScrollingParent { 18 | private TLRCalculator mCalculator; 19 | private NestedScrollingParentHelper mParentHelper; 20 | 21 | public TLRNestedLinearLayout(Context context) { 22 | this(context, null); 23 | } 24 | 25 | public TLRNestedLinearLayout(Context context, AttributeSet attrs) { 26 | this(context, attrs, 0); 27 | } 28 | 29 | public TLRNestedLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | mCalculator = calculator(); 32 | mParentHelper = new NestedScrollingParentHelper(this); 33 | ViewCompat.setNestedScrollingEnabled(this, true); 34 | } 35 | 36 | @Override 37 | public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 38 | if (!isEnableLoad() && !isEnableRefresh()) { 39 | return false; 40 | } 41 | if (mCalculator.hasAnyRunningThing()) { 42 | return false; 43 | } 44 | return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 45 | } 46 | 47 | @Override 48 | public void onNestedScrollAccepted(View child, View target, int axes) { 49 | mParentHelper.onNestedScrollAccepted(child, target, axes); 50 | } 51 | 52 | @Override 53 | public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, 54 | int dyUnconsumed) { 55 | setTouchView(target); 56 | if ((isEnableRefresh() && dyUnconsumed < 0) || (isEnableLoad() && dyUnconsumed > 0)) { 57 | mCalculator.touchMoveLayoutView((int) (-dyUnconsumed / getResistance())); 58 | } 59 | } 60 | 61 | @Override 62 | public void onStopNestedScroll(View child) { 63 | mParentHelper.onStopNestedScroll(child); 64 | mCalculator.eventUp(0, 0); 65 | } 66 | 67 | @Override 68 | public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { 69 | return super.onNestedPrePerformAccessibilityAction(target, action, args); 70 | } 71 | 72 | @Override 73 | public int getNestedScrollAxes() { 74 | return mParentHelper.getNestedScrollAxes(); 75 | } 76 | 77 | @Override 78 | public boolean dispatchTouchEvent(MotionEvent ev) { 79 | int action = ev.getAction(); 80 | if (action == MotionEvent.ACTION_DOWN) { 81 | return dispatchTouchEvent(false, ev); 82 | } 83 | if (action == MotionEvent.ACTION_MOVE && !mCalculator.isBackStatus()) { 84 | return dispatchTouchEvent(false, ev); 85 | } 86 | return dispatchTouchEvent(true, ev); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /library/src/main/java/com/think/tlr/TLRUIHandler.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * @author borney 7 | * @date 4/28/17 8 | */ 9 | public interface TLRUIHandler { 10 | /** 11 | * Callback refresh state to the specified target 12 | */ 13 | void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status); 14 | 15 | /** 16 | * Callback load state to the specified target 17 | */ 18 | void onLoadStatusChanged(View target, TLRLinearLayout.LoadStatus status); 19 | 20 | /** 21 | * Callback offset pix to the specified target 22 | * @param target 23 | * @param isRefresh 24 | * @param totalOffsetY 25 | * @param totalThresholdY 26 | * @param offsetY 27 | * @param threshOffset 28 | */ 29 | void onOffsetChanged(View target, boolean isRefresh, int totalOffsetY, int totalThresholdY, 30 | int offsetY, float threshOffset); 31 | 32 | /** 33 | * Callback finish state for refresh or load to the specified target 34 | */ 35 | void onFinish(View target, boolean isRefresh, boolean isSuccess, int errorCode); 36 | } 37 | -------------------------------------------------------------------------------- /library/src/main/java/com/think/tlr/TLRUIHandlerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * @author borney 7 | * @date 4/28/17 8 | */ 9 | public class TLRUIHandlerAdapter implements TLRUIHandler { 10 | @Override 11 | public void onRefreshStatusChanged(View target, TLRLinearLayout.RefreshStatus status) { 12 | 13 | } 14 | 15 | @Override 16 | public void onLoadStatusChanged(View target, TLRLinearLayout.LoadStatus status) { 17 | 18 | } 19 | 20 | @Override 21 | public void onOffsetChanged(View target, boolean isRefresh, int totalOffsetY, int totalThresholdY, int offsetY, float threshOffset) { 22 | 23 | } 24 | 25 | @Override 26 | public void onFinish(View target, boolean isRefresh, boolean isSuccess, int errorCode) { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /library/src/main/java/com/think/tlr/TLRUIHandlerHook.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | /** 4 | * Created by borney on 5/12/17. 5 | */ 6 | public interface TLRUIHandlerHook { 7 | void handlerHook(); 8 | } 9 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_load.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading01.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading02.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading03.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading04.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading05.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading06.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading07.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading08.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading09.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading10.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading11.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/library/src/main/res/drawable-xxhdpi/tlr_def_refresh_loading12.png -------------------------------------------------------------------------------- /library/src/main/res/drawable/tlr_def_refresh_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 11 | 14 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 38 | 41 | 42 | -------------------------------------------------------------------------------- /library/src/main/res/layout/tlr_def_foot_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 25 | 26 | -------------------------------------------------------------------------------- /library/src/main/res/layout/tlr_def_head_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 25 | 26 | -------------------------------------------------------------------------------- /library/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @color/red 5 | @color/blue 6 | @color/yellow 7 | @color/green 8 | 9 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFC93437 4 | #FF375BF1 5 | #FFF7D23E 6 | #FF34A350 7 | -------------------------------------------------------------------------------- /library/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 60dp 4 | 12sp 5 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Library 3 | 下拉刷新 4 | 释放开始刷新 5 | 刷新中... 6 | 刷新完成 7 | 8 | 上拉加载 9 | 释放开始加载 10 | 加载中... 11 | 加载完成 12 | 13 | -------------------------------------------------------------------------------- /library/src/test/java/com/think/tlr/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.think.tlr; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by Android Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # Location of the SDK. This is only used by Gradle. 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | #Tue Apr 18 15:49:29 HKT 2017 11 | ndk.dir=/home/borney/data/opt/android-sdk-linux/ndk-bundle 12 | sdk.dir=/home/borney/data/opt/android-sdk-linux 13 | -------------------------------------------------------------------------------- /png/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/png/1.gif -------------------------------------------------------------------------------- /png/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/png/2.gif -------------------------------------------------------------------------------- /png/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/png/3.gif -------------------------------------------------------------------------------- /png/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borneywpf/TLRLoadRefresh/80be68549fda6ec4bbbfe8f3d76fdeaa29bce13e/png/4.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library' 2 | --------------------------------------------------------------------------------