├── .gitignore
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pics
├── gif1.gif
├── gif2.gif
├── gif3.gif
└── jd.gif
├── sample
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── leelay
│ │ └── refresh
│ │ └── vertical
│ │ ├── BaseActivity.java
│ │ ├── ImageViewActivity.java
│ │ ├── ListViewActivity.java
│ │ ├── RecyclerViewActivity.java
│ │ ├── ScrollViewActivity.java
│ │ ├── SeekBarLayout.java
│ │ ├── SettingDialog.java
│ │ ├── TextViewActivity.java
│ │ ├── config
│ │ └── Config.java
│ │ ├── header
│ │ └── JDHeaderView.java
│ │ └── utils
│ │ └── Densityutils.java
│ └── res
│ ├── drawable-xhdpi
│ ├── jd_package.png
│ ├── jd_people01.png
│ ├── jd_people02.png
│ ├── jd_people03.png
│ └── jd_people04.png
│ ├── drawable-xxhdpi
│ ├── android1.jpg
│ └── bizhi.jpeg
│ ├── drawable
│ └── anim_jd_people.xml
│ ├── layout
│ ├── activity_imageview.xml
│ ├── activity_listview.xml
│ ├── activity_recyclerview.xml
│ ├── activity_scrollview.xml
│ ├── activity_text.xml
│ ├── header_jd.xml
│ ├── layout_seekbar.xml
│ ├── layout_setting.xml
│ └── toolbar.xml
│ ├── menu
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ids.xml
│ ├── strings.xml
│ └── styles.xml
├── settings.gradle
└── verticalrefreshlayout
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
└── main
├── AndroidManifest.xml
├── java
└── com
│ └── leelay
│ └── freshlayout
│ └── verticalre
│ ├── DefaultHeaderView.java
│ └── VRefreshLayout.java
└── res
├── drawable-xxhdpi
├── arrow_down.png
├── arrow_up.png
└── okey.png
├── layout
└── defult_header_layout.xml
└── values
└── strings.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | /swipe
10 | local.properties
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VRefreshLayout
2 | 一个竖直方向的下拉刷新控件,支持自定义Header,可配置参数,最重要的特点是**刷新时头部置顶显示,且不影响列表滑动**;
3 |
4 | ## 为什么要做头部置顶显示
5 | 现在越来越多的App下拉刷新时是置顶显示的,大家可以看`手机京东`,`天猫商城`,`小米商城`,`汽车之家`等APP,我们在下拉刷新时,头部的刷新视图是保持显示的,这样在滚动列表的时候,用户可以知道当前正在刷新状态,而传统的下拉刷新库,比如[Android-PullToRefresh](https://github.com/chrisbanes/Android-PullToRefresh)和[android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh),是不支持的头部置顶显示的,用户往下翻看时,刷新状态就可能被隐藏;google的SwpieRefreshLayout是置顶层显示的,但是它的内容区域的不会跟着动的,且可定制性太差。
6 |
7 |
8 | ## 功能预览
9 |
10 | 头部刷新时列表可以正常响应事件,且头部不会隐藏
11 |
12 | 
13 |
14 | 自定义头部样式,例如模仿一个京东的下拉刷新动画
15 |
16 | 
17 |
18 | 良好的兼容性,支持ListView/RecyclerView/ScrollView/TextViw/Image等绝大多数view
19 |
20 | 
21 |
22 | 可以配置下拉高度,阻尼度以及各部分动画时长等参数
23 |
24 | 
25 |
26 |
27 | ## 简单使用
28 |
29 | - 在布局中,包裹一个将要刷新的View/ViewGroup
30 | ```java
31 |
36 |
37 |
43 |
44 |
45 |
46 | ```
47 | java代码中,监听和控制刷新状态
48 | ```
49 | mRefreshLayout = (VRefreshLayout) findViewById(R.id.refresh_layout);
50 | mRefreshLayout.addOnRefreshListener(new VRefreshLayout.OnRefreshListener() {
51 | @Override
52 | public void onRefresh() {
53 | mRefreshLayout.postDelayed(new Runnable() {
54 | @Override
55 | public void run() {
56 | mRefreshLayout.refreshComplete();
57 | }
58 | }, 2000);
59 | }
60 | });
61 |
62 |
63 | ```
64 |
65 | 这样就能在下拉时触发刷新时间,2000ms后刷新完成,隐藏刷新状态;
66 |
67 | ## 方法介绍
68 |
69 | - setDragRate(float dragRate) 设置拖拽的阻尼比例,默认是0.5,一般设置在0-1之间比较合适;
70 |
71 | - setRatioOfHeaderHeightToRefresh(float ratio) 设置允许刷新的高度和HeaderView高度的比例,默认为1.0;
72 |
73 | - setRatioOfHeaderHeightToReach(float ratio) 设置能下拉的最大高度和HeaderView高度的比例,默认为1.6;
74 |
75 | - setToStartDuration(int toStartDuration) 设置回到初始状态动画执行时间,单位为ms,默认是200ms;
76 |
77 | - setToRetainDuration(int toRetainDuration) 设置手指离开屏幕后触发刷新时到刷新保持的位置需要的动画时长,单位为ms,默认为200ms;
78 |
79 | - setAutoRefreshDuration(int autoRefreshDuration) 自动刷新需要的动画时长 ,默认为800ms;
80 |
81 | - setCompleteStickDuration(int completeStickDuration) 刷新完成后状态保持的时长,默认为200ms;
82 |
83 | - setHeaderView(View view) 设置一个自定义的HeaderView;
84 |
85 | - setUpdateHandler(UpdateHandler updateHandler) 设置状态和进度更新处理,一般是Header实现;
86 |
87 | - addOnRefreshListener(OnRefreshListener onRefreshListener) 添加一个刷新回调监听,支持add方式;
88 |
89 | - refreshComplete() 调用次方法,可以接受刷新;
90 |
91 |
92 | ## 自定义HeaderView
93 | 自定义Header只需要集成一个ViewGroup/View,实现VRefreshLayout.UpdateHandler接口即可;
94 | 最基本的代码
95 | ```java
96 | public class CustomHeaderView extends ViewGroup implements VRefreshLayout.UpdateHandler{
97 | public CustomHeaderView(Context context) {
98 | super(context);
99 | }
100 |
101 | public CustomHeaderView(Context context, AttributeSet attrs) {
102 | super(context, attrs);
103 | }
104 |
105 | @Override
106 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
107 |
108 | }
109 |
110 | @Override
111 | public void onProgressUpdate(VRefreshLayout layout, VRefreshLayout.Progress progress, int status) {
112 |
113 | }
114 | }
115 | ```
116 | UpdateHandler只有一个方法onProgressUpdate(VRefreshLayout layout, VRefreshLayout.Progress progress, int status),重写它就能监听下拉进度和状态;
117 | ### VRefreshLayout.PProgress
118 | Progress类一个简单的Entity,其中有三个属性,分别是
119 | - totalY 可下拉的最大Y方向距离
120 | - currentY 当前Y方向距离
121 | - refreshY 触发刷新的距离这些
122 | - refreshY 触发刷新的距离
123 | 这些距离的参考点是contentView的Top点;
124 |
125 | ### status状态
126 | ```
127 | public final static int STATUS_INIT = 0;//原始状态
128 | public final static int STATUS_DRAGGING = 1;//正在下拉
129 | public final static int STATUS_RELEASE_PREPARE = 2;//松手将要刷新
130 | public final static int STATUS_REFRESHING = 3;//正在刷新
131 | public final static int STATUS_RELEASE_CANCEL = 4;//松手取消
132 | public final static int STATUS_COMPLETE = 5;//刷新完成
133 |
134 | ```
135 | 状态解析:
136 | - STATUS_INIT 原始状态,发生在MOVE事件之前和刷新完成headerView完全隐藏之后
137 | - STATUS_DRAGGING 手指拖拽状态,只有在触发下拉后且手指没有完全离开屏幕,发生在这一时间段内;
138 | - STATUS_RELEASE_PREPARE 拖拽的距离超过可触发刷新的距离,在手指放开到正在刷新之前的这一段时间内;
139 | - STATUS_REFRESHING 正在刷新,调用refreshComplete()结束该状态;
140 | - STATUS_COMPLETE 调用refreshComplete()方法开始,到headerView即将完全隐藏之后,这一点时间内
141 | 这些状态正好形成一个状态循环;
142 |
143 |
144 | ## 参考
145 | - [android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh) -liaohuqiu
146 | - [SwipeRefreshLayout](https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html) -google
147 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | COMPILE_SDK_VERSION=24
15 | BUILD_TOOLS_VERSION=24.0.3
16 | JUNIT_VERSION=4.12
17 | SUPPORT_VERSION=24.2.1
18 | #verticalrefreshlayout
19 | LIB_MINI_SDK_VERSION=11
20 | LIB_TARGET_SDK_VERSION=23
21 | #sample
22 | SAMPLE_MIN_SDK_VERSION=15
23 | SAMPLE_TARGET_SDK_VERSION=23
24 |
25 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/pics/gif1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/pics/gif1.gif
--------------------------------------------------------------------------------
/pics/gif2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/pics/gif2.gif
--------------------------------------------------------------------------------
/pics/gif3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/pics/gif3.gif
--------------------------------------------------------------------------------
/pics/jd.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/pics/jd.gif
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
5 | buildToolsVersion BUILD_TOOLS_VERSION
6 | defaultConfig {
7 | applicationId "com.leelay.refresh.vertical"
8 | minSdkVersion SAMPLE_MIN_SDK_VERSION
9 | targetSdkVersion SAMPLE_TARGET_SDK_VERSION
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile "com.android.support:appcompat-v7:$SUPPORT_VERSION"
28 | compile "com.android.support:recyclerview-v7:$SUPPORT_VERSION"
29 |
30 | testCompile "junit:junit:$JUNIT_VERSION"
31 | compile project(":verticalrefreshlayout")
32 | }
33 |
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/leelay/Documents/Android/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.content.DialogInterface;
4 | import android.graphics.Color;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.ActionBar;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.support.v7.widget.Toolbar;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 |
15 | import com.leelay.freshlayout.verticalre.VRefreshLayout;
16 | import com.leelay.refresh.vertical.config.Config;
17 | import com.leelay.refresh.vertical.header.JDHeaderView;
18 | import com.leelay.refresh.vertical.utils.Densityutils;
19 |
20 | /**
21 | * Created by Lilei on 2016.
22 | */
23 |
24 | public class BaseActivity extends AppCompatActivity {
25 |
26 | private static final String TAG = BaseActivity.class.getSimpleName();
27 | protected VRefreshLayout mRefreshLayout;
28 | private Toolbar mToolBar;
29 |
30 | private View mJDHeaderView;
31 |
32 | @Override
33 | protected void onPostCreate(@Nullable Bundle savedInstanceState) {
34 | super.onPostCreate(savedInstanceState);
35 | setToolBar();
36 | mRefreshLayout = (VRefreshLayout) findViewById(R.id.refresh_layout);
37 | if (mRefreshLayout != null) {
38 | mJDHeaderView = new JDHeaderView(this);
39 | mJDHeaderView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(64)));
40 | mRefreshLayout.setBackgroundColor(Color.DKGRAY);
41 | mRefreshLayout.setAutoRefreshDuration(400);
42 | mRefreshLayout.setRatioOfHeaderHeightToReach(1.5f);
43 | mRefreshLayout.addOnRefreshListener(new VRefreshLayout.OnRefreshListener() {
44 | @Override
45 | public void onRefresh() {
46 | mRefreshLayout.postDelayed(new Runnable() {
47 | @Override
48 | public void run() {
49 | mRefreshLayout.refreshComplete();
50 | }
51 | }, 2000);
52 | }
53 | });
54 | }
55 | }
56 |
57 | private void setToolBar() {
58 | mToolBar = (Toolbar) findViewById(R.id.toolbar);
59 | setSupportActionBar(mToolBar);
60 | ActionBar supportActionBar = getSupportActionBar();
61 | supportActionBar.setDisplayHomeAsUpEnabled(!(this instanceof ListViewActivity));
62 | mToolBar.setNavigationOnClickListener(new View.OnClickListener() {
63 | @Override
64 | public void onClick(View v) {
65 | finish();
66 | }
67 | });
68 | supportActionBar.setTitle(getClass().getSimpleName().replace("Activity", ""));
69 | }
70 |
71 | @Override
72 | public boolean onCreateOptionsMenu(Menu menu) {
73 | getMenuInflater().inflate(R.menu.menu_main, menu);
74 | return true;
75 | }
76 |
77 | @Override
78 | public boolean onOptionsItemSelected(MenuItem item) {
79 | int itemId = item.getItemId();
80 | switch (itemId) {
81 | case R.id.menu_auto:
82 | if (mRefreshLayout != null)
83 | mRefreshLayout.autoRefresh();
84 | break;
85 | case R.id.menu_setting:
86 | SettingDialog.showDialog(this, new DialogInterface.OnDismissListener() {
87 | @Override
88 | public void onDismiss(DialogInterface dialog) {
89 | refreshConfig();
90 | }
91 | });
92 | break;
93 | case R.id.menu_style:
94 | if (mRefreshLayout != null) {
95 | if (mRefreshLayout.getStatus() != VRefreshLayout.STATUS_INIT) {
96 | return false;
97 | }
98 | if (mRefreshLayout.isDefaultHeaderView()) {
99 | mRefreshLayout.setHeaderView(mJDHeaderView);
100 | mRefreshLayout.setBackgroundColor(Color.WHITE);
101 | item.setTitle(R.string.menu_header_default);
102 | } else {
103 | mRefreshLayout.setHeaderView(mRefreshLayout.getDefaultHeaderView());
104 | mRefreshLayout.setBackgroundColor(Color.DKGRAY);
105 | item.setTitle(R.string.menu_header_jd);
106 | }
107 | }
108 |
109 | break;
110 | }
111 | return false;
112 | }
113 |
114 |
115 | private void refreshConfig() {
116 | if (mRefreshLayout != null) {
117 | mRefreshLayout.postDelayed(new Runnable() {
118 | @Override
119 | public void run() {
120 | Config config = Config.getInstance();
121 | mRefreshLayout.setDragRate(config.dragRate);
122 | mRefreshLayout.setRatioOfHeaderHeightToReach(config.ratioOfHeaderHeightToReach);
123 | mRefreshLayout.setAutoRefreshDuration(config.autoRefreshDuration);
124 | mRefreshLayout.setToRetainDuration(config.toRetainDuration);
125 | mRefreshLayout.setToStartDuration(config.toStartDuration);
126 | mRefreshLayout.setCompleteStickDuration(config.completeStickDuration);
127 | }
128 | }, 200);
129 | }
130 | }
131 |
132 | protected int dp2px(float dp) {
133 | return Densityutils.dp2px(this, dp);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/ImageViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 |
6 | /**
7 | * Created by Lilei on 2016.
8 | */
9 |
10 | public class ImageViewActivity extends BaseActivity{
11 |
12 | @Override
13 | protected void onCreate(@Nullable Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_imageview);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/ListViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v4.view.PagerAdapter;
6 | import android.support.v4.view.ViewPager;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.AbsListView;
10 | import android.widget.AdapterView;
11 | import android.widget.ArrayAdapter;
12 | import android.widget.ImageView;
13 | import android.widget.ListView;
14 | import android.widget.TextView;
15 | import android.widget.Toast;
16 |
17 | import java.util.ArrayList;
18 | import java.util.Arrays;
19 | import java.util.List;
20 |
21 | public class ListViewActivity extends BaseActivity {
22 |
23 | private List mViews;
24 | private List mList;
25 | private ListView mListView;
26 |
27 | @Override
28 | protected void onCreate(Bundle savedInstanceState) {
29 | super.onCreate(savedInstanceState);
30 | setContentView(R.layout.activity_listview);
31 | initData();
32 | initView();
33 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
34 | @Override
35 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
36 | if (view instanceof TextView){
37 | String text = ((TextView) view).getText().toString().trim();
38 | Class target = null;
39 | try {
40 | target = Class.forName("com.leelay.refresh.vertical." + text + "Activity");
41 | } catch (ClassNotFoundException e) {
42 | e.printStackTrace();
43 | }
44 |
45 | if (target != null) {
46 | Intent intent = new Intent(ListViewActivity.this, target);
47 | startActivity(intent);
48 | } else {
49 | Toast.makeText(ListViewActivity.this, ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
50 | }
51 | }
52 | }
53 | });
54 |
55 |
56 | }
57 |
58 | private void initView() {
59 | ViewPager viewPager = new ViewPager(this);
60 | viewPager.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, dp2px(200)));
61 | mViews = new ArrayList<>();
62 | for (int i = 0; i < 3; i++) {
63 | ImageView imageView = new ImageView(this);
64 | imageView.setImageResource(R.drawable.android1);
65 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
66 | mViews.add(imageView);
67 | }
68 | viewPager.setAdapter(new PagerAdapter() {
69 | @Override
70 | public int getCount() {
71 | return mViews.size();
72 | }
73 |
74 | @Override
75 | public boolean isViewFromObject(View view, Object object) {
76 | return view == object;
77 | }
78 |
79 | @Override
80 | public Object instantiateItem(ViewGroup container, int position) {
81 | ImageView imageView = mViews.get(position);
82 | container.addView(imageView);
83 | return imageView;
84 | }
85 |
86 | @Override
87 | public void destroyItem(ViewGroup container, int position, Object object) {
88 | container.removeView(mViews.get(position));
89 | }
90 | });
91 | mListView = (ListView) findViewById(R.id.listView);
92 | mListView.addHeaderView(viewPager);
93 | mListView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, mList));
94 | }
95 |
96 | private void initData() {
97 | mList = new ArrayList<>();
98 | String[] array1 = getResources().getStringArray(R.array.activity_array);
99 | mList.addAll(Arrays.asList(array1));
100 | String[] array = getResources().getStringArray(R.array.language_array);
101 | mList.addAll(Arrays.asList(array));
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/RecyclerViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.TextView;
11 |
12 | import java.util.Arrays;
13 | import java.util.List;
14 |
15 | /**
16 | * Created by leelay on 2016/12/6.
17 | */
18 |
19 | public class RecyclerViewActivity extends BaseActivity {
20 |
21 | private RecyclerView mRecyclerView;
22 | private List mList;
23 |
24 | @Override
25 | protected void onCreate(@Nullable Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_recyclerview);
28 | initData();
29 | mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
30 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
31 | mRecyclerView.setAdapter(new MyAdapter());
32 | }
33 |
34 | private void initData() {
35 | String[] array = getResources().getStringArray(R.array.language_array);
36 | mList = Arrays.asList(array);
37 | }
38 |
39 | class MyAdapter extends RecyclerView.Adapter {
40 |
41 |
42 | @Override
43 | public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
44 | return new MyViewHolder(LayoutInflater.from(RecyclerViewActivity.this).inflate(android.R.layout.simple_list_item_1, parent, false));
45 | }
46 |
47 | @Override
48 | public void onBindViewHolder(MyViewHolder holder, int position) {
49 | holder.mTextView.setText(mList.get(position));
50 | }
51 |
52 | @Override
53 | public int getItemCount() {
54 | return mList.size();
55 | }
56 |
57 | class MyViewHolder extends RecyclerView.ViewHolder {
58 |
59 | TextView mTextView;
60 |
61 | public MyViewHolder(View itemView) {
62 | super(itemView);
63 | mTextView = (TextView) itemView.findViewById(android.R.id.text1);
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/ScrollViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.AppCompatTextView;
7 | import android.view.Gravity;
8 | import android.view.ViewGroup;
9 | import android.widget.LinearLayout;
10 |
11 | /**
12 | * Created by Lilei on 2016.
13 | */
14 |
15 | public class ScrollViewActivity extends BaseActivity {
16 | LinearLayout mLinearLayout;
17 | @Override
18 | protected void onCreate(@Nullable Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_scrollview);
21 | mLinearLayout = (LinearLayout) findViewById(R.id.layout_content);
22 | addChilden();
23 | }
24 |
25 | private void addChilden() {
26 | String[] array = getResources().getStringArray(R.array.language_array);
27 | for (String a : array) {
28 | AppCompatTextView textView = new AppCompatTextView(this);
29 | textView.setBackgroundColor(Color.WHITE);
30 | textView.setGravity(Gravity.CENTER);
31 | textView.setText(a);
32 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(56));
33 | layoutParams.bottomMargin = dp2px(1);
34 | mLinearLayout.addView(textView, layoutParams);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/SeekBarLayout.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.AppCompatSeekBar;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.widget.LinearLayout;
8 | import android.widget.SeekBar;
9 | import android.widget.TextView;
10 |
11 | /**
12 | * Created by leelay on 2016/12/6.
13 | */
14 |
15 | public class SeekBarLayout extends LinearLayout {
16 |
17 | private AppCompatSeekBar mSeekBar;
18 | private TextView mTitle;
19 | private TextView mValue;
20 |
21 | private int gap;
22 | private boolean isF;
23 |
24 | public SeekBarLayout(Context context) {
25 | this(context, null);
26 | }
27 |
28 | public SeekBarLayout(Context context, AttributeSet attrs) {
29 | super(context, attrs);
30 | LayoutInflater.from(context).inflate(R.layout.layout_seekbar, this, true);
31 | initView();
32 | }
33 |
34 | public void setTitle(String title) {
35 | this.mTitle.setText(title);
36 | }
37 |
38 | public void setRange(int... range) {
39 | isF = false;
40 | doRange(range);
41 | notifyValue(mSeekBar.getProgress());
42 | }
43 |
44 | public void setProgress(int progress) {
45 | mSeekBar.setProgress(progress - gap);
46 | }
47 |
48 | public void setProgress(float progress) {
49 | if (isF)
50 | mSeekBar.setProgress((int) (progress * 10) - gap);
51 | else
52 | mSeekBar.setProgress((int) progress - gap);
53 | }
54 |
55 | public void setRange(float... range) {
56 | int[] rangeI = new int[range.length];
57 | for (int i = 0; i < range.length; i++) {
58 | rangeI[i] = (int) (range[i] * 10);
59 | }
60 | setRange(rangeI);
61 | isF = true;
62 | notifyValue(mSeekBar.getProgress());
63 | }
64 |
65 | private void doRange(int... range) {
66 | if (range.length < 2) {
67 | throw new RuntimeException("range length must > 2");
68 | }
69 | int[] numbers = range.clone();
70 | int start = numbers[0];
71 | int end = numbers[1];
72 |
73 | if (end <= start) {
74 | throw new RuntimeException("range end must > start");
75 | }
76 | gap = start;
77 | mSeekBar.setMax(end - start);
78 | }
79 |
80 | private void initView() {
81 | mSeekBar = (AppCompatSeekBar) findViewById(R.id.seekbar);
82 | mTitle = (TextView) findViewById(R.id.title);
83 | mValue = (TextView) findViewById(R.id.value);
84 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
85 | @Override
86 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
87 | notifyValue(progress);
88 | }
89 |
90 | @Override
91 | public void onStartTrackingTouch(SeekBar seekBar) {
92 |
93 | }
94 |
95 | @Override
96 | public void onStopTrackingTouch(SeekBar seekBar) {
97 |
98 | }
99 | });
100 |
101 | }
102 |
103 | private void notifyValue(int progress) {
104 | if (isF) {
105 | float p = (float) (progress + gap) / 10f;
106 | mValue.setText(String.valueOf(p));
107 | } else {
108 | mValue.setText(String.valueOf(progress + gap));
109 | }
110 | }
111 |
112 | public String getProcess() {
113 | return mValue.getText().toString();
114 | }
115 |
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/SettingDialog.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.content.Context;
4 | import android.content.DialogInterface;
5 | import android.support.v7.app.AlertDialog;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 |
9 | import com.leelay.refresh.vertical.config.Config;
10 |
11 | /**
12 | * Created by leelay on 2016/12/6.
13 | */
14 |
15 | public class SettingDialog {
16 |
17 | public static void showDialog(Context context, DialogInterface.OnDismissListener onDismissListener) {
18 | final View view = LayoutInflater.from(context).inflate(R.layout.layout_setting, null, false);
19 | setupView(view);
20 | AlertDialog.Builder builder = new AlertDialog.Builder(context).setView(view);
21 | builder.setTitle("Setting");
22 | builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {
23 | @Override
24 | public void onClick(DialogInterface dialog, int which) {
25 | ok(view);
26 | }
27 | });
28 | builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {
29 | @Override
30 | public void onClick(DialogInterface dialog, int which) {
31 |
32 | }
33 | });
34 | builder.setNeutralButton("reset", new DialogInterface.OnClickListener() {
35 | @Override
36 | public void onClick(DialogInterface dialog, int which) {
37 | Config.getInstance().reset();
38 | }
39 | });
40 | AlertDialog alertDialog = builder.create();
41 | alertDialog.show();
42 | if (onDismissListener != null)
43 | alertDialog.setOnDismissListener(onDismissListener);
44 |
45 | }
46 |
47 | private static void setupView(View alertDialog) {
48 | Config config = Config.getInstance();
49 | SeekBarLayout seekBarLayout01 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout01);
50 | seekBarLayout01.setRange(0.2f, 0.8f);
51 | seekBarLayout01.setProgress(config.dragRate);
52 | seekBarLayout01.setTitle("阻力系数");
53 |
54 | SeekBarLayout seekBarLayout02 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout02);
55 | seekBarLayout02.setRange(1.0f, 3.0f);
56 | seekBarLayout02.setProgress(config.ratioOfHeaderHeightToReach);
57 | seekBarLayout02.setTitle("最大高度比例");
58 |
59 | SeekBarLayout seekBarLayout03 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout03);
60 | seekBarLayout03.setRange(100, 5000);
61 | seekBarLayout03.setProgress(config.completeStickDuration);
62 | seekBarLayout03.setTitle("头部滞留时长");
63 |
64 | SeekBarLayout seekBarLayout04 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout04);
65 | seekBarLayout04.setRange(100, 5000);
66 | seekBarLayout04.setProgress(config.autoRefreshDuration);
67 | seekBarLayout04.setTitle("自动下拉时长");
68 |
69 | SeekBarLayout seekBarLayout05 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout05);
70 | seekBarLayout05.setRange(100, 5000);
71 | seekBarLayout05.setProgress(config.toRetainDuration);
72 | seekBarLayout05.setTitle("回到刷新时长");
73 |
74 | SeekBarLayout seekBarLayout06 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout06);
75 | seekBarLayout06.setRange(100, 5000);
76 | seekBarLayout06.setProgress(config.toStartDuration);
77 | seekBarLayout06.setTitle("回到顶部时长");
78 |
79 | }
80 |
81 | private static void ok(View alertDialog) {
82 | Config config = Config.getInstance();
83 | SeekBarLayout seekBarLayout01 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout01);
84 | config.dragRate = Float.parseFloat(seekBarLayout01.getProcess());
85 |
86 | SeekBarLayout seekBarLayout02 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout02);
87 | config.ratioOfHeaderHeightToReach = Float.parseFloat(seekBarLayout02.getProcess());
88 |
89 | SeekBarLayout seekBarLayout03 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout03);
90 | config.completeStickDuration = Integer.parseInt(seekBarLayout03.getProcess());
91 | SeekBarLayout seekBarLayout04 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout04);
92 | config.autoRefreshDuration = Integer.parseInt(seekBarLayout04.getProcess());
93 |
94 | SeekBarLayout seekBarLayout05 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout05);
95 | config.toRetainDuration = Integer.parseInt(seekBarLayout05.getProcess());
96 |
97 | SeekBarLayout seekBarLayout06 = (SeekBarLayout) alertDialog.findViewById(R.id.seekbar_layout06);
98 | config.toStartDuration = Integer.parseInt(seekBarLayout06.getProcess());
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/TextViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 |
6 | /**
7 | * Created by Lilei on 2016.
8 | */
9 |
10 | public class TextViewActivity extends BaseActivity{
11 |
12 | @Override
13 | protected void onCreate(@Nullable Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_text);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/config/Config.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical.config;
2 |
3 | /**
4 | * Created by leelay on 2016/12/6.
5 | */
6 |
7 | public class Config {
8 |
9 | private static Config config = new Config();
10 | public float dragRate;
11 | public float ratioOfHeaderHeightToReach;
12 | public int completeStickDuration;
13 | public int autoRefreshDuration;
14 | public int toStartDuration;
15 | public int toRetainDuration;
16 |
17 | private Config() {
18 | reset();
19 | }
20 |
21 | public static Config getInstance() {
22 | return config;
23 | }
24 |
25 | public void reset() {
26 | dragRate = 0.5f;
27 | ratioOfHeaderHeightToReach = 1.6f;
28 | completeStickDuration = 400;
29 | autoRefreshDuration = 800;
30 | toStartDuration = 200;
31 | toRetainDuration = 200;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/header/JDHeaderView.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical.header;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.AnimationDrawable;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.widget.ImageView;
8 | import android.widget.RelativeLayout;
9 | import android.widget.TextView;
10 |
11 | import com.leelay.freshlayout.verticalre.VRefreshLayout;
12 | import com.leelay.refresh.vertical.R;
13 | import com.leelay.refresh.vertical.utils.Densityutils;
14 |
15 | /**
16 | * Created by Lilei on 2016.
17 | */
18 |
19 | public class JDHeaderView extends RelativeLayout implements VRefreshLayout.UpdateHandler {
20 |
21 | private ImageView mPeopleIv, mPackageIv;
22 | private TextView mStatusTv;
23 | private AnimationDrawable mAnimationDrawable;
24 |
25 | public JDHeaderView(Context context) {
26 | this(context, null);
27 | }
28 |
29 | public JDHeaderView(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | LayoutInflater.from(context).inflate(R.layout.header_jd, this, true);
32 | mPeopleIv = (ImageView) findViewById(R.id.iv_people);
33 | mPackageIv = (ImageView) findViewById(R.id.iv_package);
34 | mStatusTv = (TextView) findViewById(R.id.tv_status);
35 | }
36 |
37 | @Override
38 | public void onProgressUpdate(VRefreshLayout layout, VRefreshLayout.Progress progress, int status) {
39 | switch (status) {
40 | case VRefreshLayout.STATUS_INIT:
41 | mPeopleIv.setScaleX(0.1f);
42 | mPeopleIv.setScaleY(0.1f);
43 | mPeopleIv.setTranslationX(0);
44 | mPackageIv.setScaleX(0.1f);
45 | mPackageIv.setScaleY(0.1f);
46 | mPeopleIv.setImageResource(R.drawable.jd_people01);
47 | mStatusTv.setText(R.string.pull_to_refresh);
48 | if (mAnimationDrawable != null) {
49 | mAnimationDrawable.stop();
50 | mAnimationDrawable = null;
51 | }
52 |
53 | break;
54 | case VRefreshLayout.STATUS_DRAGGING:
55 | float currentY = progress.getCurrentY();
56 | float refreshY = progress.getRefreshY();
57 | float percent = Math.min(1.0f, currentY / refreshY);
58 | mPackageIv.setScaleX(percent);
59 | mPackageIv.setScaleY(percent);
60 | mPeopleIv.setScaleX(percent);
61 | mPeopleIv.setScaleY(percent);
62 | mPeopleIv.setTranslationX(percent * Densityutils.dp2px(getContext(), 18f));
63 | mPackageIv.setVisibility(VISIBLE);
64 | if (percent >= 1.0f) {
65 | mStatusTv.setText(R.string.release_to_refresh);
66 | } else {
67 | mStatusTv.setText(R.string.pull_to_refresh);
68 | }
69 | break;
70 | case VRefreshLayout.STATUS_RELEASE_PREPARE:
71 | case VRefreshLayout.STATUS_REFRESHING:
72 | if (mAnimationDrawable == null) {
73 | mStatusTv.setText(R.string.refreshing);
74 | mPackageIv.setVisibility(INVISIBLE);
75 | mPeopleIv.setImageResource(R.drawable.anim_jd_people);
76 | mAnimationDrawable = (AnimationDrawable) mPeopleIv.getDrawable();
77 | mAnimationDrawable.start();
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/leelay/refresh/vertical/utils/Densityutils.java:
--------------------------------------------------------------------------------
1 | package com.leelay.refresh.vertical.utils;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * Created by Lilei on 2016.
7 | */
8 |
9 | public class Densityutils {
10 |
11 | public static int dp2px(Context context, float dp) {
12 | return (int) (context.getResources().getDisplayMetrics().density * dp);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/jd_package.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/drawable-xhdpi/jd_package.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/jd_people01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/drawable-xhdpi/jd_people01.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/jd_people02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/drawable-xhdpi/jd_people02.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/jd_people03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/drawable-xhdpi/jd_people03.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/jd_people04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/drawable-xhdpi/jd_people04.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xxhdpi/android1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/drawable-xxhdpi/android1.jpg
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xxhdpi/bizhi.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/drawable-xxhdpi/bizhi.jpeg
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/anim_jd_people.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_imageview.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
12 |
13 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_listview.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
16 |
17 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_recyclerview.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_scrollview.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
15 |
16 |
20 |
21 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
13 |
14 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/header_jd.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
18 |
19 |
25 |
26 |
27 |
35 |
36 |
44 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_seekbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
18 |
19 |
25 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
26 |
27 |
32 |
33 |
38 |
39 |
44 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #444444
7 |
8 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VRefreshLayout
3 | 京东Style
4 | 默认Style
5 |
6 | - Java
7 | - C
8 | - C++
9 | - C#
10 | - Python
11 | - JavaScript
12 | - PHP
13 | - Perl
14 | - Objective-C
15 | - Swift
16 | - Ruby
17 | - Visual Basic
18 | - Delphi
19 | - Go
20 | - Groovy
21 | - R
22 | - Matlab
23 | - PL/SQL
24 |
25 |
26 |
27 | - ScrollView
28 | - TextView
29 | - ImageView
30 | - RecyclerView
31 |
32 |
33 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sample', ':verticalrefreshlayout'
2 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
5 | buildToolsVersion BUILD_TOOLS_VERSION
6 |
7 | defaultConfig {
8 | minSdkVersion LIB_MINI_SDK_VERSION
9 | targetSdkVersion LIB_TARGET_SDK_VERSION
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | compile "com.android.support:appcompat-v7:$SUPPORT_VERSION"
24 | testCompile "junit:junit:$JUNIT_VERSION"
25 | }
26 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/leelay/Documents/Android/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/java/com/leelay/freshlayout/verticalre/DefaultHeaderView.java:
--------------------------------------------------------------------------------
1 | package com.leelay.freshlayout.verticalre;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.support.annotation.RequiresApi;
6 | import android.util.AttributeSet;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.widget.ImageView;
10 | import android.widget.RelativeLayout;
11 | import android.widget.TextView;
12 |
13 |
14 | /**
15 | * Created by Lilei on 2016.
16 | */
17 |
18 | public class DefaultHeaderView extends RelativeLayout implements VRefreshLayout.UpdateHandler {
19 |
20 | private final ImageView mImageView;
21 | private final View mProgress;
22 | private TextView mTextView;
23 | private static final String TAG = "DefultHeaderView";
24 |
25 | public DefaultHeaderView(Context context) {
26 | this(context, null);
27 | }
28 |
29 | public DefaultHeaderView(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | LayoutInflater.from(context).inflate(R.layout.defult_header_layout, this, true);
32 | mTextView = (TextView) findViewById(R.id.text);
33 | mImageView = (ImageView) findViewById(R.id.image);
34 | mProgress = findViewById(R.id.progress);
35 | }
36 |
37 | //箭头是否朝上
38 | private boolean arrowUp;
39 |
40 | @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
41 | @Override
42 | public void onProgressUpdate(VRefreshLayout layout, VRefreshLayout.Progress progress, int status) {
43 | switch (status) {
44 | case VRefreshLayout.STATUS_INIT:
45 | mImageView.setVisibility(VISIBLE);
46 | mImageView.setImageResource(R.drawable.arrow_down);
47 | mProgress.setVisibility(GONE);
48 | break;
49 |
50 | case VRefreshLayout.STATUS_DRAGGING:
51 | if (progress.getCurrentY() >= progress.getRefreshY()) {
52 | if (arrowUp) {
53 | arrowUp = false;
54 | mTextView.setText(R.string.release_to_refresh);
55 | mImageView.setImageResource(R.drawable.arrow_up);
56 | }
57 |
58 | } else {
59 | if (!arrowUp) {
60 | arrowUp = true;
61 | mTextView.setText(R.string.pull_to_refresh);
62 | mImageView.setImageResource(R.drawable.arrow_down);
63 | }
64 | }
65 | break;
66 | case VRefreshLayout.STATUS_RELEASE_PREPARE:
67 | mTextView.setText(R.string.begin_refresh);
68 | mImageView.setVisibility(GONE);
69 | break;
70 | case VRefreshLayout.STATUS_REFRESHING:
71 | mTextView.setText(R.string.refreshing);
72 | mImageView.setImageResource(android.R.drawable.btn_dropdown);
73 | mProgress.setVisibility(VISIBLE);
74 | break;
75 | case VRefreshLayout.STATUS_COMPLETE:
76 | mTextView.setText(R.string.refresh_complete);
77 | mProgress.setVisibility(GONE);
78 | mImageView.setVisibility(VISIBLE);
79 | mImageView.setImageResource(R.drawable.okey);
80 | break;
81 | case VRefreshLayout.STATUS_RELEASE_CANCEL:
82 | mTextView.setText(R.string.cancel_refresh);
83 | break;
84 | }
85 |
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/java/com/leelay/freshlayout/verticalre/VRefreshLayout.java:
--------------------------------------------------------------------------------
1 | package com.leelay.freshlayout.verticalre;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.support.annotation.LayoutRes;
8 | import android.support.v4.view.MotionEventCompat;
9 | import android.support.v4.view.ViewCompat;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import android.util.TypedValue;
13 | import android.view.LayoutInflater;
14 | import android.view.MotionEvent;
15 | import android.view.View;
16 | import android.view.ViewConfiguration;
17 | import android.view.ViewGroup;
18 | import android.widget.AbsListView;
19 |
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | /**
24 | * 支持垂直下拉刷新的Layout
25 | * Created by leelay on 2016/12/3.
26 | */
27 |
28 | public class VRefreshLayout extends ViewGroup {
29 |
30 | public final static int STATUS_INIT = 0;//原始状态
31 | public final static int STATUS_DRAGGING = 1;//正在下拉
32 | public final static int STATUS_RELEASE_PREPARE = 2;//松手将要刷新
33 | public final static int STATUS_REFRESHING = 3;//正在刷新
34 | public final static int STATUS_RELEASE_CANCEL = 4;//松手取消
35 | public final static int STATUS_COMPLETE = 5;//刷新完成
36 | private static final String TAG = "VRefreshLayout";
37 | private int mStatus;
38 |
39 | private View mHeaderView;
40 |
41 | private View mContentView;
42 |
43 | private int mHeaderOrginTop;
44 |
45 | private int mMaxDragDistance = -1;
46 | private float mDragRate = .5f;
47 | private int mHeaderCurrentTop;
48 | private int mHeaderLayoutIndex = -1;
49 | private boolean mIsInitMesure = true;
50 | private boolean mIsBeingDragged;
51 | private float mInitDownY;
52 | private float mInitMotionY;
53 | private boolean mIsRefreshing;
54 | private int mActivePointerId = -1;
55 | private float mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
56 | private Progress mProgress = new Progress();
57 | private int mRefreshDistance;
58 | private int mToStartDuration = 200;
59 | private int mToRetainDuration = 200;
60 | private int mAutoRefreshDuration = 800;
61 | private int mCompleteStickDuration = 400;
62 | private float ratioOfHeaderHeightToRefresh = 1.0f;
63 | private float ratioOfHeaderHeightToReach = 1.6f;
64 | private List mOnRefreshListeners;
65 | private UpdateHandler mUpdateHandler;
66 | private DefaultHeaderView mDefaultHeaderView;
67 |
68 | public VRefreshLayout(Context context) {
69 | this(context, null);
70 | }
71 |
72 | public VRefreshLayout(Context context, AttributeSet attrs) {
73 | super(context, attrs);
74 | setDefaultHeaderView();
75 | setChildrenDrawingOrderEnabled(true);
76 | }
77 |
78 | public void setDragRate(float dragRate) {
79 | mDragRate = dragRate;
80 | }
81 |
82 |
83 | public int getStatus() {
84 | return mStatus;
85 | }
86 |
87 | public void setRatioOfHeaderHeightToRefresh(float ratio) {
88 | ratio = Math.max(ratio, 1.0f);
89 | this.ratioOfHeaderHeightToRefresh = ratio;
90 | this.ratioOfHeaderHeightToReach = Math.max(ratioOfHeaderHeightToRefresh, ratioOfHeaderHeightToReach);
91 | if (mHeaderView.getMeasuredHeight() > 0) {
92 | setRefreshDistance((int) (mHeaderView.getMeasuredHeight() * ratioOfHeaderHeightToRefresh));
93 | setMaxDragDistance((int) (mHeaderView.getMeasuredHeight() * ratioOfHeaderHeightToReach));
94 | }
95 | }
96 |
97 | public void setRatioOfHeaderHeightToReach(float ratio) {
98 | ratio = Math.max(Math.max(ratio, 1.0f), ratioOfHeaderHeightToRefresh);
99 | this.ratioOfHeaderHeightToReach = ratio;
100 | if (mHeaderView.getMeasuredHeight() > 0) {
101 | setMaxDragDistance((int) (mHeaderView.getMeasuredHeight() * ratioOfHeaderHeightToReach));
102 | }
103 | }
104 |
105 | public void setToStartDuration(int toStartDuration) {
106 | mToStartDuration = toStartDuration;
107 | }
108 |
109 | public void setToRetainDuration(int toRetainDuration) {
110 | mToRetainDuration = toRetainDuration;
111 | }
112 |
113 | public void setAutoRefreshDuration(int autoRefreshDuration) {
114 | mAutoRefreshDuration = autoRefreshDuration;
115 | }
116 |
117 | public void setCompleteStickDuration(int completeStickDuration) {
118 | mCompleteStickDuration = completeStickDuration;
119 | }
120 |
121 | private void setMaxDragDistance(int distance) {
122 | mMaxDragDistance = distance;
123 | mProgress.totalY = distance;
124 | }
125 |
126 | private void setRefreshDistance(int distance) {
127 | mRefreshDistance = distance;
128 | mProgress.refreshY = mRefreshDistance;
129 | }
130 |
131 | public boolean isDefaultHeaderView() {
132 | return mHeaderView == mDefaultHeaderView;
133 | }
134 |
135 | public View getDefaultHeaderView() {
136 | if (mDefaultHeaderView == null) {
137 | setDefaultHeaderView();
138 | }
139 | return mDefaultHeaderView;
140 | }
141 |
142 | private void setDefaultHeaderView() {
143 | mDefaultHeaderView = new DefaultHeaderView(getContext());
144 | mDefaultHeaderView.setPadding(0, dp2px(10), 0, dp2px(10));
145 | mDefaultHeaderView.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, dp2px(64)));
146 | setHeaderView(mDefaultHeaderView);
147 | }
148 |
149 | @Override
150 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
151 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
152 | Log.e(TAG, "onMeasure: ");
153 | ensureContent();
154 | //measure contentView
155 | if (mContentView != null) {
156 | int contentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
157 | int contentHeight = getMeasuredHeight() - getPaddingTop() + getPaddingBottom();
158 | mContentView.measure(MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY));
159 | }
160 |
161 | //measure headerView
162 | if (mHeaderView != null) {
163 | measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
164 | if (mIsInitMesure) {
165 | int measuredHeight = mHeaderView.getMeasuredHeight();
166 | mHeaderOrginTop = mHeaderCurrentTop = -measuredHeight;
167 | setMaxDragDistance((int) (measuredHeight * ratioOfHeaderHeightToReach));
168 | setRefreshDistance((int) (measuredHeight * ratioOfHeaderHeightToRefresh));
169 | mIsInitMesure = false;
170 | }
171 | }
172 | mHeaderLayoutIndex = -1;
173 | for (int i = 0; i < getChildCount(); i++) {
174 | if (mHeaderView == getChildAt(i)) {
175 | mHeaderLayoutIndex = i;
176 | }
177 | }
178 | }
179 |
180 | private void ensureContent() {
181 | if (mContentView == null) {
182 | for (int i = 0; i < getChildCount(); i++) {
183 | View childAt = getChildAt(i);
184 | if (childAt != mHeaderView) {
185 | mContentView = childAt;
186 | break;
187 | }
188 | }
189 | }
190 | }
191 |
192 | @Override
193 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
194 |
195 | if (mContentView == null) {
196 | ensureContent();
197 | }
198 |
199 | int paddingLeft = getPaddingLeft();
200 | int paddingTop = getPaddingTop();
201 | Log.e(TAG, "onLayout: mHeaderCurrentTop" + mHeaderCurrentTop);
202 | //layout headerView
203 | if (mHeaderView != null) {
204 | mHeaderView.layout(paddingLeft, mHeaderCurrentTop, paddingLeft + mHeaderView.getMeasuredWidth(), mHeaderCurrentTop + mHeaderView.getMeasuredHeight());
205 | }
206 |
207 | //layout contentView
208 | if (mContentView != null) {
209 | int distance = mHeaderCurrentTop - mHeaderOrginTop;
210 | int contentHeight = mContentView.getMeasuredHeight();
211 | int contentWidth = mContentView.getMeasuredWidth();
212 | int left = paddingLeft;
213 | int top = paddingTop + distance;
214 | int right = left + contentWidth;
215 | int bottom = top + contentHeight;
216 | mContentView.layout(left, top, right, bottom);
217 | }
218 | Log.e(TAG, "onLayout: ");
219 | }
220 |
221 | @Override
222 | protected int getChildDrawingOrder(int childCount, int i) {
223 | if (mHeaderLayoutIndex < 0) {
224 | return i;
225 | } else if (i == childCount - 1) {
226 | return mHeaderLayoutIndex;
227 | } else if (i >= mHeaderLayoutIndex) {
228 | return i + 1;
229 | } else {
230 | return i;
231 | }
232 | }
233 |
234 | @Override
235 | public boolean onInterceptTouchEvent(MotionEvent ev) {
236 | ensureContent();
237 | int action = MotionEventCompat.getActionMasked(ev);
238 | if (!isEnabled() || canChildScrollUp() || mIsRefreshing) {
239 | return false;
240 | }
241 | int pointerIndex;
242 | switch (action) {
243 | case MotionEvent.ACTION_DOWN:
244 | Log.e(TAG, "onInterceptTouchEvent: ACTION_DOWN");
245 | mIsBeingDragged = false;
246 | notifyStatus(STATUS_INIT);
247 | mActivePointerId = ev.getPointerId(0);
248 | pointerIndex = ev.findPointerIndex(mActivePointerId);
249 | if (pointerIndex < 0) {
250 | return false;
251 | }
252 | mInitDownY = ev.getY(pointerIndex);
253 | break;
254 |
255 | case MotionEvent.ACTION_MOVE:
256 | Log.e(TAG, "onInterceptTouchEvent: ACTION_MOVE");
257 | pointerIndex = ev.findPointerIndex(mActivePointerId);
258 | if (pointerIndex < 0) {
259 | return false;
260 | }
261 | float evY = ev.getY(pointerIndex);
262 | checkDragging(evY);
263 | break;
264 |
265 | case MotionEventCompat.ACTION_POINTER_UP:
266 | Log.e(TAG, "onInterceptTouchEvent: ACTION_POINTER_UP");
267 | checkOtherPointerUp(ev);
268 | break;
269 |
270 | case MotionEvent.ACTION_UP:
271 | case MotionEvent.ACTION_CANCEL:
272 | Log.e(TAG, "onInterceptTouchEvent: ACTION_DOWN");
273 | mIsBeingDragged = false;
274 | mActivePointerId = -1;
275 | break;
276 | }
277 |
278 |
279 | return mIsBeingDragged;
280 | }
281 |
282 | private void checkDragging(float y) {
283 | float dy = y - mInitDownY;
284 | mInitMotionY = mInitDownY + mTouchSlop;
285 | if (dy > mTouchSlop && !mIsBeingDragged) {
286 | mIsBeingDragged = true;
287 | }
288 | }
289 |
290 | private void checkOtherPointerUp(MotionEvent ev) {
291 | int pointIndex = MotionEventCompat.getActionIndex(ev);
292 | int pointerId = ev.getPointerId(pointIndex);
293 | if (pointerId == mActivePointerId) {
294 | int newPointIndex = pointIndex == 0 ? 1 : 0;
295 | mActivePointerId = ev.getPointerId(newPointIndex);
296 | }
297 | }
298 |
299 | @Override
300 | public boolean onTouchEvent(MotionEvent ev) {
301 | if (!isEnabled() || canChildScrollUp() || mIsRefreshing) {
302 | return false;
303 | }
304 | int action = MotionEventCompat.getActionMasked(ev);
305 | int pointerIndex;
306 | switch (action) {
307 | case MotionEvent.ACTION_DOWN:
308 | Log.e(TAG, "onTouchEvent: ACTION_DOWN");
309 | mIsBeingDragged = false;
310 | notifyStatus(STATUS_INIT);
311 | mActivePointerId = ev.getPointerId(0);
312 | pointerIndex = ev.findPointerIndex(mActivePointerId);
313 | if (pointerIndex < 0) {
314 | return false;
315 | }
316 | mInitDownY = ev.getY(pointerIndex);
317 | break;
318 |
319 | case MotionEvent.ACTION_MOVE:
320 | Log.e(TAG, "onTouchEvent: ACTION_MOVE");
321 | pointerIndex = ev.findPointerIndex(mActivePointerId);
322 | if (pointerIndex < 0) {
323 | return false;
324 | }
325 | float evY = ev.getY(pointerIndex);
326 | checkDragging(evY);
327 | if (mIsBeingDragged) {
328 | float dy = (evY - mInitMotionY) * mDragRate;
329 | if (dy > 0) {
330 | actionMoving(dy);
331 | }
332 | notifyStatus(STATUS_DRAGGING);
333 | }
334 | break;
335 |
336 | case MotionEvent.ACTION_POINTER_DOWN:
337 | Log.e(TAG, "onTouchEvent: ACTION_POINTER_DOWN");
338 | pointerIndex = MotionEventCompat.getActionIndex(ev);
339 | if (pointerIndex < 0) {
340 | return false;
341 | }
342 | mActivePointerId = ev.getPointerId(pointerIndex);
343 | break;
344 |
345 | case MotionEventCompat.ACTION_POINTER_UP:
346 | Log.e(TAG, "onTouchEvent: ACTION_POINTER_UP");
347 | checkOtherPointerUp(ev);
348 | break;
349 |
350 | case MotionEvent.ACTION_UP:
351 | Log.e(TAG, "onTouchEvent: ACTION_UP");
352 | pointerIndex = ev.findPointerIndex(mActivePointerId);
353 | if (pointerIndex < 0) {
354 | return false;
355 | }
356 | float upY = ev.getY(pointerIndex);
357 | if (mIsBeingDragged) {
358 | float upDy = (upY - mInitMotionY) * mDragRate;
359 | actionUp(upDy);
360 | mIsBeingDragged = false;
361 | }
362 | mActivePointerId = -1;
363 |
364 | break;
365 | case MotionEvent.ACTION_CANCEL:
366 | Log.e(TAG, "onTouchEvent: ACTION_CANCEL");
367 | return false;
368 | }
369 | return true;
370 |
371 | }
372 |
373 | private void actionUp(float dy) {
374 | Log.e(TAG, "actionUp: " + dy);
375 | if (dy < mRefreshDistance) {
376 | //cancel
377 | animOffsetToStartPos();
378 | mIsRefreshing = false;
379 | notifyStatus(STATUS_RELEASE_CANCEL);
380 | } else {
381 | animOffsetToRetainPos();
382 | mIsRefreshing = true;
383 | notifyStatus(STATUS_RELEASE_PREPARE);
384 | }
385 | }
386 |
387 | private void moveAnimation(int star, int end, int duration, Animator.AnimatorListener animatorListener) {
388 | ValueAnimator valueAnimator = ValueAnimator.ofInt(star, end);
389 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
390 | @Override
391 | public void onAnimationUpdate(ValueAnimator animation) {
392 | int value = (int) animation.getAnimatedValue();
393 | moveTo(value);
394 | }
395 | });
396 | if (animatorListener != null) {
397 | valueAnimator.addListener(animatorListener);
398 | }
399 | valueAnimator.setDuration(duration);
400 | valueAnimator.start();
401 | }
402 |
403 | private void animOffsetToRetainPos() {
404 | final int from = mHeaderCurrentTop = mHeaderView.getTop();
405 | int to = getPaddingTop() + mRefreshDistance - mHeaderView.getMeasuredHeight();
406 | moveAnimation(from, to, mToRetainDuration, new AnimatorListenerAdapter() {
407 | @Override
408 | public void onAnimationEnd(Animator animation) {
409 | super.onAnimationEnd(animation);
410 | notifyRefreshListeners();
411 | notifyStatus(STATUS_REFRESHING);
412 | }
413 | });
414 | }
415 |
416 | private void animOffsetAutoRefresh() {
417 | mHeaderCurrentTop = mHeaderView.getTop();
418 | final int from = mHeaderCurrentTop;
419 | int to = mMaxDragDistance + mHeaderOrginTop;
420 | moveAnimation(from, to, mAutoRefreshDuration, new AnimatorListenerAdapter() {
421 | @Override
422 | public void onAnimationEnd(Animator animation) {
423 | super.onAnimationEnd(animation);
424 | notifyStatus(STATUS_RELEASE_PREPARE);
425 | animOffsetToRetainPos();
426 | }
427 |
428 | @Override
429 | public void onAnimationStart(Animator animation) {
430 | super.onAnimationStart(animation);
431 | notifyStatus(STATUS_DRAGGING);
432 | }
433 | });
434 | }
435 |
436 | private void animOffsetToStartPos() {
437 | final int from = mHeaderCurrentTop = mHeaderView.getTop();
438 | int to = mHeaderOrginTop;
439 | moveAnimation(from, to, mToStartDuration, new AnimatorListenerAdapter() {
440 | @Override
441 | public void onAnimationEnd(Animator animation) {
442 | super.onAnimationEnd(animation);
443 | mIsRefreshing = false;
444 | notifyStatus(STATUS_INIT);
445 | }
446 | });
447 | }
448 |
449 | private void actionMoving(float y) {
450 | y = Math.min(y, mMaxDragDistance);
451 | if (y <= mMaxDragDistance) {
452 | int targetY = (int) (mHeaderOrginTop + y);
453 | moveTo(targetY);
454 | }
455 | }
456 |
457 | public void setHeaderView(View view) {
458 | if (view == null || view == mHeaderView) {
459 | return;
460 | }
461 | LayoutParams layoutParams = view.getLayoutParams();
462 | if (layoutParams == null) {
463 | layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
464 | view.setLayoutParams(layoutParams);
465 | }
466 | removeView(mHeaderView);
467 | mHeaderView = view;
468 | mIsInitMesure = true;
469 | this.addView(mHeaderView);
470 | if (view instanceof UpdateHandler) {
471 | setUpdateHandler((UpdateHandler) view);
472 | }
473 | }
474 |
475 | public void setHeaderView(@LayoutRes int redId) {
476 | View view = LayoutInflater.from(getContext()).inflate(redId, this, false);
477 | setHeaderView(view);
478 | }
479 |
480 | private int dp2px(int dp) {
481 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
482 | }
483 |
484 | public boolean canChildScrollUp() {
485 | if (android.os.Build.VERSION.SDK_INT < 14) {
486 | if (mContentView instanceof AbsListView) {
487 | final AbsListView absListView = (AbsListView) mContentView;
488 | return absListView.getChildCount() > 0
489 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
490 | .getTop() < absListView.getPaddingTop());
491 | } else {
492 | return ViewCompat.canScrollVertically(mContentView, -1) || mContentView.getScrollY() > 0;
493 | }
494 | } else {
495 | return ViewCompat.canScrollVertically(mContentView, -1);
496 | }
497 | }
498 |
499 | public void refreshComplete() {
500 | if (mIsRefreshing) {
501 | notifyStatus(STATUS_COMPLETE);
502 | postDelayed(new Runnable() {
503 | @Override
504 | public void run() {
505 | animOffsetToStartPos();
506 | }
507 | }, mCompleteStickDuration);
508 | }
509 | }
510 |
511 | public void autoRefresh() {
512 | if (!mIsRefreshing) {
513 | mIsRefreshing = true;
514 | animOffsetAutoRefresh();
515 | }
516 | }
517 |
518 | private void moveTo(int y) {
519 | int dy = y - mHeaderCurrentTop;
520 | ViewCompat.offsetTopAndBottom(mHeaderView, dy);
521 | ViewCompat.offsetTopAndBottom(mContentView, dy);
522 | mHeaderCurrentTop = mHeaderView.getTop();
523 | mProgress.currentY = mHeaderCurrentTop - mHeaderOrginTop;
524 | notifyProgress();
525 | }
526 |
527 | private void notifyProgress() {
528 | if (mUpdateHandler != null) {
529 | mUpdateHandler.onProgressUpdate(this, mProgress, mStatus);
530 | }
531 | }
532 |
533 | private void notifyStatus(int status) {
534 | mStatus = status;
535 | if (mUpdateHandler != null) {
536 | mUpdateHandler.onProgressUpdate(this, mProgress, mStatus);
537 | }
538 | }
539 |
540 | public void addOnRefreshListener(OnRefreshListener onRefreshListener) {
541 | if (mOnRefreshListeners == null) {
542 | mOnRefreshListeners = new ArrayList<>();
543 | }
544 | mOnRefreshListeners.add(onRefreshListener);
545 | }
546 |
547 | private void notifyRefreshListeners() {
548 | if (mOnRefreshListeners == null || mOnRefreshListeners.isEmpty()) {
549 | return;
550 | }
551 | for (OnRefreshListener onRefreshListener : mOnRefreshListeners) {
552 | onRefreshListener.onRefresh();
553 | }
554 | }
555 |
556 | public void setUpdateHandler(UpdateHandler updateHandler) {
557 | mUpdateHandler = updateHandler;
558 | }
559 |
560 | public interface OnRefreshListener {
561 | void onRefresh();
562 | }
563 |
564 |
565 | public interface UpdateHandler {
566 | void onProgressUpdate(VRefreshLayout layout, Progress progress, int status);
567 | }
568 |
569 | public static class Progress {
570 | private int totalY;
571 | private int currentY;
572 | private int refreshY;
573 |
574 | public int getRefreshY() {
575 | return refreshY;
576 | }
577 |
578 | public int getTotalY() {
579 | return totalY;
580 | }
581 |
582 | public int getCurrentY() {
583 | return currentY;
584 | }
585 | }
586 |
587 |
588 | }
589 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/res/drawable-xxhdpi/arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/verticalrefreshlayout/src/main/res/drawable-xxhdpi/arrow_down.png
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/res/drawable-xxhdpi/arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/verticalrefreshlayout/src/main/res/drawable-xxhdpi/arrow_up.png
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/res/drawable-xxhdpi/okey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HitenDev/VRefreshLayout/17468b0652df543a59f72882f8f21cdd77aea3d9/verticalrefreshlayout/src/main/res/drawable-xxhdpi/okey.png
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/res/layout/defult_header_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
14 |
15 |
23 |
24 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/verticalrefreshlayout/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VerticalRefreshLayout
3 | 松手刷新
4 | 下拉刷新
5 | 即将刷新
6 | 刷新中
7 | 刷新完成
8 | 取消刷新
9 |
10 |
--------------------------------------------------------------------------------