diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..388899e --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ +out/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Eclipse project files +.classpath +.project + +# Windows thumbnail db +.DS_Store + +# IDEA/Android Studio project files, because +# the project can be imported from settings.gradle +.idea +*.iml + +# Old-style IDEA project files +*.ipr +*.iws + +# Local IDEA workspace +.idea/workspace.xml + +# Gradle cache +.gradle + +# Sandbox stuff +_sandbox \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..6356aab --- /dev/null +++ b/build.gradle @@ -0,0 +1,19 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.0' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1d3591c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8c0fb64 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0c71e76 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +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 diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/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 "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@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 diff --git a/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/library/.gitignore @@ -0,0 +1 @@ +/build diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..a3ecdd5 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.library' + +def versionMajor = 1 +def versionMinor = 0 + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 21 + versionCode versionMajor * 100 + versionMinor + versionName "${versionMajor}.${versionMinor}" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:support-v4:21.0.2' +} diff --git a/library/proguard-rules.pro b/library/proguard-rules.pro new file mode 100644 index 0000000..3437851 --- /dev/null +++ b/library/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/baoyz/Developer/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# 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 *; +#} diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..582e76e --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/library/src/main/java/com/yalantis/pulltorefresh/library/PullToRefreshView.java b/library/src/main/java/com/yalantis/pulltorefresh/library/PullToRefreshView.java new file mode 100644 index 0000000..f5ac282 --- /dev/null +++ b/library/src/main/java/com/yalantis/pulltorefresh/library/PullToRefreshView.java @@ -0,0 +1,415 @@ +package com.yalantis.pulltorefresh.library; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.Transformation; +import android.widget.AbsListView; +import android.widget.ImageView; + +import com.yalantis.pulltorefresh.library.refresh_view.BaseRefreshView; +import com.yalantis.pulltorefresh.library.refresh_view.SunRefreshView; + +import java.security.InvalidParameterException; + +/** + * Created by Oleksii Shliama on 22/12/2014. + */ +public class PullToRefreshView extends ViewGroup { + + private static final int DRAG_MAX_DISTANCE = 120; + private static final float DRAG_RATE = .5f; + private static final float DECELERATE_INTERPOLATION_FACTOR = 2f; + + public static final int STYLE_SUN = 0; + public static final int STYLE_JET = 1; + public static final int MAX_OFFSET_ANIMATION_DURATION = 700; + + private static final int INVALID_POINTER = -1; + + + private View mTarget; + private ImageView mRefreshView; + private Interpolator mDecelerateInterpolator; + private int mTouchSlop; + private int mTotalDragDistance; + private BaseRefreshView mBaseRefreshView; + private float mCurrentDragPercent; + private int mCurrentOffsetTop; + private boolean mRefreshing; + private int mActivePointerId; + private boolean mIsBeingDragged; + private float mInitialMotionY; + private int mFrom; + private float mFromDragPercent; + private boolean mNotify; + private OnRefreshListener mListener; + + public PullToRefreshView(Context context) { + this(context, null); + } + + public PullToRefreshView(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RefreshView); + final int type = a.getInteger(R.styleable.RefreshView_type, STYLE_SUN); + a.recycle(); + + mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mTotalDragDistance = convertDpToPixel(DRAG_MAX_DISTANCE); + + mRefreshView = new ImageView(context); + + setRefreshStyle(type); + + addView(mRefreshView); + + setWillNotDraw(false); + ViewCompat.setChildrenDrawingOrderEnabled(this, true); + } + + public void setRefreshStyle(int type) { + setRefreshing(false); + switch (type) { + case STYLE_SUN: + mBaseRefreshView = new SunRefreshView(getContext(), this); + break; + case STYLE_JET: + // TODO + default: + throw new InvalidParameterException("Type does not exist"); + } + mRefreshView.setImageDrawable(mBaseRefreshView); + } + + public int getTotalDragDistance() { + return mTotalDragDistance; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + ensureTarget(); + if (mTarget == null) + return; + + widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingRight() - getPaddingLeft(), MeasureSpec.EXACTLY); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); + mTarget.measure(widthMeasureSpec, heightMeasureSpec); + mRefreshView.measure(widthMeasureSpec, heightMeasureSpec); + } + + private void ensureTarget() { + if (mTarget != null) + return; + if (getChildCount() > 0) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child != mRefreshView) + mTarget = child; + } + } + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + + if (!isEnabled() || canChildScrollUp() || mRefreshing) { + return false; + } + + final int action = MotionEventCompat.getActionMasked(ev); + + switch (action) { + case MotionEvent.ACTION_DOWN: + setTargetOffsetTop(0, true); + mActivePointerId = MotionEventCompat.getPointerId(ev, 0); + mIsBeingDragged = false; + final float initialMotionY = getMotionEventY(ev, mActivePointerId); + if (initialMotionY == -1) { + return false; + } + mInitialMotionY = initialMotionY; + break; + case MotionEvent.ACTION_MOVE: + if (mActivePointerId == INVALID_POINTER) { + return false; + } + final float y = getMotionEventY(ev, mActivePointerId); + if (y == -1) { + return false; + } + final float yDiff = y - mInitialMotionY; + if (yDiff > mTouchSlop && !mIsBeingDragged) { + mIsBeingDragged = true; + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIsBeingDragged = false; + mActivePointerId = INVALID_POINTER; + break; + case MotionEventCompat.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; + } + + return mIsBeingDragged; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + + if (!mIsBeingDragged) { + return super.onTouchEvent(ev); + } + + final int action = MotionEventCompat.getActionMasked(ev); + + switch (action) { + case MotionEvent.ACTION_MOVE: { + final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + if (pointerIndex < 0) { + return false; + } + + final float y = MotionEventCompat.getY(ev, pointerIndex); + final float yDiff = y - mInitialMotionY; + final float scrollTop = yDiff * DRAG_RATE; + mCurrentDragPercent = scrollTop / mTotalDragDistance; + if (mCurrentDragPercent < 0) { + return false; + } + float boundedDragPercent = Math.min(1f, Math.abs(mCurrentDragPercent)); + float extraOS = Math.abs(scrollTop) - mTotalDragDistance; + float slingshotDist = mTotalDragDistance; + float tensionSlingshotPercent = Math.max(0, + Math.min(extraOS, slingshotDist * 2) / slingshotDist); + float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow( + (tensionSlingshotPercent / 4), 2)) * 2f; + float extraMove = (slingshotDist) * tensionPercent / 2; + int targetY = (int) ((slingshotDist * boundedDragPercent) + extraMove); + + mBaseRefreshView.setPercent(mCurrentDragPercent, true); + setTargetOffsetTop(targetY - mCurrentOffsetTop, true); + break; + } + case MotionEventCompat.ACTION_POINTER_DOWN: + final int index = MotionEventCompat.getActionIndex(ev); + mActivePointerId = MotionEventCompat.getPointerId(ev, index); + break; + case MotionEventCompat.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + if (mActivePointerId == INVALID_POINTER) { + return false; + } + final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + final float y = MotionEventCompat.getY(ev, pointerIndex); + final float overScrollTop = (y - mInitialMotionY) * DRAG_RATE; + mIsBeingDragged = false; + if (overScrollTop > mTotalDragDistance) { + setRefreshing(true, true); + } else { + mRefreshing = false; + animateOffsetToStartPosition(); + } + mActivePointerId = INVALID_POINTER; + return false; + } + } + + return true; + } + + private void animateOffsetToStartPosition() { + mFrom = mCurrentOffsetTop; + mFromDragPercent = mCurrentDragPercent; + long animationDuration = Math.abs((long) (MAX_OFFSET_ANIMATION_DURATION * mFromDragPercent)); + + mAnimateToStartPosition.reset(); + mAnimateToStartPosition.setDuration(animationDuration); + mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator); + mAnimateToStartPosition.setAnimationListener(mToStartListener); + mRefreshView.clearAnimation(); + mRefreshView.startAnimation(mAnimateToStartPosition); + } + + private void animateOffsetToCorrectPosition() { + mFrom = mCurrentOffsetTop; + mFromDragPercent = mCurrentDragPercent; + + mAnimateToCorrectPosition.reset(); + mAnimateToCorrectPosition.setDuration(MAX_OFFSET_ANIMATION_DURATION); + mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator); + mRefreshView.clearAnimation(); + mRefreshView.startAnimation(mAnimateToCorrectPosition); + + if (mRefreshing) { + mBaseRefreshView.start(); + if (mNotify) { + if (mListener != null) { + mListener.onRefresh(); + } + } + } else { + mBaseRefreshView.stop(); + animateOffsetToStartPosition(); + } + mCurrentOffsetTop = mTarget.getTop(); + } + + private final Animation mAnimateToStartPosition = new Animation() { + @Override + public void applyTransformation(float interpolatedTime, Transformation t) { + moveToStart(interpolatedTime); + } + }; + + private final Animation mAnimateToCorrectPosition = new Animation() { + @Override + public void applyTransformation(float interpolatedTime, Transformation t) { + int targetTop; + int endTarget = mTotalDragDistance; + targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime)); + int offset = targetTop - mTarget.getTop(); + + mCurrentDragPercent = mFromDragPercent - (mFromDragPercent - 1.0f) * interpolatedTime; + mBaseRefreshView.setPercent(mCurrentDragPercent, false); + + setTargetOffsetTop(offset, false /* requires update */); + } + }; + + private void moveToStart(float interpolatedTime) { + int targetTop = mFrom - (int) (mFrom * interpolatedTime); + float targetPercent = mFromDragPercent * (1.0f - interpolatedTime); + int offset = targetTop - mTarget.getTop(); + + mCurrentDragPercent = targetPercent; + mBaseRefreshView.setPercent(mCurrentDragPercent, true); + setTargetOffsetTop(offset, false); + } + + public void setRefreshing(boolean refreshing) { + if (mRefreshing != refreshing) { + setRefreshing(refreshing, false /* notify */); + } + } + + private void setRefreshing(boolean refreshing, final boolean notify) { + if (mRefreshing != refreshing) { + mNotify = notify; + ensureTarget(); + mRefreshing = refreshing; + if (mRefreshing) { + mBaseRefreshView.setPercent(1f, true); + animateOffsetToCorrectPosition(); + } else { + animateOffsetToStartPosition(); + } + } + } + + private Animation.AnimationListener mToStartListener = new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + mBaseRefreshView.stop(); + mCurrentOffsetTop = mTarget.getTop(); + } + }; + + private void onSecondaryPointerUp(MotionEvent ev) { + final int pointerIndex = MotionEventCompat.getActionIndex(ev); + final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); + if (pointerId == mActivePointerId) { + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); + } + } + + private float getMotionEventY(MotionEvent ev, int activePointerId) { + final int index = MotionEventCompat.findPointerIndex(ev, activePointerId); + if (index < 0) { + return -1; + } + return MotionEventCompat.getY(ev, index); + } + + private void setTargetOffsetTop(int offset, boolean requiresUpdate) { + mTarget.offsetTopAndBottom(offset); + mBaseRefreshView.offsetTopAndBottom(offset); + mCurrentOffsetTop = mTarget.getTop(); + if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) { + invalidate(); + } + } + + private boolean canChildScrollUp() { + if (android.os.Build.VERSION.SDK_INT < 14) { + if (mTarget instanceof AbsListView) { + final AbsListView absListView = (AbsListView) mTarget; + return absListView.getChildCount() > 0 + && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) + .getTop() < absListView.getPaddingTop()); + } else { + return mTarget.getScrollY() > 0; + } + } else { + return ViewCompat.canScrollVertically(mTarget, -1); + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + + ensureTarget(); + if (mTarget == null) + return; + + int height = getMeasuredHeight(); + int width = getMeasuredWidth(); + int left = getPaddingLeft(); + int top = getPaddingTop(); + int right = getPaddingRight(); + int bottom = getPaddingBottom(); + + mTarget.layout(left, top + mCurrentOffsetTop, left + width - right, top + height - bottom + mCurrentOffsetTop); + mRefreshView.layout(left, top, left + width - right, top + height - bottom); + } + + public int convertDpToPixel(int dp) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); + } + + public void setOnRefreshListener(OnRefreshListener listener) { + mListener = listener; + } + + public static interface OnRefreshListener { + public void onRefresh(); + } + +} diff --git a/library/src/main/java/com/yalantis/pulltorefresh/library/refresh_view/BaseRefreshView.java b/library/src/main/java/com/yalantis/pulltorefresh/library/refresh_view/BaseRefreshView.java new file mode 100644 index 0000000..e97c8fc --- /dev/null +++ b/library/src/main/java/com/yalantis/pulltorefresh/library/refresh_view/BaseRefreshView.java @@ -0,0 +1,78 @@ +package com.yalantis.pulltorefresh.library.refresh_view; + +import android.content.Context; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; + +import com.yalantis.pulltorefresh.library.PullToRefreshView; + +/** + * Created by Oleksii Shliama on 22/12/2014. + */ +public abstract class BaseRefreshView extends Drawable implements Drawable.Callback, Animatable { + + private PullToRefreshView mRefreshLayout; + + public BaseRefreshView(Context context, PullToRefreshView layout) { + mRefreshLayout = layout; + } + + public Context getContext(){ + return mRefreshLayout != null ? mRefreshLayout.getContext() : null; + } + + public PullToRefreshView getRefreshLayout(){ + return mRefreshLayout; + } + + public abstract void setPercent(float percent, boolean invalidate); + + public abstract void offsetTopAndBottom(int offset); + + @Override + public void invalidateDrawable(Drawable who) { + final Callback callback = getCallback(); + if (callback != null) { + callback.invalidateDrawable(this); + } + } + + @Override + public void scheduleDrawable(Drawable who, Runnable what, long when) { + final Callback callback = getCallback(); + if (callback != null) { + callback.scheduleDrawable(this, what, when); + } + } + + @Override + public void unscheduleDrawable(Drawable who, Runnable what) { + final Callback callback = getCallback(); + if (callback != null) { + callback.unscheduleDrawable(this, what); + } + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + public int convertDpToPixel(int dp) { + float density = getContext().getResources().getDisplayMetrics().density; + return Math.round((float) dp * density); + } + +} diff --git a/library/src/main/java/com/yalantis/pulltorefresh/library/refresh_view/SunRefreshView.java b/library/src/main/java/com/yalantis/pulltorefresh/library/refresh_view/SunRefreshView.java new file mode 100644 index 0000000..1f891e5 --- /dev/null +++ b/library/src/main/java/com/yalantis/pulltorefresh/library/refresh_view/SunRefreshView.java @@ -0,0 +1,309 @@ +package com.yalantis.pulltorefresh.library.refresh_view; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Animatable; +import android.view.animation.Animation; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.Transformation; + +import com.yalantis.pulltorefresh.library.PullToRefreshView; +import com.yalantis.pulltorefresh.library.R; + +/** + * Created by Oleksii Shliama on 22/12/2014. + * https://dribbble.com/shots/1650317-Pull-to-Refresh-Rentals + */ +public class SunRefreshView extends BaseRefreshView implements Animatable { + + private static final float SCALE_START_PERCENT = 0.5f; + 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 PullToRefreshView 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; + + public SunRefreshView(Context context, PullToRefreshView parent) { + super(context, parent); + mParent = parent; + mMatrix = new Matrix(); + + initiateDimens(); + createBitmaps(); + setupAnimations(); + } + + private void initiateDimens() { + mScreenWidth = getContext().getResources().getDisplayMetrics().widthPixels; + mSkyHeight = (int) (SKY_RATIO * mScreenWidth); + mSkyTopOffset = (mSkyHeight * 0.38f); + mSkyMoveOffset = convertDpToPixel(15); + + mTownHeight = (int) (TOWN_RATIO * mScreenWidth); + mTownInitialTopOffset = (mParent.getTotalDragDistance() - mTownHeight * TOWN_INITIAL_SCALE); + mTownFinalTopOffset = (mParent.getTotalDragDistance() - mTownHeight * TOWN_FINAL_SCALE); + mTownMoveOffset = convertDpToPixel(10); + + mSunLeftOffset = 0.3f * (float) mScreenWidth; + mSunTopOffset = (mParent.getTotalDragDistance() * 0.1f); + + mTop = -mParent.getTotalDragDistance(); + } + + 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); + } + + @Override + public void setPercent(float percent, boolean invalidate) { + setPercent(percent); + if (invalidate) setRotate(percent); + } + + @Override + public void offsetTopAndBottom(int offset) { + mTop += offset; + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + final int saveCount = canvas.save(); + canvas.translate(0, mTop); + + drawSky(canvas); + drawSun(canvas); + drawTown(canvas); + + canvas.restoreToCount(saveCount); + } + + private void drawSky(Canvas canvas) { + Matrix matrix = mMatrix; + matrix.reset(); + + float dragPercent = Math.min(1f, Math.abs(mPercent)); + + float skyScale; + float scalePercentDelta = dragPercent - SCALE_START_PERCENT; + if (scalePercentDelta > 0) { + /** Change skyScale between {@link #SKY_INITIAL_SCALE} and 1.0f depending on {@link #mPercent} */ + 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 = (1.0f - dragPercent) * mParent.getTotalDragDistance() - mSkyTopOffset // Offset canvas moving + - mSkyHeight * (skyScale - 1.0f) / 2 // Offset sky scaling + + mSkyMoveOffset * dragPercent; // Give it a little move top -> bottom + + matrix.postScale(skyScale, skyScale); + matrix.postTranslate(offsetX, offsetY); + canvas.drawBitmap(mSky, matrix, null); + } + + private void drawTown(Canvas canvas) { + Matrix matrix = mMatrix; + matrix.reset(); + + 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) * mParent.getTotalDragDistance() // Offset canvas moving + + 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 + + (mParent.getTotalDragDistance() / 2) * (1.0f - dragPercent) // Move the sun up + - mTop; // Depending on Canvas position + + 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; + } + + matrix.postRotate( + (isRefreshing ? -360 : 360) * mRotate * (isRefreshing ? 1 : sunRotateGrowth), + offsetX, + offsetY); + + canvas.drawBitmap(mSun, matrix, null); + } + + public void setPercent(float percent) { + mPercent = percent; + } + + public void setRotate(float rotate) { + mRotate = rotate; + invalidateSelf(); + } + + public void resetOriginals() { + setPercent(0); + setRotate(0); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + } + + @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); + } + +} diff --git a/library/src/main/java/com/yalantis/pulltorefresh/library/util/Logger.java b/library/src/main/java/com/yalantis/pulltorefresh/library/util/Logger.java new file mode 100644 index 0000000..3c9c345 --- /dev/null +++ b/library/src/main/java/com/yalantis/pulltorefresh/library/util/Logger.java @@ -0,0 +1,101 @@ +package com.yalantis.pulltorefresh.library.util; + +import android.text.TextUtils; + +public final class Logger { + + private static final String TAG = "PullToRefresh"; + + /** + * Set true or false if you want read logs or not + */ + private static boolean logEnabled_v = true; + private static boolean logEnabled_i = true; + private static boolean logEnabled_e = true; + + public static void d() { + if (logEnabled_v) { + android.util.Log.v(TAG, getLocation()); + } + } + + public static void d(String msg) { + if (logEnabled_v) { + android.util.Log.v(TAG, getLocation() + msg); + } + } + + public static void i(String msg) { + if (logEnabled_i) { + android.util.Log.i(TAG, getLocation() + msg); + } + } + + public static void i() { + if (logEnabled_i) { + android.util.Log.i(TAG, getLocation()); + } + } + + public static void e(String msg) { + if (logEnabled_e) { + android.util.Log.e(TAG, getLocation() + msg); + } + } + + public static void e(String msg, Throwable e) { + if (logEnabled_e) { + android.util.Log.e(TAG, getLocation() + msg, e); + } + } + + public static void e(Throwable e) { + if (logEnabled_e) { + android.util.Log.e(TAG, getLocation(), e); + } + } + + public static void e() { + if (logEnabled_e) { + android.util.Log.e(TAG, getLocation()); + } + } + + private static String getLocation() { + final String className = Logger.class.getName(); + final StackTraceElement[] traces = Thread.currentThread() + .getStackTrace(); + boolean found = false; + + for (StackTraceElement trace : traces) { + try { + if (found) { + if (!trace.getClassName().startsWith(className)) { + Class clazz = Class.forName(trace.getClassName()); + return "[" + getClassName(clazz) + ":" + + trace.getMethodName() + ":" + + trace.getLineNumber() + "]: "; + } + } else if (trace.getClassName().startsWith(className)) { + found = true; + } + } catch (ClassNotFoundException ignored) { + } + } + + return "[]: "; + } + + private static String getClassName(Class clazz) { + if (clazz != null) { + if (!TextUtils.isEmpty(clazz.getSimpleName())) { + return clazz.getSimpleName(); + } + + return getClassName(clazz.getEnclosingClass()); + } + + return ""; + } + +} diff --git a/library/src/main/java/com/yalantis/pulltorefresh/library/util/Toaster.java b/library/src/main/java/com/yalantis/pulltorefresh/library/util/Toaster.java new file mode 100644 index 0000000..c985d45 --- /dev/null +++ b/library/src/main/java/com/yalantis/pulltorefresh/library/util/Toaster.java @@ -0,0 +1,18 @@ +package com.yalantis.pulltorefresh.library.util; + +import android.content.Context; +import android.widget.Toast; + +public class Toaster { + + public static void showShort(Context context, String value) { + if (context != null) + Toast.makeText(context, value, Toast.LENGTH_SHORT).show(); + } + + public static void showLong(Context context, String value) { + if (context != null) + Toast.makeText(context, value, Toast.LENGTH_LONG).show(); + } + +} diff --git a/library/src/main/java/com/yalantis/pulltorefresh/library/util/Utils.java b/library/src/main/java/com/yalantis/pulltorefresh/library/util/Utils.java new file mode 100644 index 0000000..a348cc2 --- /dev/null +++ b/library/src/main/java/com/yalantis/pulltorefresh/library/util/Utils.java @@ -0,0 +1,18 @@ +package com.yalantis.pulltorefresh.library.util; + +import android.content.Context; +import android.util.DisplayMetrics; + +public class Utils { + + public static int convertDpToPixel(Context context, int dp) { + float density = context.getResources().getDisplayMetrics().density; + return Math.round((float) dp * density); + } + + public static float convertPixelsToDp(Context context, int px) { + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + return px / (metrics.densityDpi / 160f); + } + +} diff --git a/library/src/main/res/drawable-hdpi/buildings.png b/library/src/main/res/drawable-hdpi/buildings.png new file mode 100644 index 0000000..9b148a9 Binary files /dev/null and b/library/src/main/res/drawable-hdpi/buildings.png differ diff --git a/library/src/main/res/drawable-hdpi/sky.png b/library/src/main/res/drawable-hdpi/sky.png new file mode 100644 index 0000000..65e84dd Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sky.png differ diff --git a/library/src/main/res/drawable-hdpi/sun.png b/library/src/main/res/drawable-hdpi/sun.png new file mode 100644 index 0000000..5765f8e Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sun.png differ diff --git a/library/src/main/res/drawable-mdpi/buildings.png b/library/src/main/res/drawable-mdpi/buildings.png new file mode 100644 index 0000000..d4f537e Binary files /dev/null and b/library/src/main/res/drawable-mdpi/buildings.png differ diff --git a/library/src/main/res/drawable-mdpi/sky.png b/library/src/main/res/drawable-mdpi/sky.png new file mode 100644 index 0000000..9db3b6e Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sky.png differ diff --git a/library/src/main/res/drawable-mdpi/sun.png b/library/src/main/res/drawable-mdpi/sun.png new file mode 100644 index 0000000..aeb1852 Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sun.png differ diff --git a/library/src/main/res/drawable-xhdpi/buildings.png b/library/src/main/res/drawable-xhdpi/buildings.png new file mode 100644 index 0000000..c832a11 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/buildings.png differ diff --git a/library/src/main/res/drawable-xhdpi/sky.png b/library/src/main/res/drawable-xhdpi/sky.png new file mode 100644 index 0000000..786b4de Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sky.png differ diff --git a/library/src/main/res/drawable-xhdpi/sun.png b/library/src/main/res/drawable-xhdpi/sun.png new file mode 100644 index 0000000..5d0ac04 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sun.png differ diff --git a/library/src/main/res/drawable-xxhdpi/buildings.png b/library/src/main/res/drawable-xxhdpi/buildings.png new file mode 100644 index 0000000..d4ea56c Binary files /dev/null and b/library/src/main/res/drawable-xxhdpi/buildings.png differ diff --git a/library/src/main/res/drawable-xxhdpi/sky.png b/library/src/main/res/drawable-xxhdpi/sky.png new file mode 100644 index 0000000..6e3898e Binary files /dev/null and b/library/src/main/res/drawable-xxhdpi/sky.png differ diff --git a/library/src/main/res/drawable-xxhdpi/sun.png b/library/src/main/res/drawable-xxhdpi/sun.png new file mode 100644 index 0000000..7609788 Binary files /dev/null and b/library/src/main/res/drawable-xxhdpi/sun.png differ diff --git a/library/src/main/res/drawable-xxxhdpi/buildings.png b/library/src/main/res/drawable-xxxhdpi/buildings.png new file mode 100644 index 0000000..db9f1db Binary files /dev/null and b/library/src/main/res/drawable-xxxhdpi/buildings.png differ diff --git a/library/src/main/res/drawable-xxxhdpi/sky.png b/library/src/main/res/drawable-xxxhdpi/sky.png new file mode 100644 index 0000000..c0cbd32 Binary files /dev/null and b/library/src/main/res/drawable-xxxhdpi/sky.png differ diff --git a/library/src/main/res/drawable-xxxhdpi/sun.png b/library/src/main/res/drawable-xxxhdpi/sun.png new file mode 100644 index 0000000..1feddc3 Binary files /dev/null and b/library/src/main/res/drawable-xxxhdpi/sun.png differ diff --git a/library/src/main/res/layout/test.xml b/library/src/main/res/layout/test.xml new file mode 100644 index 0000000..45d15b4 --- /dev/null +++ b/library/src/main/res/layout/test.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml new file mode 100644 index 0000000..bc56848 --- /dev/null +++ b/library/src/main/res/values/attrs.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 0000000..4a10752 --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.1" + + defaultConfig { + applicationId "com.yalantis.pulltorefresh.sample" + minSdkVersion 16 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:21.0.2' + + compile project (':library') +} diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro new file mode 100644 index 0000000..8447f5d --- /dev/null +++ b/sample/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/oleksii/dev/android-sdk-linux/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# 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 *; +#} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6c84f3a --- /dev/null +++ b/sample/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/sample/src/main/java/com/yalantis/pulltorefresh/sample/PullToRefreshActivity.java b/sample/src/main/java/com/yalantis/pulltorefresh/sample/PullToRefreshActivity.java new file mode 100644 index 0000000..6eb3328 --- /dev/null +++ b/sample/src/main/java/com/yalantis/pulltorefresh/sample/PullToRefreshActivity.java @@ -0,0 +1,106 @@ +package com.yalantis.pulltorefresh.sample; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; + +import com.yalantis.pulltorefresh.library.PullToRefreshView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PullToRefreshActivity extends ActionBarActivity { + + public static final int REFRESH_DELAY = 2000; + + private PullToRefreshView mPullToRefreshView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_pull_to_refresh); + + Map map; + List> sampleList = new ArrayList<>(); + + int[] icons = { + R.drawable.icon_1, + R.drawable.icon_2, + R.drawable.icon_3}; + + int[] colors = { + R.color.saffron, + R.color.eggplant, + R.color.sienna}; + + for (int i = 0; i < icons.length; i++) { + map = new HashMap<>(); + map.put(SampleAdapter.KEY_ICON, icons[i]); + map.put(SampleAdapter.KEY_COLOR, colors[i]); + sampleList.add(map); + } + + ListView listView = (ListView) findViewById(R.id.list_view); + listView.setAdapter(new SampleAdapter(this, R.layout.list_item, sampleList)); + + mPullToRefreshView = (PullToRefreshView) findViewById(R.id.pull_to_refresh); + mPullToRefreshView.setOnRefreshListener(new PullToRefreshView.OnRefreshListener() { + @Override + public void onRefresh() { + mPullToRefreshView.postDelayed(new Runnable() { + @Override + public void run() { + mPullToRefreshView.setRefreshing(false); + } + }, REFRESH_DELAY); + } + }); + } + + class SampleAdapter extends ArrayAdapter> { + + public static final String KEY_ICON = "icon"; + public static final String KEY_COLOR = "color"; + + private final LayoutInflater mInflater; + private final List> mData; + + public SampleAdapter(Context context, int layoutResourceId, List> data) { + super(context, layoutResourceId, data); + mData = data; + mInflater = LayoutInflater.from(context); + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + convertView = mInflater.inflate(R.layout.list_item, parent, false); + viewHolder.imageViewIcon = (ImageView) convertView.findViewById(R.id.image_view_icon); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + + viewHolder.imageViewIcon.setImageResource(mData.get(position).get(KEY_ICON)); + convertView.setBackgroundResource(mData.get(position).get(KEY_COLOR)); + + return convertView; + } + + class ViewHolder { + ImageView imageViewIcon; + } + + } + +} diff --git a/sample/src/main/res/drawable-hdpi/ic_launcher.png b/sample/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..5762885 Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-hdpi/icon_1.png b/sample/src/main/res/drawable-hdpi/icon_1.png new file mode 100644 index 0000000..78df781 Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/icon_1.png differ diff --git a/sample/src/main/res/drawable-hdpi/icon_2.png b/sample/src/main/res/drawable-hdpi/icon_2.png new file mode 100644 index 0000000..995a7d9 Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/icon_2.png differ diff --git a/sample/src/main/res/drawable-hdpi/icon_3.png b/sample/src/main/res/drawable-hdpi/icon_3.png new file mode 100644 index 0000000..1ef8457 Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/icon_3.png differ diff --git a/sample/src/main/res/drawable-mdpi/ic_launcher.png b/sample/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..741e1d6 Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-mdpi/icon_1.png b/sample/src/main/res/drawable-mdpi/icon_1.png new file mode 100644 index 0000000..8f44a0a Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/icon_1.png differ diff --git a/sample/src/main/res/drawable-mdpi/icon_2.png b/sample/src/main/res/drawable-mdpi/icon_2.png new file mode 100644 index 0000000..5085c42 Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/icon_2.png differ diff --git a/sample/src/main/res/drawable-mdpi/icon_3.png b/sample/src/main/res/drawable-mdpi/icon_3.png new file mode 100644 index 0000000..86660bb Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/icon_3.png differ diff --git a/sample/src/main/res/drawable-xhdpi/ic_launcher.png b/sample/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..83bc63d Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-xhdpi/icon_1.png b/sample/src/main/res/drawable-xhdpi/icon_1.png new file mode 100644 index 0000000..0736656 Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/icon_1.png differ diff --git a/sample/src/main/res/drawable-xhdpi/icon_2.png b/sample/src/main/res/drawable-xhdpi/icon_2.png new file mode 100644 index 0000000..b753e53 Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/icon_2.png differ diff --git a/sample/src/main/res/drawable-xhdpi/icon_3.png b/sample/src/main/res/drawable-xhdpi/icon_3.png new file mode 100644 index 0000000..f9cd492 Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/icon_3.png differ diff --git a/sample/src/main/res/drawable-xxhdpi/ic_launcher.png b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..7f89b41 Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-xxhdpi/icon_1.png b/sample/src/main/res/drawable-xxhdpi/icon_1.png new file mode 100644 index 0000000..8b5b9dc Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/icon_1.png differ diff --git a/sample/src/main/res/drawable-xxhdpi/icon_2.png b/sample/src/main/res/drawable-xxhdpi/icon_2.png new file mode 100644 index 0000000..7da12d7 Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/icon_2.png differ diff --git a/sample/src/main/res/drawable-xxhdpi/icon_3.png b/sample/src/main/res/drawable-xxhdpi/icon_3.png new file mode 100644 index 0000000..470b9df Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/icon_3.png differ diff --git a/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png b/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..d564656 Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-xxxhdpi/icon_1.png b/sample/src/main/res/drawable-xxxhdpi/icon_1.png new file mode 100644 index 0000000..9d1c69b Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/icon_1.png differ diff --git a/sample/src/main/res/drawable-xxxhdpi/icon_2.png b/sample/src/main/res/drawable-xxxhdpi/icon_2.png new file mode 100644 index 0000000..ae75436 Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/icon_2.png differ diff --git a/sample/src/main/res/drawable-xxxhdpi/icon_3.png b/sample/src/main/res/drawable-xxxhdpi/icon_3.png new file mode 100644 index 0000000..6243ad0 Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/icon_3.png differ diff --git a/sample/src/main/res/layout/activity_pull_to_refresh.xml b/sample/src/main/res/layout/activity_pull_to_refresh.xml new file mode 100644 index 0000000..e3c710f --- /dev/null +++ b/sample/src/main/res/layout/activity_pull_to_refresh.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/sample/src/main/res/layout/list_item.xml b/sample/src/main/res/layout/list_item.xml new file mode 100644 index 0000000..ec73ed8 --- /dev/null +++ b/sample/src/main/res/layout/list_item.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml new file mode 100644 index 0000000..62274a9 --- /dev/null +++ b/sample/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #F4B63E + #663D4E + #EB6460 + \ No newline at end of file diff --git a/sample/src/main/res/values/dimens.xml b/sample/src/main/res/values/dimens.xml new file mode 100644 index 0000000..82806c6 --- /dev/null +++ b/sample/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + + 222dp + \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml new file mode 100644 index 0000000..2068692 --- /dev/null +++ b/sample/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + Pull to Refresh + + diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml new file mode 100644 index 0000000..9a620be --- /dev/null +++ b/sample/src/main/res/values/styles.xml @@ -0,0 +1,5 @@ + + +