Repository: liaohuqiu/android-Ultra-Pull-To-Refresh Branch: master Commit: 46fdc861243a Files: 124 Total size: 351.8 KB Directory structure: gitextract_757m5xqu/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── README-cn.md ├── README.md ├── build.gradle ├── buildSrc/ │ └── src/ │ └── main/ │ └── groovy/ │ └── copy-libs.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── pom.xml ├── proguard-project.txt ├── project.properties ├── ptr-demo/ │ ├── AndroidManifest.xml │ ├── assets/ │ │ └── request_init/ │ │ └── demo/ │ │ └── image-list.json │ ├── build.gradle │ ├── libs/ │ │ ├── clog-1.0.2-sources.jar │ │ ├── clog-1.0.2.jar │ │ └── support-v4-r7.jar │ ├── pom.xml │ ├── proguard-project.txt │ ├── proguard-rules.pro │ ├── project.properties │ ├── res/ │ │ ├── drawable/ │ │ │ ├── image_border.xml │ │ │ ├── text_view_bg.xml │ │ │ └── with_long_press_list_item_background.xml │ │ ├── layout/ │ │ │ ├── activity_main.xml │ │ │ ├── activity_view_pager.xml │ │ │ ├── fragment_classic_header_with_gridview.xml │ │ │ ├── fragment_classic_header_with_list_view.xml │ │ │ ├── fragment_classic_header_with_list_view_and_empty_view.xml │ │ │ ├── fragment_classic_header_with_scroll_view.xml │ │ │ ├── fragment_classic_header_with_textview.xml │ │ │ ├── fragment_classic_header_with_viewgroup.xml │ │ │ ├── fragment_classic_header_with_webview.xml │ │ │ ├── fragment_materail_style.xml │ │ │ ├── fragment_ptr_home.xml │ │ │ ├── fragment_storehouse_header.xml │ │ │ ├── fragment_view_pager.xml │ │ │ ├── fragment_with_long_press.xml │ │ │ ├── list_view_item.xml │ │ │ ├── view_pager_fragment_list_view_header.xml │ │ │ ├── view_pager_indicator_item.xml │ │ │ ├── with_grid_view_item_image_list_grid.xml │ │ │ └── with_long_press_list_view_item.xml │ │ ├── values/ │ │ │ ├── arrays.xml │ │ │ ├── ptr_demo_colors.xml │ │ │ └── strings.xml │ │ └── values-zh/ │ │ └── strings.xml │ └── src/ │ └── in/ │ └── srain/ │ └── cube/ │ └── views/ │ └── ptr/ │ └── demo/ │ ├── app/ │ │ └── PtrDemoApplication.java │ ├── data/ │ │ └── DemoRequestData.java │ ├── image/ │ │ ├── DemoDuiTangImageReSizer.java │ │ ├── Images.java │ │ └── PtrImageLoadHandler.java │ └── ui/ │ ├── EnableNextPTRAtOnce.java │ ├── MaterialStyleFragment.java │ ├── MaterialStylePinContentFragment.java │ ├── PtrDemoHomeFragment.java │ ├── RentalsStyleFragment.java │ ├── Utils.java │ ├── WithLongPressFragment.java │ ├── activity/ │ │ └── PtrDemoHomeActivity.java │ ├── classic/ │ │ ├── AutoRefresh.java │ │ ├── EvenOnlyATextView.java │ │ ├── HideHeader.java │ │ ├── KeepHeader.java │ │ ├── PullToRefresh.java │ │ ├── ReleaseToRefresh.java │ │ ├── WithGridView.java │ │ ├── WithListView.java │ │ ├── WithListViewAndEmptyView.java │ │ ├── WithScrollView.java │ │ ├── WithTextViewInFrameLayoutFragment.java │ │ └── WithWebView.java │ ├── header/ │ │ ├── RentalsSunDrawable.java │ │ └── RentalsSunHeaderView.java │ ├── storehouse/ │ │ ├── StoreHouseUsingPointList.java │ │ ├── StoreHouseUsingString.java │ │ └── StoreHouseUsingStringArray.java │ └── viewpager/ │ ├── ImageListViewHolder.java │ ├── ViewPagerActivity.java │ └── ViewPagerFragment.java ├── ptr-demo.apk ├── ptr-lib/ │ ├── AndroidManifest.xml │ ├── build.gradle │ ├── checkstyle.xml │ ├── gradle-mvn-push.gradle │ ├── gradle.properties │ ├── libs/ │ │ ├── clog-1.0.2-sources.jar │ │ └── clog-1.0.2.jar │ ├── pom.xml │ ├── res/ │ │ ├── layout/ │ │ │ ├── cube_ptr_classic_default_header.xml │ │ │ └── cube_ptr_simple_loading.xml │ │ ├── values/ │ │ │ ├── cube_ptr_attrs.xml │ │ │ └── cube_ptr_string.xml │ │ └── values-zh/ │ │ └── cube_ptr_string.xml │ └── src/ │ └── in/ │ └── srain/ │ └── cube/ │ └── views/ │ └── ptr/ │ ├── PtrClassicDefaultHeader.java │ ├── PtrClassicFrameLayout.java │ ├── PtrDefaultHandler.java │ ├── PtrFrameLayout.java │ ├── PtrHandler.java │ ├── PtrUIHandler.java │ ├── PtrUIHandlerHolder.java │ ├── PtrUIHandlerHook.java │ ├── header/ │ │ ├── MaterialHeader.java │ │ ├── MaterialProgressDrawable.java │ │ ├── StoreHouseBarItem.java │ │ ├── StoreHouseHeader.java │ │ └── StoreHousePath.java │ ├── indicator/ │ │ ├── PtrIndicator.java │ │ └── PtrTensionIndicator.java │ └── util/ │ ├── PtrCLog.java │ └── PtrLocalDisplay.java ├── publish-to-maven.sh ├── settings.gradle ├── template/ │ ├── README-cn.md │ ├── README.md │ ├── ptr-demo/ │ │ ├── build.gradle │ │ └── pom.xml │ └── ptr-lib/ │ ├── gradle.properties │ └── pom.xml └── update-project.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # built application files *.ap_ # files for the dex VM *.dex *.iml # Java class files *.class # generated files bin/ gen/ *target/ *build/ .gradle/ .idea/ gen-external-apklibs/ # Local configuration file (sdk path, etc) local.properties # Eclipse project files .classpath .project # Mac os .DS_Store ================================================ FILE: .travis.yml ================================================ language: android android: components: - build-tools-20.0.0 - android-16 jdk: oraclejdk7 script: - ./gradlew check --refresh-dependencies ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) <2015> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README-cn.md ================================================ ###### 欢迎关注我 GitHub: https://github.com/liaohuqiu 微博: http://weibo.com/liaohuqiu 知乎: http://www.zhihu.com/people/liao-hu-qiu --- # Ultra Pull To Refresh 这是现在已经停止维护的下拉刷新项目的替代方案。继承于ViewGroup可以包含任何View。功能比SwipeRefreshLayout强大。 使用起来非常简单。良好的设计,如果你想定制自己的UI样式,非常简单,就像给ListView加一个Header View那么简单。 支持 `API LEVEL >= 8`。 [APK下载](https://raw.githubusercontent.com/liaohuqiu/android-Ultra-Pull-To-Refresh/master/ptr-demo.apk) #### 下拉刷新 + 加载更多? 本类库是单纯的下拉刷新。如果你需要用到`加载更多`,看这个项目: https://github.com/liaohuqiu/android-cube-app #### 使用eclipse的同学请注意, Intellij IDEA / Android Studio 请忽略 **demo可以直接在eclipse中运行, 编译demo项目的同学看这里: http://www.liaohuqiu.net/cn/posts/compile-ultra-ptr-in-eclipse/** * 先上两张StoreHouse风格的截图! 感谢 [CBStoreHouseRefreshControl](https://github.com/coolbeet/CBStoreHouseRefreshControl).
* 5.0 Material 风格 2014-12-09 新增。**阴影效果,gif图看起来有些失真,看demo吧!**
* **支持所有的View**: ListView, GridView, ScrollView, FrameLayout, 甚至 TextView.
* 支持各种下拉刷新交互. * 下拉刷新(iOS风格)
* 释放刷新(经典风格)
* 刷新时,头部保持(新浪微博) * 刷新时,头部不保持(微信朋友圈) * 自动刷新,进入界面时自动刷新 # 使用方式 #### 中央库依赖 项目已经发布到了Maven中央库,包括`aar`和`apklib`两种格式。在Maven或者Gradle下可如下直接引入: 最新版版本号: `1.0.11`, 发布到了: https://oss.sonatype.org/content/repositories/snapshots 在gradle中: ``` maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } ``` 稳定版: `1.0.11`, https://oss.sonatype.org/content/repositories/releases, in gradle: ``` mavenCentral() ``` `pom.xml` 文件中 最新版: ```xml in.srain.cube ultra-ptr aar 1.0.11 稳定版 ```xml in.srain.cube ultra-ptr aar 1.0.11 ``` gradle / Android Studio, 最新版 ``` compile 'in.srain.cube:ultra-ptr:1.0.11' ``` gradle / Android Studio, 稳定版 ``` compile 'in.srain.cube:ultra-ptr:1.0.11' ``` #### 配置 有6个参数可配置: * 阻尼系数 默认: `1.7f`,越大,感觉下拉时越吃力。 * 触发刷新时移动的位置比例 默认,`1.2f`,移动达到头部高度1.2倍时可触发刷新操作。 * 回弹延时 默认 `200ms`,回弹到刷新高度所用时间 * 头部回弹时间 默认`1000ms` * 刷新是保持头部 默认值 `true`. * 下拉刷新 / 释放刷新 默认为释放刷新 ##### xml中配置示例 ```xml ``` ### 也可以在java代码中配置 ```java // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); ``` ### 其他配置 * 刷新时,保持内容不动,仅头部下移, `setPinContent()` `Material` 风格时,效果不错,其他风格的头部,效果不好。issue #29 ## StoreHouse 风格 * 使用字符串, 支持A-Z, 0-7 以及 `-` `.` ```java // header final StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(15), 0, 0); /** * using a string, support: A-Z 0-9 - . * you can add more letters by {@link in.srain.cube.views.ptr.header.StoreHousePath#addChar} */ header.initWithString('Alibaba'); ``` * 使用资源文件中的路径信息 ```java header.initWithStringArray(R.array.storehouse); ``` 资源文件 `res/values/arrays.xml` 内容如下: ```xml 0,35,12,42, 12,42,24,35, 24,35,12,28, 0,35,12,28, 0,21,12,28, 12,28,24,21, 24,35,24,21, 24,21,12,14, 0,21,12,14, 0,21,0,7, 12,14,0,7, 12,14,24,7, 24,7,12,0, 0,7,12,0, ``` # 处理刷新 通过`PtrHandler`,可以检查确定是否可以下来刷新以及在合适的时间刷新数据。 检查是否可以下拉刷新在`PtrDefaultHandler.checkContentCanBePulledDown`中有默认简单的实现,你可以根据实际情况完成这个逻辑。 ``` public interface PtrHandler { /** * 检查是否可以执行下来刷新,比如列表为空或者列表第一项在最上面时。 *

* {@link in.srain.cube.views.ptr.PtrDefaultHandler#checkContentCanBePulledDown} */ public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); /** * 需要加载数据时触发 * * @param frame */ public void onRefreshBegin(final PtrFrameLayout frame); } ``` 例子: ```java ptrFrame.setPtrHandler(new PtrHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.refreshComplete(); } }, 1800); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { // 默认实现,根据实际情况做改动 return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } }); ``` # 常见问题 * ViewPager滑动冲突: `disableWhenHorizontalMove()` * 长按LongPressed, `setInterceptEventWhileWorking()` # 联系方式和问题建议 * 微博: http://weibo.com/liaohuqiu * QQ 群: 1. cube系列开源项目使用交流,问题解答: 271918140 (cube-sdk) 加群前请先阅读群约定: https://github.com/liaohuqiu/qq-tribe-rule 2. 如果你会通过google解决问题,喜欢独立思考,喜欢和优秀却又温和的人成为朋友,欢迎加入我们在 Slack 上的群组: 我们在 Slack 上的群组: http://join-add1bit.liaohuqiu.net/?channel=git-android-ultra-ptr * srain@php.net * twitter: https://twitter.com/liaohuqiu * blog: http://www.liaohuqiu.net ================================================ FILE: README.md ================================================ ###### Welcome to follow me on GitHub or Twitter GitHub: https://github.com/liaohuqiu Twitter: https://twitter.com/liaohuqiu --- [![Build Status](https://travis-ci.org/liaohuqiu/android-Ultra-Pull-To-Refresh.svg?branch=master)](https://travis-ci.org/liaohuqiu/android-Ultra-Pull-To-Refresh) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Ultra%20Pull%20To%20Refresh-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1180) #### [中文版文档](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/blob/master/README-cn.md) Wanna auto-load-more? This will be what you want: https://github.com/liaohuqiu/android-cube-app # Ultra Pull To Refresh It's a replacement for the deprecated pull to refresh solution. It can contain any view you want. It's easy to use and more powerful than SwipeRefreshLayout. It's well designed, you can customize the UI effect you want as easy as adding a headview to ListView. Support `API LEVEL >= 8`, all snapshots are taken from Genymotion, 2.3.7. [Download APK](https://raw.githubusercontent.com/liaohuqiu/android-Ultra-Pull-To-Refresh/master/ptr-demo.apk) * StoreHouse Style first! Thanks to [CBStoreHouseRefreshControl](https://github.com/coolbeet/CBStoreHouseRefreshControl).

* Material Style, added @ 2014-12-09. **There is a beautiful shadow which looks terrible in gif snapshot. Please Check out the DEMO.**
* **Supports all of the views**: ListView, GridView, ScrollView, FrameLayout, or Even a single TextView.
* Supports all of the refresh types. * pull to refresh
* release to refresh
* keep header when refresh * hide header when refresh * auto refresh # Usage #### Maven Central This project has been pushed to Maven Central, both in `aar` and `apklib`. The latest version: `1.0.11`, has been published to: https://oss.sonatype.org/content/repositories/snapshots, in gradle: ``` maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } ``` The stable version: `1.0.11`, https://oss.sonatype.org/content/repositories/releases, in gradle: ``` mavenCentral() ``` pom.xml, latest version: ```xml in.srain.cube ultra-ptr aar 1.0.11 ``` pom.xml, stable version: ```xml in.srain.cube ultra-ptr aar 1.0.11 ``` gradle, latest version: ``` compile 'in.srain.cube:ultra-ptr:1.0.11' ``` gradle, stable version: ``` compile 'in.srain.cube:ultra-ptr:1.0.11' ``` #### Config There are 6 properties: * Resistence This is the resistence while you are moving the frame, default is: `1.7f`. * Ratio of the Height of the Header to Refresh The ratio of the height of the header to trigger refresh, default is: `1.2f`. * Duration to Close The duration for moving from the position you relase the view to the height of header, default is `200ms`. * Duration to Close Header The default value is `1000ms` * Keep Header while Refreshing The default value is `true`. * Pull to Refresh / Release to Refresh The default value is Release to Refresh. ##### Config in xml ```xml ``` ### Or config in java code ```java // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); ``` ### Other Config * `setPinContent`. Pin the content, only the `HeaderView` will be moved. This's the the performance of material style in support package v19. ## StoreHouse Style * Config using string: ```java // header final StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(15), 0, 0); /** * using a string, support: A-Z 0-9 - . * you can add more letters by {@link in.srain.cube.views.ptr.header.StoreHousePath#addChar} */ header.initWithString('Alibaba'); ``` * Config using string array from xml: ```java header.initWithStringArray(R.array.storehouse); ``` And in `res/values/arrays.xml`: ```xml 0,35,12,42, 12,42,24,35, 24,35,12,28, 0,35,12,28, 0,21,12,28, 12,28,24,21, 24,35,24,21, 24,21,12,14, 0,21,12,14, 0,21,0,7, 12,14,0,7, 12,14,24,7, 24,7,12,0, 0,7,12,0, ``` # Process Refresh There is a `PtrHandler`, by which you can refresh the data. ``` public interface PtrHandler { /** * Check can do refresh or not. For example the content is empty or the first child is in view. *

* {@link in.srain.cube.views.ptr.PtrDefaultHandler#checkContentCanBePulledDown} */ public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); /** * When refresh begin * * @param frame */ public void onRefreshBegin(final PtrFrameLayout frame); } ``` An example: ```java ptrFrame.setPtrHandler(new PtrHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.refreshComplete(); } }, 1800); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } }); ``` # Customize You can add a `PtrUIHandler` to `PtrFrameLayout` to implement any UI effect you want. ```java public interface PtrUIHandler { /** * When the content view has reached top and refresh has been completed, view will be reset. * * @param frame */ public void onUIReset(PtrFrameLayout frame); /** * prepare for loading * * @param frame */ public void onUIRefreshPrepare(PtrFrameLayout frame); /** * perform refreshing UI */ public void onUIRefreshBegin(PtrFrameLayout frame); /** * perform UI after refresh */ public void onUIRefreshComplete(PtrFrameLayout frame); public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int oldPosition, int currentPosition, float oldPercent, float currentPercent); } ``` # Q & A * work with ViewPager: `disableWhenHorizontalMove()` * work with LongPressed, `setInterceptEventWhileWorking()` # Contact & Help Please fell free to contact me if there is any problem when using the library. * srain@php.net * twitter: https://twitter.com/liaohuqiu * weibo: http://weibo.com/liaohuqiu * blog: http://www.liaohuqiu.net 1. About how to use cube-sdk / Ultra Ptr: 271918140 (cube-sdk) This the rule for our tribes, please read it before you request to join: https://github.com/liaohuqiu/qq-tribe-rule 2. For those who like thinking independently and are good at solving problem independently. Please join us, we are all here on Slack: http://join-add1bit.liaohuqiu.net/ ================================================ FILE: build.gradle ================================================ buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.0.0' } } allprojects { repositories { jcenter() mavenCentral(); maven { url 'https://oss.sonatype.org/content/repositories/releases' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } } } ================================================ FILE: buildSrc/src/main/groovy/copy-libs.gradle ================================================ buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' } } EXCLUDE_JAR_LIST = ['cube-sdk', 'support-v4-r7-source', 'ultra-ptr', 'support-annotation'] class CopyLibsTask extends DefaultTask { @TaskAction def copyLibs() { // copy jars copy { from configurations.compile into "libs" eachFile { file -> println file.name for (name in EXCLUDE_JAR_LIST) { if (file.name.contains(name)) { file.exclude() return } } } } // copy source copy { def sourceList = configurations.compile.incoming.dependencies.collect { dependency -> dependency.artifact { artifact -> artifact.name = dependency.name artifact.type = 'source' artifact.extension = 'jar' artifact.classifier = 'sources' } dependency } from(configurations.detachedConfiguration(sourceList as Dependency[]).resolvedConfiguration. resolvedConfiguration.lenientConfiguration.getFiles(Specs.SATISFIES_ALL)) { } into("libs") eachFile { file -> println file.name for (name in EXCLUDE_JAR_LIST) { if (file.name.contains(name)) { file.exclude() return } } } } } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Thu Dec 18 15:33:58 CST 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip ================================================ FILE: gradle.properties ================================================ ANDROID_BUILD_MIN_SDK_VERSION=8 ANDROID_BUILD_TARGET_SDK_VERSION=22 ANDROID_BUILD_SDK_VERSION=22 ANDROID_BUILD_TOOLS_VERSION=22.0.1 ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: pom.xml ================================================ 4.0.0 in.srain cube-parent-for-oss 1.0.1 in.srain cube-ptr-parent 1.0.1 pom Ultra Pull to Refersh Parent ptr-lib ptr-demo ================================================ FILE: proguard-project.txt ================================================ # To enable ProGuard in your project, edit project.properties # to define the proguard.config property as described in that file. # # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in ${sdk.dir}/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the ProGuard # include property in project.properties. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} -keep class in.srain.* -dontwarn class in.srain.cube.image.ImageProvider -dontwarn class android.graphics.Bitmap -ignorewarnings class in.srain.cube.image.ImageProvider -ignorewarnings class android.graphics.Bitmap ================================================ FILE: project.properties ================================================ # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system edit # "ant.properties", and override values to adapt the script to your # project structure. # # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt android.library=true # Project target. target=android-19 android.library.reference.1=../../android-CLog ================================================ FILE: ptr-demo/AndroidManifest.xml ================================================ ================================================ FILE: ptr-demo/assets/request_init/demo/image-list.json ================================================ {"data":{"time":"2014-08-27 02:13:01 UTC","list":[{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201311\/04\/20131104193715_NCexN.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201408\/09\/20140809210610_iTSJx.thumb.jpeg"},{"pic":"http:\/\/cdn.duitang.com\/uploads\/blog\/201401\/07\/20140107223310_LH3Uy.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201405\/09\/20140509222156_kVexJ.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201306\/14\/20130614185903_raNR3.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201112\/11\/20111211191621_HU4Bj.thumb.jpg"},{"pic":"http:\/\/cdn.duitang.com\/uploads\/item\/201408\/07\/20140807224553_VXaUc.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/29\/20140729105929_GQLwC.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201408\/04\/20140804160432_LnFeB.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201408\/04\/20140804161101_JVJea.thumb.jpeg"},{"pic":"http:\/\/cdn.duitang.com\/uploads\/blog\/201408\/04\/20140804093210_FxFNd.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201408\/04\/20140804160314_hRKtv.thumb.jpeg"},{"pic":"http:\/\/cdn.duitang.com\/uploads\/item\/201408\/01\/20140801080524_SnGfE.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/23\/20140723140958_NSWfE.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201407\/22\/20140722153305_WHejQ.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/21\/20140721010148_ZBQwe.thumb.jpeg"},{"pic":"http:\/\/cdn.duitang.com\/uploads\/item\/201407\/23\/20140723085122_cmteu.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/23\/20140723130620_Z2yJB.thumb.jpeg"},{"pic":"http:\/\/cdn.duitang.com\/uploads\/blog\/201407\/20\/20140720204738_NXxLE.thumb.jpeg"},{"pic":"http:\/\/cdn.duitang.com\/uploads\/item\/201407\/20\/20140720134916_VGfyh.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/17\/20140717113117_mUssJ.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/17\/20140717121501_wfFEm.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/17\/20140717121431_w4AV8.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/17\/20140717121918_TtJjY.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/11\/20140711234806_FNLBQ.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/18\/20140718121437_UyiAS.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/17\/20140717114829_RiCXR.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201407\/17\/20140717120301_wGFYL.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/11\/20140511121106_JXS4B.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/10\/20140510094144_kfLji.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/09\/20140509201906_kERjy.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/08\/20140508193226_UaSGB.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201405\/05\/20140505201747_aPNtf.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/06\/20140506104824_jPWQj.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/05\/20140505201105_MkXdn.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/03\/20140503205822_GCzta.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/03\/20140503205535_cCHPB.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201405\/03\/20140503204354_xxSQX.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201404\/06\/20140406191307_GTxFd.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201404\/06\/20140406191247_BG2cU.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201404\/06\/20140406191114_MzYtw.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201404\/06\/20140406191127_fazJA.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/10\/20140710081204_vYnCi.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/15\/20140715133758_M2Y3J.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/item\/201407\/13\/20140713190806_TGJHm.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201407\/05\/20140705223413_5r3ze.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/13\/20140713012526_tcy5u.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/13\/20140713121424_Gy43T.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/item\/201407\/15\/20140715033844_tcvrY.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/10\/20140710181106_4HHay.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/06\/20140706122850_8PER3.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/07\/20140707192042_8xKXF.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/07\/20140707063954_mVa3y.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201407\/08\/20140708093733_AFFhc.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201407\/07\/20140707161220_unvzn.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201407\/07\/20140707113856_hBf3R.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201308\/26\/20130826090203_NtuYA.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/item\/201407\/07\/20140707145925_dHeKV.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/25\/20140625101534_xiZxN.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201406\/30\/20140630150534_EWUVY.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/25\/20140625121626_ZmT5n.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/blog\/201307\/31\/20130731231806_4yGxV.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/28\/20140628122218_fLQyP.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201406\/26\/20140626131831_MrdKP.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/blog\/201406\/16\/20140616165201_nuKWj.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/25\/20140625140308_KP4rn.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/25\/20140625121604_2auuA.thumb.jpeg"},{"pic":"http:\/\/img4.duitang.com\/uploads\/item\/201406\/25\/20140625131625_LmmLZ.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/25\/20140625132851_mPmKY.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/25\/20140625133312_ZtmW4.thumb.jpeg"},{"pic":"http:\/\/img5.duitang.com\/uploads\/item\/201406\/25\/20140625164858_AuafS.thumb.jpeg"}]},"server_time":"2014-08-27 02:13:01"} ================================================ FILE: ptr-demo/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION defaultConfig { applicationId "in.srain.cube.views.ptr.demo" minSdkVersion 8 targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) } lintOptions { abortOnError false } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['resources'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] // aidl.srcDirs = ['src'] // renderscript.srcDirs = ['src'] } androidTest.setRoot('tests') } dexOptions { preDexLibraries = false } } def EXCLUDE_JAR_LIST = ['cube-sdk', 'support-v4-r7-source', 'ultra-ptr', 'support-annotation', 'ptr-lib'] task copyLibs << { // copy jars copy { from configurations.compile into "libs" eachFile { file -> println file.name for (name in EXCLUDE_JAR_LIST) { if (file.name.contains(name)) { file.exclude() return } } } } // copy source copy { def sourceList = configurations.compile.incoming.dependencies.collect { dependency -> dependency.artifact { artifact -> artifact.name = dependency.name artifact.type = 'source' artifact.extension = 'jar' artifact.classifier = 'sources' } dependency } from(configurations.detachedConfiguration(sourceList as Dependency[]).resolvedConfiguration. resolvedConfiguration.lenientConfiguration.getFiles(Specs.SATISFIES_ALL)) { } into("libs") eachFile { file -> println file.name for (name in EXCLUDE_JAR_LIST) { if (file.name.contains(name)) { file.exclude() return } } } } } dependencies { compile(project(':ptr-lib')) { } compile 'in.srain.cube:clog:1.0.2' compile 'in.srain.cube:cube-sdk:1.0.44.39-SNAPSHOT@aar' compile 'com.google.android:support-v4:r7' } ================================================ FILE: ptr-demo/pom.xml ================================================ 4.0.0 in.srain cube-parent-for-oss 1.0.1 in.srain.cube ultra-ptr-demo apk Ultra Pull to Refresh Demo 1.0.1 com.google.android android 4.1.1.4 provided com.google.android support-v4 r7 in.srain.cube ultra-ptr aar 1.0.11 in.srain.cube cube-sdk aar 1.0.44.39-SNAPSHOT 3.8.2 16 ${project.artifactId} src/ org.apache.maven.plugins maven-compiler-plugin 1.6 1.6 com.jayway.maven.plugins.android.generation2 android-maven-plugin true ${android_maven_plugin_version} ${android_compile_api_level} false ================================================ FILE: ptr-demo/proguard-project.txt ================================================ # To enable ProGuard in your project, edit project.properties # to define the proguard.config property as described in that file. # # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in ${sdk.dir}/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the ProGuard # include property in project.properties. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: ptr-demo/proguard-rules.pro ================================================ -keep class in.srain.* -dontwarn android.graphics.Bitmap ================================================ FILE: ptr-demo/project.properties ================================================ # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system edit # "ant.properties", and override values to adapt the script to your # project structure. # # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. target=android-19 ================================================ FILE: ptr-demo/res/drawable/image_border.xml ================================================ ================================================ FILE: ptr-demo/res/drawable/text_view_bg.xml ================================================ ================================================ FILE: ptr-demo/res/drawable/with_long_press_list_item_background.xml ================================================ ================================================ FILE: ptr-demo/res/layout/activity_main.xml ================================================ ================================================ FILE: ptr-demo/res/layout/activity_view_pager.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_classic_header_with_gridview.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_classic_header_with_list_view.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_classic_header_with_list_view_and_empty_view.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_classic_header_with_scroll_view.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_classic_header_with_textview.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_classic_header_with_viewgroup.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_classic_header_with_webview.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_materail_style.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_ptr_home.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_storehouse_header.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_view_pager.xml ================================================ ================================================ FILE: ptr-demo/res/layout/fragment_with_long_press.xml ================================================ ================================================ FILE: ptr-demo/res/layout/list_view_item.xml ================================================ ================================================ FILE: ptr-demo/res/layout/view_pager_fragment_list_view_header.xml ================================================ ================================================ FILE: ptr-demo/res/layout/view_pager_indicator_item.xml ================================================ ================================================ FILE: ptr-demo/res/layout/with_grid_view_item_image_list_grid.xml ================================================ ================================================ FILE: ptr-demo/res/layout/with_long_press_list_view_item.xml ================================================ ================================================ FILE: ptr-demo/res/values/arrays.xml ================================================ 22,0,0,30, 22,0,30,0, 30,0,52,30, 0,30,26,30, 60,0,60,15, 60,15,60,30, 60,15,85,15, 85,15,108,0, 85,15,108,30, 117,0,147,0, 147,0,177,0, 147,0,147,32, 198,0,176,30, 198,0,206,0, 206,0,228,30, 176,30,202,30, 0,35,12,42, 12,42,24,35, 24,35,12,28, 0,35,12,28, 0,21,12,28, 12,28,24,21, 24,35,24,21, 24,21,12,14, 0,21,12,14, 0,21,0,7, 12,14,0,7, 12,14,24,7, 24,7,12,0, 0,7,12,0, @color/red @color/blue @color/yellow @color/green ================================================ FILE: ptr-demo/res/values/ptr_demo_colors.xml ================================================ #FFC93437 #FF375BF1 #FFF7D23E #FF34A350 ================================================ FILE: ptr-demo/res/values/strings.xml ================================================ Ultra PTR Ultra Pull to Refresh Contains A GridView Contains A ListView Contains A WebView Contains ScrollView Contains A FrameLayout Contains Even A TextView EmptyView and ListView It\' empty. \n\nClick to load data. Pull to Refresh Release to Refresh Auto Refresh Keep Header while Refreshing Hide Header while Refreshing Long Press View Pager StoreHouse Style \nusing string array from xml using xml:  StoreHouse Style \n\nusing string using string:  Using Point List Material Style Rentals Style To be continued Material Style\nPin Content\n v4 style Enable Next PTR at once ================================================ FILE: ptr-demo/res/values-zh/strings.xml ================================================ Ultra PTR 自动刷新 包含FrameLayout 包含GridView 刷新时不显示头部 刷新时显示头部 仅包含一个TextView 下拉刷新 释放刷新 StoreHouse Style \n使用xml中的字符串数组 使用xml:   StoreHouse Style \n\n使用字符串 使用字符串:  Material Style\n内容不动,原生风格 立即下拉刷新,不等UIReset ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/app/PtrDemoApplication.java ================================================ package in.srain.cube.views.ptr.demo.app; import android.app.Application; import in.srain.cube.Cube; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.request.RequestCacheManager; import in.srain.cube.util.CLog; import in.srain.cube.util.CubeDebug; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.demo.image.DemoDuiTangImageReSizer; import in.srain.cube.views.ptr.demo.image.PtrImageLoadHandler; public class PtrDemoApplication extends Application { public static PtrDemoApplication instance; @Override public void onCreate() { super.onCreate(); instance = this; String environment = ""; if (environment.equals("production")) { CLog.setLogLevel(CLog.LEVEL_ERROR); } else if (environment.equals("beta")) { CLog.setLogLevel(CLog.LEVEL_WARNING); } else { // development CLog.setLogLevel(CLog.LEVEL_VERBOSE); } CubeDebug.DEBUG_IMAGE = true; PtrFrameLayout.DEBUG = true; PtrFrameLayout.DEBUG = false; ImageLoaderFactory.setDefaultImageReSizer(DemoDuiTangImageReSizer.getInstance()); ImageLoaderFactory.setDefaultImageLoadHandler(new PtrImageLoadHandler()); String dir = "request-cache"; // ImageLoaderFactory.init(this); RequestCacheManager.init(this, dir, 1024 * 10, 1024 * 10); Cube.onCreate(this); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/data/DemoRequestData.java ================================================ package in.srain.cube.views.ptr.demo.data; import in.srain.cube.request.*; public class DemoRequestData { public static void getImageList(final RequestFinishHandler requestFinishHandler) { CacheAbleRequestHandler requestHandler = new CacheAbleRequestJsonHandler() { @Override public void onCacheAbleRequestFinish(JsonData data, CacheAbleRequest.ResultType type, boolean outOfDate) { requestFinishHandler.onRequestFinish(data); } }; CacheAbleRequest request = new CacheAbleRequest(requestHandler); String url = "http://cube-server.liaohuqiu.net/api_demo/image-list.php"; request.setCacheTime(3600); request.setTimeout(1000); request.getRequestData().setRequestUrl(url); request.setAssertInitDataPath("request_init/demo/image-list.json"); request.setCacheKey("image-list-1"); request.send(); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/image/DemoDuiTangImageReSizer.java ================================================ package in.srain.cube.views.ptr.demo.image; import in.srain.cube.image.ImageTask; import in.srain.cube.image.impl.DefaultImageReSizer; import in.srain.cube.util.CLog; /** * Created by srain on 8/19/14. */ public class DemoDuiTangImageReSizer extends DefaultImageReSizer { private static DemoDuiTangImageReSizer sInstance; public static DemoDuiTangImageReSizer getInstance() { if (sInstance == null) { sInstance = new DemoDuiTangImageReSizer(); } return sInstance; } private static String TAG = "thumb"; private static String SP = "_"; private static String DOT = "."; private static final int[] CDN_FIX_WIDTH_SIZE = {110, 150, 170, 220, 240, 290, 450, 580, 620, 790}; @Override public String getRemoteUrl(ImageTask imageTask) { String url = imageTask.getOriginUrl(); int size = findBestCDNSize(CDN_FIX_WIDTH_SIZE, imageTask.getRequestSize().x, true); url = url.replace(TAG, TAG + DOT + size + SP + size); CLog.d("test", "getRemoteUrl: %s %s", imageTask.getRequestSize(), url); return url; } private static int binarySearch(int[] srcArray, int des, boolean higher) { int low = 0; int high = srcArray.length - 1; while (low <= high) { int middle = (low + high) / 2; if (des == srcArray[middle]) { return middle; } else if (des < srcArray[middle]) { high = middle - 1; } else { low = middle + 1; } } if (high < 0) return 0; if (higher) { if (des > srcArray[high] && high + 1 <= srcArray.length - 1) { high = high + 1; } } else { if (des < srcArray[high] && high - 1 >= 0) high = high - 1; } return high; } private static int findBestCDNSize(int[] array, int size, boolean higher) { if (size >= array[array.length - 1]) { return array[array.length - 1]; } int pos = binarySearch(array, size, higher); return array[pos]; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/image/Images.java ================================================ package in.srain.cube.views.ptr.demo.image; import in.srain.cube.image.ImageReuseInfoManger; public class Images { private static final String[] sizeList = new String[]{"small_180", "big_360", "big"}; public static final ImageReuseInfoManger sImageReuseInfoManger = new ImageReuseInfoManger(sizeList); public final static String[] imageUrls = new String[]{ "http://img4.duitang.com/uploads/blog/201311/04/20131104193715_NCexN.thumb.jpeg", "http://img5.duitang.com/uploads/item/201408/09/20140809210610_iTSJx.thumb.jpeg", "http://cdn.duitang.com/uploads/blog/201401/07/20140107223310_LH3Uy.thumb.jpeg", "http://img5.duitang.com/uploads/item/201405/09/20140509222156_kVexJ.thumb.jpeg", "http://img5.duitang.com/uploads/item/201306/14/20130614185903_raNR3.thumb.jpeg", "http://img5.duitang.com/uploads/item/201112/11/20111211191621_HU4Bj.thumb.jpg", "http://cdn.duitang.com/uploads/item/201408/07/20140807224553_VXaUc.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/29/20140729105929_GQLwC.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201408/04/20140804160432_LnFeB.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201408/04/20140804161101_JVJea.thumb.jpeg", "http://cdn.duitang.com/uploads/blog/201408/04/20140804093210_FxFNd.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201408/04/20140804160314_hRKtv.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201408/01/20140801080524_SnGfE.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/23/20140723140958_NSWfE.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201407/22/20140722153305_WHejQ.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/21/20140721010148_ZBQwe.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201407/23/20140723085122_cmteu.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/23/20140723130620_Z2yJB.thumb.jpeg", "http://cdn.duitang.com/uploads/blog/201407/20/20140720204738_NXxLE.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201407/20/20140720134916_VGfyh.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/17/20140717113117_mUssJ.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/17/20140717121501_wfFEm.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/17/20140717121431_w4AV8.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/17/20140717121918_TtJjY.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/11/20140711234806_FNLBQ.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/18/20140718121437_UyiAS.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/17/20140717114829_RiCXR.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201407/17/20140717120301_wGFYL.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/11/20140511121106_JXS4B.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/10/20140510094144_kfLji.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/09/20140509201906_kERjy.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/08/20140508193226_UaSGB.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201405/05/20140505201747_aPNtf.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/06/20140506104824_jPWQj.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/05/20140505201105_MkXdn.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/03/20140503205822_GCzta.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/03/20140503205535_cCHPB.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/03/20140503204354_xxSQX.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201404/06/20140406191307_GTxFd.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201404/06/20140406191247_BG2cU.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201404/06/20140406191114_MzYtw.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201404/06/20140406191127_fazJA.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/10/20140710081204_vYnCi.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/15/20140715133758_M2Y3J.thumb.jpeg", "http://img4.duitang.com/uploads/item/201407/13/20140713190806_TGJHm.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201407/05/20140705223413_5r3ze.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/13/20140713012526_tcy5u.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/13/20140713121424_Gy43T.thumb.jpeg", "http://img4.duitang.com/uploads/item/201407/15/20140715033844_tcvrY.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/10/20140710181106_4HHay.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/06/20140706122850_8PER3.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/07/20140707192042_8xKXF.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/07/20140707063954_mVa3y.thumb.jpeg", "http://img5.duitang.com/uploads/item/201407/08/20140708093733_AFFhc.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201407/07/20140707161220_unvzn.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201407/07/20140707113856_hBf3R.thumb.jpeg", "http://img5.duitang.com/uploads/item/201308/26/20130826090203_NtuYA.thumb.jpeg", "http://img4.duitang.com/uploads/item/201407/07/20140707145925_dHeKV.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625101534_xiZxN.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201406/30/20140630150534_EWUVY.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625121626_ZmT5n.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201307/31/20130731231806_4yGxV.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/28/20140628122218_fLQyP.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201406/26/20140626131831_MrdKP.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201406/16/20140616165201_nuKWj.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625140308_KP4rn.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625121604_2auuA.thumb.jpeg", "http://img4.duitang.com/uploads/item/201406/25/20140625131625_LmmLZ.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625132851_mPmKY.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625133312_ZtmW4.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625164858_AuafS.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/24/20140624114145_e4iVw.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/25/20140625100427_Hkxj5.thumb.jpeg", "http://cdn.duitang.com/uploads/blog/201406/25/20140625213455_VHHcL.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/25/20140625132659_UuES4.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/24/20140624020050_zCE4U.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/18/20140618152533_dJjtW.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/20/20140620075216_twZE4.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/22/20140622162247_Z4WK4.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/20/20140620075158_TnyKU.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201406/18/20140618235506_5QJwc.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/18/20140618152515_AFcLy.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/03/20140603001954_NjKfX.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201405/31/20140531232042_4FkHQ.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201406/13/20140613002234_LHXcT.thumb.jpeg", "http://img4.duitang.com/uploads/item/201405/31/20140531231843_J5Euh.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/11/20140611220941_xBeyi.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/13/20140613114809_yuHRV.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/13/20140613120109_yL8hk.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201406/01/20140601185856_Q5jZr.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/06/20140606004724_GxQHQ.thumb.jpeg", "http://img4.duitang.com/uploads/item/201406/08/20140608003809_3JnEK.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/10/20140610085447_zeXJU.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201406/08/20140608193617_HyFrY.thumb.jpeg", "http://img4.duitang.com/uploads/item/201405/30/20140530190040_KQdsM.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/09/20140609101937_UBfJJ.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201406/10/20140610170410_cFhwW.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/09/20140609225334_PdGwG.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201406/09/20140609184438_e33i2.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201405/29/20140529200010_AfL8f.thumb.jpeg", "http://img4.duitang.com/uploads/blog/201406/08/20140608104649_KVtMx.thumb.png", "http://img5.duitang.com/uploads/item/201406/01/20140601215152_wi4wf.thumb.jpeg", "http://cdn.duitang.com/uploads/blog/201406/08/20140608194234_FEGkW.thumb.jpeg", "http://img4.duitang.com/uploads/item/201405/31/20140531221002_Awtv8.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/08/20140608091030_TJ3Cc.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201405/31/20140531221355_cSCTt.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/08/20140608005415_arBdK.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/08/20140608000002_2MTjn.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/03/20140603012613_z88sn.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201405/31/20140531221745_rnAzU.thumb.jpeg", "http://img5.duitang.com/uploads/item/201405/31/20140531220735_XBxFP.thumb.jpeg", "http://cdn.duitang.com/uploads/blog/201406/08/20140608194112_uEYf5.thumb.jpeg", "http://img5.duitang.com/uploads/blog/201406/08/20140608225626_xc2QT.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/07/20140607235759_sNS5Z.thumb.jpeg", "http://img5.duitang.com/uploads/item/201405/31/20140531220635_Lrw3w.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/08/20140608004245_jmBmP.thumb.jpeg", "http://img4.duitang.com/uploads/item/201406/08/20140608020213_SBfGH.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/08/20140608214105_kvVVY.thumb.jpeg", "http://img5.duitang.com/uploads/item/201406/03/20140603001556_XsMEv.thumb.jpeg", "http://cdn.duitang.com/uploads/item/201406/08/20140608024120_XjjGB.thumb.jpeg", }; } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/image/PtrImageLoadHandler.java ================================================ package in.srain.cube.views.ptr.demo.image; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageTask; import in.srain.cube.image.iface.ImageLoadHandler; public class PtrImageLoadHandler implements ImageLoadHandler { @Override public void onLoading(ImageTask imageTask, CubeImageView cubeImageView) { if (cubeImageView != null) { cubeImageView.setImageDrawable(new ColorDrawable(Color.GRAY)); } } @Override public void onLoadFinish(ImageTask imageTask, CubeImageView cubeImageView, BitmapDrawable bitmapDrawable) { if (bitmapDrawable != null && cubeImageView != null) { cubeImageView.setImageDrawable(bitmapDrawable); } } @Override public void onLoadError(ImageTask imageTask, CubeImageView cubeImageView, int i) { if (cubeImageView != null) { cubeImageView.setImageDrawable(new ColorDrawable(Color.RED)); } } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/EnableNextPTRAtOnce.java ================================================ package in.srain.cube.views.ptr.demo.ui; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.ui.classic.WithTextViewInFrameLayoutFragment; public class EnableNextPTRAtOnce extends WithTextViewInFrameLayoutFragment { @Override protected void setupViews(PtrClassicFrameLayout ptrFrame) { setHeaderTitle(R.string.ptr_demo_enable_next_ptr_at_once); ptrFrame.setEnabledNextPtrAtOnce(true); ptrFrame.setPullToRefresh(false); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/MaterialStyleFragment.java ================================================ package in.srain.cube.views.ptr.demo.ui; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.image.ImageTask; import in.srain.cube.image.iface.ImageLoadHandler; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.header.MaterialHeader; public class MaterialStyleFragment extends TitleBaseFragment { private String mUrl = "http://img5.duitang.com/uploads/blog/201407/17/20140717113117_mUssJ.thumb.jpeg"; private long mStartLoadingTime = -1; private boolean mImageHasLoaded = false; protected PtrFrameLayout mPtrFrameLayout; @Override public void onEnter(Object data) { if (data != null && data instanceof String) { mUrl = (String) data; } } @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_materail_style, null); setHeaderTitle(R.string.ptr_demo_material_style); final CubeImageView imageView = (CubeImageView) view.findViewById(R.id.material_style_image_view); final ImageLoader imageLoader = ImageLoaderFactory.create(getContext()); mPtrFrameLayout = (PtrFrameLayout) view.findViewById(R.id.material_style_ptr_frame); // header final MaterialHeader header = new MaterialHeader(getContext()); int[] colors = getResources().getIntArray(R.array.google_colors); header.setColorSchemeColors(colors); header.setLayoutParams(new PtrFrameLayout.LayoutParams(-1, -2)); header.setPadding(0, LocalDisplay.dp2px(15), 0, LocalDisplay.dp2px(10)); header.setPtrFrameLayout(mPtrFrameLayout); mPtrFrameLayout.setLoadingMinTime(1000); mPtrFrameLayout.setDurationToCloseHeader(1500); mPtrFrameLayout.setHeaderView(header); mPtrFrameLayout.addPtrUIHandler(header); mPtrFrameLayout.postDelayed(new Runnable() { @Override public void run() { mPtrFrameLayout.autoRefresh(false); } }, 100); mPtrFrameLayout.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } @Override public void onRefreshBegin(final PtrFrameLayout frame) { if (mImageHasLoaded) { long delay = (long) (1000 + Math.random() * 2000); delay = Math.max(0, delay); delay = 0; frame.postDelayed(new Runnable() { @Override public void run() { frame.refreshComplete(); } }, delay); } else { mStartLoadingTime = System.currentTimeMillis(); imageView.loadImage(imageLoader, mUrl); } } }); ImageLoadHandler imageLoadHandler = new ImageLoadHandler() { @Override public void onLoading(ImageTask imageTask, CubeImageView cubeImageView) { } @Override public void onLoadFinish(ImageTask imageTask, final CubeImageView cubeImageView, final BitmapDrawable bitmapDrawable) { mImageHasLoaded = true; long delay = Math.max(0, 1000 - (System.currentTimeMillis() - mStartLoadingTime)); delay = 0; mPtrFrameLayout.postDelayed(new Runnable() { @Override public void run() { if (cubeImageView != null && bitmapDrawable != null) { TransitionDrawable w1 = new TransitionDrawable(new Drawable[]{new ColorDrawable(Color.WHITE), (Drawable) bitmapDrawable}); imageView.setImageDrawable(w1); w1.startTransition(200); } mPtrFrameLayout.refreshComplete(); } }, delay); } @Override public void onLoadError(ImageTask imageTask, CubeImageView cubeImageView, int i) { } }; imageLoader.setImageLoadHandler(imageLoadHandler); return view; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/MaterialStylePinContentFragment.java ================================================ package in.srain.cube.views.ptr.demo.ui; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class MaterialStylePinContentFragment extends MaterialStyleFragment { @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.createView(inflater, container, savedInstanceState); // close at once mPtrFrameLayout.setDurationToClose(100); mPtrFrameLayout.setPinContent(true); return view; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/PtrDemoHomeFragment.java ================================================ package in.srain.cube.views.ptr.demo.ui; import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import in.srain.cube.mints.base.BlockMenuFragment; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.ui.classic.*; import in.srain.cube.views.ptr.demo.ui.storehouse.StoreHouseUsingPointList; import in.srain.cube.views.ptr.demo.ui.storehouse.StoreHouseUsingString; import in.srain.cube.views.ptr.demo.ui.storehouse.StoreHouseUsingStringArray; import in.srain.cube.views.ptr.demo.ui.viewpager.ViewPagerActivity; import in.srain.cube.views.ptr.header.StoreHouseHeader; import java.util.ArrayList; public class PtrDemoHomeFragment extends BlockMenuFragment { @Override protected void addItemInfo(ArrayList itemInfos) { // GridView itemInfos.add(newItemInfo(R.string.ptr_demo_block_grid_view, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(WithGridView.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_frame_layout, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(WithTextViewInFrameLayoutFragment.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_only_text_view, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(EvenOnlyATextView.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_list_view, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(WithListView.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_web_view, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(WithWebView.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_with_list_view_and_empty_view, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(WithListViewAndEmptyView.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_scroll_view, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(WithScrollView.class, null); } })); itemInfos.add(null); itemInfos.add(null); itemInfos.add(newItemInfo(R.string.ptr_demo_block_keep_header, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(KeepHeader.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_hide_header, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(HideHeader.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_release_to_refresh, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(ReleaseToRefresh.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_pull_to_refresh, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(PullToRefresh.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_auto_fresh, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(AutoRefresh.class, null); } })); itemInfos.add(null); itemInfos.add(newItemInfo(R.string.ptr_demo_block_storehouse_header_using_string_array, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(StoreHouseUsingStringArray.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_storehouse_header_using_string, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(StoreHouseUsingString.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_storehouse_header_using_point_list, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(StoreHouseUsingPointList.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_material_style, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(MaterialStyleFragment.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_material_style_pin_content, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(MaterialStylePinContentFragment.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_with_long_press, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(WithLongPressFragment.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_block_with_view_pager, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(getContext(), ViewPagerActivity.class); startActivity(intent); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_rentals_style, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(RentalsStyleFragment.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_enable_next_ptr_at_once, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { getContext().pushFragmentToBackStack(EnableNextPTRAtOnce.class, null); } })); itemInfos.add(newItemInfo(R.string.ptr_demo_placeholder, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { } })); itemInfos.add(newItemInfo(R.string.ptr_demo_placeholder, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { } })); itemInfos.add(newItemInfo(R.string.ptr_demo_placeholder, R.color.cube_mints_4d90fe, new OnClickListener() { @Override public void onClick(View v) { } })); } @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.createView(inflater, container, savedInstanceState); view.setBackgroundColor(getResources().getColor(R.color.cube_mints_333333)); final PtrFrameLayout ptrFrameLayout = (PtrFrameLayout) view.findViewById(R.id.fragment_ptr_home_ptr_frame); StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(20), 0, LocalDisplay.dp2px(20)); header.initWithString("Ultra PTR"); ptrFrameLayout.setDurationToCloseHeader(1500); ptrFrameLayout.setHeaderView(header); ptrFrameLayout.addPtrUIHandler(header); ptrFrameLayout.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } @Override public void onRefreshBegin(PtrFrameLayout frame) { ptrFrameLayout.postDelayed(new Runnable() { @Override public void run() { ptrFrameLayout.refreshComplete(); } }, 1500); } }); return view; } @Override protected int getLayoutId() { return R.layout.fragment_ptr_home; } @Override protected void setupViews(View view) { super.setupViews(view); setHeaderTitle(R.string.ptr_demo_block_for_home); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/RentalsStyleFragment.java ================================================ package in.srain.cube.views.ptr.demo.ui; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.image.ImageTask; import in.srain.cube.image.iface.ImageLoadHandler; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.ui.header.RentalsSunHeaderView; public class RentalsStyleFragment extends TitleBaseFragment { private String mUrl = "http://img4.duitang.com/uploads/blog/201407/07/20140707113856_hBf3R.thumb.jpeg"; private long mStartLoadingTime = -1; private boolean mImageHasLoaded = false; @Override public void onEnter(Object data) { if (data != null && data instanceof String) { mUrl = (String) data; } } @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_materail_style, null); setHeaderTitle(R.string.ptr_demo_rentals_style); final CubeImageView imageView = (CubeImageView) view.findViewById(R.id.material_style_image_view); final ImageLoader imageLoader = ImageLoaderFactory.create(getContext()); final PtrFrameLayout frame = (PtrFrameLayout) view.findViewById(R.id.material_style_ptr_frame); // header final RentalsSunHeaderView header = new RentalsSunHeaderView(getContext()); header.setLayoutParams(new PtrFrameLayout.LayoutParams(-1, -2)); header.setPadding(0, LocalDisplay.dp2px(15), 0, LocalDisplay.dp2px(10)); header.setUp(frame); frame.setLoadingMinTime(1000); frame.setDurationToCloseHeader(1500); frame.setHeaderView(header); frame.addPtrUIHandler(header); // frame.setPullToRefresh(true); frame.postDelayed(new Runnable() { @Override public void run() { frame.autoRefresh(true); } }, 100); frame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } @Override public void onRefreshBegin(final PtrFrameLayout frame) { if (mImageHasLoaded) { long delay = 1500; frame.postDelayed(new Runnable() { @Override public void run() { frame.refreshComplete(); } }, delay); } else { mStartLoadingTime = System.currentTimeMillis(); imageView.loadImage(imageLoader, mUrl); } } }); ImageLoadHandler imageLoadHandler = new ImageLoadHandler() { @Override public void onLoading(ImageTask imageTask, CubeImageView cubeImageView) { } @Override public void onLoadFinish(ImageTask imageTask, final CubeImageView cubeImageView, final BitmapDrawable bitmapDrawable) { mImageHasLoaded = true; long delay = 1500; frame.postDelayed(new Runnable() { @Override public void run() { if (cubeImageView != null && bitmapDrawable != null) { TransitionDrawable w1 = new TransitionDrawable(new Drawable[]{new ColorDrawable(Color.WHITE), (Drawable) bitmapDrawable}); imageView.setImageDrawable(w1); w1.startTransition(200); } frame.refreshComplete(); } }, delay); } @Override public void onLoadError(ImageTask imageTask, CubeImageView cubeImageView, int i) { } }; imageLoader.setImageLoadHandler(imageLoadHandler); return view; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/Utils.java ================================================ package in.srain.cube.views.ptr.demo.ui; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.widget.RelativeLayout; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.ptr.demo.R; public class Utils { public static View createSimpleLoadingTip(Context context) { final View view = LayoutInflater.from(context).inflate(R.layout.cube_ptr_simple_loading, null); RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(-2, -2); lp.setMargins(0, 0, 0, LocalDisplay.dp2px(4)); lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); lp.addRule(RelativeLayout.CENTER_VERTICAL); view.setLayoutParams(lp); view.setVisibility(View.INVISIBLE); return view; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/WithLongPressFragment.java ================================================ package in.srain.cube.views.ptr.demo.ui; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Toast; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.views.list.ListViewDataAdapter; import in.srain.cube.views.list.ViewHolderBase; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.image.Images; import java.util.Arrays; public class WithLongPressFragment extends TitleBaseFragment { private ImageLoader mImageLoader; @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mImageLoader = ImageLoaderFactory.create(getActivity()); View view = inflater.inflate(R.layout.fragment_with_long_press, null); setHeaderTitle(R.string.ptr_demo_block_with_long_press); final PtrFrameLayout ptrFrameLayout = (PtrFrameLayout) view.findViewById(R.id.with_long_press_list_view_frame); ListView listView = (ListView) view.findViewById(R.id.with_long_press_list_view); listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { Toast.makeText(getContext(), "Long Pressed: " + id, Toast.LENGTH_SHORT).show(); return true; } }); final ListViewDataAdapter listViewDataAdapter = new ListViewDataAdapter(); listViewDataAdapter.setViewHolderClass(this, ViewHolder.class); ptrFrameLayout.setPtrHandler(new PtrDefaultHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { ptrFrameLayout.postDelayed(new Runnable() { @Override public void run() { listViewDataAdapter.getDataList().clear(); listViewDataAdapter.getDataList().addAll(Arrays.asList(Images.imageUrls)); listViewDataAdapter.notifyDataSetChanged(); ptrFrameLayout.refreshComplete(); } }, 2000); } }); ptrFrameLayout.postDelayed(new Runnable() { @Override public void run() { ptrFrameLayout.autoRefresh(); } }, 100); listView.setAdapter(listViewDataAdapter); return view; } private class ViewHolder extends ViewHolderBase { private CubeImageView mImageView; /** * create a view from resource Xml file, and hold the view that may be used in displaying data. * * @param layoutInflater */ @Override public View createView(LayoutInflater layoutInflater) { View view = layoutInflater.inflate(R.layout.with_long_press_list_view_item, null); mImageView = (CubeImageView) view.findViewById(R.id.with_long_press_list_image); return view; } /** * using the held views to display data * * @param position * @param itemData */ @Override public void showData(int position, String itemData) { mImageView.loadImage(mImageLoader, itemData); } } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/activity/PtrDemoHomeActivity.java ================================================ package in.srain.cube.views.ptr.demo.ui.activity; import android.os.Bundle; import in.srain.cube.mints.base.MintsBaseActivity; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.ui.PtrDemoHomeFragment; public class PtrDemoHomeActivity extends MintsBaseActivity { @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.activity_main); pushFragmentToBackStack(PtrDemoHomeFragment.class, null); } @Override protected int getFragmentContainerId() { return R.id.id_fragment; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/AutoRefresh.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.demo.R; public class AutoRefresh extends WithGridView { @Override protected void setupViews(final PtrClassicFrameLayout ptrFrame) { ptrFrame.setLoadingMinTime(3000); setHeaderTitle(R.string.ptr_demo_block_auto_fresh); ptrFrame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.autoRefresh(true); } }, 150); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/EvenOnlyATextView.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.demo.R; public class EvenOnlyATextView extends TitleBaseFragment { public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_block_only_text_view); final View contentView = inflater.inflate(R.layout.fragment_classic_header_with_textview, container, false); final PtrClassicFrameLayout ptrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.fragment_rotate_header_with_text_view_frame); ptrFrame.setLastUpdateTimeRelateObject(this); ptrFrame.setPtrHandler(new PtrDefaultHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.refreshComplete(); } }, 1500); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } }); return contentView; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/HideHeader.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.view.View; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.ui.Utils; public class HideHeader extends WithTextViewInFrameLayoutFragment { @Override protected void setupViews(final PtrClassicFrameLayout ptrFrame) { setHeaderTitle(R.string.ptr_demo_block_hide_header); ptrFrame.setKeepHeaderWhenRefresh(false); final View loading = Utils.createSimpleLoadingTip(getContext()); mTitleHeaderBar.getRightViewContainer().addView(loading); ptrFrame.setPtrHandler(new PtrDefaultHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { loading.setVisibility(View.VISIBLE); frame.postDelayed(new Runnable() { @Override public void run() { loading.setVisibility(View.INVISIBLE); ptrFrame.refreshComplete(); } }, 1500); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } }); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/KeepHeader.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.demo.R; public class KeepHeader extends WithTextViewInFrameLayoutFragment { @Override protected void setupViews(PtrClassicFrameLayout ptrFrame) { setHeaderTitle(R.string.ptr_demo_block_keep_header); ptrFrame.setKeepHeaderWhenRefresh(true); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/PullToRefresh.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.demo.R; public class PullToRefresh extends WithTextViewInFrameLayoutFragment { @Override protected void setupViews(PtrClassicFrameLayout ptrFrame) { setHeaderTitle(R.string.ptr_demo_block_pull_to_refresh); ptrFrame.setPullToRefresh(true); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/ReleaseToRefresh.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.demo.R; public class ReleaseToRefresh extends WithTextViewInFrameLayoutFragment { @Override protected void setupViews(PtrClassicFrameLayout ptrFrame) { setHeaderTitle(R.string.ptr_demo_block_release_to_refresh); ptrFrame.setPullToRefresh(false); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithGridView.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.GridView; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.request.JsonData; import in.srain.cube.request.RequestFinishHandler; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.list.ListViewDataAdapter; import in.srain.cube.views.list.ViewHolderBase; import in.srain.cube.views.list.ViewHolderCreator; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.data.DemoRequestData; import in.srain.cube.views.ptr.demo.ui.MaterialStyleFragment; public class WithGridView extends TitleBaseFragment { private static final int sGirdImageSize = (LocalDisplay.SCREEN_WIDTH_PIXELS - LocalDisplay.dp2px(12 + 12 + 10)) / 2; private ImageLoader mImageLoader; private ListViewDataAdapter mAdapter; private PtrClassicFrameLayout mPtrFrame; @Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_block_grid_view); mImageLoader = ImageLoaderFactory.create(getContext()); final View contentView = inflater.inflate(R.layout.fragment_classic_header_with_gridview, null); final GridView gridListView = (GridView) contentView.findViewById(R.id.rotate_header_grid_view); gridListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (position >= 0) { final String url = mAdapter.getItem(position).optString("pic"); if (!TextUtils.isEmpty(url)) { getContext().pushFragmentToBackStack(MaterialStyleFragment.class, url); } } } }); mAdapter = new ListViewDataAdapter(new ViewHolderCreator() { @Override public ViewHolderBase createViewHolder(int position) { return new ViewHolder(); } }); gridListView.setAdapter(mAdapter); mPtrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.rotate_header_grid_view_frame); mPtrFrame.setLastUpdateTimeRelateObject(this); mPtrFrame.setPtrHandler(new PtrHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { updateData(); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } }); // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); mPtrFrame.postDelayed(new Runnable() { @Override public void run() { // mPtrFrame.autoRefresh(); } }, 100); // updateData(); setupViews(mPtrFrame); return contentView; } protected void setupViews(final PtrClassicFrameLayout ptrFrame) { } protected void updateData() { DemoRequestData.getImageList(new RequestFinishHandler() { @Override public void onRequestFinish(final JsonData data) { mPtrFrame.postDelayed(new Runnable() { @Override public void run() { mAdapter.getDataList().clear(); mAdapter.getDataList().addAll(data.optJson("data").optJson("list").toArrayList()); mPtrFrame.refreshComplete(); mAdapter.notifyDataSetChanged(); } }, 0); } }); } private class ViewHolder extends ViewHolderBase { private CubeImageView mImageView; @Override public View createView(LayoutInflater inflater) { View view = LayoutInflater.from(getActivity()).inflate(R.layout.with_grid_view_item_image_list_grid, null); mImageView = (CubeImageView) view.findViewById(R.id.with_grid_view_item_image); mImageView.setScaleType(ScaleType.CENTER_CROP); LinearLayout.LayoutParams lyp = new LinearLayout.LayoutParams(sGirdImageSize, sGirdImageSize); mImageView.setLayoutParams(lyp); return view; } @Override public void showData(int position, JsonData itemData) { String url = itemData.optString("pic"); mImageView.loadImage(mImageLoader, url); } } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithListView.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListView; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.request.JsonData; import in.srain.cube.request.RequestFinishHandler; import in.srain.cube.views.list.ListViewDataAdapter; import in.srain.cube.views.list.ViewHolderBase; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.data.DemoRequestData; import in.srain.cube.views.ptr.demo.ui.MaterialStyleFragment; public class WithListView extends TitleBaseFragment { private ImageLoader mImageLoader; private ListViewDataAdapter mAdapter; private PtrClassicFrameLayout mPtrFrame; @Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_block_list_view); mImageLoader = ImageLoaderFactory.create(getContext()); final View contentView = inflater.inflate(R.layout.fragment_classic_header_with_list_view, null); final ListView listView = (ListView) contentView.findViewById(R.id.rotate_header_list_view); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (position >= 0) { final String url = mAdapter.getItem(position).optString("pic"); if (!TextUtils.isEmpty(url)) { getContext().pushFragmentToBackStack(MaterialStyleFragment.class, url); } } } }); mAdapter = new ListViewDataAdapter(); mAdapter.setViewHolderClass(this, ViewHolder.class); listView.setAdapter(mAdapter); mPtrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.rotate_header_list_view_frame); mPtrFrame.setLastUpdateTimeRelateObject(this); mPtrFrame.setPtrHandler(new PtrHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { updateData(); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } }); // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); mPtrFrame.postDelayed(new Runnable() { @Override public void run() { mPtrFrame.autoRefresh(); } }, 100); return contentView; } protected void updateData() { DemoRequestData.getImageList(new RequestFinishHandler() { @Override public void onRequestFinish(final JsonData data) { mPtrFrame.postDelayed(new Runnable() { @Override public void run() { mAdapter.getDataList().clear(); mAdapter.getDataList().addAll(data.optJson("data").optJson("list").toArrayList()); mPtrFrame.refreshComplete(); mAdapter.notifyDataSetChanged(); } }, 0); } }); } private class ViewHolder extends ViewHolderBase { private CubeImageView mImageView; @Override public View createView(LayoutInflater inflater) { View v = inflater.inflate(R.layout.list_view_item, null); mImageView = (CubeImageView) v.findViewById(R.id.list_view_item_image_view); mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return v; } @Override public void showData(int position, JsonData itemData) { mImageView.loadImage(mImageLoader, itemData.optString("pic")); } } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithListViewAndEmptyView.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.request.JsonData; import in.srain.cube.request.RequestFinishHandler; import in.srain.cube.views.list.ListViewDataAdapter; import in.srain.cube.views.list.ViewHolderBase; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.data.DemoRequestData; import in.srain.cube.views.ptr.demo.ui.MaterialStyleFragment; public class WithListViewAndEmptyView extends TitleBaseFragment { private ImageLoader mImageLoader; private ListViewDataAdapter mAdapter; private PtrClassicFrameLayout mPtrFrame; private TextView mTextView; private ListView mListView; @Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_block_with_list_view_and_empty_view); mImageLoader = ImageLoaderFactory.create(getContext()); final View contentView = inflater.inflate(R.layout.fragment_classic_header_with_list_view_and_empty_view, null); mTextView = (TextView) contentView.findViewById(R.id.list_view_with_empty_view_fragment_empty_view); mPtrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.list_view_with_empty_view_fragment_ptr_frame); mTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPtrFrame.setVisibility(View.VISIBLE); mPtrFrame.autoRefresh(); } }); mListView = (ListView) contentView.findViewById(R.id.list_view_with_empty_view_fragment_list_view); mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (position >= 0) { final String url = mAdapter.getItem(position).optString("pic"); if (!TextUtils.isEmpty(url)) { getContext().pushFragmentToBackStack(MaterialStyleFragment.class, url); } } } }); // show empty view mPtrFrame.setVisibility(View.INVISIBLE); mTextView.setVisibility(View.VISIBLE); mAdapter = new ListViewDataAdapter(); mAdapter.setViewHolderClass(this, ViewHolder.class); mListView.setAdapter(mAdapter); mPtrFrame.setLastUpdateTimeRelateObject(this); mPtrFrame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { // here check $mListView instead of $content return PtrDefaultHandler.checkContentCanBePulledDown(frame, mListView, header); } @Override public void onRefreshBegin(PtrFrameLayout frame) { updateData(); } }); // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); return contentView; } protected void updateData() { DemoRequestData.getImageList(new RequestFinishHandler() { @Override public void onRequestFinish(final JsonData data) { displayData(data); } }); } private void displayData(JsonData data) { mTextView.setVisibility(View.GONE); mAdapter.getDataList().clear(); mAdapter.getDataList().addAll(data.optJson("data").optJson("list").toArrayList()); mPtrFrame.refreshComplete(); mAdapter.notifyDataSetChanged(); } private class ViewHolder extends ViewHolderBase { private CubeImageView mImageView; @Override public View createView(LayoutInflater inflater) { View v = inflater.inflate(R.layout.list_view_item, null); mImageView = (CubeImageView) v.findViewById(R.id.list_view_item_image_view); mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return v; } @Override public void showData(int position, JsonData itemData) { mImageView.loadImage(mImageLoader, itemData.optString("pic")); } } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithScrollView.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ScrollView; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; public class WithScrollView extends TitleBaseFragment { private PtrClassicFrameLayout mPtrFrame; private ScrollView mScrollView; @Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_block_scroll_view); final View contentView = inflater.inflate(R.layout.fragment_classic_header_with_scroll_view, null); mScrollView = (ScrollView) contentView.findViewById(R.id.rotate_header_scroll_view); mPtrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.rotate_header_web_view_frame); mPtrFrame.setLastUpdateTimeRelateObject(this); mPtrFrame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, mScrollView, header); } @Override public void onRefreshBegin(PtrFrameLayout frame) { mPtrFrame.postDelayed(new Runnable() { @Override public void run() { mPtrFrame.refreshComplete(); } }, 100); } }); // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); mPtrFrame.postDelayed(new Runnable() { @Override public void run() { mPtrFrame.autoRefresh(); } }, 100); return contentView; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithTextViewInFrameLayoutFragment.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; public class WithTextViewInFrameLayoutFragment extends TitleBaseFragment { @Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_block_frame_layout); final View contentView = inflater.inflate(R.layout.fragment_classic_header_with_viewgroup, container, false); final PtrClassicFrameLayout ptrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.fragment_rotate_header_with_view_group_frame); ptrFrame.setPtrHandler(new PtrHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.refreshComplete(); } }, 1800); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } }); ptrFrame.setLastUpdateTimeRelateObject(this); // the following are default settings ptrFrame.setResistance(1.7f); ptrFrame.setRatioOfHeaderHeightToRefresh(1.2f); ptrFrame.setDurationToClose(200); ptrFrame.setDurationToCloseHeader(1000); // default is false ptrFrame.setPullToRefresh(false); // default is true ptrFrame.setKeepHeaderWhenRefresh(true); // scroll then refresh // comment in base fragment ptrFrame.postDelayed(new Runnable() { @Override public void run() { // ptrFrame.autoRefresh(); } }, 150); setupViews(ptrFrame); return contentView; } protected void setupViews(final PtrClassicFrameLayout ptrFrame) { } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithWebView.java ================================================ package in.srain.cube.views.ptr.demo.ui.classic; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.webkit.WebViewClient; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; public class WithWebView extends TitleBaseFragment { private PtrClassicFrameLayout mPtrFrame; private WebView mWebView; @Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_block_web_view); final View contentView = inflater.inflate(R.layout.fragment_classic_header_with_webview, null); mWebView = (WebView) contentView.findViewById(R.id.rotate_header_web_view); mWebView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { mPtrFrame.refreshComplete(); } }); mPtrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.rotate_header_web_view_frame); mPtrFrame.setLastUpdateTimeRelateObject(this); mPtrFrame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, mWebView, header); } @Override public void onRefreshBegin(PtrFrameLayout frame) { updateData(); } }); // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); mPtrFrame.postDelayed(new Runnable() { @Override public void run() { mPtrFrame.autoRefresh(); } }, 100); return contentView; } private void updateData() { mWebView.loadUrl("http://www.liaohuqiu.net/"); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/header/RentalsSunDrawable.java ================================================ package in.srain.cube.views.ptr.demo.ui.header; import android.content.Context; import android.graphics.*; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.view.View; import android.view.animation.Animation; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.Transformation; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.util.PtrLocalDisplay; public class RentalsSunDrawable extends Drawable implements Animatable { private static final float SCALE_START_PERCENT = 0.3f; private static final int ANIMATION_DURATION = 1000; private final static float SKY_RATIO = 0.65f; private static final float SKY_INITIAL_SCALE = 1.05f; private final static float TOWN_RATIO = 0.22f; private static final float TOWN_INITIAL_SCALE = 1.20f; private static final float TOWN_FINAL_SCALE = 1.30f; private static final float SUN_FINAL_SCALE = 0.75f; private static final float SUN_INITIAL_ROTATE_GROWTH = 1.2f; private static final float SUN_FINAL_ROTATE_GROWTH = 1.5f; private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private View mParent; private Matrix mMatrix; private Animation mAnimation; private int mTop; private int mScreenWidth; private int mSkyHeight; private float mSkyTopOffset; private float mSkyMoveOffset; private int mTownHeight; private float mTownInitialTopOffset; private float mTownFinalTopOffset; private float mTownMoveOffset; private int mSunSize = 100; private float mSunLeftOffset; private float mSunTopOffset; private float mPercent = 0.0f; private float mRotate = 0.0f; private Bitmap mSky; private Bitmap mSun; private Bitmap mTown; private boolean isRefreshing = false; private Context mContext; private int mTotalDragDistance; public RentalsSunDrawable(Context context, View parent) { mContext = context; mParent = parent; mMatrix = new Matrix(); initiateDimens(); createBitmaps(); setupAnimations(); } private Context getContext() { return mContext; } private void initiateDimens() { PtrLocalDisplay.init(mContext); mTotalDragDistance = PtrLocalDisplay.dp2px(120); mScreenWidth = getContext().getResources().getDisplayMetrics().widthPixels; mSkyHeight = (int) (SKY_RATIO * mScreenWidth); mSkyTopOffset = -(mSkyHeight * 0.28f); mSkyMoveOffset = PtrLocalDisplay.designedDP2px(15); mTownHeight = (int) (TOWN_RATIO * mScreenWidth); mTownInitialTopOffset = (mTotalDragDistance - mTownHeight * TOWN_INITIAL_SCALE) + mTotalDragDistance * .42f; mTownFinalTopOffset = (mTotalDragDistance - mTownHeight * TOWN_FINAL_SCALE) + mTotalDragDistance * .42f; mTownMoveOffset = PtrLocalDisplay.designedDP2px(10); mSunLeftOffset = 0.3f * (float) mScreenWidth; mSunTopOffset = (mTotalDragDistance * 0.5f); mTop = 0; } private void createBitmaps() { mSky = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sky); mSky = Bitmap.createScaledBitmap(mSky, mScreenWidth, mSkyHeight, true); mTown = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.buildings); mTown = Bitmap.createScaledBitmap(mTown, mScreenWidth, (int) (mScreenWidth * TOWN_RATIO), true); mSun = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sun); mSun = Bitmap.createScaledBitmap(mSun, mSunSize, mSunSize, true); } public void offsetTopAndBottom(int offset) { mTop = offset; invalidateSelf(); } @Override public void draw(Canvas canvas) { final int saveCount = canvas.save(); canvas.translate(0, mTotalDragDistance - mTop); drawSky(canvas); drawSun(canvas); drawTown(canvas); canvas.restoreToCount(saveCount); } private void drawSky(Canvas canvas) { Matrix matrix = mMatrix; matrix.reset(); int y = Math.max(0, mTop - mTotalDragDistance); // 0 ~ 1 float dragPercent = Math.min(1f, Math.abs(mPercent)); /** Change skyScale between {@link #SKY_INITIAL_SCALE} and 1.0f depending on {@link #mPercent} */ float skyScale; float scalePercentDelta = dragPercent - SCALE_START_PERCENT; /** less than {@link SCALE_START_PERCENT} will be {@link SKY_INITIAL_SCALE} */ if (scalePercentDelta > 0) { /** will change from 0 ~ 1 **/ float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); skyScale = SKY_INITIAL_SCALE - (SKY_INITIAL_SCALE - 1.0f) * scalePercent; } else { skyScale = SKY_INITIAL_SCALE; } float offsetX = -(mScreenWidth * skyScale - mScreenWidth) / 2.0f; float offsetY = y + 50 + mSkyTopOffset // Offset canvas moving, goes lower when goes down - mSkyHeight * (skyScale - 1.0f) / 2 // Offset sky scaling, lower than 0, will go greater when goes down + mSkyMoveOffset * dragPercent; // Give it a little move top -> bottom // will go greater when goes down matrix.postScale(skyScale, skyScale); matrix.postTranslate(offsetX, offsetY); canvas.drawBitmap(mSky, matrix, null); } private void drawTown(Canvas canvas) { Matrix matrix = mMatrix; matrix.reset(); int y = Math.max(0, mTop - mTotalDragDistance); float dragPercent = Math.min(1f, Math.abs(mPercent)); float townScale; float townTopOffset; float townMoveOffset; float scalePercentDelta = dragPercent - SCALE_START_PERCENT; if (scalePercentDelta > 0) { /** * Change townScale between {@link #TOWN_INITIAL_SCALE} and {@link #TOWN_FINAL_SCALE} depending on {@link #mPercent} * Change townTopOffset between {@link #mTownInitialTopOffset} and {@link #mTownFinalTopOffset} depending on {@link #mPercent} */ float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); townScale = TOWN_INITIAL_SCALE + (TOWN_FINAL_SCALE - TOWN_INITIAL_SCALE) * scalePercent; townTopOffset = mTownInitialTopOffset - (mTownFinalTopOffset - mTownInitialTopOffset) * scalePercent; townMoveOffset = mTownMoveOffset * (1.0f - scalePercent); } else { float scalePercent = dragPercent / SCALE_START_PERCENT; townScale = TOWN_INITIAL_SCALE; townTopOffset = mTownInitialTopOffset; townMoveOffset = mTownMoveOffset * scalePercent; } float offsetX = -(mScreenWidth * townScale - mScreenWidth) / 2.0f; // float offsetY = (1.0f - dragPercent) * mTotalDragDistance // Offset canvas moving float offsetY = y + +townTopOffset - mTownHeight * (townScale - 1.0f) / 2 // Offset town scaling + townMoveOffset; // Give it a little move matrix.postScale(townScale, townScale); matrix.postTranslate(offsetX, offsetY); canvas.drawBitmap(mTown, matrix, null); } private void drawSun(Canvas canvas) { Matrix matrix = mMatrix; matrix.reset(); float dragPercent = mPercent; if (dragPercent > 1.0f) { // Slow down if pulling over set height dragPercent = (dragPercent + 9.0f) / 10; } float sunRadius = (float) mSunSize / 2.0f; float sunRotateGrowth = SUN_INITIAL_ROTATE_GROWTH; float offsetX = mSunLeftOffset; float offsetY = mSunTopOffset + (mTotalDragDistance / 2) * (1.0f - dragPercent); // Move the sun up float scalePercentDelta = dragPercent - SCALE_START_PERCENT; if (scalePercentDelta > 0) { float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); float sunScale = 1.0f - (1.0f - SUN_FINAL_SCALE) * scalePercent; sunRotateGrowth += (SUN_FINAL_ROTATE_GROWTH - SUN_INITIAL_ROTATE_GROWTH) * scalePercent; matrix.preTranslate(offsetX + (sunRadius - sunRadius * sunScale), offsetY * (2.0f - sunScale)); matrix.preScale(sunScale, sunScale); offsetX += sunRadius; offsetY = offsetY * (2.0f - sunScale) + sunRadius * sunScale; } else { matrix.postTranslate(offsetX, offsetY); offsetX += sunRadius; offsetY += sunRadius; } float r = (isRefreshing ? -360 : 360) * mRotate * (isRefreshing ? 1 : sunRotateGrowth); matrix.postRotate(r, offsetX, offsetY); canvas.drawBitmap(mSun, matrix, null); } public void setPercent(float percent) { mPercent = percent; setRotate(percent); } public void setRotate(float rotate) { mRotate = rotate; mParent.invalidate(); invalidateSelf(); } public void resetOriginals() { setPercent(0); setRotate(0); } @Override public void setBounds(int left, int top, int right, int bottom) { super.setBounds(left, top, right, mSkyHeight + top); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public boolean isRunning() { return false; } @Override public void start() { mAnimation.reset(); isRefreshing = true; mParent.startAnimation(mAnimation); } @Override public void stop() { mParent.clearAnimation(); isRefreshing = false; resetOriginals(); } private void setupAnimations() { mAnimation = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { setRotate(interpolatedTime); } }; mAnimation.setRepeatCount(Animation.INFINITE); mAnimation.setRepeatMode(Animation.RESTART); mAnimation.setInterpolator(LINEAR_INTERPOLATOR); mAnimation.setDuration(ANIMATION_DURATION); } public int getTotalDragDistance() { return mTotalDragDistance; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/header/RentalsSunHeaderView.java ================================================ package in.srain.cube.views.ptr.demo.ui.header; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrUIHandler; import in.srain.cube.views.ptr.indicator.PtrIndicator; import in.srain.cube.views.ptr.indicator.PtrTensionIndicator; public class RentalsSunHeaderView extends View implements PtrUIHandler { private RentalsSunDrawable mDrawable; private PtrFrameLayout mPtrFrameLayout; private PtrTensionIndicator mPtrTensionIndicator; public RentalsSunHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public RentalsSunHeaderView(Context context) { super(context); init(); } public RentalsSunHeaderView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public void setUp(PtrFrameLayout ptrFrameLayout) { mPtrFrameLayout = ptrFrameLayout; mPtrTensionIndicator = new PtrTensionIndicator(); mPtrFrameLayout.setPtrIndicator(mPtrTensionIndicator); } private void init() { mDrawable = new RentalsSunDrawable(getContext(), this); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = mDrawable.getTotalDragDistance() * 5 / 4; heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int pl = getPaddingLeft(); int pt = getPaddingTop(); mDrawable.setBounds(pl, pt, pl + right - left, pt + bottom - top); } @Override public void onUIReset(PtrFrameLayout frame) { mDrawable.resetOriginals(); } @Override protected void onDraw(Canvas canvas) { mDrawable.draw(canvas); float percent = mPtrTensionIndicator.getOverDragPercent(); } @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { } @Override public void onUIRefreshBegin(PtrFrameLayout frame) { mDrawable.start(); float percent = mPtrTensionIndicator.getOverDragPercent(); mDrawable.offsetTopAndBottom(mPtrTensionIndicator.getCurrentPosY()); mDrawable.setPercent(percent); invalidate(); } @Override public void onUIRefreshComplete(PtrFrameLayout frame) { float percent = mPtrTensionIndicator.getOverDragPercent(); mDrawable.stop(); mDrawable.offsetTopAndBottom(mPtrTensionIndicator.getCurrentPosY()); mDrawable.setPercent(percent); invalidate(); } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { float percent = mPtrTensionIndicator.getOverDragPercent(); mDrawable.offsetTopAndBottom(mPtrTensionIndicator.getCurrentPosY()); mDrawable.setPercent(percent); invalidate(); } @Override public void invalidateDrawable(Drawable dr) { if (dr == mDrawable) { invalidate(); } else { super.invalidateDrawable(dr); } } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingPointList.java ================================================ package in.srain.cube.views.ptr.demo.ui.storehouse; import android.graphics.Point; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.header.StoreHouseHeader; import java.util.ArrayList; import java.util.List; /** * the point list used here is taken from: * https://github.com/cloay/CRefreshLayout */ public class StoreHouseUsingPointList extends TitleBaseFragment { @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHeaderTitle(R.string.ptr_demo_storehouse_header_using_point_list); View view = inflater.inflate(R.layout.fragment_storehouse_header, null); CubeImageView imageView = (CubeImageView) view.findViewById(R.id.store_house_ptr_image); ImageLoader imageLoader = ImageLoaderFactory.create(getContext()); String pic = "http://img5.duitang.com/uploads/item/201406/28/20140628122218_fLQyP.thumb.jpeg"; imageView.loadImage(imageLoader, pic); final PtrFrameLayout frame = (PtrFrameLayout) view.findViewById(R.id.store_house_ptr_frame); // header final StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(15), 0, 0); header.initWithPointList(getPointList()); frame.setDurationToCloseHeader(3000); frame.setHeaderView(header); frame.addPtrUIHandler(header); frame.postDelayed(new Runnable() { @Override public void run() { frame.autoRefresh(false); } }, 100); frame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } @Override public void onRefreshBegin(final PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { frame.refreshComplete(); } }, 2000); } }); return view; } private ArrayList getPointList() { // this point is taken from https://github.com/cloay/CRefreshLayout List startPoints = new ArrayList(); startPoints.add(new Point(240, 80)); startPoints.add(new Point(270, 80)); startPoints.add(new Point(265, 103)); startPoints.add(new Point(255, 65)); startPoints.add(new Point(275, 80)); startPoints.add(new Point(275, 80)); startPoints.add(new Point(302, 80)); startPoints.add(new Point(275, 107)); startPoints.add(new Point(320, 70)); startPoints.add(new Point(313, 80)); startPoints.add(new Point(330, 63)); startPoints.add(new Point(315, 87)); startPoints.add(new Point(330, 80)); startPoints.add(new Point(315, 100)); startPoints.add(new Point(330, 90)); startPoints.add(new Point(315, 110)); startPoints.add(new Point(345, 65)); startPoints.add(new Point(357, 67)); startPoints.add(new Point(363, 103)); startPoints.add(new Point(375, 80)); startPoints.add(new Point(375, 80)); startPoints.add(new Point(425, 80)); startPoints.add(new Point(380, 95)); startPoints.add(new Point(400, 63)); List endPoints = new ArrayList(); endPoints.add(new Point(270, 80)); endPoints.add(new Point(270, 110)); endPoints.add(new Point(270, 110)); endPoints.add(new Point(250, 110)); endPoints.add(new Point(275, 107)); endPoints.add(new Point(302, 80)); endPoints.add(new Point(302, 107)); endPoints.add(new Point(302, 107)); endPoints.add(new Point(340, 70)); endPoints.add(new Point(360, 80)); endPoints.add(new Point(330, 80)); endPoints.add(new Point(340, 87)); endPoints.add(new Point(315, 100)); endPoints.add(new Point(345, 98)); endPoints.add(new Point(330, 120)); endPoints.add(new Point(345, 108)); endPoints.add(new Point(360, 120)); endPoints.add(new Point(363, 75)); endPoints.add(new Point(345, 117)); endPoints.add(new Point(380, 95)); endPoints.add(new Point(425, 80)); endPoints.add(new Point(420, 95)); endPoints.add(new Point(420, 95)); endPoints.add(new Point(400, 120)); ArrayList list = new ArrayList(); int offsetX = Integer.MAX_VALUE; int offsetY = Integer.MAX_VALUE; for (int i = 0; i < startPoints.size(); i++) { offsetX = Math.min(startPoints.get(i).x, offsetX); offsetY = Math.min(startPoints.get(i).y, offsetY); } for (int i = 0; i < endPoints.size(); i++) { float[] point = new float[4]; point[0] = startPoints.get(i).x - offsetX; point[1] = startPoints.get(i).y - offsetY; point[2] = endPoints.get(i).x - offsetX; point[3] = endPoints.get(i).y - offsetY; list.add(point); } return list; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingString.java ================================================ package in.srain.cube.views.ptr.demo.ui.storehouse; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.PtrUIHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.header.StoreHouseHeader; import in.srain.cube.views.ptr.indicator.PtrIndicator; public class StoreHouseUsingString extends TitleBaseFragment { final String[] mStringList = {"Alibaba", "TMALL 11-11"}; private String mTitlePre; @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_storehouse_header, null); mTitlePre = getString(R.string.ptr_demo_storehouse_header_using_string_in_title); CubeImageView imageView = (CubeImageView) view.findViewById(R.id.store_house_ptr_image); ImageLoader imageLoader = ImageLoaderFactory.create(getContext()); String pic = "http://img5.duitang.com/uploads/item/201406/28/20140628122218_fLQyP.thumb.jpeg"; imageView.loadImage(imageLoader, pic); final PtrFrameLayout frame = (PtrFrameLayout) view.findViewById(R.id.store_house_ptr_frame); // header final StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(15), 0, 0); /** * using a string, support: A-Z 0-9 - . * you can add more letters by {@link in.srain.cube.views.ptr.header.StoreHousePath#addChar} */ header.initWithString(mStringList[0]); setHeaderTitle(mTitlePre + mStringList[0]); // for changing string frame.addPtrUIHandler(new PtrUIHandler() { private int mLoadTime = 0; @Override public void onUIReset(PtrFrameLayout frame) { mLoadTime++; String string = mStringList[mLoadTime % mStringList.length]; header.initWithString(string); } @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { String string = mStringList[mLoadTime % mStringList.length]; setHeaderTitle(mTitlePre + string); } @Override public void onUIRefreshBegin(PtrFrameLayout frame) { } @Override public void onUIRefreshComplete(PtrFrameLayout frame) { } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { } }); frame.setDurationToCloseHeader(3000); frame.setHeaderView(header); frame.addPtrUIHandler(header); frame.postDelayed(new Runnable() { @Override public void run() { frame.autoRefresh(false); } }, 100); frame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } @Override public void onRefreshBegin(final PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { frame.refreshComplete(); } }, 2000); } }); return view; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingStringArray.java ================================================ package in.srain.cube.views.ptr.demo.ui.storehouse; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.PtrUIHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.header.StoreHouseHeader; import in.srain.cube.views.ptr.indicator.PtrIndicator; public class StoreHouseUsingStringArray extends TitleBaseFragment { private String mTitlePre; @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_storehouse_header, null); mTitlePre = getString(R.string.ptr_demo_storehouse_header_using_string_array_in_title); setHeaderTitle(mTitlePre + "R.array.storehouse"); // loading image CubeImageView imageView = (CubeImageView) view.findViewById(R.id.store_house_ptr_image); ImageLoader imageLoader = ImageLoaderFactory.create(getContext()); String pic = "http://img5.duitang.com/uploads/item/201406/28/20140628122218_fLQyP.thumb.jpeg"; imageView.loadImage(imageLoader, pic); final PtrFrameLayout frame = (PtrFrameLayout) view.findViewById(R.id.store_house_ptr_frame); final StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(15), 0, 0); // using string array from resource xml file header.initWithStringArray(R.array.storehouse); frame.setDurationToCloseHeader(1500); frame.setHeaderView(header); frame.addPtrUIHandler(header); frame.postDelayed(new Runnable() { @Override public void run() { frame.autoRefresh(false); } }, 100); // change header after loaded frame.addPtrUIHandler(new PtrUIHandler() { private int mLoadTime = 0; @Override public void onUIReset(PtrFrameLayout frame) { mLoadTime++; if (mLoadTime % 2 == 0) { header.setScale(1); header.initWithStringArray(R.array.storehouse); } else { header.setScale(0.5f); header.initWithStringArray(R.array.akta); } } @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { if (mLoadTime % 2 == 0) { setHeaderTitle(mTitlePre + "R.array.storehouse"); } else { setHeaderTitle(mTitlePre + "R.array.akta"); } } @Override public void onUIRefreshBegin(PtrFrameLayout frame) { } @Override public void onUIRefreshComplete(PtrFrameLayout frame) { } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { } }); frame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return true; } @Override public void onRefreshBegin(final PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { frame.refreshComplete(); } }, 2000); } }); return view; } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ImageListViewHolder.java ================================================ package in.srain.cube.views.ptr.demo.ui.viewpager; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.request.JsonData; import in.srain.cube.views.list.ViewHolderBase; import in.srain.cube.views.ptr.demo.R; public class ImageListViewHolder extends ViewHolderBase { private ImageLoader mImageLoader; private CubeImageView mImageView; private ImageListViewHolder(ImageLoader imageLoader) { mImageLoader = imageLoader; } @Override public View createView(LayoutInflater inflater) { View v = inflater.inflate(R.layout.list_view_item, null); mImageView = (CubeImageView) v.findViewById(R.id.list_view_item_image_view); mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return v; } @Override public void showData(int position, JsonData itemData) { mImageView.loadImage(mImageLoader, itemData.optString("pic")); } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerActivity.java ================================================ package in.srain.cube.views.ptr.demo.ui.viewpager; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.TextView; import in.srain.cube.mints.base.TitleBaseActivity; import in.srain.cube.request.JsonData; import in.srain.cube.request.RequestFinishHandler; import in.srain.cube.util.LocalDisplay; import in.srain.cube.views.pager.TabPageIndicator; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.data.DemoRequestData; import java.util.ArrayList; public class ViewPagerActivity extends TitleBaseActivity { private TabPageIndicator mCatTabPageIndicator; private ViewPager mFragmentViewPager; private FragmentViewPagerAdapter mPagerAdapter; private PtrFrameLayout mPtrFrame; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHeaderTitle(R.string.ptr_demo_block_with_view_pager); setContentView(R.layout.activity_view_pager); initCateViews(); } private void initCateViews() { int startIndex = 0; mCatTabPageIndicator = (TabPageIndicator) findViewById(R.id.view_pager_tab_indicator); mFragmentViewPager = (ViewPager) this.findViewById(R.id.view_pager_view_pager); ArrayList list = new ArrayList(); for (int i = 1; i <= 8; i++) { list.add(ViewPagerFragment.create(i)); } mPagerAdapter = new FragmentViewPagerAdapter(getSupportFragmentManager(), list); mFragmentViewPager.setAdapter(mPagerAdapter); mCatTabPageIndicator.setViewHolderCreator(new TabPageIndicator.ViewHolderCreator() { @Override public TabPageIndicator.ViewHolderBase createViewHolder() { return new HomeCatItemViewHolder(); } }); mCatTabPageIndicator.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int i) { switchTo(i); } }); mCatTabPageIndicator.setViewPager(mFragmentViewPager); mPtrFrame = (PtrClassicFrameLayout) findViewById(R.id.view_pager_ptr_frame); mPtrFrame.disableWhenHorizontalMove(true); mPtrFrame.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return mPagerAdapter.checkCanDoRefresh(); } @Override public void onRefreshBegin(PtrFrameLayout frame) { mPagerAdapter.updateData(); } }); mFragmentViewPager.setCurrentItem(startIndex); } @Override protected void onResume() { super.onResume(); mCatTabPageIndicator.moveToItem(mFragmentViewPager.getCurrentItem()); } private void switchTo(int position) { mPagerAdapter.switchTO(position); } private class FragmentViewPagerAdapter extends FragmentStatePagerAdapter { private final ArrayList mViewPagerFragments; private ViewPagerFragment mCurrentFragment; public FragmentViewPagerAdapter(FragmentManager fm, ArrayList list) { super(fm); mViewPagerFragments = list; } @Override public Fragment getItem(int position) { return mViewPagerFragments.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { } protected void updateData() { final ViewPagerFragment fragment = mCurrentFragment; DemoRequestData.getImageList(new RequestFinishHandler() { @Override public void onRequestFinish(final JsonData data) { if (fragment == mCurrentFragment) { fragment.update(data); mPtrFrame.refreshComplete(); } } }); } @Override public int getCount() { return mViewPagerFragments.size(); } public void switchTO(final int position) { int len = mViewPagerFragments.size(); for (int i = 0; i < len; i++) { ViewPagerFragment fragment = mViewPagerFragments.get(i); if (i == position) { mCurrentFragment = fragment; fragment.show(); } else { fragment.hide(); } } } public boolean checkCanDoRefresh() { if (mCurrentFragment == null) { return true; } return mCurrentFragment.checkCanDoRefresh(); } } private class HomeCatItemViewHolder extends TabPageIndicator.ViewHolderBase { private TextView mNameView; private View mTagView; @Override public View createView(LayoutInflater layoutInflater, int position) { View view = layoutInflater.inflate(R.layout.view_pager_indicator_item, null); view.setLayoutParams(new AbsListView.LayoutParams(LocalDisplay.dp2px(40), -1)); mNameView = (TextView) view.findViewById(R.id.view_pager_indicator_name); mTagView = view.findViewById(R.id.view_pager_indicator_tab_current); return view; } @Override public void updateView(int position, boolean isCurrent) { mNameView.setText(position + ""); if (isCurrent) { mTagView.setVisibility(View.VISIBLE); } else { mTagView.setVisibility(View.INVISIBLE); } } } } ================================================ FILE: ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerFragment.java ================================================ package in.srain.cube.views.ptr.demo.ui.viewpager; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; import android.widget.TextView; import in.srain.cube.app.CubeFragment; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; import in.srain.cube.request.JsonData; import in.srain.cube.util.CLog; import in.srain.cube.views.list.ListViewDataAdapter; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.ui.MaterialStyleFragment; public class ViewPagerFragment extends CubeFragment { private int mPage; private ListView mListView; private ImageLoader mImageLoader; private ListViewDataAdapter mAdapter; public static ViewPagerFragment create(int page) { ViewPagerFragment fragment = new ViewPagerFragment(); fragment.mPage = page; return fragment; } @Override protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return null; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mImageLoader = ImageLoaderFactory.create(getContext()).tryToAttachToContainer(getContext()); View view = inflater.inflate(R.layout.fragment_view_pager, null); mListView = (ListView) view.findViewById(R.id.view_pager_list_view); View headerView = inflater.inflate(R.layout.view_pager_fragment_list_view_header, null); TextView mHeaderTextView = (TextView) headerView.findViewById(R.id.view_pager_fragment_list_view_header_title); mHeaderTextView.setBackgroundColor(0xff4d90fe * mPage / 30); mHeaderTextView.setText("Page: " + mPage); mListView.addHeaderView(headerView); mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (position >= 0) { JsonData js = mAdapter.getItem(position); final String url = js!=null?js.optString("pic"):null; if (!TextUtils.isEmpty(url)) { getContext().pushFragmentToBackStack(MaterialStyleFragment.class, url); } } } }); mAdapter = new ListViewDataAdapter(); mAdapter.setViewHolderClass(this, ImageListViewHolder.class, mImageLoader); mListView.setAdapter(mAdapter); return view; } public void update(JsonData data) { mAdapter.getDataList().clear(); mAdapter.getDataList().addAll(data.optJson("data").optJson("list").toArrayList()); mAdapter.notifyDataSetChanged(); } public void show() { } public void hide() { } public boolean checkCanDoRefresh() { if (mAdapter.getCount() == 0 || mListView == null) { return true; } CLog.d("test", "checkCanDoRefresh: %s %s", mListView.getFirstVisiblePosition(), mListView.getChildAt(0).getTop()); return mListView.getFirstVisiblePosition() == 0 && mListView.getChildAt(0).getTop() == 0; } } ================================================ FILE: ptr-lib/AndroidManifest.xml ================================================ ================================================ FILE: ptr-lib/build.gradle ================================================ apply plugin: 'com.android.library' android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION defaultConfig { minSdkVersion 8 targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) } lintOptions { abortOnError false } sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['resources'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] // aidl.srcDirs = ['src'] // renderscript.srcDirs = ['src'] } androidTest.setRoot('tests') } } dependencies { } apply from: './gradle-mvn-push.gradle' ================================================ FILE: ptr-lib/checkstyle.xml ================================================ ================================================ FILE: ptr-lib/gradle-mvn-push.gradle ================================================ apply plugin: 'maven' apply plugin: 'signing' def isReleaseBuild() { return VERSION_NAME.contains("SNAPSHOT") == false } def getReleaseRepositoryUrl() { return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" } def getSnapshotRepositoryUrl() { return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL : "https://oss.sonatype.org/content/repositories/snapshots/" } def getRepositoryUsername() { return hasProperty('OSS_USERNAME') ? OSS_USERNAME : '' } def getRepositoryPassword() { return hasProperty('OSS_PWD') ? OSS_PWD : '' } afterEvaluate { project -> uploadArchives { repositories { mavenDeployer { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } pom.groupId = POM_GROUP pom.artifactId = POM_ARTIFACT_ID pom.version = VERSION_NAME repository(url: getReleaseRepositoryUrl()) { authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) } snapshotRepository(url: getSnapshotRepositoryUrl()) { authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) } pom.project { name POM_NAME packaging POM_PACKAGING description POM_DESCRIPTION url POM_URL scm { url POM_SCM_URL connection POM_SCM_CONNECTION developerConnection POM_SCM_DEV_CONNECTION } licenses { license { name POM_LICENCE_NAME url POM_LICENCE_URL distribution POM_LICENCE_DIST } } issueManagement { system POM_ISSUE_SYSTEM url POM_ISSUE_URL } developers { developer { id POM_DEVELOPER_ID name POM_DEVELOPER_NAME } } } } } } signing { required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } sign configurations.archives } task apklib(type: Zip) { appendix = extension = 'apklib' from 'AndroidManifest.xml' into('res') { from 'res' } into('src') { from 'src' } } task androidJavadocs(type: Javadoc) { source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { classifier = 'javadoc' from androidJavadocs.destinationDir } task androidSourcesJar(type: Jar) { classifier = 'sources' from android.sourceSets.main.java.srcDirs } artifacts { archives androidSourcesJar archives androidJavadocsJar archives apklib } } ================================================ FILE: ptr-lib/gradle.properties ================================================ VERSION_NAME=1.0.11 POM_GROUP=in.srain.cube POM_ARTIFACT_ID=ultra-ptr POM_PACKAGING=aar POM_NAME=Android Ultra Pull to Refresh POM_DESCRIPTION=Ultra Pull to Refresh Solution. POM_URL=https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh POM_SCM_URL=https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh POM_SCM_CONNECTION=scm:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh.git POM_SCM_DEV_CONNECTION=scm:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh.git POM_LICENCE_NAME=MIT POM_LICENCE_URL=http://opensource.org/licenses/MIT POM_LICENCE_DIST=repo POM_DEVELOPER_ID=liaohuqiu POM_DEVELOPER_NAME=liaohuqiu POM_ISSUE_SYSTEM=GitHub Issues POM_ISSUE_URL=https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/issues ================================================ FILE: ptr-lib/pom.xml ================================================ 4.0.0 in.srain cube-parent-for-oss 1.0.1 in.srain.cube ultra-ptr aar Ultra Pull to Refresh 1.0.11 Ultra Pull to Refresh in Android https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh 2014 https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh scm:git:git@github.com:liaohuqiu/android-Ultra-Pull-To-Refresh.git scm:git:git@github.com:liaohuqiu/android-Ultra-Pull-To-Refresh.git Huqiu Liao liaohuqiu@gmail.com liaohuqiu http://www.liaohuqiu.net +8 developer MIT http://opensource.org/licenses/MIT repo Huqiu Liao http://www.liaohuqiu.net GitHub Issues https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/issues com.google.android android 4.1.1.4 provided 3.8.2 19 ${project.artifactId} src/ org.apache.maven.plugins maven-compiler-plugin 1.6 1.6 target/generated-sources/r/* com.jayway.maven.plugins.android.generation2 android-maven-plugin true ${android.maven.plugin.version} ${android.compile.api_level} false ================================================ FILE: ptr-lib/res/layout/cube_ptr_classic_default_header.xml ================================================ ================================================ FILE: ptr-lib/res/layout/cube_ptr_simple_loading.xml ================================================ ================================================ FILE: ptr-lib/res/values/cube_ptr_attrs.xml ================================================ ================================================ FILE: ptr-lib/res/values/cube_ptr_string.xml ================================================ Pull Down Pull Down to Refresh Release To Refresh Updating... Updated. last update:   seconds ago  minutes ago  hours ago ================================================ FILE: ptr-lib/res/values-zh/cube_ptr_string.xml ================================================ 下拉 下拉刷新 释放刷新 加载中... 更新完成. 上次更新:   秒之前  分钟之前  小时之前 ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultHeader.java ================================================ package in.srain.cube.views.ptr; import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.FrameLayout; import android.widget.TextView; import in.srain.cube.views.ptr.indicator.PtrIndicator; import java.text.SimpleDateFormat; import java.util.Date; public class PtrClassicDefaultHeader extends FrameLayout implements PtrUIHandler { private final static String KEY_SharedPreferences = "cube_ptr_classic_last_update"; private static SimpleDateFormat sDataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private int mRotateAniTime = 150; private RotateAnimation mFlipAnimation; private RotateAnimation mReverseFlipAnimation; private TextView mTitleTextView; private View mRotateView; private View mProgressBar; private long mLastUpdateTime = -1; private TextView mLastUpdateTextView; private String mLastUpdateTimeKey; private boolean mShouldShowLastUpdate; private LastUpdateTimeUpdater mLastUpdateTimeUpdater = new LastUpdateTimeUpdater(); public PtrClassicDefaultHeader(Context context) { super(context); initViews(null); } public PtrClassicDefaultHeader(Context context, AttributeSet attrs) { super(context, attrs); initViews(attrs); } public PtrClassicDefaultHeader(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initViews(attrs); } protected void initViews(AttributeSet attrs) { TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.PtrClassicHeader, 0, 0); if (arr != null) { mRotateAniTime = arr.getInt(R.styleable.PtrClassicHeader_ptr_rotate_ani_time, mRotateAniTime); } buildAnimation(); View header = LayoutInflater.from(getContext()).inflate(R.layout.cube_ptr_classic_default_header, this); mRotateView = header.findViewById(R.id.ptr_classic_header_rotate_view); mTitleTextView = (TextView) header.findViewById(R.id.ptr_classic_header_rotate_view_header_title); mLastUpdateTextView = (TextView) header.findViewById(R.id.ptr_classic_header_rotate_view_header_last_update); mProgressBar = header.findViewById(R.id.ptr_classic_header_rotate_view_progressbar); resetView(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mLastUpdateTimeUpdater != null) { mLastUpdateTimeUpdater.stop(); } } public void setRotateAniTime(int time) { if (time == mRotateAniTime || time == 0) { return; } mRotateAniTime = time; buildAnimation(); } /** * Specify the last update time by this key string * * @param key */ public void setLastUpdateTimeKey(String key) { if (TextUtils.isEmpty(key)) { return; } mLastUpdateTimeKey = key; } /** * Using an object to specify the last update time. * * @param object */ public void setLastUpdateTimeRelateObject(Object object) { setLastUpdateTimeKey(object.getClass().getName()); } private void buildAnimation() { mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(mRotateAniTime); mFlipAnimation.setFillAfter(true); mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mReverseFlipAnimation.setInterpolator(new LinearInterpolator()); mReverseFlipAnimation.setDuration(mRotateAniTime); mReverseFlipAnimation.setFillAfter(true); } private void resetView() { hideRotateView(); mProgressBar.setVisibility(INVISIBLE); } private void hideRotateView() { mRotateView.clearAnimation(); mRotateView.setVisibility(INVISIBLE); } @Override public void onUIReset(PtrFrameLayout frame) { resetView(); mShouldShowLastUpdate = true; tryUpdateLastUpdateTime(); } @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { mShouldShowLastUpdate = true; tryUpdateLastUpdateTime(); mLastUpdateTimeUpdater.start(); mProgressBar.setVisibility(INVISIBLE); mRotateView.setVisibility(VISIBLE); mTitleTextView.setVisibility(VISIBLE); if (frame.isPullToRefresh()) { mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh)); } else { mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down)); } } @Override public void onUIRefreshBegin(PtrFrameLayout frame) { mShouldShowLastUpdate = false; hideRotateView(); mProgressBar.setVisibility(VISIBLE); mTitleTextView.setVisibility(VISIBLE); mTitleTextView.setText(R.string.cube_ptr_refreshing); tryUpdateLastUpdateTime(); mLastUpdateTimeUpdater.stop(); } @Override public void onUIRefreshComplete(PtrFrameLayout frame) { hideRotateView(); mProgressBar.setVisibility(INVISIBLE); mTitleTextView.setVisibility(VISIBLE); mTitleTextView.setText(getResources().getString(R.string.cube_ptr_refresh_complete)); // update last update time SharedPreferences sharedPreferences = getContext().getSharedPreferences(KEY_SharedPreferences, 0); if (!TextUtils.isEmpty(mLastUpdateTimeKey)) { mLastUpdateTime = new Date().getTime(); sharedPreferences.edit().putLong(mLastUpdateTimeKey, mLastUpdateTime).commit(); } } private void tryUpdateLastUpdateTime() { if (TextUtils.isEmpty(mLastUpdateTimeKey) || !mShouldShowLastUpdate) { mLastUpdateTextView.setVisibility(GONE); } else { String time = getLastUpdateTime(); if (TextUtils.isEmpty(time)) { mLastUpdateTextView.setVisibility(GONE); } else { mLastUpdateTextView.setVisibility(VISIBLE); mLastUpdateTextView.setText(time); } } } private String getLastUpdateTime() { if (mLastUpdateTime == -1 && !TextUtils.isEmpty(mLastUpdateTimeKey)) { mLastUpdateTime = getContext().getSharedPreferences(KEY_SharedPreferences, 0).getLong(mLastUpdateTimeKey, -1); } if (mLastUpdateTime == -1) { return null; } long diffTime = new Date().getTime() - mLastUpdateTime; int seconds = (int) (diffTime / 1000); if (diffTime < 0) { return null; } if (seconds <= 0) { return null; } StringBuilder sb = new StringBuilder(); sb.append(getContext().getString(R.string.cube_ptr_last_update)); if (seconds < 60) { sb.append(seconds + getContext().getString(R.string.cube_ptr_seconds_ago)); } else { int minutes = (seconds / 60); if (minutes > 60) { int hours = minutes / 60; if (hours > 24) { Date date = new Date(mLastUpdateTime); sb.append(sDataFormat.format(date)); } else { sb.append(hours + getContext().getString(R.string.cube_ptr_hours_ago)); } } else { sb.append(minutes + getContext().getString(R.string.cube_ptr_minutes_ago)); } } return sb.toString(); } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { final int mOffsetToRefresh = frame.getOffsetToRefresh(); final int currentPos = ptrIndicator.getCurrentPosY(); final int lastPos = ptrIndicator.getLastPosY(); if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) { if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) { crossRotateLineFromBottomUnderTouch(frame); if (mRotateView != null) { mRotateView.clearAnimation(); mRotateView.startAnimation(mReverseFlipAnimation); } } } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) { if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) { crossRotateLineFromTopUnderTouch(frame); if (mRotateView != null) { mRotateView.clearAnimation(); mRotateView.startAnimation(mFlipAnimation); } } } } private void crossRotateLineFromTopUnderTouch(PtrFrameLayout frame) { if (!frame.isPullToRefresh()) { mTitleTextView.setVisibility(VISIBLE); mTitleTextView.setText(R.string.cube_ptr_release_to_refresh); } } private void crossRotateLineFromBottomUnderTouch(PtrFrameLayout frame) { mTitleTextView.setVisibility(VISIBLE); if (frame.isPullToRefresh()) { mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh)); } else { mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down)); } } private class LastUpdateTimeUpdater implements Runnable { private boolean mRunning = false; private void start() { if (TextUtils.isEmpty(mLastUpdateTimeKey)) { return; } mRunning = true; run(); } private void stop() { mRunning = false; removeCallbacks(this); } @Override public void run() { tryUpdateLastUpdateTime(); if (mRunning) { postDelayed(this, 1000); } } } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrClassicFrameLayout.java ================================================ package in.srain.cube.views.ptr; import android.content.Context; import android.util.AttributeSet; public class PtrClassicFrameLayout extends PtrFrameLayout { private PtrClassicDefaultHeader mPtrClassicHeader; public PtrClassicFrameLayout(Context context) { super(context); initViews(); } public PtrClassicFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); initViews(); } public PtrClassicFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initViews(); } private void initViews() { mPtrClassicHeader = new PtrClassicDefaultHeader(getContext()); setHeaderView(mPtrClassicHeader); addPtrUIHandler(mPtrClassicHeader); } public PtrClassicDefaultHeader getHeader() { return mPtrClassicHeader; } /** * Specify the last update time by this key string * * @param key */ public void setLastUpdateTimeKey(String key) { if (mPtrClassicHeader != null) { mPtrClassicHeader.setLastUpdateTimeKey(key); } } /** * Using an object to specify the last update time. * * @param object */ public void setLastUpdateTimeRelateObject(Object object) { if (mPtrClassicHeader != null) { mPtrClassicHeader.setLastUpdateTimeRelateObject(object); } } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler.java ================================================ package in.srain.cube.views.ptr; import android.view.View; import android.widget.AbsListView; public abstract class PtrDefaultHandler implements PtrHandler { public static boolean canChildScrollUp(View view) { if (android.os.Build.VERSION.SDK_INT < 14) { if (view instanceof AbsListView) { final AbsListView absListView = (AbsListView) view; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) .getTop() < absListView.getPaddingTop()); } else { return view.getScrollY() > 0; } } else { return view.canScrollVertically(-1); } } /** * Default implement for check can perform pull to refresh * * @param frame * @param content * @param header * @return */ public static boolean checkContentCanBePulledDown(PtrFrameLayout frame, View content, View header) { return !canChildScrollUp(content); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return checkContentCanBePulledDown(frame, content, header); } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrFrameLayout.java ================================================ package in.srain.cube.views.ptr; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.*; import android.widget.Scroller; import android.widget.TextView; import in.srain.cube.views.ptr.indicator.PtrIndicator; import in.srain.cube.views.ptr.util.PtrCLog; /** * This layout view for "Pull to Refresh(Ptr)" support all of the view, you can contain everything you want. * support: pull to refresh / release to refresh / auto refresh / keep header view while refreshing / hide header view while refreshing * It defines {@link in.srain.cube.views.ptr.PtrUIHandler}, which allows you customize the UI easily. */ public class PtrFrameLayout extends ViewGroup { // status enum public final static byte PTR_STATUS_INIT = 1; private byte mStatus = PTR_STATUS_INIT; public final static byte PTR_STATUS_PREPARE = 2; public final static byte PTR_STATUS_LOADING = 3; public final static byte PTR_STATUS_COMPLETE = 4; private static final boolean DEBUG_LAYOUT = true; public static boolean DEBUG = false; private static int ID = 1; protected final String LOG_TAG = "ptr-frame-" + ++ID; // auto refresh status private final static byte FLAG_AUTO_REFRESH_AT_ONCE = 0x01; private final static byte FLAG_AUTO_REFRESH_BUT_LATER = 0x01 << 1; private final static byte FLAG_ENABLE_NEXT_PTR_AT_ONCE = 0x01 << 2; private final static byte FLAG_PIN_CONTENT = 0x01 << 3; private final static byte MASK_AUTO_REFRESH = 0x03; protected View mContent; // optional config for define header and content in xml file private int mHeaderId = 0; private int mContainerId = 0; // config private int mDurationToClose = 200; private int mDurationToCloseHeader = 1000; private boolean mKeepHeaderWhenRefresh = true; private boolean mPullToRefresh = false; private View mHeaderView; private PtrUIHandlerHolder mPtrUIHandlerHolder = PtrUIHandlerHolder.create(); private PtrHandler mPtrHandler; // working parameters private ScrollChecker mScrollChecker; private int mPagingTouchSlop; private int mHeaderHeight; private boolean mDisableWhenHorizontalMove = false; private int mFlag = 0x00; // disable when detect moving horizontally private boolean mPreventForHorizontal = false; private MotionEvent mLastMoveEvent; private PtrUIHandlerHook mRefreshCompleteHook; private int mLoadingMinTime = 500; private long mLoadingStartTime = 0; private PtrIndicator mPtrIndicator; private boolean mHasSendCancelEvent = false; private Runnable mPerformRefreshCompleteDelay = new Runnable() { @Override public void run() { performRefreshComplete(); } }; public PtrFrameLayout(Context context) { this(context, null); } public PtrFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mPtrIndicator = new PtrIndicator(); TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PtrFrameLayout, 0, 0); if (arr != null) { mHeaderId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_header, mHeaderId); mContainerId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_content, mContainerId); mPtrIndicator.setResistance( arr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, mPtrIndicator.getResistance())); mDurationToClose = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close, mDurationToClose); mDurationToCloseHeader = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close_header, mDurationToCloseHeader); float ratio = mPtrIndicator.getRatioOfHeaderToHeightRefresh(); ratio = arr.getFloat(R.styleable.PtrFrameLayout_ptr_ratio_of_header_height_to_refresh, ratio); mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio); mKeepHeaderWhenRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_keep_header_when_refresh, mKeepHeaderWhenRefresh); mPullToRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_pull_to_fresh, mPullToRefresh); arr.recycle(); } mScrollChecker = new ScrollChecker(); final ViewConfiguration conf = ViewConfiguration.get(getContext()); mPagingTouchSlop = conf.getScaledTouchSlop() * 2; } @Override protected void onFinishInflate() { final int childCount = getChildCount(); if (childCount > 2) { throw new IllegalStateException("PtrFrameLayout can only contains 2 children"); } else if (childCount == 2) { if (mHeaderId != 0 && mHeaderView == null) { mHeaderView = findViewById(mHeaderId); } if (mContainerId != 0 && mContent == null) { mContent = findViewById(mContainerId); } // not specify header or content if (mContent == null || mHeaderView == null) { View child1 = getChildAt(0); View child2 = getChildAt(1); if (child1 instanceof PtrUIHandler) { mHeaderView = child1; mContent = child2; } else if (child2 instanceof PtrUIHandler) { mHeaderView = child2; mContent = child1; } else { // both are not specified if (mContent == null && mHeaderView == null) { mHeaderView = child1; mContent = child2; } // only one is specified else { if (mHeaderView == null) { mHeaderView = mContent == child1 ? child2 : child1; } else { mContent = mHeaderView == child1 ? child2 : child1; } } } } } else if (childCount == 1) { mContent = getChildAt(0); } else { TextView errorView = new TextView(getContext()); errorView.setClickable(true); errorView.setTextColor(0xffff6600); errorView.setGravity(Gravity.CENTER); errorView.setTextSize(20); errorView.setText("The content view in PtrFrameLayout is empty. Do you forget to specify its id in xml layout file?"); mContent = errorView; addView(mContent); } if (mHeaderView != null) { mHeaderView.bringToFront(); } super.onFinishInflate(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mScrollChecker != null) { mScrollChecker.destroy(); } if (mPerformRefreshCompleteDelay != null) { removeCallbacks(mPerformRefreshCompleteDelay); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (isDebug()) { PtrCLog.d(LOG_TAG, "onMeasure frame: width: %s, height: %s, padding: %s %s %s %s", getMeasuredHeight(), getMeasuredWidth(), getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom()); } if (mHeaderView != null) { measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0); MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams(); mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; mPtrIndicator.setHeaderHeight(mHeaderHeight); } if (mContent != null) { measureContentView(mContent, widthMeasureSpec, heightMeasureSpec); if (isDebug()) { ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams(); PtrCLog.d(LOG_TAG, "onMeasure content, width: %s, height: %s, margin: %s %s %s %s", getMeasuredWidth(), getMeasuredHeight(), lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin); PtrCLog.d(LOG_TAG, "onMeasure, currentPos: %s, lastPos: %s, top: %s", mPtrIndicator.getCurrentPosY(), mPtrIndicator.getLastPosY(), mContent.getTop()); } } } private void measureContentView(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, getPaddingTop() + getPaddingBottom() + lp.topMargin, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } @Override protected void onLayout(boolean flag, int i, int j, int k, int l) { layoutChildren(); } private void layoutChildren() { int offset = mPtrIndicator.getCurrentPosY(); int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); if (mHeaderView != null) { MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams(); final int left = paddingLeft + lp.leftMargin; // enhance readability(header is layout above screen when first init) final int top = -(mHeaderHeight - paddingTop - lp.topMargin - offset); final int right = left + mHeaderView.getMeasuredWidth(); final int bottom = top + mHeaderView.getMeasuredHeight(); mHeaderView.layout(left, top, right, bottom); if (isDebug()) { PtrCLog.d(LOG_TAG, "onLayout header: %s %s %s %s", left, top, right, bottom); } } if (mContent != null) { if (isPinContent()) { offset = 0; } MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams(); final int left = paddingLeft + lp.leftMargin; final int top = paddingTop + lp.topMargin + offset; final int right = left + mContent.getMeasuredWidth(); final int bottom = top + mContent.getMeasuredHeight(); if (isDebug()) { PtrCLog.d(LOG_TAG, "onLayout content: %s %s %s %s", left, top, right, bottom); } mContent.layout(left, top, right, bottom); } } @SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions"}) private boolean isDebug() { return DEBUG && DEBUG_LAYOUT; } public boolean dispatchTouchEventSupper(MotionEvent e) { return super.dispatchTouchEvent(e); } @Override public boolean dispatchTouchEvent(MotionEvent e) { if (!isEnabled() || mContent == null || mHeaderView == null) { return dispatchTouchEventSupper(e); } int action = e.getAction(); switch (action) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mPtrIndicator.onRelease(); if (mPtrIndicator.hasLeftStartPosition()) { if (DEBUG) { PtrCLog.d(LOG_TAG, "call onRelease when user release"); } onRelease(false); if (mPtrIndicator.hasMovedAfterPressedDown()) { sendCancelEvent(); return true; } return dispatchTouchEventSupper(e); } else { return dispatchTouchEventSupper(e); } case MotionEvent.ACTION_DOWN: mHasSendCancelEvent = false; mPtrIndicator.onPressDown(e.getX(), e.getY()); mScrollChecker.abortIfWorking(); mPreventForHorizontal = false; // The cancel event will be sent once the position is moved. // So let the event pass to children. // fix #93, #102 dispatchTouchEventSupper(e); return true; case MotionEvent.ACTION_MOVE: mLastMoveEvent = e; mPtrIndicator.onMove(e.getX(), e.getY()); float offsetX = mPtrIndicator.getOffsetX(); float offsetY = mPtrIndicator.getOffsetY(); if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) { if (mPtrIndicator.isInStartPosition()) { mPreventForHorizontal = true; } } if (mPreventForHorizontal) { return dispatchTouchEventSupper(e); } boolean moveDown = offsetY > 0; boolean moveUp = !moveDown; boolean canMoveUp = mPtrIndicator.hasLeftStartPosition(); if (DEBUG) { boolean canMoveDown = mPtrHandler != null && mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView); PtrCLog.v(LOG_TAG, "ACTION_MOVE: offsetY:%s, currentPos: %s, moveUp: %s, canMoveUp: %s, moveDown: %s: canMoveDown: %s", offsetY, mPtrIndicator.getCurrentPosY(), moveUp, canMoveUp, moveDown, canMoveDown); } // disable move when header not reach top if (moveDown && mPtrHandler != null && !mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView)) { return dispatchTouchEventSupper(e); } if ((moveUp && canMoveUp) || moveDown) { movePos(offsetY); return true; } } return dispatchTouchEventSupper(e); } /** * if deltaY > 0, move the content down * * @param deltaY */ private void movePos(float deltaY) { // has reached the top if ((deltaY < 0 && mPtrIndicator.isInStartPosition())) { if (DEBUG) { PtrCLog.e(LOG_TAG, String.format("has reached the top")); } return; } int to = mPtrIndicator.getCurrentPosY() + (int) deltaY; // over top if (mPtrIndicator.willOverTop(to)) { if (DEBUG) { PtrCLog.e(LOG_TAG, String.format("over top")); } to = PtrIndicator.POS_START; } mPtrIndicator.setCurrentPos(to); int change = to - mPtrIndicator.getLastPosY(); updatePos(change); } private void updatePos(int change) { if (change == 0) { return; } boolean isUnderTouch = mPtrIndicator.isUnderTouch(); // once moved, cancel event will be sent to child if (isUnderTouch && !mHasSendCancelEvent && mPtrIndicator.hasMovedAfterPressedDown()) { mHasSendCancelEvent = true; sendCancelEvent(); } // leave initiated position or just refresh complete if ((mPtrIndicator.hasJustLeftStartPosition() && mStatus == PTR_STATUS_INIT) || (mPtrIndicator.goDownCrossFinishPosition() && mStatus == PTR_STATUS_COMPLETE && isEnabledNextPtrAtOnce())) { mStatus = PTR_STATUS_PREPARE; mPtrUIHandlerHolder.onUIRefreshPrepare(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mFlag %s", mFlag); } } // back to initiated position if (mPtrIndicator.hasJustBackToStartPosition()) { tryToNotifyReset(); // recover event to children if (isUnderTouch) { sendDownEvent(); } } // Pull to Refresh if (mStatus == PTR_STATUS_PREPARE) { // reach fresh height while moving from top to bottom if (isUnderTouch && !isAutoRefresh() && mPullToRefresh && mPtrIndicator.crossRefreshLineFromTopToBottom()) { tryToPerformRefresh(); } // reach header height while auto refresh if (performAutoRefreshButLater() && mPtrIndicator.hasJustReachedHeaderHeightFromTopToBottom()) { tryToPerformRefresh(); } } if (DEBUG) { PtrCLog.v(LOG_TAG, "updatePos: change: %s, current: %s last: %s, top: %s, headerHeight: %s", change, mPtrIndicator.getCurrentPosY(), mPtrIndicator.getLastPosY(), mContent.getTop(), mHeaderHeight); } mHeaderView.offsetTopAndBottom(change); if (!isPinContent()) { mContent.offsetTopAndBottom(change); } invalidate(); if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIPositionChange(this, isUnderTouch, mStatus, mPtrIndicator); } onPositionChange(isUnderTouch, mStatus, mPtrIndicator); } protected void onPositionChange(boolean isInTouching, byte status, PtrIndicator mPtrIndicator) { } @SuppressWarnings("unused") public int getHeaderHeight() { return mHeaderHeight; } private void onRelease(boolean stayForLoading) { tryToPerformRefresh(); if (mStatus == PTR_STATUS_LOADING) { // keep header for fresh if (mKeepHeaderWhenRefresh) { // scroll header back if (mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && !stayForLoading) { mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToKeepHeaderWhileLoading(), mDurationToClose); } else { // do nothing } } else { tryScrollBackToTopWhileLoading(); } } else { if (mStatus == PTR_STATUS_COMPLETE) { notifyUIRefreshComplete(false); } else { tryScrollBackToTopAbortRefresh(); } } } /** * please DO REMEMBER resume the hook * * @param hook */ public void setRefreshCompleteHook(PtrUIHandlerHook hook) { mRefreshCompleteHook = hook; hook.setResumeAction(new Runnable() { @Override public void run() { if (DEBUG) { PtrCLog.d(LOG_TAG, "mRefreshCompleteHook resume."); } notifyUIRefreshComplete(true); } }); } /** * Scroll back to to if is not under touch */ private void tryScrollBackToTop() { if (!mPtrIndicator.isUnderTouch()) { mScrollChecker.tryToScrollTo(PtrIndicator.POS_START, mDurationToCloseHeader); } } /** * just make easier to understand */ private void tryScrollBackToTopWhileLoading() { tryScrollBackToTop(); } /** * just make easier to understand */ private void tryScrollBackToTopAfterComplete() { tryScrollBackToTop(); } /** * just make easier to understand */ private void tryScrollBackToTopAbortRefresh() { tryScrollBackToTop(); } private boolean tryToPerformRefresh() { if (mStatus != PTR_STATUS_PREPARE) { return false; } // if ((mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && isAutoRefresh()) || mPtrIndicator.isOverOffsetToRefresh()) { mStatus = PTR_STATUS_LOADING; performRefresh(); } return false; } private void performRefresh() { mLoadingStartTime = System.currentTimeMillis(); if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIRefreshBegin(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshBegin"); } } if (mPtrHandler != null) { mPtrHandler.onRefreshBegin(this); } } /** * If at the top and not in loading, reset */ private boolean tryToNotifyReset() { if ((mStatus == PTR_STATUS_COMPLETE || mStatus == PTR_STATUS_PREPARE) && mPtrIndicator.isInStartPosition()) { if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIReset(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIReset"); } } mStatus = PTR_STATUS_INIT; clearFlag(); return true; } return false; } protected void onPtrScrollAbort() { if (mPtrIndicator.hasLeftStartPosition() && isAutoRefresh()) { if (DEBUG) { PtrCLog.d(LOG_TAG, "call onRelease after scroll abort"); } onRelease(true); } } protected void onPtrScrollFinish() { if (mPtrIndicator.hasLeftStartPosition() && isAutoRefresh()) { if (DEBUG) { PtrCLog.d(LOG_TAG, "call onRelease after scroll finish"); } onRelease(true); } } /** * Detect whether is refreshing. * * @return */ public boolean isRefreshing() { return mStatus == PTR_STATUS_LOADING; } /** * Call this when data is loaded. * The UI will perform complete at once or after a delay, depends on the time elapsed is greater then {@link #mLoadingMinTime} or not. */ final public void refreshComplete() { if (DEBUG) { PtrCLog.i(LOG_TAG, "refreshComplete"); } if (mRefreshCompleteHook != null) { mRefreshCompleteHook.reset(); } int delay = (int) (mLoadingMinTime - (System.currentTimeMillis() - mLoadingStartTime)); if (delay <= 0) { if (DEBUG) { PtrCLog.d(LOG_TAG, "performRefreshComplete at once"); } performRefreshComplete(); } else { postDelayed(mPerformRefreshCompleteDelay, delay); if (DEBUG) { PtrCLog.d(LOG_TAG, "performRefreshComplete after delay: %s", delay); } } } /** * Do refresh complete work when time elapsed is greater than {@link #mLoadingMinTime} */ private void performRefreshComplete() { mStatus = PTR_STATUS_COMPLETE; // if is auto refresh do nothing, wait scroller stop if (mScrollChecker.mIsRunning && isAutoRefresh()) { // do nothing if (DEBUG) { PtrCLog.d(LOG_TAG, "performRefreshComplete do nothing, scrolling: %s, auto refresh: %s", mScrollChecker.mIsRunning, mFlag); } return; } notifyUIRefreshComplete(false); } /** * Do real refresh work. If there is a hook, execute the hook first. * * @param ignoreHook */ private void notifyUIRefreshComplete(boolean ignoreHook) { /** * After hook operation is done, {@link #notifyUIRefreshComplete} will be call in resume action to ignore hook. */ if (mPtrIndicator.hasLeftStartPosition() && !ignoreHook && mRefreshCompleteHook != null) { if (DEBUG) { PtrCLog.d(LOG_TAG, "notifyUIRefreshComplete mRefreshCompleteHook run."); } mRefreshCompleteHook.takeOver(); return; } if (mPtrUIHandlerHolder.hasHandler()) { if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshComplete"); } mPtrUIHandlerHolder.onUIRefreshComplete(this); } mPtrIndicator.onUIRefreshComplete(); tryScrollBackToTopAfterComplete(); tryToNotifyReset(); } public void autoRefresh() { autoRefresh(true, mDurationToCloseHeader); } public void autoRefresh(boolean atOnce) { autoRefresh(atOnce, mDurationToCloseHeader); } private void clearFlag() { // remove auto fresh flag mFlag = mFlag & ~MASK_AUTO_REFRESH; } public void autoRefresh(boolean atOnce, int duration) { if (mStatus != PTR_STATUS_INIT) { return; } mFlag |= atOnce ? FLAG_AUTO_REFRESH_AT_ONCE : FLAG_AUTO_REFRESH_BUT_LATER; mStatus = PTR_STATUS_PREPARE; if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIRefreshPrepare(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mFlag %s", mFlag); } } mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToRefresh(), duration); if (atOnce) { mStatus = PTR_STATUS_LOADING; performRefresh(); } } public boolean isAutoRefresh() { return (mFlag & MASK_AUTO_REFRESH) > 0; } private boolean performAutoRefreshButLater() { return (mFlag & MASK_AUTO_REFRESH) == FLAG_AUTO_REFRESH_BUT_LATER; } public boolean isEnabledNextPtrAtOnce() { return (mFlag & FLAG_ENABLE_NEXT_PTR_AT_ONCE) > 0; } /** * If @param enable has been set to true. The user can perform next PTR at once. * * @param enable */ public void setEnabledNextPtrAtOnce(boolean enable) { if (enable) { mFlag = mFlag | FLAG_ENABLE_NEXT_PTR_AT_ONCE; } else { mFlag = mFlag & ~FLAG_ENABLE_NEXT_PTR_AT_ONCE; } } public boolean isPinContent() { return (mFlag & FLAG_PIN_CONTENT) > 0; } /** * The content view will now move when {@param pinContent} set to true. * * @param pinContent */ public void setPinContent(boolean pinContent) { if (pinContent) { mFlag = mFlag | FLAG_PIN_CONTENT; } else { mFlag = mFlag & ~FLAG_PIN_CONTENT; } } /** * It's useful when working with viewpager. * * @param disable */ public void disableWhenHorizontalMove(boolean disable) { mDisableWhenHorizontalMove = disable; } /** * loading will last at least for so long * * @param time */ public void setLoadingMinTime(int time) { mLoadingMinTime = time; } /** * Not necessary any longer. Once moved, cancel event will be sent to child. * * @param yes */ @Deprecated public void setInterceptEventWhileWorking(boolean yes) { } @SuppressWarnings({"unused"}) public View getContentView() { return mContent; } public void setPtrHandler(PtrHandler ptrHandler) { mPtrHandler = ptrHandler; } public void addPtrUIHandler(PtrUIHandler ptrUIHandler) { PtrUIHandlerHolder.addHandler(mPtrUIHandlerHolder, ptrUIHandler); } @SuppressWarnings({"unused"}) public void removePtrUIHandler(PtrUIHandler ptrUIHandler) { mPtrUIHandlerHolder = PtrUIHandlerHolder.removeHandler(mPtrUIHandlerHolder, ptrUIHandler); } public void setPtrIndicator(PtrIndicator slider) { if (mPtrIndicator != null && mPtrIndicator != slider) { slider.convertFrom(mPtrIndicator); } mPtrIndicator = slider; } @SuppressWarnings({"unused"}) public float getResistance() { return mPtrIndicator.getResistance(); } public void setResistance(float resistance) { mPtrIndicator.setResistance(resistance); } @SuppressWarnings({"unused"}) public float getDurationToClose() { return mDurationToClose; } /** * The duration to return back to the refresh position * * @param duration */ public void setDurationToClose(int duration) { mDurationToClose = duration; } @SuppressWarnings({"unused"}) public long getDurationToCloseHeader() { return mDurationToCloseHeader; } /** * The duration to close time * * @param duration */ public void setDurationToCloseHeader(int duration) { mDurationToCloseHeader = duration; } public void setRatioOfHeaderHeightToRefresh(float ratio) { mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio); } public int getOffsetToRefresh() { return mPtrIndicator.getOffsetToRefresh(); } @SuppressWarnings({"unused"}) public void setOffsetToRefresh(int offset) { mPtrIndicator.setOffsetToRefresh(offset); } @SuppressWarnings({"unused"}) public float getRatioOfHeaderToHeightRefresh() { return mPtrIndicator.getRatioOfHeaderToHeightRefresh(); } @SuppressWarnings({"unused"}) public int getOffsetToKeepHeaderWhileLoading() { return mPtrIndicator.getOffsetToKeepHeaderWhileLoading(); } @SuppressWarnings({"unused"}) public void setOffsetToKeepHeaderWhileLoading(int offset) { mPtrIndicator.setOffsetToKeepHeaderWhileLoading(offset); } @SuppressWarnings({"unused"}) public boolean isKeepHeaderWhenRefresh() { return mKeepHeaderWhenRefresh; } public void setKeepHeaderWhenRefresh(boolean keepOrNot) { mKeepHeaderWhenRefresh = keepOrNot; } public boolean isPullToRefresh() { return mPullToRefresh; } public void setPullToRefresh(boolean pullToRefresh) { mPullToRefresh = pullToRefresh; } @SuppressWarnings({"unused"}) public View getHeaderView() { return mHeaderView; } public void setHeaderView(View header) { if (mHeaderView != null && header != null && mHeaderView != header) { removeView(mHeaderView); } ViewGroup.LayoutParams lp = header.getLayoutParams(); if (lp == null) { lp = new LayoutParams(-1, -2); header.setLayoutParams(lp); } mHeaderView = header; addView(header); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p != null && p instanceof LayoutParams; } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } private void sendCancelEvent() { if (DEBUG) { PtrCLog.d(LOG_TAG, "send cancel event"); } // The ScrollChecker will update position and lead to send cancel event when mLastMoveEvent is null. // fix #104, #80, #92 if (mLastMoveEvent == null) { return; } MotionEvent last = mLastMoveEvent; MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, last.getX(), last.getY(), last.getMetaState()); dispatchTouchEventSupper(e); } private void sendDownEvent() { if (DEBUG) { PtrCLog.d(LOG_TAG, "send down event"); } final MotionEvent last = mLastMoveEvent; MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime(), MotionEvent.ACTION_DOWN, last.getX(), last.getY(), last.getMetaState()); dispatchTouchEventSupper(e); } public static class LayoutParams extends MarginLayoutParams { public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } public LayoutParams(int width, int height) { super(width, height); } @SuppressWarnings({"unused"}) public LayoutParams(MarginLayoutParams source) { super(source); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } } class ScrollChecker implements Runnable { private int mLastFlingY; private Scroller mScroller; private boolean mIsRunning = false; private int mStart; private int mTo; public ScrollChecker() { mScroller = new Scroller(getContext()); } public void run() { boolean finish = !mScroller.computeScrollOffset() || mScroller.isFinished(); int curY = mScroller.getCurrY(); int deltaY = curY - mLastFlingY; if (DEBUG) { if (deltaY != 0) { PtrCLog.v(LOG_TAG, "scroll: %s, start: %s, to: %s, currentPos: %s, current :%s, last: %s, delta: %s", finish, mStart, mTo, mPtrIndicator.getCurrentPosY(), curY, mLastFlingY, deltaY); } } if (!finish) { mLastFlingY = curY; movePos(deltaY); post(this); } else { finish(); } } private void finish() { if (DEBUG) { PtrCLog.v(LOG_TAG, "finish, currentPos:%s", mPtrIndicator.getCurrentPosY()); } reset(); onPtrScrollFinish(); } private void reset() { mIsRunning = false; mLastFlingY = 0; removeCallbacks(this); } private void destroy() { reset(); if (!mScroller.isFinished()) { mScroller.forceFinished(true); } } public void abortIfWorking() { if (mIsRunning) { if (!mScroller.isFinished()) { mScroller.forceFinished(true); } onPtrScrollAbort(); reset(); } } public void tryToScrollTo(int to, int duration) { if (mPtrIndicator.isAlreadyHere(to)) { return; } mStart = mPtrIndicator.getCurrentPosY(); mTo = to; int distance = to - mStart; if (DEBUG) { PtrCLog.d(LOG_TAG, "tryToScrollTo: start: %s, distance:%s, to:%s", mStart, distance, to); } removeCallbacks(this); mLastFlingY = 0; // fix #47: Scroller should be reused, https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/issues/47 if (!mScroller.isFinished()) { mScroller.forceFinished(true); } mScroller.startScroll(0, 0, 0, distance, duration); post(this); mIsRunning = true; } } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrHandler.java ================================================ package in.srain.cube.views.ptr; import android.view.View; public interface PtrHandler { /** * Check can do refresh or not. For example the content is empty or the first child is in view. *

* {@link in.srain.cube.views.ptr.PtrDefaultHandler#checkContentCanBePulledDown} */ public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); /** * When refresh begin * * @param frame */ public void onRefreshBegin(final PtrFrameLayout frame); } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrUIHandler.java ================================================ package in.srain.cube.views.ptr; import in.srain.cube.views.ptr.indicator.PtrIndicator; /** * */ public interface PtrUIHandler { /** * When the content view has reached top and refresh has been completed, view will be reset. * * @param frame */ public void onUIReset(PtrFrameLayout frame); /** * prepare for loading * * @param frame */ public void onUIRefreshPrepare(PtrFrameLayout frame); /** * perform refreshing UI */ public void onUIRefreshBegin(PtrFrameLayout frame); /** * perform UI after refresh */ public void onUIRefreshComplete(PtrFrameLayout frame); public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator); } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrUIHandlerHolder.java ================================================ package in.srain.cube.views.ptr; import in.srain.cube.views.ptr.indicator.PtrIndicator; /** * A single linked list to wrap PtrUIHandler */ class PtrUIHandlerHolder implements PtrUIHandler { private PtrUIHandler mHandler; private PtrUIHandlerHolder mNext; private boolean contains(PtrUIHandler handler) { return mHandler != null && mHandler == handler; } private PtrUIHandlerHolder() { } public boolean hasHandler() { return mHandler != null; } private PtrUIHandler getHandler() { return mHandler; } public static void addHandler(PtrUIHandlerHolder head, PtrUIHandler handler) { if (null == handler) { return; } if (head == null) { return; } if (null == head.mHandler) { head.mHandler = handler; return; } PtrUIHandlerHolder current = head; for (; ; current = current.mNext) { // duplicated if (current.contains(handler)) { return; } if (current.mNext == null) { break; } } PtrUIHandlerHolder newHolder = new PtrUIHandlerHolder(); newHolder.mHandler = handler; current.mNext = newHolder; } public static PtrUIHandlerHolder create() { return new PtrUIHandlerHolder(); } public static PtrUIHandlerHolder removeHandler(PtrUIHandlerHolder head, PtrUIHandler handler) { if (head == null || handler == null || null == head.mHandler) { return head; } PtrUIHandlerHolder current = head; PtrUIHandlerHolder pre = null; do { // delete current: link pre to next, unlink next from current; // pre will no change, current move to next element; if (current.contains(handler)) { // current is head if (pre == null) { head = current.mNext; current.mNext = null; current = head; } else { pre.mNext = current.mNext; current.mNext = null; current = pre.mNext; } } else { pre = current; current = current.mNext; } } while (current != null); if (head == null) { head = new PtrUIHandlerHolder(); } return head; } @Override public void onUIReset(PtrFrameLayout frame) { PtrUIHandlerHolder current = this; do { final PtrUIHandler handler = current.getHandler(); if (null != handler) { handler.onUIReset(frame); } } while ((current = current.mNext) != null); } @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { if (!hasHandler()) { return; } PtrUIHandlerHolder current = this; do { final PtrUIHandler handler = current.getHandler(); if (null != handler) { handler.onUIRefreshPrepare(frame); } } while ((current = current.mNext) != null); } @Override public void onUIRefreshBegin(PtrFrameLayout frame) { PtrUIHandlerHolder current = this; do { final PtrUIHandler handler = current.getHandler(); if (null != handler) { handler.onUIRefreshBegin(frame); } } while ((current = current.mNext) != null); } @Override public void onUIRefreshComplete(PtrFrameLayout frame) { PtrUIHandlerHolder current = this; do { final PtrUIHandler handler = current.getHandler(); if (null != handler) { handler.onUIRefreshComplete(frame); } } while ((current = current.mNext) != null); } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { PtrUIHandlerHolder current = this; do { final PtrUIHandler handler = current.getHandler(); if (null != handler) { handler.onUIPositionChange(frame, isUnderTouch, status, ptrIndicator); } } while ((current = current.mNext) != null); } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/PtrUIHandlerHook.java ================================================ package in.srain.cube.views.ptr; /** * Run a hook runnable, the runnable will run only once. * After the runnable is done, call resume to resume. * Once run, call takeover will directory call the resume action */ public abstract class PtrUIHandlerHook implements Runnable { private Runnable mResumeAction; private static final byte STATUS_PREPARE = 0; private static final byte STATUS_IN_HOOK = 1; private static final byte STATUS_RESUMED = 2; private byte mStatus = STATUS_PREPARE; public void takeOver() { takeOver(null); } public void takeOver(Runnable resumeAction) { if (resumeAction != null) { mResumeAction = resumeAction; } switch (mStatus) { case STATUS_PREPARE: mStatus = STATUS_IN_HOOK; run(); break; case STATUS_IN_HOOK: break; case STATUS_RESUMED: resume(); break; } } public void reset() { mStatus = STATUS_PREPARE; } public void resume() { if (mResumeAction != null) { mResumeAction.run(); } mStatus = STATUS_RESUMED; } /** * Hook should always have a resume action, which is hooked by this hook. * * @param runnable */ public void setResumeAction(Runnable runnable) { mResumeAction = runnable; } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/header/MaterialHeader.java ================================================ package in.srain.cube.views.ptr.header; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.view.animation.Animation; import android.view.animation.Transformation; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrUIHandler; import in.srain.cube.views.ptr.PtrUIHandlerHook; import in.srain.cube.views.ptr.indicator.PtrIndicator; public class MaterialHeader extends View implements PtrUIHandler { private MaterialProgressDrawable mDrawable; private float mScale = 1f; private PtrFrameLayout mPtrFrameLayout; private Animation mScaleAnimation = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { mScale = 1f - interpolatedTime; mDrawable.setAlpha((int) (255 * mScale)); invalidate(); } }; public MaterialHeader(Context context) { super(context); initView(); } public MaterialHeader(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public MaterialHeader(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } public void setPtrFrameLayout(PtrFrameLayout layout) { final PtrUIHandlerHook mPtrUIHandlerHook = new PtrUIHandlerHook() { @Override public void run() { startAnimation(mScaleAnimation); } }; mScaleAnimation.setDuration(200); mScaleAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mPtrUIHandlerHook.resume(); } @Override public void onAnimationRepeat(Animation animation) { } }); mPtrFrameLayout = layout; mPtrFrameLayout.setRefreshCompleteHook(mPtrUIHandlerHook); } private void initView() { mDrawable = new MaterialProgressDrawable(getContext(), this); mDrawable.setBackgroundColor(Color.WHITE); mDrawable.setCallback(this); } @Override public void invalidateDrawable(Drawable dr) { if (dr == mDrawable) { invalidate(); } else { super.invalidateDrawable(dr); } } public void setColorSchemeColors(int[] colors) { mDrawable.setColorSchemeColors(colors); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = mDrawable.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom(); heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final int size = mDrawable.getIntrinsicHeight(); mDrawable.setBounds(0, 0, size, size); } @Override protected void onDraw(Canvas canvas) { final int saveCount = canvas.save(); Rect rect = mDrawable.getBounds(); int l = getPaddingLeft() + (getMeasuredWidth() - mDrawable.getIntrinsicWidth()) / 2; canvas.translate(l, getPaddingTop()); canvas.scale(mScale, mScale, rect.exactCenterX(), rect.exactCenterY()); mDrawable.draw(canvas); canvas.restoreToCount(saveCount); } /** * When the content view has reached top and refresh has been completed, view will be reset. * * @param frame */ @Override public void onUIReset(PtrFrameLayout frame) { mScale = 1f; mDrawable.stop(); } /** * prepare for loading * * @param frame */ @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { } /** * perform refreshing UI * * @param frame */ @Override public void onUIRefreshBegin(PtrFrameLayout frame) { mDrawable.setAlpha(255); mDrawable.start(); } /** * perform UI after refresh * * @param frame */ @Override public void onUIRefreshComplete(PtrFrameLayout frame) { mDrawable.stop(); } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { float percent = Math.min(1f, ptrIndicator.getCurrentPercent()); if (status == PtrFrameLayout.PTR_STATUS_PREPARE) { mDrawable.setAlpha((int) (255 * percent)); mDrawable.showArrow(true); float strokeStart = ((percent) * .8f); mDrawable.setStartEndTrim(0f, Math.min(0.8f, strokeStart)); mDrawable.setArrowScale(Math.min(1f, percent)); // magic float rotation = (-0.25f + .4f * percent + percent * 2) * .5f; mDrawable.setProgressRotation(rotation); invalidate(); } } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/header/MaterialProgressDrawable.java ================================================ /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package in.srain.cube.views.ptr.header; import android.content.Context; import android.content.res.Resources; import android.graphics.*; import android.graphics.Paint.Style; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.os.Build; import android.util.DisplayMetrics; import android.view.View; import android.view.animation.*; import android.view.animation.Interpolator; import in.srain.cube.views.ptr.util.PtrLocalDisplay; import java.util.ArrayList; /** * Fancy progress indicator for Material theme. * It's taken from {@link android.support.v4.widget}. * I've done some slight changes. * * @hide */ public class MaterialProgressDrawable extends Drawable implements Animatable { // Maps to ProgressBar.Large style public static final int LARGE = 0; // Maps to ProgressBar default style public static final int DEFAULT = 1; private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final Interpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator(); private static final Interpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator(); private static final Interpolator EASE_INTERPOLATOR = new AccelerateDecelerateInterpolator(); // Maps to ProgressBar default style private static final int CIRCLE_DIAMETER = 40; private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width private static final float STROKE_WIDTH = 2.5f; // Maps to ProgressBar.Large style private static final int CIRCLE_DIAMETER_LARGE = 56; private static final float CENTER_RADIUS_LARGE = 12.5f; private static final float STROKE_WIDTH_LARGE = 3f; /** * The duration of a single progress spin in milliseconds. */ private static final int ANIMATION_DURATION = 1000 * 80 / 60; /** * The number of points in the progress "star". */ private static final float NUM_POINTS = 5f; /** * Layout info for the arrowhead in dp */ private static final int ARROW_WIDTH = 10; private static final int ARROW_HEIGHT = 5; private static final float ARROW_OFFSET_ANGLE = 5; /** * Layout info for the arrowhead for the large spinner in dp */ private static final int ARROW_WIDTH_LARGE = 12; private static final int ARROW_HEIGHT_LARGE = 6; private static final float MAX_PROGRESS_ARC = .8f; private static final int KEY_SHADOW_COLOR = 0x1E000000; private static final int FILL_SHADOW_COLOR = 0x3D000000; private static final float SHADOW_RADIUS = 3.5f; private static final float X_OFFSET = 0f; private static final float Y_OFFSET = 1.75f; private final int[] COLORS = new int[]{ 0xFFC93437, 0xFF375BF1, 0xFFF7D23E, 0xFF34A350 }; /** * The list of animators operating on this drawable. */ private final ArrayList mAnimators = new ArrayList(); /** * The indicator ring, used to manage animation state. */ private final Ring mRing; private final Callback mCallback = new Callback() { @Override public void invalidateDrawable(Drawable d) { invalidateSelf(); } @Override public void scheduleDrawable(Drawable d, Runnable what, long when) { scheduleSelf(what, when); } @Override public void unscheduleDrawable(Drawable d, Runnable what) { unscheduleSelf(what); } }; /** * Canvas rotation in degrees. */ private float mRotation; private Resources mResources; private View mParent; private Animation mAnimation; private float mRotationCount; private double mWidth; private double mHeight; private Animation mFinishAnimation; private int mBackgroundColor; private ShapeDrawable mShadow; public MaterialProgressDrawable(Context context, View parent) { mParent = parent; mResources = context.getResources(); mRing = new Ring(mCallback); mRing.setColors(COLORS); updateSizes(DEFAULT); setupAnimators(); } private void setSizeParameters(double progressCircleWidth, double progressCircleHeight, double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) { final Ring ring = mRing; final DisplayMetrics metrics = mResources.getDisplayMetrics(); final float screenDensity = metrics.density; mWidth = progressCircleWidth * screenDensity; mHeight = progressCircleHeight * screenDensity; ring.setStrokeWidth((float) strokeWidth * screenDensity); ring.setCenterRadius(centerRadius * screenDensity); ring.setColorIndex(0); ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity); ring.setInsets((int) mWidth, (int) mHeight); setUp(mWidth); } private void setUp(final double diameter) { PtrLocalDisplay.init(mParent.getContext()); final int shadowYOffset = PtrLocalDisplay.dp2px(Y_OFFSET); final int shadowXOffset = PtrLocalDisplay.dp2px(X_OFFSET); int mShadowRadius = PtrLocalDisplay.dp2px(SHADOW_RADIUS); OvalShape oval = new OvalShadow(mShadowRadius, (int) diameter); mShadow = new ShapeDrawable(oval); if (Build.VERSION.SDK_INT >= 11) { mParent.setLayerType(View.LAYER_TYPE_SOFTWARE, mShadow.getPaint()); } mShadow.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR); } /** * Set the overall size for the progress spinner. This updates the radius * and stroke width of the ring. * * @param size One of {@link MaterialProgressDrawable#LARGE} or * {@link MaterialProgressDrawable#DEFAULT} */ public void updateSizes(int size) { if (size == LARGE) { setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE, STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE); } else { setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH, ARROW_WIDTH, ARROW_HEIGHT); } } /** * @param show Set to true to display the arrowhead on the progress spinner. */ public void showArrow(boolean show) { mRing.setShowArrow(show); } /** * @param scale Set the scale of the arrowhead for the spinner. */ public void setArrowScale(float scale) { mRing.setArrowScale(scale); } /** * Set the start and end trim for the progress spinner arc. * * @param startAngle start angle * @param endAngle end angle */ public void setStartEndTrim(float startAngle, float endAngle) { mRing.setStartTrim(startAngle); mRing.setEndTrim(endAngle); } /** * Set the amount of rotation to apply to the progress spinner. * * @param rotation Rotation is from [0..1] */ public void setProgressRotation(float rotation) { mRing.setRotation(rotation); } /** * Update the background color of the circle image view. */ public void setBackgroundColor(int color) { mBackgroundColor = color; mRing.setBackgroundColor(color); } /** * Set the colors used in the progress animation from color resources. * The first color will also be the color of the bar that grows in response * to a user swipe gesture. * * @param colors */ public void setColorSchemeColors(int... colors) { mRing.setColors(colors); mRing.setColorIndex(0); } @Override public int getIntrinsicHeight() { return (int) mHeight; } @Override public int getIntrinsicWidth() { return (int) mWidth; } @Override public void draw(Canvas c) { if (mShadow != null) { mShadow.getPaint().setColor(mBackgroundColor); mShadow.draw(c); } final Rect bounds = getBounds(); final int saveCount = c.save(); c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY()); mRing.draw(c, bounds); c.restoreToCount(saveCount); } public int getAlpha() { return mRing.getAlpha(); } @Override public void setAlpha(int alpha) { mRing.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter colorFilter) { mRing.setColorFilter(colorFilter); } @SuppressWarnings("unused") private float getRotation() { return mRotation; } @SuppressWarnings("unused") void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public boolean isRunning() { final ArrayList animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animation animator = animators.get(i); if (animator.hasStarted() && !animator.hasEnded()) { return true; } } return false; } @Override public void start() { mAnimation.reset(); mRing.storeOriginals(); // Already showing some part of the ring if (mRing.getEndTrim() != mRing.getStartTrim()) { mParent.startAnimation(mFinishAnimation); } else { mRing.setColorIndex(0); mRing.resetOriginals(); mParent.startAnimation(mAnimation); } } @Override public void stop() { mParent.clearAnimation(); setRotation(0); mRing.setShowArrow(false); mRing.setColorIndex(0); mRing.resetOriginals(); } private void setupAnimators() { final Ring ring = mRing; final Animation finishRingAnimation = new Animation() { public void applyTransformation(float interpolatedTime, Transformation t) { // shrink back down and complete a full rotation before starting other circles // Rotation goes between [0..1]. float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC) + 1f); final float startTrim = ring.getStartingStartTrim() + (ring.getStartingEndTrim() - ring.getStartingStartTrim()) * interpolatedTime; ring.setStartTrim(startTrim); final float rotation = ring.getStartingRotation() + ((targetRotation - ring.getStartingRotation()) * interpolatedTime); ring.setRotation(rotation); ring.setArrowScale(1 - interpolatedTime); } }; finishRingAnimation.setInterpolator(EASE_INTERPOLATOR); finishRingAnimation.setDuration(ANIMATION_DURATION / 2); finishRingAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { ring.goToNextColor(); ring.storeOriginals(); ring.setShowArrow(false); mParent.startAnimation(mAnimation); } @Override public void onAnimationRepeat(Animation animation) { } }); final Animation animation = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { // The minProgressArc is calculated from 0 to create an angle that // matches the stroke width. final float minProgressArc = (float) Math.toRadians(ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius())); final float startingEndTrim = ring.getStartingEndTrim(); final float startingTrim = ring.getStartingStartTrim(); final float startingRotation = ring.getStartingRotation(); // Offset the minProgressArc to where the endTrim is located. final float minArc = MAX_PROGRESS_ARC - minProgressArc; final float endTrim = startingEndTrim + (minArc * START_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime)); ring.setEndTrim(endTrim); final float startTrim = startingTrim + (MAX_PROGRESS_ARC * END_CURVE_INTERPOLATOR .getInterpolation(interpolatedTime)); ring.setStartTrim(startTrim); final float rotation = startingRotation + (0.25f * interpolatedTime); ring.setRotation(rotation); float groupRotation = ((720.0f / NUM_POINTS) * interpolatedTime) + (720.0f * (mRotationCount / NUM_POINTS)); setRotation(groupRotation); } }; animation.setRepeatCount(Animation.INFINITE); animation.setRepeatMode(Animation.RESTART); animation.setInterpolator(LINEAR_INTERPOLATOR); animation.setDuration(ANIMATION_DURATION); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mRotationCount = 0; } @Override public void onAnimationEnd(Animation animation) { // do nothing } @Override public void onAnimationRepeat(Animation animation) { ring.storeOriginals(); ring.goToNextColor(); ring.setStartTrim(ring.getEndTrim()); mRotationCount = (mRotationCount + 1) % (NUM_POINTS); } }); mFinishAnimation = finishRingAnimation; mAnimation = animation; } private static class Ring { private final RectF mTempBounds = new RectF(); private final Paint mArcPaint = new Paint(); private final Paint mArrowPaint = new Paint(); private final Callback mRingCallback; private final Paint mCirclePaint = new Paint(); private float mStartTrim = 0.0f; private float mEndTrim = 0.0f; private float mRotation = 0.0f; private float mStrokeWidth = 5.0f; private float mStrokeInset = 2.5f; private int[] mColors; // mColorIndex represents the offset into the available mColors that the // progress circle should currently display. As the progress circle is // animating, the mColorIndex moves by one to the next available color. private int mColorIndex; private float mStartingStartTrim; private float mStartingEndTrim; private float mStartingRotation; private boolean mShowArrow; private Path mArrow; private float mArrowScale; private double mRingCenterRadius; private int mArrowWidth; private int mArrowHeight; private int mAlpha; private int mBackgroundColor; public Ring(Callback callback) { mRingCallback = callback; mArcPaint.setStrokeCap(Paint.Cap.SQUARE); mArcPaint.setAntiAlias(true); mArcPaint.setStyle(Style.STROKE); mArrowPaint.setStyle(Paint.Style.FILL); mArrowPaint.setAntiAlias(true); mCirclePaint.setAntiAlias(true); } public void setBackgroundColor(int color) { mBackgroundColor = color; } /** * Set the dimensions of the arrowhead. * * @param width Width of the hypotenuse of the arrow head * @param height Height of the arrow point */ public void setArrowDimensions(float width, float height) { mArrowWidth = (int) width; mArrowHeight = (int) height; } /** * Draw the progress spinner */ public void draw(Canvas c, Rect bounds) { mCirclePaint.setColor(mBackgroundColor); mCirclePaint.setAlpha(mAlpha); c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2, mCirclePaint); final RectF arcBounds = mTempBounds; arcBounds.set(bounds); arcBounds.inset(mStrokeInset, mStrokeInset); final float startAngle = (mStartTrim + mRotation) * 360; final float endAngle = (mEndTrim + mRotation) * 360; float sweepAngle = endAngle - startAngle; mArcPaint.setColor(mColors[mColorIndex]); mArcPaint.setAlpha(mAlpha); c.drawArc(arcBounds, startAngle, sweepAngle, false, mArcPaint); drawTriangle(c, startAngle, sweepAngle, bounds); } private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) { if (mShowArrow) { if (mArrow == null) { mArrow = new android.graphics.Path(); mArrow.setFillType(android.graphics.Path.FillType.EVEN_ODD); } else { mArrow.reset(); } // Adjust the position of the triangle so that it is inset as // much as the arc, but also centered on the arc. float inset = (int) mStrokeInset / 2 * mArrowScale; float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX()); float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY()); // Update the path each time. This works around an issue in SKIA // where concatenating a rotation matrix to a scale matrix // ignored a starting negative rotation. This appears to have // been fixed as of API 21. mArrow.moveTo(0, 0); mArrow.lineTo(mArrowWidth * mArrowScale, 0); mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight * mArrowScale)); mArrow.offset(x - inset, y); mArrow.close(); // draw a triangle mArrowPaint.setColor(mColors[mColorIndex]); mArrowPaint.setAlpha(mAlpha); c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(), bounds.exactCenterY()); c.drawPath(mArrow, mArrowPaint); } } /** * Set the colors the progress spinner alternates between. * * @param colors Array of integers describing the colors. Must be non-null. */ public void setColors(int[] colors) { mColors = colors; // if colors are reset, make sure to reset the color index as well setColorIndex(0); } /** * @param index Index into the color array of the color to display in * the progress spinner. */ public void setColorIndex(int index) { mColorIndex = index; } /** * Proceed to the next available ring color. This will automatically * wrap back to the beginning of colors. */ public void goToNextColor() { mColorIndex = (mColorIndex + 1) % (mColors.length); } public void setColorFilter(ColorFilter filter) { mArcPaint.setColorFilter(filter); invalidateSelf(); } /** * @return Current alpha of the progress spinner and arrowhead. */ public int getAlpha() { return mAlpha; } /** * @param alpha Set the alpha of the progress spinner and associated arrowhead. */ public void setAlpha(int alpha) { mAlpha = alpha; } @SuppressWarnings("unused") public float getStrokeWidth() { return mStrokeWidth; } /** * @param strokeWidth Set the stroke width of the progress spinner in pixels. */ public void setStrokeWidth(float strokeWidth) { mStrokeWidth = strokeWidth; mArcPaint.setStrokeWidth(strokeWidth); invalidateSelf(); } @SuppressWarnings("unused") public float getStartTrim() { return mStartTrim; } @SuppressWarnings("unused") public void setStartTrim(float startTrim) { mStartTrim = startTrim; invalidateSelf(); } public float getStartingStartTrim() { return mStartingStartTrim; } public float getStartingEndTrim() { return mStartingEndTrim; } @SuppressWarnings("unused") public float getEndTrim() { return mEndTrim; } @SuppressWarnings("unused") public void setEndTrim(float endTrim) { mEndTrim = endTrim; invalidateSelf(); } @SuppressWarnings("unused") public float getRotation() { return mRotation; } @SuppressWarnings("unused") public void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } public void setInsets(int width, int height) { final float minEdge = (float) Math.min(width, height); float insets; if (mRingCenterRadius <= 0 || minEdge < 0) { insets = (float) Math.ceil(mStrokeWidth / 2.0f); } else { insets = (float) (minEdge / 2.0f - mRingCenterRadius); } mStrokeInset = insets; } @SuppressWarnings("unused") public float getInsets() { return mStrokeInset; } public double getCenterRadius() { return mRingCenterRadius; } /** * @param centerRadius Inner radius in px of the circle the progress * spinner arc traces. */ public void setCenterRadius(double centerRadius) { mRingCenterRadius = centerRadius; } /** * @param show Set to true to show the arrow head on the progress spinner. */ public void setShowArrow(boolean show) { if (mShowArrow != show) { mShowArrow = show; invalidateSelf(); } } /** * @param scale Set the scale of the arrowhead for the spinner. */ public void setArrowScale(float scale) { if (scale != mArrowScale) { mArrowScale = scale; invalidateSelf(); } } /** * @return The amount the progress spinner is currently rotated, between [0..1]. */ public float getStartingRotation() { return mStartingRotation; } /** * If the start / end trim are offset to begin with, store them so that * animation starts from that offset. */ public void storeOriginals() { mStartingStartTrim = mStartTrim; mStartingEndTrim = mEndTrim; mStartingRotation = mRotation; } /** * Reset the progress spinner to default rotation, start and end angles. */ public void resetOriginals() { mStartingStartTrim = 0; mStartingEndTrim = 0; mStartingRotation = 0; setStartTrim(0); setEndTrim(0); setRotation(0); } private void invalidateSelf() { mRingCallback.invalidateDrawable(null); } } /** * Squishes the interpolation curve into the second half of the animation. */ private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f)); } } /** * Squishes the interpolation curve into the first half of the animation. */ private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.min(1, input * 2.0f)); } } /** * Taken from {@link package android.support.v4.widget} */ private class OvalShadow extends OvalShape { private RadialGradient mRadialGradient; private int mShadowRadius; private Paint mShadowPaint; private int mCircleDiameter; public OvalShadow(int shadowRadius, int circleDiameter) { super(); mShadowPaint = new Paint(); mShadowRadius = shadowRadius; mCircleDiameter = circleDiameter; mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2, mShadowRadius, new int[]{ FILL_SHADOW_COLOR, Color.TRANSPARENT }, null, Shader.TileMode.CLAMP); mShadowPaint.setShader(mRadialGradient); } @Override public void draw(Canvas canvas, Paint paint) { final int viewWidth = getBounds().width(); final int viewHeight = getBounds().height(); canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius), mShadowPaint); canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint); } } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/header/StoreHouseBarItem.java ================================================ package in.srain.cube.views.ptr.header; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PointF; import android.view.animation.Animation; import android.view.animation.Transformation; import java.util.Random; /** * Created by srain on 11/6/14. */ public class StoreHouseBarItem extends Animation { public PointF midPoint; public float translationX; public int index; private final Paint mPaint = new Paint(); private float mFromAlpha = 1.0f; private float mToAlpha = 0.4f; private PointF mCStartPoint; private PointF mCEndPoint; public StoreHouseBarItem(int index, PointF start, PointF end, int color, int lineWidth) { this.index = index; midPoint = new PointF((start.x + end.x) / 2, (start.y + end.y) / 2); mCStartPoint = new PointF(start.x - midPoint.x, start.y - midPoint.y); mCEndPoint = new PointF(end.x - midPoint.x, end.y - midPoint.y); setColor(color); setLineWidth(lineWidth); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); } public void setLineWidth(int width) { mPaint.setStrokeWidth(width); } public void setColor(int color) { mPaint.setColor(color); } public void resetPosition(int horizontalRandomness) { Random random = new Random(); int randomNumber = -random.nextInt(horizontalRandomness) + horizontalRandomness; translationX = randomNumber; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { float alpha = mFromAlpha; alpha = alpha + ((mToAlpha - alpha) * interpolatedTime); setAlpha(alpha); } public void start(float fromAlpha, float toAlpha) { mFromAlpha = fromAlpha; mToAlpha = toAlpha; super.start(); } public void setAlpha(float alpha) { mPaint.setAlpha((int) (alpha * 255)); } public void draw(Canvas canvas) { canvas.drawLine(mCStartPoint.x, mCStartPoint.y, mCEndPoint.x, mCEndPoint.y, mPaint); } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/header/StoreHouseHeader.java ================================================ package in.srain.cube.views.ptr.header; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.PointF; import android.util.AttributeSet; import android.view.View; import android.view.animation.Transformation; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrUIHandler; import in.srain.cube.views.ptr.indicator.PtrIndicator; import in.srain.cube.views.ptr.util.PtrLocalDisplay; import java.util.ArrayList; public class StoreHouseHeader extends View implements PtrUIHandler { public ArrayList mItemList = new ArrayList(); private int mLineWidth = -1; private float mScale = 1; private int mDropHeight = -1; private float mInternalAnimationFactor = 0.7f; private int mHorizontalRandomness = -1; private float mProgress = 0; private int mDrawZoneWidth = 0; private int mDrawZoneHeight = 0; private int mOffsetX = 0; private int mOffsetY = 0; private float mBarDarkAlpha = 0.4f; private float mFromAlpha = 1.0f; private float mToAlpha = 0.4f; private int mLoadingAniDuration = 1000; private int mLoadingAniSegDuration = 1000; private int mLoadingAniItemDuration = 400; private Transformation mTransformation = new Transformation(); private boolean mIsInLoading = false; private AniController mAniController = new AniController(); private int mTextColor = Color.WHITE; public StoreHouseHeader(Context context) { super(context); initView(); } public StoreHouseHeader(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public StoreHouseHeader(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } private void initView() { PtrLocalDisplay.init(getContext()); mLineWidth = PtrLocalDisplay.dp2px(1); mDropHeight = PtrLocalDisplay.dp2px(40); mHorizontalRandomness = PtrLocalDisplay.SCREEN_WIDTH_PIXELS / 2; } private void setProgress(float progress) { mProgress = progress; } public int getLoadingAniDuration() { return mLoadingAniDuration; } public void setLoadingAniDuration(int duration) { mLoadingAniDuration = duration; mLoadingAniSegDuration = duration; } public StoreHouseHeader setLineWidth(int width) { mLineWidth = width; for (int i = 0; i < mItemList.size(); i++) { mItemList.get(i).setLineWidth(width); } return this; } public StoreHouseHeader setTextColor(int color) { mTextColor = color; for (int i = 0; i < mItemList.size(); i++) { mItemList.get(i).setColor(color); } return this; } public StoreHouseHeader setDropHeight(int height) { mDropHeight = height; return this; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = getTopOffset() + mDrawZoneHeight + getBottomOffset(); heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); mOffsetX = (getMeasuredWidth() - mDrawZoneWidth) / 2; mOffsetY = getTopOffset(); mDropHeight = getTopOffset(); } private int getTopOffset() { return getPaddingTop() + PtrLocalDisplay.dp2px(10); } private int getBottomOffset() { return getPaddingBottom() + PtrLocalDisplay.dp2px(10); } public void initWithString(String str) { initWithString(str, 25); } public void initWithString(String str, int fontSize) { ArrayList pointList = StoreHousePath.getPath(str, fontSize * 0.01f, 14); initWithPointList(pointList); } public void initWithStringArray(int id) { String[] points = getResources().getStringArray(id); ArrayList pointList = new ArrayList(); for (int i = 0; i < points.length; i++) { String[] x = points[i].split(","); float[] f = new float[4]; for (int j = 0; j < 4; j++) { f[j] = Float.parseFloat(x[j]); } pointList.add(f); } initWithPointList(pointList); } public float getScale() { return mScale; } public void setScale(float scale) { mScale = scale; } public void initWithPointList(ArrayList pointList) { float drawWidth = 0; float drawHeight = 0; boolean shouldLayout = mItemList.size() > 0; mItemList.clear(); for (int i = 0; i < pointList.size(); i++) { float[] line = pointList.get(i); PointF startPoint = new PointF(PtrLocalDisplay.dp2px(line[0]) * mScale, PtrLocalDisplay.dp2px(line[1]) * mScale); PointF endPoint = new PointF(PtrLocalDisplay.dp2px(line[2]) * mScale, PtrLocalDisplay.dp2px(line[3]) * mScale); drawWidth = Math.max(drawWidth, startPoint.x); drawWidth = Math.max(drawWidth, endPoint.x); drawHeight = Math.max(drawHeight, startPoint.y); drawHeight = Math.max(drawHeight, endPoint.y); StoreHouseBarItem item = new StoreHouseBarItem(i, startPoint, endPoint, mTextColor, mLineWidth); item.resetPosition(mHorizontalRandomness); mItemList.add(item); } mDrawZoneWidth = (int) Math.ceil(drawWidth); mDrawZoneHeight = (int) Math.ceil(drawHeight); if (shouldLayout) { requestLayout(); } } private void beginLoading() { mIsInLoading = true; mAniController.start(); invalidate(); } private void loadFinish() { mIsInLoading = false; mAniController.stop(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); float progress = mProgress; int c1 = canvas.save(); int len = mItemList.size(); for (int i = 0; i < len; i++) { canvas.save(); StoreHouseBarItem storeHouseBarItem = mItemList.get(i); float offsetX = mOffsetX + storeHouseBarItem.midPoint.x; float offsetY = mOffsetY + storeHouseBarItem.midPoint.y; if (mIsInLoading) { storeHouseBarItem.getTransformation(getDrawingTime(), mTransformation); canvas.translate(offsetX, offsetY); } else { if (progress == 0) { storeHouseBarItem.resetPosition(mHorizontalRandomness); continue; } float startPadding = (1 - mInternalAnimationFactor) * i / len; float endPadding = 1 - mInternalAnimationFactor - startPadding; // done if (progress == 1 || progress >= 1 - endPadding) { canvas.translate(offsetX, offsetY); storeHouseBarItem.setAlpha(mBarDarkAlpha); } else { float realProgress; if (progress <= startPadding) { realProgress = 0; } else { realProgress = Math.min(1, (progress - startPadding) / mInternalAnimationFactor); } offsetX += storeHouseBarItem.translationX * (1 - realProgress); offsetY += -mDropHeight * (1 - realProgress); Matrix matrix = new Matrix(); matrix.postRotate(360 * realProgress); matrix.postScale(realProgress, realProgress); matrix.postTranslate(offsetX, offsetY); storeHouseBarItem.setAlpha(mBarDarkAlpha * realProgress); canvas.concat(matrix); } } storeHouseBarItem.draw(canvas); canvas.restore(); } if (mIsInLoading) { invalidate(); } canvas.restoreToCount(c1); } @Override public void onUIReset(PtrFrameLayout frame) { loadFinish(); for (int i = 0; i < mItemList.size(); i++) { mItemList.get(i).resetPosition(mHorizontalRandomness); } } @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { } @Override public void onUIRefreshBegin(PtrFrameLayout frame) { beginLoading(); } @Override public void onUIRefreshComplete(PtrFrameLayout frame) { loadFinish(); } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { float currentPercent = Math.min(1f, ptrIndicator.getCurrentPercent()); setProgress(currentPercent); invalidate(); } private class AniController implements Runnable { private int mTick = 0; private int mCountPerSeg = 0; private int mSegCount = 0; private int mInterval = 0; private boolean mRunning = true; private void start() { mRunning = true; mTick = 0; mInterval = mLoadingAniDuration / mItemList.size(); mCountPerSeg = mLoadingAniSegDuration / mInterval; mSegCount = mItemList.size() / mCountPerSeg + 1; run(); } @Override public void run() { int pos = mTick % mCountPerSeg; for (int i = 0; i < mSegCount; i++) { int index = i * mCountPerSeg + pos; if (index > mTick) { continue; } index = index % mItemList.size(); StoreHouseBarItem item = mItemList.get(index); item.setFillAfter(false); item.setFillEnabled(true); item.setFillBefore(false); item.setDuration(mLoadingAniItemDuration); item.start(mFromAlpha, mToAlpha); } mTick++; if (mRunning) { postDelayed(this, mInterval); } } private void stop() { mRunning = false; removeCallbacks(this); } } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/header/StoreHousePath.java ================================================ package in.srain.cube.views.ptr.header; import android.util.SparseArray; import java.util.ArrayList; /** * Created by srain on 11/7/14. */ public class StoreHousePath { private static final SparseArray sPointList; static { sPointList = new SparseArray(); float[][] LETTERS = new float[][]{ new float[]{ // A 24, 0, 1, 22, 1, 22, 1, 72, 24, 0, 47, 22, 47, 22, 47, 72, 1, 48, 47, 48 }, new float[]{ // B 0, 0, 0, 72, 0, 0, 37, 0, 37, 0, 47, 11, 47, 11, 47, 26, 47, 26, 38, 36, 38, 36, 0, 36, 38, 36, 47, 46, 47, 46, 47, 61, 47, 61, 38, 71, 37, 72, 0, 72, }, new float[]{ // C 47, 0, 0, 0, 0, 0, 0, 72, 0, 72, 47, 72, }, new float[]{ // D 0, 0, 0, 72, 0, 0, 24, 0, 24, 0, 47, 22, 47, 22, 47, 48, 47, 48, 23, 72, 23, 72, 0, 72, }, new float[]{ // E 0, 0, 0, 72, 0, 0, 47, 0, 0, 36, 37, 36, 0, 72, 47, 72, }, new float[]{ // F 0, 0, 0, 72, 0, 0, 47, 0, 0, 36, 37, 36, }, new float[]{ // G 47, 23, 47, 0, 47, 0, 0, 0, 0, 0, 0, 72, 0, 72, 47, 72, 47, 72, 47, 48, 47, 48, 24, 48, }, new float[]{ // H 0, 0, 0, 72, 0, 36, 47, 36, 47, 0, 47, 72, }, new float[]{ // I 0, 0, 47, 0, 24, 0, 24, 72, 0, 72, 47, 72, }, new float[]{ // J 47, 0, 47, 72, 47, 72, 24, 72, 24, 72, 0, 48, }, new float[]{ // K 0, 0, 0, 72, 47, 0, 3, 33, 3, 38, 47, 72, }, new float[]{ // L 0, 0, 0, 72, 0, 72, 47, 72, }, new float[]{ // M 0, 0, 0, 72, 0, 0, 24, 23, 24, 23, 47, 0, 47, 0, 47, 72, }, new float[]{ // N 0, 0, 0, 72, 0, 0, 47, 72, 47, 72, 47, 0, }, new float[]{ // O 0, 0, 0, 72, 0, 72, 47, 72, 47, 72, 47, 0, 47, 0, 0, 0, }, new float[]{ // P 0, 0, 0, 72, 0, 0, 47, 0, 47, 0, 47, 36, 47, 36, 0, 36, }, new float[]{ // Q 0, 0, 0, 72, 0, 72, 23, 72, 23, 72, 47, 48, 47, 48, 47, 0, 47, 0, 0, 0, 24, 28, 47, 71, }, new float[]{ // R 0, 0, 0, 72, 0, 0, 47, 0, 47, 0, 47, 36, 47, 36, 0, 36, 0, 37, 47, 72, }, new float[]{ // S 47, 0, 0, 0, 0, 0, 0, 36, 0, 36, 47, 36, 47, 36, 47, 72, 47, 72, 0, 72, }, new float[]{ // T 0, 0, 47, 0, 24, 0, 24, 72, }, new float[]{ // U 0, 0, 0, 72, 0, 72, 47, 72, 47, 72, 47, 0, }, new float[]{ // V 0, 0, 24, 72, 24, 72, 47, 0, }, new float[]{ // W 0, 0, 0, 72, 0, 72, 24, 49, 24, 49, 47, 72, 47, 72, 47, 0 }, new float[]{ // X 0, 0, 47, 72, 47, 0, 0, 72 }, new float[]{ // Y 0, 0, 24, 23, 47, 0, 24, 23, 24, 23, 24, 72 }, new float[]{ // Z 0, 0, 47, 0, 47, 0, 0, 72, 0, 72, 47, 72 }, }; final float[][] NUMBERS = new float[][]{ new float[]{ // 0 0, 0, 0, 72, 0, 72, 47, 72, 47, 72, 47, 0, 47, 0, 0, 0, }, new float[]{ // 1 24, 0, 24, 72, }, new float[]{ // 2 0, 0, 47, 0, 47, 0, 47, 36, 47, 36, 0, 36, 0, 36, 0, 72, 0, 72, 47, 72 }, new float[]{ // 3 0, 0, 47, 0, 47, 0, 47, 36, 47, 36, 0, 36, 47, 36, 47, 72, 47, 72, 0, 72, }, new float[]{ // 4 0, 0, 0, 36, 0, 36, 47, 36, 47, 0, 47, 72, }, new float[]{ // 5 0, 0, 0, 36, 0, 36, 47, 36, 47, 36, 47, 72, 47, 72, 0, 72, 0, 0, 47, 0 }, new float[]{ // 6 0, 0, 0, 72, 0, 72, 47, 72, 47, 72, 47, 36, 47, 36, 0, 36 }, new float[]{ // 7 0, 0, 47, 0, 47, 0, 47, 72 }, new float[]{ // 8 0, 0, 0, 72, 0, 72, 47, 72, 47, 72, 47, 0, 47, 0, 0, 0, 0, 36, 47, 36 }, new float[]{ // 9 47, 0, 0, 0, 0, 0, 0, 36, 0, 36, 47, 36, 47, 0, 47, 72, } }; // A - Z for (int i = 0; i < LETTERS.length; i++) { sPointList.append(i + 65, LETTERS[i]); } // a - z for (int i = 0; i < LETTERS.length; i++) { sPointList.append(i + 65 + 32, LETTERS[i]); } // 0 - 9 for (int i = 0; i < NUMBERS.length; i++) { sPointList.append(i + 48, NUMBERS[i]); } // blank addChar(' ', new float[]{}); // - addChar('-', new float[]{ 0, 36, 47, 36 }); // . addChar('.', new float[]{ 24, 60, 24, 72 }); } public static void addChar(char c, float[] points) { sPointList.append(c, points); } public static ArrayList getPath(String str) { return getPath(str, 1, 14); } /** * @param str * @param scale * @param gapBetweenLetter * @return ArrayList of float[] {x1, y1, x2, y2} */ public static ArrayList getPath(String str, float scale, int gapBetweenLetter) { ArrayList list = new ArrayList(); float offsetForWidth = 0; for (int i = 0; i < str.length(); i++) { int pos = str.charAt(i); int key = sPointList.indexOfKey(pos); if (key == -1) { continue; } float[] points = sPointList.get(pos); int pointCount = points.length / 4; for (int j = 0; j < pointCount; j++) { float[] line = new float[4]; for (int k = 0; k < 4; k++) { float l = points[j * 4 + k]; // x if (k % 2 == 0) { line[k] = (l + offsetForWidth) * scale; } // y else { line[k] = l * scale; } } list.add(line); } offsetForWidth += 57 + gapBetweenLetter; } return list; } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrIndicator.java ================================================ package in.srain.cube.views.ptr.indicator; import android.graphics.PointF; public class PtrIndicator { public final static int POS_START = 0; protected int mOffsetToRefresh = 0; private PointF mPtLastMove = new PointF(); private float mOffsetX; private float mOffsetY; private int mCurrentPos = 0; private int mLastPos = 0; private int mHeaderHeight; private int mPressedPos = 0; private float mRatioOfHeaderHeightToRefresh = 1.2f; private float mResistance = 1.7f; private boolean mIsUnderTouch = false; private int mOffsetToKeepHeaderWhileLoading = -1; // record the refresh complete position private int mRefreshCompleteY = 0; public boolean isUnderTouch() { return mIsUnderTouch; } public float getResistance() { return mResistance; } public void setResistance(float resistance) { mResistance = resistance; } public void onRelease() { mIsUnderTouch = false; } public void onUIRefreshComplete() { mRefreshCompleteY = mCurrentPos; } public boolean goDownCrossFinishPosition() { return mCurrentPos >= mRefreshCompleteY; } protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) { setOffset(offsetX, offsetY / mResistance); } public void setRatioOfHeaderHeightToRefresh(float ratio) { mRatioOfHeaderHeightToRefresh = ratio; mOffsetToRefresh = (int) (mHeaderHeight * ratio); } public float getRatioOfHeaderToHeightRefresh() { return mRatioOfHeaderHeightToRefresh; } public int getOffsetToRefresh() { return mOffsetToRefresh; } public void setOffsetToRefresh(int offset) { mRatioOfHeaderHeightToRefresh = mHeaderHeight * 1f / offset; mOffsetToRefresh = offset; } public void onPressDown(float x, float y) { mIsUnderTouch = true; mPressedPos = mCurrentPos; mPtLastMove.set(x, y); } public final void onMove(float x, float y) { float offsetX = x - mPtLastMove.x; float offsetY = (y - mPtLastMove.y); processOnMove(x, y, offsetX, offsetY); mPtLastMove.set(x, y); } protected void setOffset(float x, float y) { mOffsetX = x; mOffsetY = y; } public float getOffsetX() { return mOffsetX; } public float getOffsetY() { return mOffsetY; } public int getLastPosY() { return mLastPos; } public int getCurrentPosY() { return mCurrentPos; } /** * Update current position before update the UI */ public final void setCurrentPos(int current) { mLastPos = mCurrentPos; mCurrentPos = current; onUpdatePos(current, mLastPos); } protected void onUpdatePos(int current, int last) { } public int getHeaderHeight() { return mHeaderHeight; } public void setHeaderHeight(int height) { mHeaderHeight = height; updateHeight(); } protected void updateHeight() { mOffsetToRefresh = (int) (mRatioOfHeaderHeightToRefresh * mHeaderHeight); } public void convertFrom(PtrIndicator ptrSlider) { mCurrentPos = ptrSlider.mCurrentPos; mLastPos = ptrSlider.mLastPos; mHeaderHeight = ptrSlider.mHeaderHeight; } public boolean hasLeftStartPosition() { return mCurrentPos > POS_START; } public boolean hasJustLeftStartPosition() { return mLastPos == POS_START && hasLeftStartPosition(); } public boolean hasJustBackToStartPosition() { return mLastPos != POS_START && isInStartPosition(); } public boolean isOverOffsetToRefresh() { return mCurrentPos >= getOffsetToRefresh(); } public boolean hasMovedAfterPressedDown() { return mCurrentPos != mPressedPos; } public boolean isInStartPosition() { return mCurrentPos == POS_START; } public boolean crossRefreshLineFromTopToBottom() { return mLastPos < getOffsetToRefresh() && mCurrentPos >= getOffsetToRefresh(); } public boolean hasJustReachedHeaderHeightFromTopToBottom() { return mLastPos < mHeaderHeight && mCurrentPos >= mHeaderHeight; } public boolean isOverOffsetToKeepHeaderWhileLoading() { return mCurrentPos > getOffsetToKeepHeaderWhileLoading(); } public void setOffsetToKeepHeaderWhileLoading(int offset) { mOffsetToKeepHeaderWhileLoading = offset; } public int getOffsetToKeepHeaderWhileLoading() { return mOffsetToKeepHeaderWhileLoading >= 0 ? mOffsetToKeepHeaderWhileLoading : mHeaderHeight; } public boolean isAlreadyHere(int to) { return mCurrentPos == to; } public float getLastPercent() { final float oldPercent = mHeaderHeight == 0 ? 0 : mLastPos * 1f / mHeaderHeight; return oldPercent; } public float getCurrentPercent() { final float currentPercent = mHeaderHeight == 0 ? 0 : mCurrentPos * 1f / mHeaderHeight; return currentPercent; } public boolean willOverTop(int to) { return to < POS_START; } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrTensionIndicator.java ================================================ package in.srain.cube.views.ptr.indicator; public class PtrTensionIndicator extends PtrIndicator { private float DRAG_RATE = 0.5f; private float mDownY; private float mDownPos; private float mOneHeight = 0; private float mCurrentDragPercent; private int mReleasePos; private float mReleasePercent = -1; @Override public void onPressDown(float x, float y) { super.onPressDown(x, y); mDownY = y; mDownPos = getCurrentPosY(); } @Override public void onRelease() { super.onRelease(); mReleasePos = getCurrentPosY(); mReleasePercent = mCurrentDragPercent; } @Override public void onUIRefreshComplete() { mReleasePos = getCurrentPosY(); mReleasePercent = getOverDragPercent(); } @Override public void setHeaderHeight(int height) { super.setHeaderHeight(height); mOneHeight = height * 4f / 5; } @Override protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) { if (currentY < mDownY) { super.processOnMove(currentX, currentY, offsetX, offsetY); return; } // distance from top final float scrollTop = (currentY - mDownY) * DRAG_RATE + mDownPos; final float currentDragPercent = scrollTop / mOneHeight; if (currentDragPercent < 0) { setOffset(offsetX, 0); return; } mCurrentDragPercent = currentDragPercent; // 0 ~ 1 float boundedDragPercent = Math.min(1f, Math.abs(currentDragPercent)); float extraOS = scrollTop - mOneHeight; // 0 ~ 2 // if extraOS lower than 0, which means scrollTop lower than onHeight, tensionSlingshotPercent will be 0. float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, mOneHeight * 2) / mOneHeight); float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2)) * 2f; float extraMove = (mOneHeight) * tensionPercent / 2; int targetY = (int) ((mOneHeight * boundedDragPercent) + extraMove); int change = targetY - getCurrentPosY(); setOffset(currentX, change); } private float offsetToTarget(float scrollTop) { // distance from top final float currentDragPercent = scrollTop / mOneHeight; mCurrentDragPercent = currentDragPercent; // 0 ~ 1 float boundedDragPercent = Math.min(1f, Math.abs(currentDragPercent)); float extraOS = scrollTop - mOneHeight; // 0 ~ 2 // if extraOS lower than 0, which means scrollTop lower than mOneHeight, tensionSlingshotPercent will be 0. float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, mOneHeight * 2) / mOneHeight); float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2)) * 2f; float extraMove = (mOneHeight) * tensionPercent / 2; int targetY = (int) ((mOneHeight * boundedDragPercent) + extraMove); return 0; } @Override public int getOffsetToKeepHeaderWhileLoading() { return getOffsetToRefresh(); } @Override public int getOffsetToRefresh() { return (int) mOneHeight; } public float getOverDragPercent() { if (isUnderTouch()) { return mCurrentDragPercent; } else { if (mReleasePercent <= 0) { return 1.0f * getCurrentPosY() / getOffsetToKeepHeaderWhileLoading(); } // after release return mReleasePercent * getCurrentPosY() / mReleasePos; } } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/util/PtrCLog.java ================================================ package in.srain.cube.views.ptr.util; import android.util.Log; /** * An encapsulation of {@link Log}, enable log level and print log with parameters. * * @author http://www.liaohuqiu.net/ */ public class PtrCLog { public static final int LEVEL_VERBOSE = 0; public static final int LEVEL_DEBUG = 1; public static final int LEVEL_INFO = 2; public static final int LEVEL_WARNING = 3; public static final int LEVEL_ERROR = 4; public static final int LEVEL_FATAL = 5; private static int sLevel = LEVEL_VERBOSE; /** * set log level, the level lower than this level will not be logged * * @param level */ public static void setLogLevel(int level) { sLevel = level; } /** * Send a VERBOSE log message. * * @param tag * @param msg */ public static void v(String tag, String msg) { if (sLevel > LEVEL_VERBOSE) { return; } Log.v(tag, msg); } /** * Send a VERBOSE log message. * * @param tag * @param msg * @param throwable */ public static void v(String tag, String msg, Throwable throwable) { if (sLevel > LEVEL_VERBOSE) { return; } Log.v(tag, msg, throwable); } /** * Send a VERBOSE log message. * * @param tag * @param msg * @param args */ public static void v(String tag, String msg, Object... args) { if (sLevel > LEVEL_VERBOSE) { return; } if (args.length > 0) { msg = String.format(msg, args); } Log.v(tag, msg); } /** * Send a DEBUG log message * * @param tag * @param msg */ public static void d(String tag, String msg) { if (sLevel > LEVEL_DEBUG) { return; } Log.d(tag, msg); } /** * Send a DEBUG log message * * @param tag * @param msg * @param args */ public static void d(String tag, String msg, Object... args) { if (sLevel > LEVEL_DEBUG) { return; } if (args.length > 0) { msg = String.format(msg, args); } Log.d(tag, msg); } /** * Send a DEBUG log message * * @param tag * @param msg * @param throwable */ public static void d(String tag, String msg, Throwable throwable) { if (sLevel > LEVEL_DEBUG) { return; } Log.d(tag, msg, throwable); } /** * Send an INFO log message * * @param tag * @param msg */ public static void i(String tag, String msg) { if (sLevel > LEVEL_INFO) { return; } Log.i(tag, msg); } /** * Send an INFO log message * * @param tag * @param msg * @param args */ public static void i(String tag, String msg, Object... args) { if (sLevel > LEVEL_INFO) { return; } if (args.length > 0) { msg = String.format(msg, args); } Log.i(tag, msg); } /** * Send an INFO log message * * @param tag * @param msg */ public static void i(String tag, String msg, Throwable throwable) { if (sLevel > LEVEL_INFO) { return; } Log.i(tag, msg, throwable); } /** * Send a WARNING log message * * @param tag * @param msg */ public static void w(String tag, String msg) { if (sLevel > LEVEL_WARNING) { return; } Log.w(tag, msg); } /** * Send a WARNING log message * * @param tag * @param msg * @param args */ public static void w(String tag, String msg, Object... args) { if (sLevel > LEVEL_WARNING) { return; } if (args.length > 0) { msg = String.format(msg, args); } Log.w(tag, msg); } /** * Send a WARNING log message * * @param tag * @param msg * @param throwable */ public static void w(String tag, String msg, Throwable throwable) { if (sLevel > LEVEL_WARNING) { return; } Log.w(tag, msg, throwable); } /** * Send an ERROR log message * * @param tag * @param msg */ public static void e(String tag, String msg) { if (sLevel > LEVEL_ERROR) { return; } Log.e(tag, msg); } /** * Send an ERROR log message * * @param tag * @param msg * @param args */ public static void e(String tag, String msg, Object... args) { if (sLevel > LEVEL_ERROR) { return; } if (args.length > 0) { msg = String.format(msg, args); } Log.e(tag, msg); } /** * Send an ERROR log message * * @param tag * @param msg * @param throwable */ public static void e(String tag, String msg, Throwable throwable) { if (sLevel > LEVEL_ERROR) { return; } Log.e(tag, msg, throwable); } /** * Send a FATAL ERROR log message * * @param tag * @param msg */ public static void f(String tag, String msg) { if (sLevel > LEVEL_FATAL) { return; } Log.wtf(tag, msg); } /** * Send a FATAL ERROR log message * * @param tag * @param msg * @param args */ public static void f(String tag, String msg, Object... args) { if (sLevel > LEVEL_FATAL) { return; } if (args.length > 0) { msg = String.format(msg, args); } Log.wtf(tag, msg); } /** * Send a FATAL ERROR log message * * @param tag * @param msg * @param throwable */ public static void f(String tag, String msg, Throwable throwable) { if (sLevel > LEVEL_FATAL) { return; } Log.wtf(tag, msg, throwable); } } ================================================ FILE: ptr-lib/src/in/srain/cube/views/ptr/util/PtrLocalDisplay.java ================================================ package in.srain.cube.views.ptr.util; import android.content.Context; import android.util.DisplayMetrics; import android.view.View; import android.view.WindowManager; public class PtrLocalDisplay { public static int SCREEN_WIDTH_PIXELS; public static int SCREEN_HEIGHT_PIXELS; public static float SCREEN_DENSITY; public static int SCREEN_WIDTH_DP; public static int SCREEN_HEIGHT_DP; public static void init(Context context) { if (context == null) { return; } DisplayMetrics dm = new DisplayMetrics(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(dm); SCREEN_WIDTH_PIXELS = dm.widthPixels; SCREEN_HEIGHT_PIXELS = dm.heightPixels; SCREEN_DENSITY = dm.density; SCREEN_WIDTH_DP = (int) (SCREEN_WIDTH_PIXELS / dm.density); SCREEN_HEIGHT_DP = (int) (SCREEN_HEIGHT_PIXELS / dm.density); } public static int dp2px(float dp) { final float scale = SCREEN_DENSITY; return (int) (dp * scale + 0.5f); } public static int designedDP2px(float designedDp) { if (SCREEN_WIDTH_DP != 320) { designedDp = designedDp * SCREEN_WIDTH_DP / 320f; } return dp2px(designedDp); } public static void setPadding(final View view, float left, float top, float right, float bottom) { view.setPadding(designedDP2px(left), dp2px(top), designedDP2px(right), dp2px(bottom)); } } ================================================ FILE: publish-to-maven.sh ================================================ python update-project.py ./gradlew clean :ptr-lib:uploadArchives ================================================ FILE: settings.gradle ================================================ rootProject.name = 'android-Ultra-Pull-To-Refresh' include ':ptr-lib' include ':ptr-demo' ================================================ FILE: template/README-cn.md ================================================ ###### 欢迎关注我 GitHub: https://github.com/liaohuqiu 微博: http://weibo.com/liaohuqiu 知乎: http://www.zhihu.com/people/liao-hu-qiu --- # Ultra Pull To Refresh 这是现在已经停止维护的下拉刷新项目的替代方案。继承于ViewGroup可以包含任何View。功能比SwipeRefreshLayout强大。 使用起来非常简单。良好的设计,如果你想定制自己的UI样式,非常简单,就像给ListView加一个Header View那么简单。 支持 `API LEVEL >= 8`。 [APK下载](https://raw.githubusercontent.com/liaohuqiu/android-Ultra-Pull-To-Refresh/master/ptr-demo.apk) #### 下拉刷新 + 加载更多? 本类库是单纯的下拉刷新。如果你需要用到`加载更多`,看这个项目: https://github.com/liaohuqiu/android-cube-app #### 使用eclipse的同学请注意, Intellij IDEA / Android Studio 请忽略 **demo可以直接在eclipse中运行, 编译demo项目的同学看这里: http://www.liaohuqiu.net/cn/posts/compile-ultra-ptr-in-eclipse/** * 先上两张StoreHouse风格的截图! 感谢 [CBStoreHouseRefreshControl](https://github.com/coolbeet/CBStoreHouseRefreshControl).

* 5.0 Material 风格 2014-12-09 新增。**阴影效果,gif图看起来有些失真,看demo吧!**
* **支持所有的View**: ListView, GridView, ScrollView, FrameLayout, 甚至 TextView.
* 支持各种下拉刷新交互. * 下拉刷新(iOS风格)
* 释放刷新(经典风格)
* 刷新时,头部保持(新浪微博) * 刷新时,头部不保持(微信朋友圈) * 自动刷新,进入界面时自动刷新 # 使用方式 #### 中央库依赖 项目已经发布到了Maven中央库,包括`aar`和`apklib`两种格式。在Maven或者Gradle下可如下直接引入: 最新版版本号: `{ptr_lib_version}`, 发布到了: https://oss.sonatype.org/content/repositories/snapshots 在gradle中: ``` maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } ``` 稳定版: `{ptr_lib_stable_version}`, https://oss.sonatype.org/content/repositories/releases, in gradle: ``` mavenCentral() ``` `pom.xml` 文件中 最新版: ```xml in.srain.cube ultra-ptr aar {ptr_lib_version} 稳定版 ```xml in.srain.cube ultra-ptr aar {ptr_lib_stable_version} ``` gradle / Android Studio, 最新版 ``` compile 'in.srain.cube:ultra-ptr:{ptr_lib_version}' ``` gradle / Android Studio, 稳定版 ``` compile 'in.srain.cube:ultra-ptr:{ptr_lib_stable_version}' ``` #### 配置 有6个参数可配置: * 阻尼系数 默认: `1.7f`,越大,感觉下拉时越吃力。 * 触发刷新时移动的位置比例 默认,`1.2f`,移动达到头部高度1.2倍时可触发刷新操作。 * 回弹延时 默认 `200ms`,回弹到刷新高度所用时间 * 头部回弹时间 默认`1000ms` * 刷新是保持头部 默认值 `true`. * 下拉刷新 / 释放刷新 默认为释放刷新 ##### xml中配置示例 ```xml ``` ### 也可以在java代码中配置 ```java // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); ``` ### 其他配置 * 刷新时,保持内容不动,仅头部下移, `setPinContent()` `Material` 风格时,效果不错,其他风格的头部,效果不好。issue #29 ## StoreHouse 风格 * 使用字符串, 支持A-Z, 0-7 以及 `-` `.` ```java // header final StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(15), 0, 0); /** * using a string, support: A-Z 0-9 - . * you can add more letters by {@link in.srain.cube.views.ptr.header.StoreHousePath#addChar} */ header.initWithString('Alibaba'); ``` * 使用资源文件中的路径信息 ```java header.initWithStringArray(R.array.storehouse); ``` 资源文件 `res/values/arrays.xml` 内容如下: ```xml 0,35,12,42, 12,42,24,35, 24,35,12,28, 0,35,12,28, 0,21,12,28, 12,28,24,21, 24,35,24,21, 24,21,12,14, 0,21,12,14, 0,21,0,7, 12,14,0,7, 12,14,24,7, 24,7,12,0, 0,7,12,0, ``` # 处理刷新 通过`PtrHandler`,可以检查确定是否可以下来刷新以及在合适的时间刷新数据。 检查是否可以下拉刷新在`PtrDefaultHandler.checkContentCanBePulledDown`中有默认简单的实现,你可以根据实际情况完成这个逻辑。 ``` public interface PtrHandler { /** * 检查是否可以执行下来刷新,比如列表为空或者列表第一项在最上面时。 *

* {@link in.srain.cube.views.ptr.PtrDefaultHandler#checkContentCanBePulledDown} */ public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); /** * 需要加载数据时触发 * * @param frame */ public void onRefreshBegin(final PtrFrameLayout frame); } ``` 例子: ```java ptrFrame.setPtrHandler(new PtrHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.refreshComplete(); } }, 1800); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { // 默认实现,根据实际情况做改动 return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } }); ``` # 常见问题 * ViewPager滑动冲突: `disableWhenHorizontalMove()` * 长按LongPressed, `setInterceptEventWhileWorking()` # 联系方式和问题建议 * 微博: http://weibo.com/liaohuqiu * QQ 群: 1. cube系列开源项目使用交流,问题解答: 271918140 (cube-sdk) 加群前请先阅读群约定: https://github.com/liaohuqiu/qq-tribe-rule 2. 如果你会通过google解决问题,喜欢独立思考,喜欢和优秀却又温和的人成为朋友,欢迎加入我们在 Slack 上的群组: 我们在 Slack 上的群组: http://join-add1bit.liaohuqiu.net/?channel=git-android-ultra-ptr * srain@php.net * twitter: https://twitter.com/liaohuqiu * blog: http://www.liaohuqiu.net ================================================ FILE: template/README.md ================================================ ###### Welcome to follow me on GitHub or Twitter GitHub: https://github.com/liaohuqiu Twitter: https://twitter.com/liaohuqiu --- [![Build Status](https://travis-ci.org/liaohuqiu/android-Ultra-Pull-To-Refresh.svg?branch=master)](https://travis-ci.org/liaohuqiu/android-Ultra-Pull-To-Refresh) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Ultra%20Pull%20To%20Refresh-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1180) #### [中文版文档](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/blob/master/README-cn.md) Wanna auto-load-more? This will be what you want: https://github.com/liaohuqiu/android-cube-app # Ultra Pull To Refresh It's a replacement for the deprecated pull to refresh solution. It can contain any view you want. It's easy to use and more powerful than SwipeRefreshLayout. It's well designed, you can customize the UI effect you want as easy as adding a headview to ListView. Support `API LEVEL >= 8`, all snapshots are taken from Genymotion, 2.3.7. [Download APK](https://raw.githubusercontent.com/liaohuqiu/android-Ultra-Pull-To-Refresh/master/ptr-demo.apk) * StoreHouse Style first! Thanks to [CBStoreHouseRefreshControl](https://github.com/coolbeet/CBStoreHouseRefreshControl).

* Material Style, added @ 2014-12-09. **There is a beautiful shadow which looks terrible in gif snapshot. Please Check out the DEMO.**
* **Supports all of the views**: ListView, GridView, ScrollView, FrameLayout, or Even a single TextView.
* Supports all of the refresh types. * pull to refresh
* release to refresh
* keep header when refresh * hide header when refresh * auto refresh # Usage #### Maven Central This project has been pushed to Maven Central, both in `aar` and `apklib`. The latest version: `{ptr_lib_version}`, has been published to: https://oss.sonatype.org/content/repositories/snapshots, in gradle: ``` maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } ``` The stable version: `{ptr_lib_stable_version}`, https://oss.sonatype.org/content/repositories/releases, in gradle: ``` mavenCentral() ``` pom.xml, latest version: ```xml in.srain.cube ultra-ptr aar {ptr_lib_version} ``` pom.xml, stable version: ```xml in.srain.cube ultra-ptr aar {ptr_lib_stable_version} ``` gradle, latest version: ``` compile 'in.srain.cube:ultra-ptr:{ptr_lib_version}' ``` gradle, stable version: ``` compile 'in.srain.cube:ultra-ptr:{ptr_lib_stable_version}' ``` #### Config There are 6 properties: * Resistence This is the resistence while you are moving the frame, default is: `1.7f`. * Ratio of the Height of the Header to Refresh The ratio of the height of the header to trigger refresh, default is: `1.2f`. * Duration to Close The duration for moving from the position you relase the view to the height of header, default is `200ms`. * Duration to Close Header The default value is `1000ms` * Keep Header while Refreshing The default value is `true`. * Pull to Refresh / Release to Refresh The default value is Release to Refresh. ##### Config in xml ```xml ``` ### Or config in java code ```java // the following are default settings mPtrFrame.setResistance(1.7f); mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f); mPtrFrame.setDurationToClose(200); mPtrFrame.setDurationToCloseHeader(1000); // default is false mPtrFrame.setPullToRefresh(false); // default is true mPtrFrame.setKeepHeaderWhenRefresh(true); ``` ### Other Config * `setPinContent`. Pin the content, only the `HeaderView` will be moved. This's the the performance of material style in support package v19. ## StoreHouse Style * Config using string: ```java // header final StoreHouseHeader header = new StoreHouseHeader(getContext()); header.setPadding(0, LocalDisplay.dp2px(15), 0, 0); /** * using a string, support: A-Z 0-9 - . * you can add more letters by {@link in.srain.cube.views.ptr.header.StoreHousePath#addChar} */ header.initWithString('Alibaba'); ``` * Config using string array from xml: ```java header.initWithStringArray(R.array.storehouse); ``` And in `res/values/arrays.xml`: ```xml 0,35,12,42, 12,42,24,35, 24,35,12,28, 0,35,12,28, 0,21,12,28, 12,28,24,21, 24,35,24,21, 24,21,12,14, 0,21,12,14, 0,21,0,7, 12,14,0,7, 12,14,24,7, 24,7,12,0, 0,7,12,0, ``` # Process Refresh There is a `PtrHandler`, by which you can refresh the data. ``` public interface PtrHandler { /** * Check can do refresh or not. For example the content is empty or the first child is in view. *

* {@link in.srain.cube.views.ptr.PtrDefaultHandler#checkContentCanBePulledDown} */ public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); /** * When refresh begin * * @param frame */ public void onRefreshBegin(final PtrFrameLayout frame); } ``` An example: ```java ptrFrame.setPtrHandler(new PtrHandler() { @Override public void onRefreshBegin(PtrFrameLayout frame) { frame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.refreshComplete(); } }, 1800); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } }); ``` # Customize You can add a `PtrUIHandler` to `PtrFrameLayout` to implement any UI effect you want. ```java public interface PtrUIHandler { /** * When the content view has reached top and refresh has been completed, view will be reset. * * @param frame */ public void onUIReset(PtrFrameLayout frame); /** * prepare for loading * * @param frame */ public void onUIRefreshPrepare(PtrFrameLayout frame); /** * perform refreshing UI */ public void onUIRefreshBegin(PtrFrameLayout frame); /** * perform UI after refresh */ public void onUIRefreshComplete(PtrFrameLayout frame); public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int oldPosition, int currentPosition, float oldPercent, float currentPercent); } ``` # Q & A * work with ViewPager: `disableWhenHorizontalMove()` * work with LongPressed, `setInterceptEventWhileWorking()` # Contact & Help Please fell free to contact me if there is any problem when using the library. * srain@php.net * twitter: https://twitter.com/liaohuqiu * weibo: http://weibo.com/liaohuqiu * blog: http://www.liaohuqiu.net 1. About how to use cube-sdk / Ultra Ptr: 271918140 (cube-sdk) This the rule for our tribes, please read it before you request to join: https://github.com/liaohuqiu/qq-tribe-rule 2. For those who like thinking independently and are good at solving problem independently. Please join us, we are all here on Slack: http://join-add1bit.liaohuqiu.net/ ================================================ FILE: template/ptr-demo/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION defaultConfig { applicationId "in.srain.cube.views.ptr.demo" minSdkVersion 8 targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) } lintOptions { abortOnError false } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['resources'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] // aidl.srcDirs = ['src'] // renderscript.srcDirs = ['src'] } androidTest.setRoot('tests') } dexOptions { preDexLibraries = false } } def EXCLUDE_JAR_LIST = ['cube-sdk', 'support-v4-r7-source', 'ultra-ptr', 'support-annotation', 'ptr-lib'] task copyLibs << { // copy jars copy { from configurations.compile into "libs" eachFile { file -> println file.name for (name in EXCLUDE_JAR_LIST) { if (file.name.contains(name)) { file.exclude() return } } } } // copy source copy { def sourceList = configurations.compile.incoming.dependencies.collect { dependency -> dependency.artifact { artifact -> artifact.name = dependency.name artifact.type = 'source' artifact.extension = 'jar' artifact.classifier = 'sources' } dependency } from(configurations.detachedConfiguration(sourceList as Dependency[]).resolvedConfiguration. resolvedConfiguration.lenientConfiguration.getFiles(Specs.SATISFIES_ALL)) { } into("libs") eachFile { file -> println file.name for (name in EXCLUDE_JAR_LIST) { if (file.name.contains(name)) { file.exclude() return } } } } } dependencies { compile(project(':ptr-lib')) { } compile 'in.srain.cube:clog:1.0.2' compile 'in.srain.cube:cube-sdk:{cube_sdk_version}@aar' compile 'com.google.android:support-v4:r7' } ================================================ FILE: template/ptr-demo/pom.xml ================================================ 4.0.0 in.srain cube-parent-for-oss 1.0.1 in.srain.cube ultra-ptr-demo apk Ultra Pull to Refresh Demo 1.0.1 com.google.android android 4.1.1.4 provided com.google.android support-v4 r7 in.srain.cube ultra-ptr aar {ptr_lib_version} in.srain.cube cube-sdk aar {cube_sdk_version} 3.8.2 16 ${project.artifactId} src/ org.apache.maven.plugins maven-compiler-plugin 1.6 1.6 com.jayway.maven.plugins.android.generation2 android-maven-plugin true ${android_maven_plugin_version} ${android_compile_api_level} false ================================================ FILE: template/ptr-lib/gradle.properties ================================================ VERSION_NAME={ptr_lib_version} POM_GROUP=in.srain.cube POM_ARTIFACT_ID=ultra-ptr POM_PACKAGING=aar POM_NAME=Android Ultra Pull to Refresh POM_DESCRIPTION=Ultra Pull to Refresh Solution. POM_URL=https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh POM_SCM_URL=https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh POM_SCM_CONNECTION=scm:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh.git POM_SCM_DEV_CONNECTION=scm:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh.git POM_LICENCE_NAME=MIT POM_LICENCE_URL=http://opensource.org/licenses/MIT POM_LICENCE_DIST=repo POM_DEVELOPER_ID=liaohuqiu POM_DEVELOPER_NAME=liaohuqiu POM_ISSUE_SYSTEM=GitHub Issues POM_ISSUE_URL=https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/issues ================================================ FILE: template/ptr-lib/pom.xml ================================================ 4.0.0 in.srain cube-parent-for-oss 1.0.1 in.srain.cube ultra-ptr aar Ultra Pull to Refresh {ptr_lib_version} Ultra Pull to Refresh in Android https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh 2014 https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh scm:git:git@github.com:liaohuqiu/android-Ultra-Pull-To-Refresh.git scm:git:git@github.com:liaohuqiu/android-Ultra-Pull-To-Refresh.git Huqiu Liao liaohuqiu@gmail.com liaohuqiu http://www.liaohuqiu.net +8 developer MIT http://opensource.org/licenses/MIT repo Huqiu Liao http://www.liaohuqiu.net GitHub Issues https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/issues com.google.android android 4.1.1.4 provided 3.8.2 19 ${project.artifactId} src/ org.apache.maven.plugins maven-compiler-plugin 1.6 1.6 target/generated-sources/r/* com.jayway.maven.plugins.android.generation2 android-maven-plugin true ${android.maven.plugin.version} ${android.compile.api_level} false ================================================ FILE: update-project.py ================================================ import os vars = { 'ptr_lib_version': '1.0.11', 'ptr_lib_stable_version': '1.0.11', 'cube_sdk_version': '1.0.44.39-SNAPSHOT' } files = [ 'README.md', 'README-cn.md', 'ptr-lib/pom.xml', 'ptr-lib/gradle.properties', 'ptr-demo/pom.xml', 'ptr-demo/build.gradle', ] current_dir = os.path.dirname(os.path.realpath(__file__)) print current_dir src_dir = current_dir + '/template/' dst_dir = current_dir + '/' def update_var_for_file(file, vars): src_file = src_dir + file dst_file = dst_dir + file print "update_var_for_file: %s => %s" % (src_file, dst_file) infile = open(src_file) outfile = open(dst_file, 'w') for line in infile: for src, target in vars.iteritems(): line = line.replace(src, target) outfile.write(line) infile.close() outfile.close() real_vars = {} for src, target in vars.iteritems(): real_vars['{' + src + '}'] = target for f in files: update_var_for_file(f, real_vars)