├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── app-release.apk ├── build.gradle ├── libs │ └── glide-3.6.1.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── liangmutian │ │ └── airrecyclerview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── liangmutian │ │ │ └── airrecyclerview │ │ │ ├── ActType.java │ │ │ ├── Adapter.java │ │ │ ├── ChangeScrollStateCallback.java │ │ │ ├── MainActivity.java │ │ │ └── swipetoloadlayout │ │ │ ├── BaseRecyclerAdapter.java │ │ │ ├── OnLoadMoreListener.java │ │ │ ├── OnRefreshListener.java │ │ │ ├── RefreshHeaderView.java │ │ │ ├── SuperRefreshRecyclerView.java │ │ │ ├── SwipeLoadMoreFooterLayout.java │ │ │ ├── SwipeLoadMoreTrigger.java │ │ │ ├── SwipeRefreshHeaderLayout.java │ │ │ ├── SwipeRefreshTrigger.java │ │ │ ├── SwipeToLoadLayout.java │ │ │ └── SwipeTrigger.java │ └── res │ │ ├── drawable │ │ ├── anim_refresh.xml │ │ └── bg_progressbar.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── item_choose_act_type.xml │ │ ├── item_choose_act_type2.xml │ │ ├── layout_footer_loading.xml │ │ ├── layout_refresh_header.xml │ │ ├── layout_super_recycler.xml │ │ └── layout_super_refresh_recycler.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ ├── bg_loading_circle.png │ │ ├── gpf.png │ │ ├── gpg.png │ │ ├── gph.png │ │ ├── gpi.png │ │ ├── gpj.png │ │ ├── gpk.png │ │ ├── gpl.png │ │ ├── gpm.png │ │ ├── gpn.png │ │ ├── gpo.png │ │ ├── gpq.png │ │ ├── gpr.png │ │ ├── ic_launcher.png │ │ ├── icon_refresh.png │ │ ├── n1.png │ │ ├── n2.png │ │ ├── n3.png │ │ ├── n4.png │ │ └── n5.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ ├── attrs.xml │ │ ├── dimens.xml │ │ └── ids.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── liangmutian │ └── airrecyclerview │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Abstraction issuesJava 46 | 47 | 48 | Android 49 | 50 | 51 | Android > Lint > Accessibility 52 | 53 | 54 | Android > Lint > Correctness 55 | 56 | 57 | Android > Lint > Correctness > Messages 58 | 59 | 60 | Android > Lint > Internationalization 61 | 62 | 63 | Android > Lint > Internationalization > Bidirectional Text 64 | 65 | 66 | Android > Lint > Performance 67 | 68 | 69 | Android > Lint > Security 70 | 71 | 72 | Android > Lint > Usability 73 | 74 | 75 | Android > Lint > Usability > Icons 76 | 77 | 78 | Android > Lint > Usability > Typography 79 | 80 | 81 | Assignment issuesJava 82 | 83 | 84 | Bitwise operation issuesJava 85 | 86 | 87 | C/C++ 88 | 89 | 90 | Class metricsJava 91 | 92 | 93 | Class structureJava 94 | 95 | 96 | Cloning issuesJava 97 | 98 | 99 | Code maturity issuesJava 100 | 101 | 102 | Code style issuesJava 103 | 104 | 105 | Compiler issuesJava 106 | 107 | 108 | Control FlowGroovy 109 | 110 | 111 | Control flow issuesJava 112 | 113 | 114 | Data flow issuesJava 115 | 116 | 117 | Dependency issuesJava 118 | 119 | 120 | Encapsulation issuesJava 121 | 122 | 123 | Error handlingJava 124 | 125 | 126 | Finalization issuesJava 127 | 128 | 129 | General 130 | 131 | 132 | GeneralC/C++ 133 | 134 | 135 | GeneralJava 136 | 137 | 138 | Google Cloud Endpoints 139 | 140 | 141 | Gradle 142 | 143 | 144 | Groovy 145 | 146 | 147 | HTML 148 | 149 | 150 | ImportsJava 151 | 152 | 153 | Inheritance issuesJava 154 | 155 | 156 | Initialization issuesJava 157 | 158 | 159 | Internationalization issues 160 | 161 | 162 | Internationalization issuesJava 163 | 164 | 165 | J2ME issuesJava 166 | 167 | 168 | JUnit issuesJava 169 | 170 | 171 | Java 172 | 173 | 174 | Java language level issuesJava 175 | 176 | 177 | Java language level migration aidsJava 178 | 179 | 180 | JavaBeans issuesJava 181 | 182 | 183 | Javadoc issuesJava 184 | 185 | 186 | Logging issuesJava 187 | 188 | 189 | Manifest 190 | 191 | 192 | Memory issuesJava 193 | 194 | 195 | Method MetricsGroovy 196 | 197 | 198 | Method metricsJava 199 | 200 | 201 | Modularization issuesJava 202 | 203 | 204 | Naming ConventionsGroovy 205 | 206 | 207 | Naming conventionsJava 208 | 209 | 210 | Numeric issuesJava 211 | 212 | 213 | Packaging issuesJava 214 | 215 | 216 | Pattern Validation 217 | 218 | 219 | Performance issuesJava 220 | 221 | 222 | Portability issuesJava 223 | 224 | 225 | Potentially confusing code constructsGroovy 226 | 227 | 228 | Probable bugsGradle 229 | 230 | 231 | Probable bugsGroovy 232 | 233 | 234 | Probable bugsJava 235 | 236 | 237 | Properties Files 238 | 239 | 240 | Properties FilesJava 241 | 242 | 243 | Security issuesJava 244 | 245 | 246 | Serialization issuesJava 247 | 248 | 249 | StyleGroovy 250 | 251 | 252 | TestNGJava 253 | 254 | 255 | Threading issuesGroovy 256 | 257 | 258 | Threading issuesJava 259 | 260 | 261 | Visibility issuesJava 262 | 263 | 264 | XML 265 | 266 | 267 | toString() issuesJava 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 289 | 290 | 291 | 292 | 293 | 294 | 299 | 300 | 301 | 302 | 303 | 304 | 1.8 305 | 306 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 322 | 323 | 324 | 325 | 326 | 327 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 让RecyclerView优先加载可见item,如何在RecyclerView滑动中停止图片的加载保证页面流畅。 3 | 4 | 先看效果图: 5 | ![这里写图片描述](http://img.blog.csdn.net/20161110135815675) 6 | ![这里写图片描述](http://img.blog.csdn.net/20161110135838081) 7 | 8 | 额。算了再流畅GIF也无法展示,想看效果去GITHUB下载代码吧。 9 | 本文Github代码链接 10 | https://github.com/AndroidMsky/GlideScrollRecyclerView 11 | 欢迎star哦。 12 | 13 | 14 | 对下拉刷新上滑加载RecyclerView还不明白的请看: 15 | http://blog.csdn.net/androidmsky/article/details/52922348 16 | 对多样式布局Gilde基本用法还不懂得请看: 17 | http://blog.csdn.net/androidmsky/article/details/52944370 18 | 两篇都是笔者的博文,相信入门搭建起可用框架还是有帮助的。 19 | 20 | 让我们来回顾一下陈旧的Listview,如果item中有网络图片,那时候我们怎么做的呢?我们要注意一下几个重点: 21 | 1.网络图片获取 22 | 2.网络图片二级缓存(至少一级缓存) 23 | 3.在高速滑动时候停止图片加载线程 24 | 4.在低速滑动,停止滑动时候开启线程 25 | 5.下拉刷新 26 | 6.上滑加载 27 | 7.优先加载可见item 28 | 29 | 如果不用第三方框架,自己维护线程池等等东西,想想都复杂。而如今我们不需要这些东西了,本文的Dome轻轻松松做到这7点。 30 | 首先1 2 glide给予我们完美的支持,5 6 我前文提到过的RecyclerView也有完美的支持。今天主要讲 3 4 7这三点如何实现。 31 | 32 | 首先看7.优先加载可见item,之前在listview我们可能这样做,写个滑动监听并且获取可见item的位置,然后加载对应图片,那么我们使用RecyclerView+Gilde到底如何做这一点呢,笔者一开始也有些犹豫,于是直接issues到了github : 33 | 34 | https://github.com/bumptech/glide/issues/1575#event-852660755 35 | 36 | ![这里写图片描述](http://img.blog.csdn.net/20161110141357542) 37 | 38 | 39 | 看来老外对于自己的亲儿子项目还是很关爱的生怕我不懂啪啪啪举了几个例子,我只好用CET3.9的水平翻译一下了。 40 | 我的意思是当glide和recyclerview配合使用的时候,glide会不会优先加载可见item。 41 | TWiStErRob大概说,所有的加载优先级都是默认的,但是可以通过设置priority来这是优先级,但是不是在特别的情况下这个方法一般不被使用,他举了个例子说,如果一个item中有3个图片,他们的信息有重叠的部分,其中有一张图是最重要,信息最全的时候,可以提高这张图的优先级。 42 | 然而TWiStErRob给我的答案似乎并没有理解我想问的(可能是我CET3.9)的原因,我想算啦吧,问别人不如自己实践出来的更真实。于是我准备出如下场景: 43 | 1.网速很渣(因为速度快瞬间加载完很多图难以测试) 44 | 2.打开一个游戏的下载(先把比较渣的网速给我占了!) 45 | 3.item比较多 46 | 4.当前可见item比较少 47 | 5.设置Glide跳过二级缓存 48 | 6.对Glide加载成功进行监听并且打印图片对应位置log 49 | 50 | 51 | ``` 52 | Glide.with(mContext).load(data.URl) 53 | .placeholder(R.mipmap.ic_launcher) 54 | .centerCrop() 55 | .skipMemoryCache(skipcache) 56 | .diskCacheStrategy(DiskCacheStrategy.NONE) 57 | .into(new GlideDrawableImageViewTarget(productHolder.imgRight) { 58 | @Override 59 | public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) { 60 | super.onResourceReady(drawable, anim); 61 | Log.d("-------------------+", "" + (position+1)); 62 | } 63 | ``` 64 | 65 | 首先进来缓慢滑动几次: 66 | 67 | ![这里写图片描述](http://img.blog.csdn.net/20161110143357771) 68 | 69 | 前四张图片被加载了符合预期。 70 | 71 | 72 | 接下来我们快速滑动到底部: 73 | 74 | ![这里写图片描述](http://img.blog.csdn.net/20161110143657469) 75 | 76 | 77 | 如我所料,优先加载了可见的底部的item。 78 | 79 | 到这里7问题就解决了。我们以正缺的姿势去使用RecyclerView+Gilde就可以了。 80 | 81 | 接下来是: 82 | 3.在高速滑动时候停止图片加载线程 83 | 4.在低速滑动,停止滑动时候开启线程 84 | RecyclerView是可以滑动监听的但是别高兴的太早: 85 | 86 | ``` 87 | SuperRefreshRecyclerView.一脸懵逼 88 | ``` 89 | 你点不出设置滑动监听方法,其实是这样的: 90 | 91 | ``` 92 | public class SuperRefreshRecyclerView extends FrameLayout { 93 | 94 | private RelativeLayout emptyView,errorView; 95 | private SwipeToLoadLayout swipeToLoadLayout; 96 | private SwipeLoadMoreFooterLayout swipeLoadMoreFooterLayout; 97 | private RecyclerView recyclerView; 98 | ``` 99 | SuperRefreshRecyclerView中有个原声的RecyclerView所以我们肯定不能直接.到RecyclerView的方法了。于是决定自己写个callback接口就好了: 100 | 101 | ``` 102 | public interface ChangeScrollStateCallback { 103 | 104 | public void change(int c); 105 | 106 | } 107 | ``` 108 | 然后提供set方法: 109 | 110 | ``` 111 | public void setChangeScrollStateCallback(ChangeScrollStateCallback mChangeScrollStateCallback){ 112 | this.mChangeScrollStateCallback=mChangeScrollStateCallback; 113 | 114 | } 115 | ``` 116 | 117 | 并在Activity中调用: 118 | 119 | ``` 120 | superRecyclerView.setChangeScrollStateCallback(new ChangeScrollStateCallback() { 121 | 122 | //0 表示停止滑动的状态 SCROLL_STATE_IDLE 123 | //1表示正在滚动,用户手指在屏幕上 SCROLL_STATE_TOUCH_SCROLL 124 | //2表示正在滑动。用户手指已经离开屏幕 SCROLL_STATE_FLING 125 | @Override 126 | public void change(int c) { 127 | 128 | switch (c) { 129 | case 2: 130 | Glide.with(MainActivity.this).pauseRequests(); 131 | Log.d("AAAAAAAAAAAAAAA", "暂停加载" + c); 132 | 133 | break; 134 | case 0: 135 | Glide.with(MainActivity.this).resumeRequests(); 136 | Log.d("AAAAAAAAAAAAAAA","恢复加载" + c); 137 | 138 | break; 139 | case 1: 140 | Glide.with(MainActivity.this).resumeRequests(); 141 | Log.d("AAAAAAAAAAAAAAA", "恢复加载" + c); 142 | 143 | break; 144 | 145 | 146 | } 147 | 148 | } 149 | }); 150 | ``` 151 | 152 | Glide提供的暂停恢复方法有没有很爽: 153 | 154 | ``` 155 | Glide.with(MainActivity.this).pauseRequests(); 156 | Glide.with(MainActivity.this).resumeRequests(); 157 | 158 | ``` 159 | 160 | 161 | 高速滑动一下屏幕: 162 | 163 | ![这里写图片描述](http://img.blog.csdn.net/20161110144657107) 164 | 165 | 166 | 缓慢滑动屏幕: 167 | 168 | ![这里写图片描述](http://img.blog.csdn.net/20161110144753311) 169 | 170 | 171 | 一切符合预期,1-7点我们就这样轻松的完成了。同事也给大家建议,在怀疑一个地方的时候不妨打断点打日志去看看到底是怎么回事,不断提高自己解决问题的能力,不要只会问而不会做。 172 | 173 | 欢迎关注作者。欢迎评论讨论。欢迎拍砖。 174 | 如果觉得这篇文章对你有帮助,欢迎打赏, 175 | 欢迎star,Fork我的github。 176 | 喜欢作者的也可以Follow。也算对作者的一种支持。 177 | 本文Github代码链接 178 | https://github.com/AndroidMsky/GlideScrollRecyclerView 179 | 180 | 181 | 182 | 欢迎加作者自营安卓开发交流群:308372687 183 | 184 | ![这里写图片描述](http://img.blog.csdn.net/20161028111556438) 185 | 186 | 187 | 博主原创未经允许不得转载,转载必究。 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/app-release.apk -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | defaultConfig { 7 | applicationId "com.example.liangmutian.airrecyclerview" 8 | minSdkVersion 15 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.1', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:23.1.1' 28 | compile 'com.android.support:recyclerview-v7:23.1.1' 29 | testCompile 'junit:junit:4.12' 30 | compile files('libs/glide-3.6.1.jar') 31 | } 32 | -------------------------------------------------------------------------------- /app/libs/glide-3.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/libs/glide-3.6.1.jar -------------------------------------------------------------------------------- /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 /Users/ex-liyongqiang001/Desktop/andriodstudio/adt-bundle-mac-x86_64-20140702/sdk/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/liangmutian/airrecyclerview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview; 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.example.liangmutian.airrecyclerview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/ActType.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview; 2 | 3 | 4 | import java.io.Serializable; 5 | import java.util.ArrayList; 6 | 7 | /** 8 | * Created by wuduogen838 on 16/10/8. 9 | */ 10 | 11 | public class ActType implements Serializable { 12 | public static final int SHOW_TYPE_1=1; 13 | public static final int SHOW_TYPE_2=2; 14 | public int showType; 15 | public String name; 16 | public String code; 17 | 18 | public String URl; 19 | public ArrayList actTypeList=new ArrayList<>(); 20 | public ActType(String code,int showType,String url){ 21 | this.showType=showType; 22 | this.URl=url; 23 | this.name= code; 24 | this.code=code; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/Adapter.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | 12 | 13 | import com.bumptech.glide.Glide; 14 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 15 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 16 | import com.bumptech.glide.request.animation.GlideAnimation; 17 | import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; 18 | import com.example.liangmutian.airrecyclerview.swipetoloadlayout.BaseRecyclerAdapter; 19 | 20 | import java.util.List; 21 | 22 | 23 | /** 24 | */ 25 | public class Adapter extends BaseRecyclerAdapter { 26 | 27 | private List list; 28 | private Context mContext; 29 | 30 | private boolean skipcache = true; 31 | 32 | 33 | public Adapter(List list, Context context) { 34 | super(list); 35 | this.mContext = context; 36 | this.list = list; 37 | } 38 | 39 | @Override 40 | public BaseRecyclerViewHolder createViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType) { 41 | BaseRecyclerViewHolder holder = null; 42 | 43 | 44 | switch (viewType) { 45 | case ActType.SHOW_TYPE_1: 46 | holder = new ProductHolder(inflater.inflate(R.layout.item_choose_act_type, parent, false)); 47 | break; 48 | case ActType.SHOW_TYPE_2: 49 | holder = new ProductHolder2(inflater.inflate(R.layout.item_choose_act_type2, parent, false)); 50 | break; 51 | 52 | } 53 | return holder; 54 | 55 | 56 | } 57 | 58 | @Override 59 | public int getItemViewType(int position) { 60 | return list.get(position).showType; 61 | } 62 | 63 | @Override 64 | public void onBindViewHolder(BaseRecyclerViewHolder holder, final int position, final ActType data) { 65 | 66 | 67 | switch (getItemViewType(position)) { 68 | case ActType.SHOW_TYPE_1: 69 | ProductHolder productHolder = (ProductHolder) holder; 70 | productHolder.tvName.setText(data.name); 71 | 72 | productHolder.imgRight.setOnClickListener(new View.OnClickListener() { 73 | @Override 74 | public void onClick(View view) { 75 | Toast.makeText(mContext, "" + position, Toast.LENGTH_LONG).show(); 76 | } 77 | }); 78 | 79 | Glide.with(mContext).load(data.URl) 80 | .placeholder(R.mipmap.ic_launcher) 81 | .centerCrop() 82 | .skipMemoryCache(skipcache) 83 | .diskCacheStrategy(DiskCacheStrategy.NONE) 84 | .into(new GlideDrawableImageViewTarget(productHolder.imgRight) { 85 | @Override 86 | public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) { 87 | super.onResourceReady(drawable, anim); 88 | Log.d("-------------------+", "" + (position+1)); 89 | } 90 | }); 91 | 92 | 93 | // Glide.with(mContext).load(data.URl) 94 | // .placeholder(R.mipmap.ic_launcher) 95 | // .centerCrop() 96 | // .skipMemoryCache(true) 97 | // .diskCacheStrategy(DiskCacheStrategy.NONE) 98 | // .into(); 99 | 100 | 101 | break; 102 | case ActType.SHOW_TYPE_2: 103 | 104 | 105 | ProductHolder2 productHolder2 = (ProductHolder2) holder; 106 | productHolder2.tvName.setText(data.name); 107 | 108 | productHolder2.imgRight.setOnClickListener(new View.OnClickListener() { 109 | @Override 110 | public void onClick(View view) { 111 | Toast.makeText(mContext, "w:::" + position, Toast.LENGTH_LONG).show(); 112 | } 113 | }); 114 | 115 | 116 | Glide.with(mContext).load(data.URl) 117 | .placeholder(R.mipmap.ic_launcher) 118 | .diskCacheStrategy(DiskCacheStrategy.NONE) 119 | .skipMemoryCache(skipcache) 120 | 121 | .centerCrop() 122 | 123 | .into(new GlideDrawableImageViewTarget(productHolder2.imgRight) { 124 | @Override 125 | public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) { 126 | super.onResourceReady(drawable, anim); 127 | Log.d("--------------------", "" + (position+1)); 128 | //progressBar.setVisibility(View.GONE); 129 | } 130 | }); 131 | break; 132 | 133 | } 134 | 135 | 136 | } 137 | 138 | class ProductHolder extends BaseRecyclerViewHolder { 139 | public TextView tvName; 140 | public ImageView imgRight; 141 | 142 | public ProductHolder(View itemView) { 143 | super(itemView); 144 | tvName = findView(R.id.tv_type_name); 145 | imgRight = findView(R.id.img_right); 146 | 147 | } 148 | } 149 | 150 | class ProductHolder2 extends BaseRecyclerViewHolder { 151 | public TextView tvName; 152 | public ImageView imgRight; 153 | 154 | public ProductHolder2(View itemView) { 155 | super(itemView); 156 | tvName = findView(R.id.tv_type_name); 157 | imgRight = findView(R.id.img_right); 158 | 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/ChangeScrollStateCallback.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview; 2 | 3 | /** 4 | * Created by wuduogen838 on 16/11/10. 5 | */ 6 | 7 | 8 | public interface ChangeScrollStateCallback { 9 | 10 | public void change(int c); 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview; 2 | 3 | import android.os.Handler; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.util.Log; 8 | 9 | import com.bumptech.glide.Glide; 10 | import com.example.liangmutian.airrecyclerview.swipetoloadlayout.ChangeScrollStateCallback; 11 | import com.example.liangmutian.airrecyclerview.swipetoloadlayout.OnLoadMoreListener; 12 | import com.example.liangmutian.airrecyclerview.swipetoloadlayout.OnRefreshListener; 13 | import com.example.liangmutian.airrecyclerview.swipetoloadlayout.SuperRefreshRecyclerView; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public class MainActivity extends AppCompatActivity 19 | implements OnRefreshListener, OnLoadMoreListener { 20 | 21 | private SuperRefreshRecyclerView superRecyclerView; 22 | private Adapter adapter; 23 | private List list = new ArrayList<>(); 24 | 25 | private String url1 = "http://img2.imgtn.bdimg.com/it/u=2313041955,732263015&fm=21&gp=0.jpg"; 26 | private String url2 = "http://p4.yokacdn.com/pic/cr/2013/0115/2026031677.jpg"; 27 | private String url3 = "http://img5.imgtn.bdimg.com/it/u=3931738195,194607292&fm=21&gp=0.jpg"; 28 | private String url4 = "http://img0.imgtn.bdimg.com/it/u=1174544731,79277711&fm=21&gp=0.jpg"; 29 | 30 | private String url11 = "http://a.hiphotos.baidu.com/zhidao/pic/item/4afbfbedab64034ff00f9069a9c379310b551d9a.jpg"; 31 | private String url12 = "http://www.286868.org.cn/imgall/mnsg453fmixgenlnfzrw63i/web/cmsphp/article/201505/8d19ff6db94a466c861f4adf760e3d4e.jpg"; 32 | private String url13 = "http://imgsrc.baidu.com/forum/w%3D580/sign=8f6610cf5e6034a829e2b889fb1249d9/9e0049afa40f4bfbedae079a014f78f0f6361887.jpg"; 33 | private String url14 = "http://www.1919111.net/imgall/on2gc5djmmxhoz3qmv2c4y3pnu/editor/attached/image/20141124/20141124225933_71813.jpg"; 34 | private String url15 = "http://img4.duitang.com/uploads/item/201407/16/20140716112423_BBvCR.thumb.700_0.jpeg"; 35 | private String url16 = "http://www.22777788.cc/imgall/nfwwomzomrxxkytbnyxgg33n/view/commodity_story/imedium/public/p7634330.jpg"; 36 | private String url17 = "http://cdn.duitang.com/uploads/item/201501/07/20150107194411_VQEAy.thumb.700_0.jpeg"; 37 | private String url18 = "http://static.wgpet.com/editor/attached/image/20141124/20141124230747_89589.jpeg"; 38 | private String url19 = "http://img4.duitang.com/uploads/item/201601/28/20160128211836_ivVs2.thumb.700_0.jpeg"; 39 | private String url110 = "http://cdnq.duitang.com/uploads/item/201412/11/20141211192322_EytUS.jpeg"; 40 | private String url111 = ""; 41 | private String url112= ""; 42 | private String url113 = ""; 43 | private String url114= ""; 44 | 45 | 46 | @Override 47 | protected void onCreate(Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | setContentView(R.layout.activity_main); 50 | 51 | 52 | superRecyclerView = (SuperRefreshRecyclerView) findViewById(R.id.super_recyclerview); 53 | superRecyclerView.init(new LinearLayoutManager(this), this, this); 54 | superRecyclerView.setRefreshEnabled(true); 55 | superRecyclerView.setLoadingMoreEnable(true); 56 | // superRecyclerView.addOnAttachStateChangeListener(); 57 | 58 | superRecyclerView.setChangeScrollStateCallback(new ChangeScrollStateCallback() { 59 | 60 | // 0 表示停止滑动的状态 SCROLL_STATE_IDLE 61 | // 62 | // 1表示正在滚动,用户手指在屏幕上 SCROLL_STATE_TOUCH_SCROLL 63 | // 64 | // 2表示正在滑动。用户手指已经离开屏幕 SCROLL_STATE_FLING 65 | @Override 66 | public void change(int c) { 67 | 68 | switch (c) { 69 | case 2: 70 | Glide.with(MainActivity.this).pauseRequests(); 71 | Log.d("AAAAAAAAAAAAAAA", "暂停加载" + c); 72 | 73 | break; 74 | case 0: 75 | Glide.with(MainActivity.this).resumeRequests(); 76 | Log.d("AAAAAAAAAAAAAAA","恢复加载" + c); 77 | 78 | break; 79 | case 1: 80 | Glide.with(MainActivity.this).resumeRequests(); 81 | Log.d("AAAAAAAAAAAAAAA", "恢复加载" + c); 82 | 83 | break; 84 | 85 | 86 | } 87 | 88 | } 89 | }); 90 | 91 | 92 | for (int i = 0; i < 5; i++) { 93 | ActType info1 = new ActType("01", 1, url11); 94 | ActType info2 = new ActType("02", 1, url12); 95 | ActType info3 = new ActType("03", 1, url13); 96 | ActType info4 = new ActType("04", 1, url14); 97 | ActType info5 = new ActType("05", 1, url15); 98 | ActType info6 = new ActType("06", 1, url16); 99 | ActType info7 = new ActType("07", 1, url17); 100 | 101 | 102 | list.add(info1); 103 | list.add(info2); 104 | list.add(info3); 105 | list.add(info4); 106 | list.add(info5); 107 | list.add(info6); 108 | list.add(info7); 109 | 110 | } 111 | adapter = new Adapter(list, this); 112 | superRecyclerView.setAdapter(adapter); 113 | superRecyclerView.showData(); 114 | // adapter.setOnItemClickListener(itemClickListener); 115 | 116 | 117 | } 118 | 119 | private void setData() { 120 | list.clear(); 121 | for (int i = 0; i < 3; i++) { 122 | ActType info5 = new ActType("01", 2, url11); 123 | ActType info3 = new ActType("04", 1, url4); 124 | ActType info2 = new ActType("02", 2, url2); 125 | list.add(info5); 126 | list.add(info3); 127 | list.add(info2); 128 | } 129 | 130 | } 131 | 132 | @Override 133 | public void onLoadMore() { 134 | 135 | 136 | ActType info2 = new ActType("04", 1, url4); 137 | ActType info3 = new ActType("02", 1, url2); 138 | ActType info4 = new ActType("01", 2, url11); 139 | 140 | 141 | list.add(info2); 142 | list.add(info3); 143 | 144 | list.add(info4); 145 | 146 | 147 | adapter.notifyDataSetChanged(); 148 | 149 | superRecyclerView.setLoadingMore(false); 150 | 151 | } 152 | 153 | @Override 154 | public void onRefresh() { 155 | 156 | new Handler().postDelayed(new Runnable() { 157 | 158 | public void run() { 159 | 160 | setData(); 161 | superRecyclerView.setRefreshing(false); 162 | 163 | } 164 | 165 | }, 3000); 166 | 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/BaseRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import java.util.List; 9 | 10 | 11 | public abstract class BaseRecyclerAdapter extends RecyclerView.Adapter { 12 | 13 | private List mDataList ; 14 | 15 | private OnItemClickListener mListener ; 16 | private OnItemLongClickListener mLongListener ; 17 | 18 | public BaseRecyclerAdapter(List mDataList) { 19 | this.mDataList = mDataList; 20 | } 21 | public List getDataList(){ 22 | return mDataList ; 23 | } 24 | @Override 25 | public T onCreateViewHolder(ViewGroup parent, int viewType) { 26 | T holder ; 27 | holder = createViewHolder(LayoutInflater.from(parent.getContext()),parent, viewType) ; 28 | if (holder == null){ 29 | holder = createViewHolder(LayoutInflater.from(parent.getContext()), viewType) ; 30 | } 31 | 32 | return holder; 33 | } 34 | 35 | 36 | @Override 37 | public void onBindViewHolder(final BaseRecyclerViewHolder holder, final int position) { 38 | 39 | holder.itemView.setOnClickListener(new View.OnClickListener() { 40 | @Override 41 | public void onClick(View v) { 42 | if (mListener != null){ 43 | mListener.onItemClick(v,holder.getPosition(),getItemId(holder.getPosition())); 44 | } 45 | } 46 | }); 47 | holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { 48 | @Override 49 | public boolean onLongClick(View v) { 50 | if (mLongListener != null){ 51 | return mLongListener.onItemLongClick(v,holder.getPosition(),getItemId(holder.getPosition())); 52 | } 53 | return false; 54 | } 55 | }); 56 | onBindViewHolder((T) holder, position, mDataList.get(position)) ; 57 | } 58 | 59 | public void setOnItemClickListener(OnItemClickListener listener){ 60 | mListener = listener ; 61 | } 62 | public void setOnItemLongClickListener(OnItemLongClickListener listener){ 63 | mLongListener = listener ; 64 | } 65 | 66 | @Override 67 | public int getItemCount() { 68 | return mDataList.size(); 69 | } 70 | 71 | static public class BaseRecyclerViewHolder extends RecyclerView.ViewHolder{ 72 | public BaseRecyclerViewHolder(View itemView) { 73 | super(itemView); 74 | } 75 | public K findView(int id){ 76 | return (K) itemView.findViewById(id); 77 | } 78 | } 79 | 80 | @Deprecated 81 | public T createViewHolder(LayoutInflater inflater,int viewType){ 82 | return null ; 83 | } 84 | 85 | public T createViewHolder(LayoutInflater inflater,ViewGroup parent, int viewType){ 86 | return null ; 87 | }; 88 | 89 | public abstract void onBindViewHolder(T holder, int position,D data); 90 | 91 | public interface OnItemClickListener{ 92 | void onItemClick(View view, int position, long id) ; 93 | 94 | } 95 | public interface OnItemLongClickListener{ 96 | boolean onItemLongClick(View view, int position, long id) ; 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/OnLoadMoreListener.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | /** 4 | * Created by Aspsine on 2015/8/13. 5 | */ 6 | public interface OnLoadMoreListener { 7 | public void onLoadMore(); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/OnRefreshListener.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | /** 4 | * Created by Aspsine on 2015/8/13. 5 | */ 6 | public interface OnRefreshListener { 7 | public void onRefresh(); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/RefreshHeaderView.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.AnimationDrawable; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.LayoutInflater; 8 | import android.widget.FrameLayout; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.example.liangmutian.airrecyclerview.R; 13 | 14 | 15 | /** 16 | * Created by ex-liyongqiang001 on 16/7/28. 17 | */ 18 | public class RefreshHeaderView extends FrameLayout implements SwipeRefreshTrigger,SwipeTrigger { 19 | 20 | private Context mcontext; 21 | private TextView tvTip; 22 | private ImageView iconRefresh; 23 | private AnimationDrawable animationDrawable; 24 | 25 | public RefreshHeaderView(Context context) { 26 | super(context); 27 | init(context); 28 | } 29 | 30 | public RefreshHeaderView(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | init(context); 33 | } 34 | 35 | public RefreshHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { 36 | super(context, attrs, defStyleAttr); 37 | init(context); 38 | } 39 | 40 | private void init(Context context){ 41 | LayoutInflater.from(context).inflate(R.layout.layout_refresh_header,this); 42 | tvTip=(TextView)findViewById(R.id.tv_tip); 43 | iconRefresh=(ImageView)findViewById(R.id.icon_refresh); 44 | } 45 | 46 | @Override 47 | public void onRefresh() { 48 | //下拉到一定位置松开之后,调用此方法 49 | tvTip.setText("正在刷新..."); 50 | iconRefresh.setRotationX(0); 51 | iconRefresh.setImageResource(R.drawable.anim_refresh); 52 | animationDrawable = (AnimationDrawable) iconRefresh.getDrawable(); 53 | animationDrawable.start(); 54 | Log.i("info", "onRefresh"); 55 | } 56 | 57 | @Override 58 | public void onPrepare() { 59 | //下拉之前调用此方法 60 | tvTip.setText("下拉刷新"); 61 | Log.i("info", "onPrepare"); 62 | } 63 | 64 | @Override 65 | public void onMove(int yScrolled, boolean isComplete, boolean automatic) { 66 | if (!isComplete) { 67 | //当前Y轴偏移量大于控件高度时,标识下拉到界限,显示“松开已刷新” 68 | if (yScrolled >= getHeight()) { 69 | iconRefresh.setRotationX(180); 70 | tvTip.setText("释放立即刷新"); 71 | tvTip.postInvalidate(); 72 | } else { 73 | //未达到偏移量 74 | tvTip.setText("下拉刷新"); 75 | iconRefresh.setRotationX(0); 76 | 77 | } 78 | } 79 | //Log.i("info","onMove"); 80 | } 81 | 82 | @Override 83 | public void onRelease() { 84 | //达到一定滑动距离,松开刷新时调用 85 | //setText("释放立即刷新"); 86 | Log.i("info","onRelease"); 87 | } 88 | 89 | @Override 90 | public void onComplete() { 91 | //加载完成之后调用此方法 92 | tvTip.setText("刷新完成"); 93 | animationDrawable.stop(); 94 | //iconRefresh.setImageResource(R.mipmap.icon_refresh); 95 | Log.i("info","onComplete"); 96 | } 97 | 98 | @Override 99 | public void onReset() { 100 | //重置 101 | tvTip.setText("下拉刷新"); 102 | iconRefresh.setImageResource(R.mipmap.icon_refresh); 103 | Log.i("info","onReset"); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/SuperRefreshRecyclerView.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.util.AttributeSet; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.widget.FrameLayout; 11 | import android.widget.RelativeLayout; 12 | 13 | import com.example.liangmutian.airrecyclerview.R; 14 | 15 | 16 | /** 17 | * Created by ex-liyongqiang001 on 16/6/30. 18 | */ 19 | public class SuperRefreshRecyclerView extends FrameLayout { 20 | 21 | private RelativeLayout emptyView,errorView; 22 | private SwipeToLoadLayout swipeToLoadLayout; 23 | private SwipeLoadMoreFooterLayout swipeLoadMoreFooterLayout; 24 | private RecyclerView recyclerView; 25 | private boolean move; 26 | private int mIndex; 27 | private RecyclerView.LayoutManager layoutManager; 28 | 29 | private Context mContext; 30 | 31 | 32 | ChangeScrollStateCallback mChangeScrollStateCallback; 33 | 34 | public SuperRefreshRecyclerView(Context context) { 35 | super(context); 36 | initView(context); 37 | } 38 | 39 | public SuperRefreshRecyclerView(Context context, AttributeSet attrs) { 40 | super(context, attrs); 41 | initView(context); 42 | } 43 | 44 | public SuperRefreshRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 45 | super(context, attrs, defStyleAttr); 46 | initView(context); 47 | } 48 | 49 | private void initView(Context context){ 50 | mContext=context; 51 | LayoutInflater.from(mContext).inflate(R.layout.layout_super_refresh_recycler,this); 52 | emptyView=(RelativeLayout)findViewById(R.id.layout_empty); 53 | errorView=(RelativeLayout)findViewById(R.id.layout_error); 54 | swipeToLoadLayout=(SwipeToLoadLayout)findViewById(R.id.swipe_to_load); 55 | swipeLoadMoreFooterLayout=(SwipeLoadMoreFooterLayout)findViewById(R.id.swipe_load_more_footer); 56 | recyclerView=(RecyclerView)findViewById(R.id.swipe_target); 57 | } 58 | 59 | 60 | public void init(RecyclerView.LayoutManager layoutManager,OnRefreshListener onRefreshListener,OnLoadMoreListener onLoadMoreListener){ 61 | recyclerView.setLayoutManager(layoutManager); 62 | this.layoutManager=layoutManager; 63 | swipeToLoadLayout.setOnRefreshListener(onRefreshListener); 64 | swipeToLoadLayout.setOnLoadMoreListener(onLoadMoreListener); 65 | recyclerView.setOnScrollListener(new RecyclerViewListener()); 66 | } 67 | 68 | public void showEmpty(OnClickListener onEmptyClick){ 69 | swipeToLoadLayout.setVisibility(GONE); 70 | emptyView.setVisibility(VISIBLE); 71 | errorView.setVisibility(GONE); 72 | if(onEmptyClick!=null){ 73 | emptyView.setOnClickListener(onEmptyClick); 74 | } 75 | } 76 | 77 | public void showError(OnClickListener onErrorClick){ 78 | swipeToLoadLayout.setVisibility(GONE); 79 | emptyView.setVisibility(GONE); 80 | errorView.setVisibility(VISIBLE); 81 | if(onErrorClick!=null){ 82 | errorView.setOnClickListener(onErrorClick); 83 | } 84 | } 85 | 86 | public void showData(){ 87 | swipeToLoadLayout.setVisibility(VISIBLE); 88 | emptyView.setVisibility(GONE); 89 | errorView.setVisibility(GONE); 90 | } 91 | 92 | public void setEmptyView(View view){ 93 | emptyView.removeAllViews(); 94 | emptyView.addView(view); 95 | } 96 | 97 | public boolean isRefreshing(){ 98 | return swipeToLoadLayout.isRefreshing(); 99 | } 100 | 101 | public void setRefreshing(boolean refreshing){ 102 | swipeToLoadLayout.setRefreshing(refreshing); 103 | } 104 | 105 | public boolean isLoadingMore(){ 106 | return swipeToLoadLayout.isLoadingMore(); 107 | } 108 | 109 | public void setLoadingMore(boolean loadMore){ 110 | swipeToLoadLayout.setLoadingMore(loadMore); 111 | } 112 | 113 | public void setLoadingMoreEnable(boolean enable){ 114 | swipeToLoadLayout.setLoadMoreEnabled(enable); 115 | } 116 | 117 | public void setRefreshEnabled(boolean enable){ 118 | swipeToLoadLayout.setRefreshEnabled(enable); 119 | } 120 | 121 | public void setAdapter(RecyclerView.Adapter adapter){ 122 | recyclerView.setAdapter(adapter); 123 | } 124 | 125 | public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration){ 126 | recyclerView.addItemDecoration(itemDecoration); 127 | } 128 | 129 | public void moveToPosition(int n) { 130 | mIndex=n; 131 | LinearLayoutManager mLinearLayoutManager=(LinearLayoutManager)layoutManager; 132 | int firstItem = mLinearLayoutManager.findFirstVisibleItemPosition(); 133 | int lastItem = mLinearLayoutManager.findLastVisibleItemPosition(); 134 | //然后区分情况 135 | if (n <= firstItem ){ 136 | //当要置顶的项在当前显示的第一个项的前面时 137 | recyclerView.scrollToPosition(n); 138 | }else if ( n <= lastItem ){ 139 | //当要置顶的项已经在屏幕上显示时 140 | int top = recyclerView.getChildAt(n - firstItem).getTop(); 141 | recyclerView.scrollBy(0, top); 142 | }else{ 143 | //当要置顶的项在当前显示的最后一项的后面时 144 | recyclerView.scrollToPosition(n); 145 | //这里这个变量是用在RecyclerView滚动监听里面的 146 | move = true; 147 | } 148 | 149 | } 150 | 151 | class RecyclerViewListener extends RecyclerView.OnScrollListener{ 152 | 153 | @Override 154 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 155 | super.onScrollStateChanged(recyclerView, newState); 156 | 157 | mChangeScrollStateCallback.change(newState); 158 | 159 | //Log.d("AAAAAAAAAAAAAAA", "" + newState); 160 | 161 | 162 | } 163 | 164 | @Override 165 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 166 | super.onScrolled(recyclerView, dx, dy); 167 | //在这里进行第二次滚动(最后的100米!) 168 | if (move ){ 169 | move = false; 170 | //获取要置顶的项在当前屏幕的位置,mIndex是记录的要置顶项在RecyclerView中的位置 171 | LinearLayoutManager mLinearLayoutManager=(LinearLayoutManager)layoutManager; 172 | int n = mIndex - mLinearLayoutManager.findFirstVisibleItemPosition(); 173 | if ( 0 <= n && n < recyclerView.getChildCount()){ 174 | //获取要置顶的项顶部离RecyclerView顶部的距离 175 | int top = recyclerView.getChildAt(n).getTop(); 176 | //最后的移动 177 | recyclerView.scrollBy(0, top); 178 | } 179 | } 180 | } 181 | } 182 | 183 | public void setChangeScrollStateCallback(ChangeScrollStateCallback mChangeScrollStateCallback){ 184 | this.mChangeScrollStateCallback=mChangeScrollStateCallback; 185 | 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/SwipeLoadMoreFooterLayout.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | import android.view.LayoutInflater; 7 | import android.widget.FrameLayout; 8 | 9 | import com.example.liangmutian.airrecyclerview.R; 10 | 11 | 12 | /** 13 | * Created by Aspsine on 2015/8/13. 14 | */ 15 | public class SwipeLoadMoreFooterLayout extends FrameLayout implements SwipeLoadMoreTrigger, SwipeTrigger { 16 | 17 | private Context mContext; 18 | 19 | public SwipeLoadMoreFooterLayout(Context context) { 20 | this(context, null); 21 | init(context); 22 | } 23 | 24 | public SwipeLoadMoreFooterLayout(Context context, AttributeSet attrs) { 25 | this(context, attrs, 0); 26 | init(context); 27 | } 28 | 29 | public SwipeLoadMoreFooterLayout(Context context, AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | init(context); 32 | } 33 | 34 | private void init(Context context){ 35 | mContext=context; 36 | LayoutInflater.from(mContext).inflate(R.layout.layout_footer_loading, this); 37 | } 38 | 39 | @Override 40 | public void onLoadMore() { 41 | //setVisibility(VISIBLE); 42 | Log.i("info", "onLoadMore"); 43 | } 44 | 45 | @Override 46 | public void onPrepare() { 47 | Log.i("info","onPrepare"); 48 | } 49 | 50 | @Override 51 | public void onMove(int y, boolean isComplete, boolean automatic) { 52 | //Log.i("info","onMove"); 53 | } 54 | 55 | @Override 56 | public void onRelease() { 57 | Log.i("info","onRelease"); 58 | } 59 | 60 | @Override 61 | public void onComplete() { 62 | //setVisibility(GONE); 63 | Log.i("info","onComplete"); 64 | } 65 | 66 | @Override 67 | public void onReset() { 68 | Log.i("info","onReset"); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/SwipeLoadMoreTrigger.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | /** 4 | * Created by Aspsine on 2015/8/17. 5 | */ 6 | public interface SwipeLoadMoreTrigger { 7 | void onLoadMore(); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/SwipeRefreshHeaderLayout.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.FrameLayout; 6 | 7 | /** 8 | * Created by Aspsine on 2015/8/13. 9 | */ 10 | public class SwipeRefreshHeaderLayout extends FrameLayout implements SwipeRefreshTrigger, SwipeTrigger { 11 | 12 | public SwipeRefreshHeaderLayout(Context context) { 13 | this(context, null); 14 | } 15 | 16 | public SwipeRefreshHeaderLayout(Context context, AttributeSet attrs) { 17 | this(context, attrs, 0); 18 | } 19 | 20 | public SwipeRefreshHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | } 23 | 24 | @Override 25 | public void onRefresh() { 26 | } 27 | 28 | @Override 29 | public void onPrepare() { 30 | } 31 | 32 | @Override 33 | public void onMove(int y, boolean isComplete, boolean automatic) { 34 | } 35 | 36 | @Override 37 | public void onRelease() { 38 | 39 | } 40 | 41 | @Override 42 | public void onComplete() { 43 | 44 | } 45 | 46 | @Override 47 | public void onReset() { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/SwipeRefreshTrigger.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | /** 4 | * Created by Aspsine on 2015/8/13. 5 | */ 6 | public interface SwipeRefreshTrigger { 7 | 8 | void onRefresh(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/SwipeToLoadLayout.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.v4.view.MotionEventCompat; 6 | import android.support.v4.view.ViewCompat; 7 | import android.util.AttributeSet; 8 | import android.util.Log; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.ViewConfiguration; 12 | import android.view.ViewGroup; 13 | import android.widget.AbsListView; 14 | import android.widget.Scroller; 15 | 16 | import com.example.liangmutian.airrecyclerview.R; 17 | 18 | 19 | /** 20 | * Created by Aspsine on 2015/8/13. 21 | */ 22 | public class SwipeToLoadLayout extends ViewGroup { 23 | 24 | private static final String TAG = SwipeToLoadLayout.class.getSimpleName(); 25 | 26 | private static final int DEFAULT_SWIPING_TO_REFRESH_TO_DEFAULT_SCROLLING_DURATION = 200; 27 | 28 | private static final int DEFAULT_RELEASE_TO_REFRESHING_SCROLLING_DURATION = 200; 29 | 30 | private static final int DEFAULT_REFRESH_COMPLETE_DELAY_DURATION = 300; 31 | 32 | private static final int DEFAULT_REFRESH_COMPLETE_TO_DEFAULT_SCROLLING_DURATION = 500; 33 | 34 | private static final int DEFAULT_DEFAULT_TO_REFRESHING_SCROLLING_DURATION = 500; 35 | 36 | private static final int DEFAULT_SWIPING_TO_LOAD_MORE_TO_DEFAULT_SCROLLING_DURATION = 200; 37 | 38 | private static final int DEFAULT_RELEASE_TO_LOADING_MORE_SCROLLING_DURATION = 200; 39 | 40 | private static final int DEFAULT_LOAD_MORE_COMPLETE_DELAY_DURATION = 300; 41 | 42 | private static final int DEFAULT_LOAD_MORE_COMPLETE_TO_DEFAULT_SCROLLING_DURATION = 300; 43 | 44 | private static final int DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION = 300; 45 | 46 | /** 47 | * how hard to drag 48 | */ 49 | private static final float DEFAULT_DRAG_RATIO = 0.5f; 50 | 51 | private static final int INVALID_POINTER = -1; 52 | 53 | private static final int INVALID_COORDINATE = -1; 54 | 55 | private AutoScroller mAutoScroller; 56 | 57 | private OnRefreshListener mRefreshListener; 58 | 59 | private OnLoadMoreListener mLoadMoreListener; 60 | 61 | private View mHeaderView; 62 | 63 | private View mTargetView; 64 | 65 | private View mFooterView; 66 | 67 | private int mHeaderHeight; 68 | 69 | private int mFooterHeight; 70 | 71 | private boolean mHasHeaderView; 72 | 73 | private boolean mHasFooterView; 74 | 75 | /** 76 | * indicate whether in debug mode 77 | */ 78 | private boolean mDebug; 79 | 80 | private float mDragRatio = DEFAULT_DRAG_RATIO; 81 | 82 | private boolean mAutoLoading; 83 | 84 | /** 85 | * the threshold of the touch event 86 | */ 87 | private final int mTouchSlop; 88 | 89 | /** 90 | * status of SwipeToLoadLayout 91 | */ 92 | private int mStatus = STATUS.STATUS_DEFAULT; 93 | 94 | /** 95 | * target view top offset 96 | */ 97 | private int mHeaderOffset; 98 | 99 | /** 100 | * target offset 101 | */ 102 | private int mTargetOffset; 103 | 104 | /** 105 | * target view bottom offset 106 | */ 107 | private int mFooterOffset; 108 | 109 | /** 110 | * init touch action down point.y 111 | */ 112 | private float mInitDownY; 113 | 114 | /** 115 | * init touch action down point.x 116 | */ 117 | private float mInitDownX; 118 | 119 | /** 120 | * last touch point.y 121 | */ 122 | private float mLastY; 123 | 124 | /** 125 | * last touch point.x 126 | */ 127 | private float mLastX; 128 | 129 | /** 130 | * action touch pointer's id 131 | */ 132 | private int mActivePointerId; 133 | 134 | /** 135 | * ATTRIBUTE: 136 | * a switcher indicate whither refresh function is enabled 137 | */ 138 | private boolean mRefreshEnabled = true; 139 | 140 | /** 141 | * ATTRIBUTE: 142 | * a switcher indicate whiter load more function is enabled 143 | */ 144 | private boolean mLoadMoreEnabled = true; 145 | 146 | /** 147 | * ATTRIBUTE: 148 | * the style default classic 149 | */ 150 | private int mStyle = STYLE.CLASSIC; 151 | 152 | /** 153 | * ATTRIBUTE: 154 | * offset to trigger refresh 155 | */ 156 | private float mRefreshTriggerOffset; 157 | 158 | /** 159 | * ATTRIBUTE: 160 | * offset to trigger load more 161 | */ 162 | private float mLoadMoreTriggerOffset; 163 | 164 | /** 165 | * ATTRIBUTE: 166 | * the max value of top offset 167 | */ 168 | private float mRefreshFinalDragOffset; 169 | 170 | /** 171 | * ATTRIBUTE: 172 | * the max value of bottom offset 173 | */ 174 | private float mLoadMoreFinalDragOffset; 175 | 176 | /** 177 | * ATTRIBUTE: 178 | * Scrolling duration swiping to refresh -> default 179 | */ 180 | private int mSwipingToRefreshToDefaultScrollingDuration = DEFAULT_SWIPING_TO_REFRESH_TO_DEFAULT_SCROLLING_DURATION; 181 | 182 | /** 183 | * ATTRIBUTE: 184 | * Scrolling duration status release to refresh -> refreshing 185 | */ 186 | private int mReleaseToRefreshToRefreshingScrollingDuration = DEFAULT_RELEASE_TO_REFRESHING_SCROLLING_DURATION; 187 | 188 | /** 189 | * ATTRIBUTE: 190 | * Refresh complete delay duration 191 | */ 192 | private int mRefreshCompleteDelayDuration = DEFAULT_REFRESH_COMPLETE_DELAY_DURATION; 193 | 194 | /** 195 | * ATTRIBUTE: 196 | * Scrolling duration status refresh complete -> default 197 | * {@link #setRefreshing(boolean)} false 198 | */ 199 | private int mRefreshCompleteToDefaultScrollingDuration = DEFAULT_REFRESH_COMPLETE_TO_DEFAULT_SCROLLING_DURATION; 200 | 201 | /** 202 | * ATTRIBUTE: 203 | * Scrolling duration status default -> refreshing, mainly for auto refresh 204 | * {@link #setRefreshing(boolean)} true 205 | */ 206 | private int mDefaultToRefreshingScrollingDuration = DEFAULT_DEFAULT_TO_REFRESHING_SCROLLING_DURATION; 207 | 208 | /** 209 | * ATTRIBUTE: 210 | * Scrolling duration status release to loading more -> loading more 211 | */ 212 | private int mReleaseToLoadMoreToLoadingMoreScrollingDuration = DEFAULT_RELEASE_TO_LOADING_MORE_SCROLLING_DURATION; 213 | 214 | 215 | /** 216 | * ATTRIBUTE: 217 | * Load more complete delay duration 218 | */ 219 | private int mLoadMoreCompleteDelayDuration = DEFAULT_LOAD_MORE_COMPLETE_DELAY_DURATION; 220 | 221 | /** 222 | * ATTRIBUTE: 223 | * Scrolling duration status load more complete -> default 224 | * {@link #setLoadingMore(boolean)} false 225 | */ 226 | private int mLoadMoreCompleteToDefaultScrollingDuration = DEFAULT_LOAD_MORE_COMPLETE_TO_DEFAULT_SCROLLING_DURATION; 227 | 228 | /** 229 | * ATTRIBUTE: 230 | * Scrolling duration swiping to load more -> default 231 | */ 232 | private int mSwipingToLoadMoreToDefaultScrollingDuration = DEFAULT_SWIPING_TO_LOAD_MORE_TO_DEFAULT_SCROLLING_DURATION; 233 | 234 | /** 235 | * ATTRIBUTE: 236 | * Scrolling duration status default -> loading more, mainly for auto load more 237 | * {@link #setLoadingMore(boolean)} true 238 | */ 239 | private int mDefaultToLoadingMoreScrollingDuration = DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION; 240 | 241 | /** 242 | * the style enum 243 | */ 244 | public static final class STYLE { 245 | public static final int CLASSIC = 0; 246 | public static final int ABOVE = 1; 247 | public static final int BLEW = 2; 248 | public static final int SCALE = 3; 249 | } 250 | 251 | public SwipeToLoadLayout(Context context) { 252 | this(context, null); 253 | } 254 | 255 | public SwipeToLoadLayout(Context context, AttributeSet attrs) { 256 | this(context, attrs, 0); 257 | } 258 | 259 | public SwipeToLoadLayout(Context context, AttributeSet attrs, int defStyleAttr) { 260 | super(context, attrs, defStyleAttr); 261 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeToLoadLayout, defStyleAttr, 0); 262 | try { 263 | final int N = a.getIndexCount(); 264 | for (int i = 0; i < N; i++) { 265 | int attr = a.getIndex(i); 266 | if (attr == R.styleable.SwipeToLoadLayout_refresh_enabled) { 267 | setRefreshEnabled(a.getBoolean(attr, true)); 268 | 269 | } else if (attr == R.styleable.SwipeToLoadLayout_load_more_enabled) { 270 | setLoadMoreEnabled(a.getBoolean(attr, true)); 271 | 272 | } else if (attr == R.styleable.SwipeToLoadLayout_swipe_style) { 273 | setSwipeStyle(a.getInt(attr, STYLE.CLASSIC)); 274 | 275 | } else if (attr == R.styleable.SwipeToLoadLayout_drag_ratio) { 276 | setDragRatio(a.getFloat(attr, DEFAULT_DRAG_RATIO)); 277 | 278 | } else if (attr == R.styleable.SwipeToLoadLayout_refresh_final_drag_offset) { 279 | setRefreshFinalDragOffset(a.getDimensionPixelOffset(attr, 0)); 280 | 281 | } else if (attr == R.styleable.SwipeToLoadLayout_load_more_final_drag_offset) { 282 | setLoadMoreFinalDragOffset(a.getDimensionPixelOffset(attr, 0)); 283 | 284 | } else if (attr == R.styleable.SwipeToLoadLayout_refresh_trigger_offset) { 285 | setRefreshTriggerOffset(a.getDimensionPixelOffset(attr, 0)); 286 | 287 | } else if (attr == R.styleable.SwipeToLoadLayout_load_more_trigger_offset) { 288 | setLoadMoreTriggerOffset(a.getDimensionPixelOffset(attr, 0)); 289 | 290 | } else if (attr == R.styleable.SwipeToLoadLayout_swiping_to_refresh_to_default_scrolling_duration) { 291 | setSwipingToRefreshToDefaultScrollingDuration(a.getInt(attr, DEFAULT_SWIPING_TO_REFRESH_TO_DEFAULT_SCROLLING_DURATION)); 292 | 293 | } else if (attr == R.styleable.SwipeToLoadLayout_release_to_refreshing_scrolling_duration) { 294 | setReleaseToRefreshingScrollingDuration(a.getInt(attr, DEFAULT_RELEASE_TO_REFRESHING_SCROLLING_DURATION)); 295 | 296 | } else if (attr == R.styleable.SwipeToLoadLayout_refresh_complete_delay_duration) { 297 | setRefreshCompleteDelayDuration(a.getInt(attr, DEFAULT_REFRESH_COMPLETE_DELAY_DURATION)); 298 | 299 | } else if (attr == R.styleable.SwipeToLoadLayout_refresh_complete_to_default_scrolling_duration) { 300 | setRefreshCompleteToDefaultScrollingDuration(a.getInt(attr, DEFAULT_REFRESH_COMPLETE_TO_DEFAULT_SCROLLING_DURATION)); 301 | 302 | } else if (attr == R.styleable.SwipeToLoadLayout_default_to_refreshing_scrolling_duration) { 303 | setDefaultToRefreshingScrollingDuration(a.getInt(attr, DEFAULT_DEFAULT_TO_REFRESHING_SCROLLING_DURATION)); 304 | 305 | } else if (attr == R.styleable.SwipeToLoadLayout_swiping_to_load_more_to_default_scrolling_duration) { 306 | setSwipingToLoadMoreToDefaultScrollingDuration(a.getInt(attr, DEFAULT_SWIPING_TO_LOAD_MORE_TO_DEFAULT_SCROLLING_DURATION)); 307 | 308 | } else if (attr == R.styleable.SwipeToLoadLayout_release_to_loading_more_scrolling_duration) { 309 | setReleaseToLoadingMoreScrollingDuration(a.getInt(attr, DEFAULT_RELEASE_TO_LOADING_MORE_SCROLLING_DURATION)); 310 | 311 | } else if (attr == R.styleable.SwipeToLoadLayout_load_more_complete_delay_duration) { 312 | setLoadMoreCompleteDelayDuration(a.getInt(attr, DEFAULT_LOAD_MORE_COMPLETE_DELAY_DURATION)); 313 | 314 | } else if (attr == R.styleable.SwipeToLoadLayout_load_more_complete_to_default_scrolling_duration) { 315 | setLoadMoreCompleteToDefaultScrollingDuration(a.getInt(attr, DEFAULT_LOAD_MORE_COMPLETE_TO_DEFAULT_SCROLLING_DURATION)); 316 | 317 | } else if (attr == R.styleable.SwipeToLoadLayout_default_to_loading_more_scrolling_duration) { 318 | setDefaultToLoadingMoreScrollingDuration(a.getInt(attr, DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION)); 319 | 320 | } 321 | } 322 | } finally { 323 | a.recycle(); 324 | } 325 | 326 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 327 | mAutoScroller = new AutoScroller(); 328 | } 329 | 330 | @Override 331 | protected void onFinishInflate() { 332 | super.onFinishInflate(); 333 | final int childNum = getChildCount(); 334 | if (childNum == 0) { 335 | // no child return 336 | return; 337 | } else if (0 < childNum && childNum < 4) { 338 | mHeaderView = findViewById(R.id.swipe_refresh_header); 339 | mTargetView = findViewById(R.id.swipe_target); 340 | mFooterView = findViewById(R.id.swipe_load_more_footer); 341 | } else { 342 | // more than three children: unsupported! 343 | throw new IllegalStateException("Children num must equal or less than 3"); 344 | } 345 | if (mTargetView == null) { 346 | return; 347 | } 348 | if (mHeaderView != null && mHeaderView instanceof SwipeTrigger) { 349 | mHeaderView.setVisibility(GONE); 350 | } 351 | if (mFooterView != null && mFooterView instanceof SwipeTrigger) { 352 | mFooterView.setVisibility(GONE); 353 | } 354 | } 355 | 356 | @Override 357 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 358 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 359 | // header 360 | if (mHeaderView != null) { 361 | final View headerView = mHeaderView; 362 | measureChildWithMargins(headerView, widthMeasureSpec, 0, heightMeasureSpec, 0); 363 | MarginLayoutParams lp = ((MarginLayoutParams) headerView.getLayoutParams()); 364 | mHeaderHeight = headerView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 365 | if (mRefreshTriggerOffset < mHeaderHeight) { 366 | mRefreshTriggerOffset = mHeaderHeight; 367 | } 368 | } 369 | // target 370 | if (mTargetView != null) { 371 | final View targetView = mTargetView; 372 | measureChildWithMargins(targetView, widthMeasureSpec, 0, heightMeasureSpec, 0); 373 | } 374 | // footer 375 | if (mFooterView != null) { 376 | final View footerView = mFooterView; 377 | measureChildWithMargins(footerView, widthMeasureSpec, 0, heightMeasureSpec, 0); 378 | MarginLayoutParams lp = ((MarginLayoutParams) footerView.getLayoutParams()); 379 | mFooterHeight = footerView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 380 | if (mLoadMoreTriggerOffset < mFooterHeight) { 381 | mLoadMoreTriggerOffset = mFooterHeight; 382 | } 383 | } 384 | } 385 | 386 | @Override 387 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 388 | layoutChildren(); 389 | 390 | mHasHeaderView = (mHeaderView != null); 391 | mHasFooterView = (mFooterView != null); 392 | } 393 | 394 | /** 395 | * TODO add gravity 396 | * LayoutParams of RefreshLoadMoreLayout 397 | */ 398 | public static class LayoutParams extends MarginLayoutParams { 399 | 400 | public LayoutParams(Context c, AttributeSet attrs) { 401 | super(c, attrs); 402 | } 403 | 404 | public LayoutParams(int width, int height) { 405 | super(width, height); 406 | } 407 | 408 | public LayoutParams(MarginLayoutParams source) { 409 | super(source); 410 | } 411 | 412 | public LayoutParams(ViewGroup.LayoutParams source) { 413 | super(source); 414 | } 415 | } 416 | 417 | /** 418 | * {@inheritDoc} 419 | */ 420 | @Override 421 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 422 | return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 423 | } 424 | 425 | /** 426 | * {@inheritDoc} 427 | */ 428 | @Override 429 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 430 | return new LayoutParams(p); 431 | } 432 | 433 | /** 434 | * {@inheritDoc} 435 | */ 436 | @Override 437 | public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 438 | return new LayoutParams(getContext(), attrs); 439 | } 440 | 441 | @Override 442 | public boolean dispatchTouchEvent(MotionEvent ev) { 443 | final int action = MotionEventCompat.getActionMasked(ev); 444 | switch (action) { 445 | case MotionEvent.ACTION_CANCEL: 446 | case MotionEvent.ACTION_UP: 447 | // swipeToRefresh -> finger up -> finger down if the status is still swipeToRefresh 448 | // in onInterceptTouchEvent ACTION_DOWN event will stop the scroller 449 | // if the event pass to the child view while ACTION_MOVE(condition is false) 450 | // in onInterceptTouchEvent ACTION_MOVE the ACTION_UP or ACTION_CANCEL will not be 451 | // passed to onInterceptTouchEvent and onTouchEvent. Instead It will be passed to 452 | // child view's onTouchEvent. So we must deal this situation in dispatchTouchEvent 453 | onActivePointerUp(); 454 | break; 455 | } 456 | return super.dispatchTouchEvent(ev); 457 | } 458 | 459 | @Override 460 | public boolean onInterceptTouchEvent(MotionEvent event) { 461 | final int action = MotionEventCompat.getActionMasked(event); 462 | switch (action) { 463 | case MotionEvent.ACTION_DOWN: 464 | 465 | mActivePointerId = MotionEventCompat.getPointerId(event, 0); 466 | mInitDownY = mLastY = getMotionEventY(event, mActivePointerId); 467 | mInitDownX = mLastX = getMotionEventX(event, mActivePointerId); 468 | 469 | // if it isn't an ing status or default status 470 | if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isSwipingToLoadMore(mStatus) || 471 | STATUS.isReleaseToRefresh(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) { 472 | // abort autoScrolling, not trigger the method #autoScrollFinished() 473 | mAutoScroller.abortIfRunning(); 474 | if (mDebug) { 475 | Log.i(TAG, "Another finger down, abort auto scrolling, let the new finger handle"); 476 | } 477 | } 478 | 479 | if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isReleaseToRefresh(mStatus) 480 | || STATUS.isSwipingToLoadMore(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) { 481 | return true; 482 | } 483 | 484 | // let children view handle the ACTION_DOWN; 485 | 486 | // 1. children consumed: 487 | // if at least one of children onTouchEvent() ACTION_DOWN return true. 488 | // ACTION_DOWN event will not return to SwipeToLoadLayout#onTouchEvent(). 489 | // but the others action can be handled by SwipeToLoadLayout#onInterceptTouchEvent() 490 | 491 | // 2. children not consumed: 492 | // if children onTouchEvent() ACTION_DOWN return false. 493 | // ACTION_DOWN event will return to SwipeToLoadLayout's onTouchEvent(). 494 | // SwipeToLoadLayout#onTouchEvent() ACTION_DOWN return true to consume the ACTION_DOWN event. 495 | 496 | // anyway: handle action down in onInterceptTouchEvent() to init is an good option 497 | break; 498 | case MotionEvent.ACTION_MOVE: 499 | if (mActivePointerId == INVALID_POINTER) { 500 | return false; 501 | } 502 | float y = getMotionEventY(event, mActivePointerId); 503 | float x = getMotionEventX(event, mActivePointerId); 504 | final float yInitDiff = y - mInitDownY; 505 | final float xInitDiff = x - mInitDownX; 506 | mLastY = y; 507 | mLastX = x; 508 | boolean moved = Math.abs(yInitDiff) > Math.abs(xInitDiff); 509 | boolean triggerCondition = 510 | // refresh trigger condition 511 | (yInitDiff > 0 && moved && onCheckCanRefresh()) || 512 | //load more trigger condition 513 | (yInitDiff < 0 && moved && onCheckCanLoadMore()); 514 | if (triggerCondition) { 515 | // if the refresh's or load more's trigger condition is true, 516 | // intercept the move action event and pass it to SwipeToLoadLayout#onTouchEvent() 517 | return true; 518 | } 519 | break; 520 | case MotionEvent.ACTION_POINTER_UP: { 521 | onSecondaryPointerUp(event); 522 | mInitDownY = mLastY = getMotionEventY(event, mActivePointerId); 523 | mInitDownX = mLastX = getMotionEventX(event, mActivePointerId); 524 | break; 525 | } 526 | case MotionEvent.ACTION_UP: 527 | case MotionEvent.ACTION_CANCEL: 528 | mActivePointerId = INVALID_POINTER; 529 | break; 530 | } 531 | return super.onInterceptTouchEvent(event); 532 | } 533 | 534 | @Override 535 | public boolean onTouchEvent(MotionEvent event) { 536 | final int action = MotionEventCompat.getActionMasked(event); 537 | 538 | switch (action) { 539 | case MotionEvent.ACTION_DOWN: 540 | mActivePointerId = MotionEventCompat.getPointerId(event, 0); 541 | return true; 542 | 543 | case MotionEvent.ACTION_MOVE: 544 | // take over the ACTION_MOVE event from SwipeToLoadLayout#onInterceptTouchEvent() 545 | // if condition is true 546 | final float y = getMotionEventY(event, mActivePointerId); 547 | final float x = getMotionEventX(event, mActivePointerId); 548 | 549 | final float yDiff = y - mLastY; 550 | final float xDiff = x - mLastX; 551 | mLastY = y; 552 | mLastX = x; 553 | 554 | if (Math.abs(xDiff) > Math.abs(yDiff) && Math.abs(xDiff) > mTouchSlop) { 555 | return false; 556 | } 557 | 558 | if (STATUS.isStatusDefault(mStatus)) { 559 | if (yDiff > 0 && onCheckCanRefresh()) { 560 | mRefreshCallback.onPrepare(); 561 | setStatus(STATUS.STATUS_SWIPING_TO_REFRESH); 562 | } else if (yDiff < 0 && onCheckCanLoadMore()) { 563 | mLoadMoreCallback.onPrepare(); 564 | setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE); 565 | } 566 | } else if (STATUS.isRefreshStatus(mStatus)) { 567 | if (mTargetOffset <= 0) { 568 | setStatus(STATUS.STATUS_DEFAULT); 569 | fixCurrentStatusLayout(); 570 | return false; 571 | } 572 | } else if (STATUS.isLoadMoreStatus(mStatus)) { 573 | if (mTargetOffset >= 0) { 574 | setStatus(STATUS.STATUS_DEFAULT); 575 | fixCurrentStatusLayout(); 576 | return false; 577 | } 578 | } 579 | 580 | if (STATUS.isRefreshStatus(mStatus)) { 581 | if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isReleaseToRefresh(mStatus)) { 582 | if (mTargetOffset >= mRefreshTriggerOffset) { 583 | setStatus(STATUS.STATUS_RELEASE_TO_REFRESH); 584 | } else { 585 | setStatus(STATUS.STATUS_SWIPING_TO_REFRESH); 586 | } 587 | fingerScroll(yDiff); 588 | } 589 | } else if (STATUS.isLoadMoreStatus(mStatus)) { 590 | if (STATUS.isSwipingToLoadMore(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) { 591 | if (-mTargetOffset >= mLoadMoreTriggerOffset) { 592 | setStatus(STATUS.STATUS_RELEASE_TO_LOAD_MORE); 593 | } else { 594 | setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE); 595 | } 596 | fingerScroll(yDiff); 597 | } 598 | } 599 | return true; 600 | 601 | case MotionEvent.ACTION_POINTER_DOWN: { 602 | final int pointerIndex = MotionEventCompat.getActionIndex(event); 603 | final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex); 604 | if (pointerId != INVALID_POINTER) { 605 | mActivePointerId = pointerId; 606 | } 607 | mInitDownY = mLastY = getMotionEventY(event, mActivePointerId); 608 | mInitDownX = mLastX = getMotionEventX(event, mActivePointerId); 609 | break; 610 | } 611 | case MotionEvent.ACTION_POINTER_UP: { 612 | onSecondaryPointerUp(event); 613 | mInitDownY = mLastY = getMotionEventY(event, mActivePointerId); 614 | mInitDownX = mLastX = getMotionEventX(event, mActivePointerId); 615 | break; 616 | } 617 | case MotionEvent.ACTION_UP: 618 | case MotionEvent.ACTION_CANCEL: 619 | if (mActivePointerId == INVALID_POINTER) { 620 | return false; 621 | } 622 | mActivePointerId = INVALID_POINTER; 623 | break; 624 | default: 625 | break; 626 | } 627 | return super.onTouchEvent(event); 628 | } 629 | 630 | /** 631 | * set debug mode(default value false) 632 | * 633 | * @param debug if true log on, false log off 634 | */ 635 | public void setDebug(boolean debug) { 636 | this.mDebug = debug; 637 | } 638 | 639 | /** 640 | * is refresh function is enabled 641 | * 642 | * @return 643 | */ 644 | public boolean isRefreshEnabled() { 645 | return mRefreshEnabled; 646 | } 647 | 648 | /** 649 | * switch refresh function on or off 650 | * 651 | * @param enable 652 | */ 653 | public void setRefreshEnabled(boolean enable) { 654 | this.mRefreshEnabled = enable; 655 | } 656 | 657 | /** 658 | * is load more function is enabled 659 | * 660 | * @return 661 | */ 662 | public boolean isLoadMoreEnabled() { 663 | return mLoadMoreEnabled; 664 | } 665 | 666 | /** 667 | * switch load more function on or off 668 | * 669 | * @param enable 670 | */ 671 | public void setLoadMoreEnabled(boolean enable) { 672 | this.mLoadMoreEnabled = enable; 673 | } 674 | 675 | /** 676 | * is current status refreshing 677 | * 678 | * @return 679 | */ 680 | public boolean isRefreshing() { 681 | return STATUS.isRefreshing(mStatus); 682 | } 683 | 684 | /** 685 | * is current status loading more 686 | * 687 | * @return 688 | */ 689 | public boolean isLoadingMore() { 690 | return STATUS.isLoadingMore(mStatus); 691 | } 692 | 693 | /** 694 | * set refresh header view, the view must at lease be an implement of {@code SwipeRefreshTrigger}. 695 | * the view can also implement {@code SwipeTrigger} for more extension functions 696 | * 697 | * @param view 698 | */ 699 | public void setRefreshHeaderView(View view) { 700 | if (view instanceof SwipeRefreshTrigger) { 701 | if (mHeaderView != null && mHeaderView != view) { 702 | removeView(mHeaderView); 703 | } 704 | if (mHeaderView != view) { 705 | this.mHeaderView = view; 706 | addView(view); 707 | } 708 | } else { 709 | Log.e(TAG, "Refresh header view must be an implement of SwipeRefreshTrigger"); 710 | } 711 | } 712 | 713 | /** 714 | * set load more footer view, the view must at least be an implement of SwipeLoadTrigger 715 | * the view can also implement {@code SwipeTrigger} for more extension functions 716 | * 717 | * @param view 718 | */ 719 | public void setLoadMoreFooterView(View view) { 720 | if (view instanceof SwipeLoadMoreTrigger) { 721 | if (mFooterView != null && mFooterView != view) { 722 | removeView(mFooterView); 723 | } 724 | if (mFooterView != view) { 725 | this.mFooterView = view; 726 | addView(mFooterView); 727 | } 728 | } else { 729 | Log.e(TAG, "Load more footer view must be an implement of SwipeLoadTrigger"); 730 | } 731 | } 732 | 733 | /** 734 | * set the style of the refresh header 735 | * 736 | * @param style 737 | */ 738 | public void setSwipeStyle(int style) { 739 | this.mStyle = style; 740 | requestLayout(); 741 | } 742 | 743 | /** 744 | * set how hard to drag. bigger easier, smaller harder; 745 | * 746 | * @param dragRatio default value is {@link #DEFAULT_DRAG_RATIO} 747 | */ 748 | public void setDragRatio(float dragRatio) { 749 | this.mDragRatio = dragRatio; 750 | } 751 | 752 | /** 753 | * set the value of {@link #mRefreshTriggerOffset}. 754 | * Default value is the refresh header view height {@link #mHeaderHeight}

755 | * If the offset you set is smaller than {@link #mHeaderHeight} or not set, 756 | * using {@link #mHeaderHeight} as default value 757 | * 758 | * @param offset 759 | */ 760 | public void setRefreshTriggerOffset(int offset) { 761 | mRefreshTriggerOffset = offset; 762 | } 763 | 764 | /** 765 | * set the value of {@link #mLoadMoreTriggerOffset}. 766 | * Default value is the load more footer view height {@link #mFooterHeight}

767 | * If the offset you set is smaller than {@link #mFooterHeight} or not set, 768 | * using {@link #mFooterHeight} as default value 769 | * 770 | * @param offset 771 | */ 772 | public void setLoadMoreTriggerOffset(int offset) { 773 | mLoadMoreTriggerOffset = offset; 774 | } 775 | 776 | /** 777 | * Set the final offset you can swipe to refresh.
778 | * If the offset you set is 0(default value) or smaller than {@link #mRefreshTriggerOffset} 779 | * there no final offset 780 | * 781 | * @param offset 782 | */ 783 | public void setRefreshFinalDragOffset(int offset) { 784 | mRefreshFinalDragOffset = offset; 785 | } 786 | 787 | /** 788 | * Set the final offset you can swipe to load more.
789 | * If the offset you set is 0(default value) or smaller than {@link #mLoadMoreTriggerOffset}, 790 | * there no final offset 791 | * 792 | * @param offset 793 | */ 794 | public void setLoadMoreFinalDragOffset(int offset) { 795 | mLoadMoreFinalDragOffset = offset; 796 | } 797 | 798 | /** 799 | * set {@link #mSwipingToRefreshToDefaultScrollingDuration} in milliseconds 800 | * 801 | * @param duration 802 | */ 803 | public void setSwipingToRefreshToDefaultScrollingDuration(int duration) { 804 | this.mSwipingToRefreshToDefaultScrollingDuration = duration; 805 | } 806 | 807 | /** 808 | * set {@link #mReleaseToRefreshToRefreshingScrollingDuration} in milliseconds 809 | * 810 | * @param duration 811 | */ 812 | public void setReleaseToRefreshingScrollingDuration(int duration) { 813 | this.mReleaseToRefreshToRefreshingScrollingDuration = duration; 814 | } 815 | 816 | /** 817 | * set {@link #mRefreshCompleteDelayDuration} in milliseconds 818 | * 819 | * @param duration 820 | */ 821 | public void setRefreshCompleteDelayDuration(int duration) { 822 | this.mRefreshCompleteDelayDuration = duration; 823 | } 824 | 825 | /** 826 | * set {@link #mRefreshCompleteToDefaultScrollingDuration} in milliseconds 827 | * 828 | * @param duration 829 | */ 830 | public void setRefreshCompleteToDefaultScrollingDuration(int duration) { 831 | this.mRefreshCompleteToDefaultScrollingDuration = duration; 832 | } 833 | 834 | /** 835 | * set {@link #mDefaultToRefreshingScrollingDuration} in milliseconds 836 | * 837 | * @param duration 838 | */ 839 | public void setDefaultToRefreshingScrollingDuration(int duration) { 840 | this.mDefaultToRefreshingScrollingDuration = duration; 841 | } 842 | 843 | /** 844 | * set {@link @mSwipingToLoadMoreToDefaultScrollingDuration} in milliseconds 845 | * 846 | * @param duration 847 | */ 848 | public void setSwipingToLoadMoreToDefaultScrollingDuration(int duration) { 849 | this.mSwipingToLoadMoreToDefaultScrollingDuration = duration; 850 | } 851 | 852 | /** 853 | * set {@link #mReleaseToLoadMoreToLoadingMoreScrollingDuration} in milliseconds 854 | * 855 | * @param duration 856 | */ 857 | public void setReleaseToLoadingMoreScrollingDuration(int duration) { 858 | this.mReleaseToLoadMoreToLoadingMoreScrollingDuration = duration; 859 | } 860 | 861 | /** 862 | * set {@link #mLoadMoreCompleteDelayDuration} in milliseconds 863 | * 864 | * @param duration 865 | */ 866 | public void setLoadMoreCompleteDelayDuration(int duration) { 867 | this.mLoadMoreCompleteDelayDuration = duration; 868 | } 869 | 870 | /** 871 | * set {@link #mLoadMoreCompleteToDefaultScrollingDuration} in milliseconds 872 | * 873 | * @param duration 874 | */ 875 | public void setLoadMoreCompleteToDefaultScrollingDuration(int duration) { 876 | this.mLoadMoreCompleteToDefaultScrollingDuration = duration; 877 | } 878 | 879 | /** 880 | * set {@link #mDefaultToLoadingMoreScrollingDuration} in milliseconds 881 | * 882 | * @param duration 883 | */ 884 | public void setDefaultToLoadingMoreScrollingDuration(int duration) { 885 | this.mDefaultToLoadingMoreScrollingDuration = duration; 886 | } 887 | 888 | /** 889 | * set an {@link OnRefreshListener} to listening refresh event 890 | * 891 | * @param listener 892 | */ 893 | public void setOnRefreshListener(OnRefreshListener listener) { 894 | this.mRefreshListener = listener; 895 | } 896 | 897 | /** 898 | * set an {@link OnLoadMoreListener} to listening load more event 899 | * 900 | * @param listener 901 | */ 902 | public void setOnLoadMoreListener(OnLoadMoreListener listener) { 903 | this.mLoadMoreListener = listener; 904 | } 905 | 906 | /** 907 | * auto refresh or cancel 908 | * 909 | * @param refreshing 910 | */ 911 | public void setRefreshing(boolean refreshing) { 912 | if (!isRefreshEnabled() || mHeaderView == null) { 913 | return; 914 | } 915 | this.mAutoLoading = refreshing; 916 | if (refreshing) { 917 | if (STATUS.isStatusDefault(mStatus)) { 918 | setStatus(STATUS.STATUS_SWIPING_TO_REFRESH); 919 | scrollDefaultToRefreshing(); 920 | } 921 | } else { 922 | if (STATUS.isRefreshing(mStatus)) { 923 | mRefreshCallback.onComplete(); 924 | postDelayed(new Runnable() { 925 | @Override 926 | public void run() { 927 | scrollRefreshingToDefault(); 928 | } 929 | }, mRefreshCompleteDelayDuration); 930 | } 931 | } 932 | } 933 | 934 | /** 935 | * auto loading more or cancel 936 | * 937 | * @param loadingMore 938 | */ 939 | public void setLoadingMore(boolean loadingMore) { 940 | if (!isLoadMoreEnabled() || mFooterView == null) { 941 | return; 942 | } 943 | this.mAutoLoading = loadingMore; 944 | if (loadingMore) { 945 | if (STATUS.isStatusDefault(mStatus)) { 946 | setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE); 947 | scrollDefaultToLoadingMore(); 948 | } 949 | } else { 950 | if (STATUS.isLoadingMore(mStatus)) { 951 | mLoadMoreCallback.onComplete(); 952 | postDelayed(new Runnable() { 953 | @Override 954 | public void run() { 955 | scrollLoadingMoreToDefault(); 956 | } 957 | }, mLoadMoreCompleteDelayDuration); 958 | } 959 | } 960 | } 961 | 962 | /** 963 | * copy from {@link android.support.v4.widget.SwipeRefreshLayout#canChildScrollUp()} 964 | * 965 | * @return Whether it is possible for the child view of this layout to 966 | * scroll up. Override this if the child view is a custom view. 967 | */ 968 | protected boolean canChildScrollUp() { 969 | if (android.os.Build.VERSION.SDK_INT < 14) { 970 | if (mTargetView instanceof AbsListView) { 971 | final AbsListView absListView = (AbsListView) mTargetView; 972 | return absListView.getChildCount() > 0 973 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) 974 | .getTop() < absListView.getPaddingTop()); 975 | } else { 976 | return ViewCompat.canScrollVertically(mTargetView, -1) || mTargetView.getScrollY() > 0; 977 | } 978 | } else { 979 | return ViewCompat.canScrollVertically(mTargetView, -1); 980 | } 981 | } 982 | 983 | /** 984 | * Whether it is possible for the child view of this layout to 985 | * scroll down. Override this if the child view is a custom view. 986 | * 987 | * @return 988 | */ 989 | protected boolean canChildScrollDown() { 990 | if (android.os.Build.VERSION.SDK_INT < 14) { 991 | if (mTargetView instanceof AbsListView) { 992 | final AbsListView absListView = (AbsListView) mTargetView; 993 | return absListView.getChildCount() > 0 994 | && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1 995 | || absListView.getChildAt(absListView.getChildCount() - 1).getBottom() > absListView.getPaddingBottom()); 996 | } else { 997 | return ViewCompat.canScrollVertically(mTargetView, 1) || mTargetView.getScrollY() < 0; 998 | } 999 | } else { 1000 | return ViewCompat.canScrollVertically(mTargetView, 1); 1001 | } 1002 | } 1003 | 1004 | /** 1005 | * @see #onLayout(boolean, int, int, int, int) 1006 | */ 1007 | private void layoutChildren() { 1008 | final int width = getMeasuredWidth(); 1009 | final int height = getMeasuredHeight(); 1010 | 1011 | final int paddingLeft = getPaddingLeft(); 1012 | final int paddingTop = getPaddingTop(); 1013 | final int paddingRight = getPaddingRight(); 1014 | final int paddingBottom = getPaddingBottom(); 1015 | 1016 | if (mTargetView == null) { 1017 | return; 1018 | } 1019 | 1020 | // layout header 1021 | if (mHeaderView != null) { 1022 | final View headerView = mHeaderView; 1023 | MarginLayoutParams lp = (MarginLayoutParams) headerView.getLayoutParams(); 1024 | final int headerLeft = paddingLeft + lp.leftMargin; 1025 | final int headerTop; 1026 | switch (mStyle) { 1027 | case STYLE.CLASSIC: 1028 | // classic 1029 | headerTop = paddingTop + lp.topMargin - mHeaderHeight + mHeaderOffset; 1030 | break; 1031 | case STYLE.ABOVE: 1032 | // classic 1033 | headerTop = paddingTop + lp.topMargin - mHeaderHeight + mHeaderOffset; 1034 | break; 1035 | case STYLE.BLEW: 1036 | // blew 1037 | headerTop = paddingTop + lp.topMargin; 1038 | break; 1039 | case STYLE.SCALE: 1040 | // scale 1041 | headerTop = paddingTop + lp.topMargin - mHeaderHeight / 2 + mHeaderOffset / 2; 1042 | break; 1043 | default: 1044 | // classic 1045 | headerTop = paddingTop + lp.topMargin - mHeaderHeight + mHeaderOffset; 1046 | break; 1047 | } 1048 | final int headerRight = headerLeft + headerView.getMeasuredWidth(); 1049 | final int headerBottom = headerTop + headerView.getMeasuredHeight(); 1050 | headerView.layout(headerLeft, headerTop, headerRight, headerBottom); 1051 | } 1052 | 1053 | 1054 | // layout target 1055 | if (mTargetView != null) { 1056 | final View targetView = mTargetView; 1057 | MarginLayoutParams lp = (MarginLayoutParams) targetView.getLayoutParams(); 1058 | final int targetLeft = paddingLeft + lp.leftMargin; 1059 | final int targetTop; 1060 | 1061 | switch (mStyle) { 1062 | case STYLE.CLASSIC: 1063 | // classic 1064 | targetTop = paddingTop + lp.topMargin + mTargetOffset; 1065 | break; 1066 | case STYLE.ABOVE: 1067 | // above 1068 | targetTop = paddingTop + lp.topMargin; 1069 | break; 1070 | case STYLE.BLEW: 1071 | // classic 1072 | targetTop = paddingTop + lp.topMargin + mTargetOffset; 1073 | break; 1074 | case STYLE.SCALE: 1075 | // classic 1076 | targetTop = paddingTop + lp.topMargin + mTargetOffset; 1077 | break; 1078 | default: 1079 | // classic 1080 | targetTop = paddingTop + lp.topMargin + mTargetOffset; 1081 | break; 1082 | } 1083 | final int targetRight = targetLeft + targetView.getMeasuredWidth(); 1084 | final int targetBottom = targetTop + targetView.getMeasuredHeight(); 1085 | targetView.layout(targetLeft, targetTop, targetRight, targetBottom); 1086 | } 1087 | 1088 | // layout footer 1089 | if (mFooterView != null) { 1090 | final View footerView = mFooterView; 1091 | MarginLayoutParams lp = (MarginLayoutParams) footerView.getLayoutParams(); 1092 | final int footerLeft = paddingLeft + lp.leftMargin; 1093 | final int footerBottom; 1094 | switch (mStyle) { 1095 | case STYLE.CLASSIC: 1096 | // classic 1097 | footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight + mFooterOffset; 1098 | break; 1099 | case STYLE.ABOVE: 1100 | // classic 1101 | footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight + mFooterOffset; 1102 | break; 1103 | case STYLE.BLEW: 1104 | // blew 1105 | footerBottom = height - paddingBottom - lp.bottomMargin; 1106 | break; 1107 | case STYLE.SCALE: 1108 | // scale 1109 | footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight / 2 + mFooterOffset / 2; 1110 | break; 1111 | default: 1112 | // classic 1113 | footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight + mFooterOffset; 1114 | break; 1115 | } 1116 | final int footerTop = footerBottom - footerView.getMeasuredHeight(); 1117 | final int footerRight = footerLeft + footerView.getMeasuredWidth(); 1118 | 1119 | footerView.layout(footerLeft, footerTop, footerRight, footerBottom); 1120 | } 1121 | 1122 | if (mStyle == STYLE.CLASSIC 1123 | || mStyle == STYLE.ABOVE) { 1124 | if (mHeaderView != null) { 1125 | mHeaderView.bringToFront(); 1126 | } 1127 | if (mFooterView != null) { 1128 | mFooterView.bringToFront(); 1129 | } 1130 | } else if (mStyle == STYLE.BLEW || mStyle == STYLE.SCALE) { 1131 | if (mTargetView != null) { 1132 | mTargetView.bringToFront(); 1133 | } 1134 | } 1135 | } 1136 | 1137 | private void fixCurrentStatusLayout() { 1138 | if (STATUS.isRefreshing(mStatus)) { 1139 | mTargetOffset = (int) (mRefreshTriggerOffset + 0.5f); 1140 | mHeaderOffset = mTargetOffset; 1141 | mFooterOffset = 0; 1142 | layoutChildren(); 1143 | invalidate(); 1144 | } else if (STATUS.isStatusDefault(mStatus)) { 1145 | mTargetOffset = 0; 1146 | mHeaderOffset = 0; 1147 | mFooterOffset = 0; 1148 | layoutChildren(); 1149 | invalidate(); 1150 | } else if (STATUS.isLoadingMore(mStatus)) { 1151 | mTargetOffset = -(int) (mLoadMoreTriggerOffset + 0.5f); 1152 | mHeaderOffset = 0; 1153 | mFooterOffset = mTargetOffset; 1154 | layoutChildren(); 1155 | invalidate(); 1156 | } 1157 | } 1158 | 1159 | /** 1160 | * scrolling by physical touch with your fingers 1161 | * 1162 | * @param yDiff 1163 | */ 1164 | private void fingerScroll(final float yDiff) { 1165 | float ratio = mDragRatio; 1166 | float yScrolled = yDiff * ratio; 1167 | 1168 | // make sure (targetOffset>0 -> targetOffset=0 -> default status) 1169 | // or (targetOffset<0 -> targetOffset=0 -> default status) 1170 | // forbidden fling (targetOffset>0 -> targetOffset=0 ->targetOffset<0 -> default status) 1171 | // or (targetOffset<0 -> targetOffset=0 ->targetOffset>0 -> default status) 1172 | // I am so smart :) 1173 | 1174 | float tmpTargetOffset = yScrolled + mTargetOffset; 1175 | if ((tmpTargetOffset > 0 && mTargetOffset < 0) 1176 | || (tmpTargetOffset < 0 && mTargetOffset > 0)) { 1177 | yScrolled = -mTargetOffset; 1178 | } 1179 | 1180 | 1181 | if (mRefreshFinalDragOffset >= mRefreshTriggerOffset && tmpTargetOffset > mRefreshFinalDragOffset) { 1182 | yScrolled = mRefreshFinalDragOffset - mTargetOffset; 1183 | } else if (mLoadMoreFinalDragOffset >= mLoadMoreTriggerOffset && -tmpTargetOffset > mLoadMoreFinalDragOffset) { 1184 | yScrolled = -mLoadMoreFinalDragOffset - mTargetOffset; 1185 | } 1186 | 1187 | if (STATUS.isRefreshStatus(mStatus)) { 1188 | mRefreshCallback.onMove(mTargetOffset, false, false); 1189 | } else if (STATUS.isLoadMoreStatus(mStatus)) { 1190 | mLoadMoreCallback.onMove(mTargetOffset, false, false); 1191 | } 1192 | updateScroll(yScrolled); 1193 | } 1194 | 1195 | private void autoScroll(final float yScrolled) { 1196 | 1197 | if (STATUS.isSwipingToRefresh(mStatus)) { 1198 | mRefreshCallback.onMove(mTargetOffset, false, true); 1199 | } else if (STATUS.isReleaseToRefresh(mStatus)) { 1200 | mRefreshCallback.onMove(mTargetOffset, false, true); 1201 | } else if (STATUS.isRefreshing(mStatus)) { 1202 | mRefreshCallback.onMove(mTargetOffset, true, true); 1203 | } else if (STATUS.isSwipingToLoadMore(mStatus)) { 1204 | mLoadMoreCallback.onMove(mTargetOffset, false, true); 1205 | } else if (STATUS.isReleaseToLoadMore(mStatus)) { 1206 | mLoadMoreCallback.onMove(mTargetOffset, false, true); 1207 | } else if (STATUS.isLoadingMore(mStatus)) { 1208 | mLoadMoreCallback.onMove(mTargetOffset, true, true); 1209 | } 1210 | updateScroll(yScrolled); 1211 | } 1212 | 1213 | /** 1214 | * Process the scrolling(auto or physical) and append the diff values to mTargetOffset 1215 | * I think it's the most busy and core method. :) a ha ha ha ha... 1216 | * 1217 | * @param yScrolled 1218 | */ 1219 | private void updateScroll(final float yScrolled) { 1220 | if (yScrolled == 0) { 1221 | return; 1222 | } 1223 | mTargetOffset += yScrolled; 1224 | 1225 | if (STATUS.isRefreshStatus(mStatus)) { 1226 | mHeaderOffset = mTargetOffset; 1227 | mFooterOffset = 0; 1228 | } else if (STATUS.isLoadMoreStatus(mStatus)) { 1229 | mFooterOffset = mTargetOffset; 1230 | mHeaderOffset = 0; 1231 | } 1232 | 1233 | if (mDebug) { 1234 | Log.i(TAG, "mTargetOffset = " + mTargetOffset); 1235 | } 1236 | layoutChildren(); 1237 | invalidate(); 1238 | } 1239 | 1240 | /** 1241 | * on active finger up 1242 | */ 1243 | private void onActivePointerUp() { 1244 | if (STATUS.isSwipingToRefresh(mStatus)) { 1245 | // simply return 1246 | scrollSwipingToRefreshToDefault(); 1247 | 1248 | } else if (STATUS.isSwipingToLoadMore(mStatus)) { 1249 | // simply return 1250 | scrollSwipingToLoadMoreToDefault(); 1251 | 1252 | } else if (STATUS.isReleaseToRefresh(mStatus)) { 1253 | // return to header height and perform refresh 1254 | mRefreshCallback.onRelease(); 1255 | scrollReleaseToRefreshToRefreshing(); 1256 | 1257 | } else if (STATUS.isReleaseToLoadMore(mStatus)) { 1258 | // return to footer height and perform loadMore 1259 | mLoadMoreCallback.onRelease(); 1260 | scrollReleaseToLoadMoreToLoadingMore(); 1261 | 1262 | } 1263 | } 1264 | 1265 | /** 1266 | * on not active finger up 1267 | * 1268 | * @param ev 1269 | */ 1270 | private void onSecondaryPointerUp(MotionEvent ev) { 1271 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 1272 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 1273 | if (pointerId == mActivePointerId) { 1274 | // This was our active pointer going up. Choose a new 1275 | // active pointer and adjust accordingly. 1276 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1277 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 1278 | } 1279 | } 1280 | 1281 | private void scrollDefaultToRefreshing() { 1282 | mAutoScroller.autoScroll((int) (mRefreshTriggerOffset + 0.5f), mDefaultToRefreshingScrollingDuration); 1283 | } 1284 | 1285 | private void scrollDefaultToLoadingMore() { 1286 | mAutoScroller.autoScroll(-(int) (mLoadMoreTriggerOffset + 0.5f), mDefaultToLoadingMoreScrollingDuration); 1287 | } 1288 | 1289 | private void scrollSwipingToRefreshToDefault() { 1290 | mAutoScroller.autoScroll(-mHeaderOffset, mSwipingToRefreshToDefaultScrollingDuration); 1291 | } 1292 | 1293 | private void scrollSwipingToLoadMoreToDefault() { 1294 | mAutoScroller.autoScroll(-mFooterOffset, mSwipingToLoadMoreToDefaultScrollingDuration); 1295 | } 1296 | 1297 | private void scrollReleaseToRefreshToRefreshing() { 1298 | mAutoScroller.autoScroll(mHeaderHeight - mHeaderOffset, mReleaseToRefreshToRefreshingScrollingDuration); 1299 | } 1300 | 1301 | private void scrollReleaseToLoadMoreToLoadingMore() { 1302 | mAutoScroller.autoScroll(-mFooterOffset - mFooterHeight, mReleaseToLoadMoreToLoadingMoreScrollingDuration); 1303 | } 1304 | 1305 | private void scrollRefreshingToDefault() { 1306 | mAutoScroller.autoScroll(-mHeaderOffset, mRefreshCompleteToDefaultScrollingDuration); 1307 | } 1308 | 1309 | private void scrollLoadingMoreToDefault() { 1310 | mAutoScroller.autoScroll(-mFooterOffset, mLoadMoreCompleteToDefaultScrollingDuration); 1311 | } 1312 | 1313 | /** 1314 | * invoke when {@link AutoScroller#finish()} is automatic 1315 | */ 1316 | private void autoScrollFinished() { 1317 | int mLastStatus = mStatus; 1318 | 1319 | if (STATUS.isReleaseToRefresh(mStatus)) { 1320 | setStatus(STATUS.STATUS_REFRESHING); 1321 | fixCurrentStatusLayout(); 1322 | mRefreshCallback.onRefresh(); 1323 | 1324 | } else if (STATUS.isRefreshing(mStatus)) { 1325 | setStatus(STATUS.STATUS_DEFAULT); 1326 | fixCurrentStatusLayout(); 1327 | mRefreshCallback.onReset(); 1328 | 1329 | } else if (STATUS.isSwipingToRefresh(mStatus)) { 1330 | if (mAutoLoading) { 1331 | mAutoLoading = false; 1332 | setStatus(STATUS.STATUS_REFRESHING); 1333 | fixCurrentStatusLayout(); 1334 | mRefreshCallback.onRefresh(); 1335 | } else { 1336 | setStatus(STATUS.STATUS_DEFAULT); 1337 | fixCurrentStatusLayout(); 1338 | mRefreshCallback.onReset(); 1339 | } 1340 | } else if (STATUS.isStatusDefault(mStatus)) { 1341 | 1342 | } else if (STATUS.isSwipingToLoadMore(mStatus)) { 1343 | if (mAutoLoading) { 1344 | mAutoLoading = false; 1345 | setStatus(STATUS.STATUS_LOADING_MORE); 1346 | fixCurrentStatusLayout(); 1347 | mLoadMoreCallback.onLoadMore(); 1348 | } else { 1349 | setStatus(STATUS.STATUS_DEFAULT); 1350 | fixCurrentStatusLayout(); 1351 | mLoadMoreCallback.onReset(); 1352 | } 1353 | } else if (STATUS.isLoadingMore(mStatus)) { 1354 | setStatus(STATUS.STATUS_DEFAULT); 1355 | fixCurrentStatusLayout(); 1356 | mLoadMoreCallback.onReset(); 1357 | } else if (STATUS.isReleaseToLoadMore(mStatus)) { 1358 | setStatus(STATUS.STATUS_LOADING_MORE); 1359 | fixCurrentStatusLayout(); 1360 | mLoadMoreCallback.onLoadMore(); 1361 | } else { 1362 | throw new IllegalStateException("illegal state: " + STATUS.getStatus(mStatus)); 1363 | } 1364 | 1365 | if (mDebug) { 1366 | Log.i(TAG, STATUS.getStatus(mLastStatus) + " -> " + STATUS.getStatus(mStatus)); 1367 | } 1368 | } 1369 | 1370 | /** 1371 | * check if it can refresh 1372 | * 1373 | * @return 1374 | */ 1375 | private boolean onCheckCanRefresh() { 1376 | 1377 | return mRefreshEnabled && !canChildScrollUp() && mHasHeaderView && mRefreshTriggerOffset > 0; 1378 | } 1379 | 1380 | /** 1381 | * check if it can load more 1382 | * 1383 | * @return 1384 | */ 1385 | private boolean onCheckCanLoadMore() { 1386 | 1387 | return mLoadMoreEnabled && !canChildScrollDown() && mHasFooterView && mLoadMoreTriggerOffset > 0; 1388 | } 1389 | 1390 | private float getMotionEventY(MotionEvent event, int activePointerId) { 1391 | final int index = MotionEventCompat.findPointerIndex(event, activePointerId); 1392 | if (index < 0) { 1393 | return INVALID_COORDINATE; 1394 | } 1395 | return MotionEventCompat.getY(event, index); 1396 | } 1397 | 1398 | private float getMotionEventX(MotionEvent event, int activePointId) { 1399 | final int index = MotionEventCompat.findPointerIndex(event, activePointId); 1400 | if (index < 0) { 1401 | return INVALID_COORDINATE; 1402 | } 1403 | return MotionEventCompat.getX(event, index); 1404 | } 1405 | 1406 | RefreshCallback mRefreshCallback = new RefreshCallback() { 1407 | @Override 1408 | public void onPrepare() { 1409 | if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) { 1410 | mHeaderView.setVisibility(VISIBLE); 1411 | ((SwipeTrigger) mHeaderView).onPrepare(); 1412 | } 1413 | } 1414 | 1415 | @Override 1416 | public void onMove(int y, boolean isComplete, boolean automatic) { 1417 | if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isRefreshStatus(mStatus)) { 1418 | if (mHeaderView.getVisibility() != VISIBLE) { 1419 | mHeaderView.setVisibility(VISIBLE); 1420 | } 1421 | ((SwipeTrigger) mHeaderView).onMove(y, isComplete, automatic); 1422 | } 1423 | } 1424 | 1425 | @Override 1426 | public void onRelease() { 1427 | if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isReleaseToRefresh(mStatus)) { 1428 | ((SwipeTrigger) mHeaderView).onRelease(); 1429 | } 1430 | } 1431 | 1432 | @Override 1433 | public void onRefresh() { 1434 | if (mHeaderView != null && STATUS.isRefreshing(mStatus)) { 1435 | if (mHeaderView instanceof SwipeRefreshTrigger) { 1436 | ((SwipeRefreshTrigger) mHeaderView).onRefresh(); 1437 | } 1438 | if (mRefreshListener != null) { 1439 | mRefreshListener.onRefresh(); 1440 | } 1441 | } 1442 | } 1443 | 1444 | @Override 1445 | public void onComplete() { 1446 | if (mHeaderView != null && mHeaderView instanceof SwipeTrigger) { 1447 | ((SwipeTrigger) mHeaderView).onComplete(); 1448 | } 1449 | } 1450 | 1451 | @Override 1452 | public void onReset() { 1453 | if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) { 1454 | ((SwipeTrigger) mHeaderView).onReset(); 1455 | mHeaderView.setVisibility(GONE); 1456 | } 1457 | } 1458 | }; 1459 | 1460 | LoadMoreCallback mLoadMoreCallback = new LoadMoreCallback() { 1461 | 1462 | @Override 1463 | public void onPrepare() { 1464 | if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) { 1465 | mFooterView.setVisibility(VISIBLE); 1466 | ((SwipeTrigger) mFooterView).onPrepare(); 1467 | } 1468 | } 1469 | 1470 | @Override 1471 | public void onMove(int y, boolean isComplete, boolean automatic) { 1472 | if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isLoadMoreStatus(mStatus)) { 1473 | if (mFooterView.getVisibility() != VISIBLE) { 1474 | mFooterView.setVisibility(VISIBLE); 1475 | } 1476 | ((SwipeTrigger) mFooterView).onMove(y, isComplete, automatic); 1477 | } 1478 | } 1479 | 1480 | @Override 1481 | public void onRelease() { 1482 | if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isReleaseToLoadMore(mStatus)) { 1483 | ((SwipeTrigger) mFooterView).onRelease(); 1484 | } 1485 | } 1486 | 1487 | @Override 1488 | public void onLoadMore() { 1489 | if (mFooterView != null && STATUS.isLoadingMore(mStatus)) { 1490 | if (mFooterView instanceof SwipeLoadMoreTrigger) { 1491 | ((SwipeLoadMoreTrigger) mFooterView).onLoadMore(); 1492 | } 1493 | if (mLoadMoreListener != null) { 1494 | mLoadMoreListener.onLoadMore(); 1495 | } 1496 | } 1497 | } 1498 | 1499 | @Override 1500 | public void onComplete() { 1501 | if (mFooterView != null && mFooterView instanceof SwipeTrigger) { 1502 | ((SwipeTrigger) mFooterView).onComplete(); 1503 | } 1504 | } 1505 | 1506 | @Override 1507 | public void onReset() { 1508 | if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) { 1509 | ((SwipeTrigger) mFooterView).onReset(); 1510 | mFooterView.setVisibility(GONE); 1511 | } 1512 | } 1513 | }; 1514 | 1515 | /** 1516 | * refresh event callback 1517 | */ 1518 | abstract class RefreshCallback implements SwipeTrigger, SwipeRefreshTrigger { 1519 | } 1520 | 1521 | /** 1522 | * load more event callback 1523 | */ 1524 | abstract class LoadMoreCallback implements SwipeTrigger, SwipeLoadMoreTrigger { 1525 | } 1526 | 1527 | private class AutoScroller implements Runnable { 1528 | 1529 | private Scroller mScroller; 1530 | 1531 | private int mmLastY; 1532 | 1533 | private boolean mRunning = false; 1534 | 1535 | private boolean mAbort = false; 1536 | 1537 | public AutoScroller() { 1538 | mScroller = new Scroller(getContext()); 1539 | } 1540 | 1541 | @Override 1542 | public void run() { 1543 | boolean finish = !mScroller.computeScrollOffset() || mScroller.isFinished(); 1544 | int currY = mScroller.getCurrY(); 1545 | int yDiff = currY - mmLastY; 1546 | if (finish) { 1547 | finish(); 1548 | } else { 1549 | mmLastY = currY; 1550 | SwipeToLoadLayout.this.autoScroll(yDiff); 1551 | post(this); 1552 | } 1553 | } 1554 | 1555 | /** 1556 | * remove the post callbacks and reset default values 1557 | */ 1558 | private void finish() { 1559 | mmLastY = 0; 1560 | mRunning = false; 1561 | removeCallbacks(this); 1562 | // if abort by user, don't call 1563 | if (!mAbort) { 1564 | autoScrollFinished(); 1565 | } 1566 | } 1567 | 1568 | /** 1569 | * abort scroll if it is scrolling 1570 | */ 1571 | public void abortIfRunning() { 1572 | if (mRunning) { 1573 | if (!mScroller.isFinished()) { 1574 | mAbort = true; 1575 | mScroller.forceFinished(true); 1576 | } 1577 | finish(); 1578 | mAbort = false; 1579 | } 1580 | } 1581 | 1582 | /** 1583 | * The param yScrolled here isn't final pos of y. 1584 | * It's just like the yScrolled param in the 1585 | * {@link #updateScroll(float yScrolled)} 1586 | * 1587 | * @param yScrolled 1588 | * @param duration 1589 | */ 1590 | private void autoScroll(int yScrolled, int duration) { 1591 | removeCallbacks(this); 1592 | mmLastY = 0; 1593 | if (!mScroller.isFinished()) { 1594 | mScroller.forceFinished(true); 1595 | } 1596 | mScroller.startScroll(0, 0, 0, yScrolled, duration); 1597 | post(this); 1598 | mRunning = true; 1599 | } 1600 | } 1601 | 1602 | /** 1603 | * Set the current status for better control 1604 | * 1605 | * @param status 1606 | */ 1607 | private void setStatus(int status) { 1608 | mStatus = status; 1609 | if (mDebug) { 1610 | STATUS.printStatus(status); 1611 | } 1612 | } 1613 | 1614 | /** 1615 | * an inner util class. 1616 | * enum of status 1617 | */ 1618 | private final static class STATUS { 1619 | private static final int STATUS_REFRESH_RETURNING = -4; 1620 | private static final int STATUS_REFRESHING = -3; 1621 | private static final int STATUS_RELEASE_TO_REFRESH = -2; 1622 | private static final int STATUS_SWIPING_TO_REFRESH = -1; 1623 | private static final int STATUS_DEFAULT = 0; 1624 | private static final int STATUS_SWIPING_TO_LOAD_MORE = 1; 1625 | private static final int STATUS_RELEASE_TO_LOAD_MORE = 2; 1626 | private static final int STATUS_LOADING_MORE = 3; 1627 | private static final int STATUS_LOAD_MORE_RETURNING = 4; 1628 | 1629 | private static boolean isRefreshing(final int status) { 1630 | return status == STATUS.STATUS_REFRESHING; 1631 | } 1632 | 1633 | private static boolean isLoadingMore(final int status) { 1634 | return status == STATUS.STATUS_LOADING_MORE; 1635 | } 1636 | 1637 | private static boolean isReleaseToRefresh(final int status) { 1638 | return status == STATUS.STATUS_RELEASE_TO_REFRESH; 1639 | } 1640 | 1641 | private static boolean isReleaseToLoadMore(final int status) { 1642 | return status == STATUS.STATUS_RELEASE_TO_LOAD_MORE; 1643 | } 1644 | 1645 | private static boolean isSwipingToRefresh(final int status) { 1646 | return status == STATUS.STATUS_SWIPING_TO_REFRESH; 1647 | } 1648 | 1649 | private static boolean isSwipingToLoadMore(final int status) { 1650 | return status == STATUS.STATUS_SWIPING_TO_LOAD_MORE; 1651 | } 1652 | 1653 | private static boolean isRefreshStatus(final int status) { 1654 | return status < STATUS.STATUS_DEFAULT; 1655 | } 1656 | 1657 | public static boolean isLoadMoreStatus(final int status) { 1658 | return status > STATUS.STATUS_DEFAULT; 1659 | } 1660 | 1661 | private static boolean isStatusDefault(final int status) { 1662 | return status == STATUS.STATUS_DEFAULT; 1663 | } 1664 | 1665 | private static String getStatus(int status) { 1666 | final String statusInfo; 1667 | switch (status) { 1668 | case STATUS_REFRESH_RETURNING: 1669 | statusInfo = "status_refresh_returning"; 1670 | break; 1671 | case STATUS_REFRESHING: 1672 | statusInfo = "status_refreshing"; 1673 | break; 1674 | case STATUS_RELEASE_TO_REFRESH: 1675 | statusInfo = "status_release_to_refresh"; 1676 | break; 1677 | case STATUS_SWIPING_TO_REFRESH: 1678 | statusInfo = "status_swiping_to_refresh"; 1679 | break; 1680 | case STATUS_DEFAULT: 1681 | statusInfo = "status_default"; 1682 | break; 1683 | case STATUS_SWIPING_TO_LOAD_MORE: 1684 | statusInfo = "status_swiping_to_load_more"; 1685 | break; 1686 | case STATUS_RELEASE_TO_LOAD_MORE: 1687 | statusInfo = "status_release_to_load_more"; 1688 | break; 1689 | case STATUS_LOADING_MORE: 1690 | statusInfo = "status_loading_more"; 1691 | break; 1692 | case STATUS_LOAD_MORE_RETURNING: 1693 | statusInfo = "status_load_more_returning"; 1694 | break; 1695 | default: 1696 | statusInfo = "status_illegal!"; 1697 | break; 1698 | } 1699 | return statusInfo; 1700 | } 1701 | 1702 | private static void printStatus(int status) { 1703 | Log.i(TAG, "printStatus:" + getStatus(status)); 1704 | } 1705 | } 1706 | } 1707 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/liangmutian/airrecyclerview/swipetoloadlayout/SwipeTrigger.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview.swipetoloadlayout; 2 | 3 | /** 4 | * Created by Aspsine on 2015/8/17. 5 | */ 6 | public interface SwipeTrigger { 7 | void onPrepare(); 8 | 9 | void onMove(int y, boolean isComplete, boolean automatic); 10 | 11 | void onRelease(); 12 | 13 | void onComplete(); 14 | 15 | void onReset(); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/anim_refresh.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_progressbar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | ] 19 | 20 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_choose_act_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | 29 | 30 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_choose_act_type2.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 20 | 21 | 22 | 31 | 32 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_footer_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_refresh_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 15 | 16 | 21 | 22 | 23 | 30 | 31 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_super_recycler.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 20 | 21 | 22 | 27 | 28 | 35 | 36 | 37 | 42 | 43 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_super_refresh_recycler.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 16 | 17 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 44 | 45 | 46 | 51 | 52 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/bg_loading_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/bg_loading_circle.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpf.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpg.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gph.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpi.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpj.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpk.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpl.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpm.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpn.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpq.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/gpr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/gpr.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/icon_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/icon_refresh.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/n1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/n1.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/n2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/n2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/n3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/n3.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/n4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/n4.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/n5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxhdpi/n5.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/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 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AirRecyclerView 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/liangmutian/airrecyclerview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.liangmutian.airrecyclerview; 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 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /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/AndroidMsky/GlideScrollRecyclerView/4ad14e6ef05b0ab32dc447bc41f73d41fa489e78/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 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-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------