├── .gitignore
├── README.md
├── assets
├── capture.gif
└── googleplay.png
├── build.gradle
├── demo
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── nshmura
│ │ └── snappyimageviewer
│ │ └── demo
│ │ ├── ImageViewerActivity.java
│ │ └── MainActivity.java
│ └── res
│ ├── drawable-xxhdpi
│ └── sample.jpg
│ ├── layout
│ ├── activity_image_viewer.xml
│ └── activity_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
│ ├── strings.xml
│ └── styles.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── nshmura
│ └── snappyimageviewer
│ ├── DegreeVelocityTracker.java
│ ├── SnappyDragHelper.java
│ ├── SnappyImageView.java
│ └── SnappyImageViewer.java
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | Thumbs.db
3 |
4 | # Files for the Dalvik VM
5 | *.dex
6 |
7 | # Java class files
8 | *.class
9 |
10 | # Generated files
11 | bin/
12 | gen/
13 | obj/
14 | apk/
15 | target/
16 | build/
17 |
18 | # Gradle files
19 | .gradle/
20 | .idea
21 | *.iml
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
29 | # docs
30 | docs/
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SnappyImageViewer
2 |
3 | Android Image Viewer inspired by StackOverflow's with swipe-to-dimiss and moving animations.
4 |
5 |
6 |
7 |
8 | ## Demo
9 |
10 |
11 |
12 | ## Getting Started
13 |
14 | In your `build.gradle`:
15 |
16 | ```gradle
17 | repositories {
18 | jcenter()
19 | }
20 |
21 | dependencies {
22 | compile 'com.nshmura:snappyimageviewer:1.1.0'
23 | }
24 | ```
25 |
26 | In your layout file:
27 | ```xml
28 |
32 | ```
33 |
34 | Setup the `SnappyImageViewer`:
35 | ```java
36 | SnappyImageViewer snappyImageViewer = (SnappyImageViewer) findViewById(R.id.snappy_image_viewer);
37 | snappyImageViewer.setImageResource(R.drawable.sample);
38 | snappyImageViewer.addOnClosedListener(new SnappyImageViewer.OnClosedListener() {
39 | @Override
40 | public void onClosed() {
41 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
42 | getWindow().setSharedElementReturnTransition(new Fade(Fade.IN));
43 | }
44 | ActivityCompat.finishAfterTransition(ImageViewerActivity.this);
45 | }
46 | });
47 | ```
48 |
49 | ## TODO
50 | - Pinch to Zoom
51 | - Embed in ViewPager
52 | - Add `ImageViwerActivity` in library
53 |
54 |
55 | ## Contributions
56 |
57 | Your contributions always welcome!
58 |
59 |
60 | ## License
61 | ```
62 | Copyright (C) 2016 nshmura
63 |
64 | Licensed under the Apache License, Version 2.0 (the "License");
65 | you may not use this file except in compliance with the License.
66 | You may obtain a copy of the License at
67 |
68 | http://www.apache.org/licenses/LICENSE-2.0
69 |
70 | Unless required by applicable law or agreed to in writing, software
71 | distributed under the License is distributed on an "AS IS" BASIS,
72 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
73 | See the License for the specific language governing permissions and
74 | limitations under the License.
75 | ```
76 |
--------------------------------------------------------------------------------
/assets/capture.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/assets/capture.gif
--------------------------------------------------------------------------------
/assets/googleplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/assets/googleplay.png
--------------------------------------------------------------------------------
/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.3'
9 | classpath 'com.novoda:bintray-release:0.3.4'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
26 |
27 | ext {
28 | minSdkVersion = 15
29 | compileSdkVersion = 24
30 | targetSdkVersion = compileSdkVersion
31 | buildToolsVersion = '24.0.2'
32 | javaVersion = JavaVersion.VERSION_1_7
33 | supportLibraryVersion = '24.2.1'
34 | versionCode = 2
35 | versionName = "1.1.0"
36 |
37 | POM_DEVELOPER_ID = "nshmura"
38 | POM_GROUP = "com.nshmura"
39 | POM_VERSION_NAME = versionName
40 | POM_URL = "https://github.com/nshmura/SnappyImageViewer"
41 |
42 | POM_ARTIFACT_ID = "snappyimageviewer"
43 | POM_ARTIFACT_NAME = "SnappyImageViewer"
44 | POM_DESCRIPTION = "Android Image Viewer inspired by StackOverflow's with swipe-to-dimiss and moving animations."
45 | }
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 |
7 | defaultConfig {
8 | applicationId "com.nshmura.snappyimageviewer.demo"
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode rootProject.ext.versionCode
12 | versionName rootProject.ext.versionName
13 | }
14 |
15 | signingConfigs {
16 | release {
17 | if (hasProperty('NSHMURA_KEYSTORE')) {
18 | storeFile file(NSHMURA_KEYSTORE)
19 | keyAlias NSHMURA_KEYALIAS
20 | storePassword NSHMURA_KEYSTOREPASSWORD
21 | keyPassword NSHMURA_KEYALIASPASSWORD
22 | }
23 | }
24 | }
25 |
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
30 | if (hasProperty('NSHMURA_KEYSTORE')) {
31 | signingConfig signingConfigs.release
32 | }
33 | }
34 | }
35 | }
36 |
37 | dependencies {
38 | compile fileTree(dir: 'libs', include: ['*.jar'])
39 | testCompile 'junit:junit:4.12'
40 | compile "com.android.support:appcompat-v7:$rootProject.ext.supportLibraryVersion"
41 | compile 'com.squareup.picasso:picasso:2.5.2'
42 | //compile 'com.nshmura:snappyimageviewer:1.0.0'
43 | compile project(":library")
44 | }
45 |
--------------------------------------------------------------------------------
/demo/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/a13088/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/nshmura/snappyimageviewer/demo/ImageViewerActivity.java:
--------------------------------------------------------------------------------
1 | package com.nshmura.snappyimageviewer.demo;
2 |
3 | import com.nshmura.snappyimageviewer.SnappyImageViewer;
4 | import com.squareup.picasso.Picasso;
5 |
6 | import android.app.Activity;
7 | import android.content.Intent;
8 | import android.os.Build;
9 | import android.os.Bundle;
10 | import android.support.v4.app.ActivityCompat;
11 | import android.support.v4.app.ActivityOptionsCompat;
12 | import android.support.v4.util.Pair;
13 | import android.support.v4.view.ViewCompat;
14 | import android.support.v7.app.AppCompatActivity;
15 | import android.transition.Fade;
16 | import android.view.View;
17 | import android.widget.ImageView;
18 |
19 | public class ImageViewerActivity extends AppCompatActivity {
20 |
21 | private static final String EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL";
22 | private static final String TRANSITION_NAME_PHOTO = "TRANSITION_NAME_PHOTO";
23 |
24 | public static void start(Activity activity, String uri, ImageView imageView) {
25 | Intent intent = new Intent(activity, ImageViewerActivity.class);
26 | intent.putExtra(EXTRA_IMAGE_URL, uri);
27 |
28 | //noinspection unchecked
29 | Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
30 | Pair.create((View) imageView, TRANSITION_NAME_PHOTO))
31 | .toBundle();
32 |
33 | ActivityCompat.startActivity(activity, intent, options);
34 | }
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_image_viewer);
40 |
41 | String uri = getIntent().getStringExtra(EXTRA_IMAGE_URL);
42 |
43 | SnappyImageViewer snappyImageViewer = (SnappyImageViewer) findViewById(R.id.snappy_image_viewer);
44 |
45 | Picasso.with(this)
46 | .load(uri)
47 | .into(snappyImageViewer.getImageView());
48 |
49 | snappyImageViewer.addOnClosedListener(new SnappyImageViewer.OnClosedListener() {
50 | @Override
51 | public void onClosed() {
52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
53 | getWindow().setSharedElementReturnTransition(new Fade(Fade.IN));
54 | }
55 | ActivityCompat.finishAfterTransition(ImageViewerActivity.this);
56 | }
57 | });
58 | ViewCompat.setTransitionName(snappyImageViewer.getImageView(), TRANSITION_NAME_PHOTO);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/nshmura/snappyimageviewer/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.nshmura.snappyimageviewer.demo;
2 |
3 | import com.squareup.picasso.Picasso;
4 |
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.view.View;
8 | import android.widget.ImageView;
9 |
10 | public class MainActivity extends AppCompatActivity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_main);
16 |
17 | final ImageView imageView = (ImageView) findViewById(R.id.image);
18 |
19 | final String uri = "https://developer.android.com/images/home/nougat_bg.jpg";
20 |
21 | Picasso.with(this)
22 | .load(uri)
23 | .into(imageView);
24 |
25 | imageView.setOnClickListener(new View.OnClickListener() {
26 | @Override
27 | public void onClick(View v) {
28 | ImageViewerActivity.start(MainActivity.this, uri, imageView);
29 | }
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/sample.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/demo/src/main/res/drawable-xxhdpi/sample.jpg
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_image_viewer.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ee000000
4 |
5 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SnappyImageViewer
3 |
4 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshmura/SnappyImageViewer/7497171d8dc4b4f05a110ca063dd3544bc1598e3/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Sep 02 17:44:53 JST 2016
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 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.novoda.bintray-release'
3 |
4 | android {
5 | compileSdkVersion rootProject.ext.compileSdkVersion
6 | buildToolsVersion rootProject.ext.buildToolsVersion
7 |
8 | defaultConfig {
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | }
12 | }
13 |
14 | publish {
15 | userOrg = rootProject.ext.POM_DEVELOPER_ID
16 | groupId = rootProject.ext.POM_GROUP
17 | artifactId = rootProject.ext.POM_ARTIFACT_ID
18 | uploadName = rootProject.ext.POM_ARTIFACT_NAME
19 | publishVersion = rootProject.ext.POM_VERSION_NAME
20 | desc = rootProject.ext.POM_DESCRIPTION
21 | website = rootProject.ext.POM_URL
22 | }
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/a13088/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/library/src/main/java/com/nshmura/snappyimageviewer/DegreeVelocityTracker.java:
--------------------------------------------------------------------------------
1 | package com.nshmura.snappyimageviewer;
2 |
3 | public class DegreeVelocityTracker {
4 | private float degreeVelocity;
5 | private float lastMsec;
6 |
7 | public float getDegreeVelocity() {
8 | return degreeVelocity;
9 | }
10 |
11 | public void addDegree(float degree) {
12 | float currMsec = getMsec();
13 | float deltaMsec = currMsec - lastMsec;
14 | lastMsec = currMsec;
15 | degreeVelocity = degree / deltaMsec;
16 | }
17 |
18 | public void reset() {
19 | lastMsec = getMsec();
20 | }
21 |
22 | public float getMsec() {
23 | return System.nanoTime() / 1000000f;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/library/src/main/java/com/nshmura/snappyimageviewer/SnappyDragHelper.java:
--------------------------------------------------------------------------------
1 | package com.nshmura.snappyimageviewer;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.graphics.Matrix;
6 | import android.graphics.RectF;
7 | import android.view.animation.OvershootInterpolator;
8 |
9 | class SnappyDragHelper {
10 |
11 | private float CLOSE_VELOCITY_THRESHOLD = 2500;
12 |
13 | private final int viewerWidth;
14 | private final int viewerHeight;
15 | private final int imageWidth;
16 | private final int imageHeight;
17 | private final Matrix imgInitMatrix;
18 | private Listener listener;
19 |
20 | private float bmpDegree;
21 | private float[] workPoint = new float[2];
22 | private Matrix touchMatrix = new Matrix();
23 | private Matrix imgMatrix = new Matrix();
24 | private Matrix imgInvertMatrix = new Matrix();
25 |
26 | private ValueAnimator animator;
27 | private DegreeVelocityTracker degreeVelocityTracker = new DegreeVelocityTracker();
28 |
29 | interface Listener {
30 | void onMove(Matrix imgMatrix);
31 |
32 | void onRestored();
33 |
34 | void onCastAwayed();
35 | }
36 |
37 | SnappyDragHelper(int viewerWidth, int viewerHeight, int imageWidth, int imageHeight,
38 | Matrix imgInitMatrix, Listener listener) {
39 | this.viewerWidth = viewerWidth;
40 | this.viewerHeight = viewerHeight;
41 | this.imageWidth = imageWidth;
42 | this.imageHeight = imageHeight;
43 | this.imgInitMatrix = imgInitMatrix;
44 | this.listener = listener;
45 | }
46 |
47 | void onTouch() {
48 | touchMatrix.reset();
49 | imgInvertMatrix.reset();
50 | imgInitMatrix.invert(imgInvertMatrix);
51 | bmpDegree = 0;
52 | degreeVelocityTracker.reset();
53 | }
54 |
55 | void onMove(float currentX, float currentY, float newX, float newY) {
56 | float dx = newX - currentX;
57 | float dy = newY - currentY;
58 |
59 | //Convert current touch point to bitmap's local coordinate.
60 | workPoint[0] = currentX;
61 | workPoint[1] = currentY;
62 | imgInvertMatrix.mapPoints(workPoint);
63 | float currentTx = workPoint[0] - imageWidth / 2f;
64 | float currentTy = workPoint[1] - imageHeight / 2f;
65 |
66 | //Convert new touch point to bitmap's local coordinate.
67 | workPoint[0] = newX;
68 | workPoint[1] = newY;
69 | imgInvertMatrix.mapPoints(workPoint);
70 | float newTx = workPoint[0] - imageWidth / 2f;
71 | float newTy = workPoint[1] - imageHeight / 2f;
72 |
73 | //calculate the degrees
74 | float radius0 = (float) Math.atan2(currentTy, currentTx); //current degree
75 | float radius1 = (float) Math.atan2(newTy, newTx); //new degree
76 |
77 | float radius = (radius1 - radius0);
78 | if (Math.abs(radius) > Math.PI) {
79 | if (radius < 0) {
80 | radius = (float) (radius + 2 * Math.PI);
81 | } else {
82 | radius = (float) (radius - 2 * Math.PI);
83 | }
84 | }
85 |
86 | float distance = getLength(newTx, newTy); //distance from center of bitmap
87 | float maxDistance = getLength(imageWidth, imageHeight);
88 | float factor = (float) Math.min(1, Math.max(0, 1.5 * Math.pow(distance / maxDistance, 1.3)));
89 | float deltaDegree = (float) (radius * factor * 180 / Math.PI);
90 |
91 | bmpDegree += deltaDegree;
92 | degreeVelocityTracker.addDegree(deltaDegree);
93 |
94 | touchMatrix.postRotate(deltaDegree, newX, newY);
95 | touchMatrix.postTranslate(dx, dy);
96 |
97 | workPoint[0] = workPoint[1] = 0; //left top
98 | touchMatrix.mapPoints(workPoint);
99 | moveTo(workPoint[0], workPoint[1], bmpDegree);
100 | }
101 |
102 | void onRelease(int xvel, int yvel) {
103 | float vel = getLength(Math.abs(xvel), Math.abs(yvel));
104 |
105 | if (vel > CLOSE_VELOCITY_THRESHOLD) {
106 | castAway(xvel, yvel, degreeVelocityTracker.getDegreeVelocity());
107 |
108 | } else {
109 | restore();
110 | }
111 | }
112 |
113 | void cancelAnimation() {
114 | if (animator != null) {
115 | animator.cancel();
116 | }
117 | }
118 |
119 | private void castAway(int xvel, int yvel, float degreeVel) {
120 | final float dx = xvel / 1000f;
121 | final float dy = yvel / 1000f;
122 | final float dDegree = Math.max(-0.5f, Math.min(0.5f, degreeVel * 5));
123 | final Matrix centerMatrix = new Matrix(touchMatrix);
124 |
125 | //bitmap's center coordinate
126 | workPoint[0] = imageWidth / 2f;
127 | workPoint[1] = imageHeight / 2f;
128 | imgMatrix.mapPoints(workPoint);
129 | final float cx = workPoint[0];
130 | final float cy = workPoint[1];
131 |
132 | cancelAnimation();
133 | animator = ValueAnimator.ofFloat(1, 0);
134 | animator.setDuration(3000);
135 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
136 | float centerX = cx;
137 | float centerY = cy;
138 | float degree = bmpDegree;
139 | float time = System.nanoTime();
140 |
141 | @Override
142 | public void onAnimationUpdate(ValueAnimator animator) {
143 | float currTime = System.nanoTime();
144 | float msec = (currTime - time) / 1000000f;
145 | time = currTime;
146 |
147 | centerX += dx * msec;
148 | centerY += dy * msec;
149 | degree += dDegree * msec;
150 | centerMatrix.postRotate(dDegree * msec, centerX, centerY);
151 | centerMatrix.postTranslate(dx * msec, dy * msec);
152 |
153 | workPoint[0] = workPoint[1] = 0; //left top
154 | centerMatrix.mapPoints(workPoint);
155 | float bmpX = workPoint[0];
156 | float bmpY = workPoint[1];
157 |
158 | moveTo(bmpX, bmpY, degree);
159 |
160 | if (isOutOfView()) {
161 | animator.cancel();
162 | }
163 | }
164 | });
165 | animator.addListener(new Animator.AnimatorListener() {
166 | @Override
167 | public void onAnimationStart(Animator animation) {
168 |
169 | }
170 |
171 | @Override
172 | public void onAnimationEnd(Animator animation) {
173 | listener.onCastAwayed();
174 | animator = null;
175 | }
176 |
177 | @Override
178 | public void onAnimationCancel(Animator animation) {
179 | }
180 |
181 | @Override
182 | public void onAnimationRepeat(Animator animation) {
183 |
184 | }
185 | });
186 | animator.start();
187 | }
188 |
189 | private boolean isOutOfView() {
190 | RectF rect = new RectF(0, 0, imageWidth, imageHeight);
191 | imgMatrix.mapRect(rect);
192 |
193 | boolean outOfLeft = (rect.left < 0 && rect.right < 0);
194 | boolean outOfRight = (rect.left > viewerWidth && rect.right > viewerWidth);
195 | boolean outOfTop = (rect.top < 0 && rect.bottom < 0);
196 | boolean outOfBottom = (rect.top > viewerHeight && rect.bottom > viewerHeight);
197 |
198 | return outOfLeft || outOfRight || outOfTop || outOfBottom;
199 | }
200 |
201 | private void restore() {
202 | workPoint[0] = workPoint[1] = 0; //left top
203 | touchMatrix.mapPoints(workPoint);
204 | final float bmpX = workPoint[0];
205 | final float bmpY = workPoint[1];
206 | final float degree = bmpDegree;
207 |
208 | cancelAnimation();
209 | animator = ValueAnimator.ofFloat(1, 0);
210 | animator.setDuration(400);
211 | animator.setInterpolator(new OvershootInterpolator(1.002f));
212 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
213 | @Override
214 | public void onAnimationUpdate(ValueAnimator animator) {
215 | float value = (float) animator.getAnimatedValue();
216 | moveTo(bmpX * value, bmpY * value, degree * value);
217 | }
218 | });
219 | animator.addListener(new Animator.AnimatorListener() {
220 | @Override
221 | public void onAnimationStart(Animator animation) {
222 |
223 | }
224 |
225 | @Override
226 | public void onAnimationEnd(Animator animation) {
227 | listener.onRestored();
228 | animator = null;
229 | }
230 |
231 | @Override
232 | public void onAnimationCancel(Animator animation) {
233 |
234 | }
235 |
236 | @Override
237 | public void onAnimationRepeat(Animator animation) {
238 |
239 | }
240 | });
241 | animator.start();
242 | }
243 |
244 | private void moveTo(float bmpX, float bmpY, float bmpDegree) {
245 | imgMatrix.reset();
246 | imgMatrix.set(imgInitMatrix);
247 | imgMatrix.postTranslate(bmpX, bmpY);
248 | imgMatrix.postRotate(bmpDegree, bmpX, bmpY);
249 | imgMatrix.invert(imgInvertMatrix);
250 | listener.onMove(imgMatrix);
251 | }
252 |
253 | private float getLength(float x, float y) {
254 | return (float) Math.sqrt(x * x + y * y);
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/library/src/main/java/com/nshmura/snappyimageviewer/SnappyImageView.java:
--------------------------------------------------------------------------------
1 | package com.nshmura.snappyimageviewer;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.drawable.Drawable;
7 | import android.net.Uri;
8 | import android.widget.ImageView;
9 |
10 | class SnappyImageView extends ImageView {
11 |
12 | interface Listener {
13 | void onImageChanged();
14 | void onDraw();
15 | }
16 |
17 | private SnappyImageView.Listener mListener;
18 |
19 | public SnappyImageView(Context context) {
20 | super(context);
21 | }
22 |
23 |
24 | @Override
25 | public void setImageBitmap(Bitmap bitmap) {
26 | super.setImageBitmap(bitmap);
27 | if (mListener != null) {
28 | mListener.onImageChanged();
29 | }
30 | }
31 |
32 | @Override
33 | public void setImageResource(int resId) {
34 | super.setImageResource(resId);
35 | if (mListener != null) {
36 | mListener.onImageChanged();
37 | }
38 | }
39 |
40 | @Override
41 | public void setImageURI(Uri uri) {
42 | super.setImageURI(uri);
43 | if (mListener != null) {
44 | mListener.onImageChanged();
45 | }
46 | }
47 |
48 | @Override
49 | public void setImageDrawable(Drawable drawable) {
50 | super.setImageDrawable(drawable);
51 | if (mListener != null) {
52 | mListener.onImageChanged();
53 | }
54 | }
55 |
56 | @Override
57 | protected void onDraw(Canvas canvas) {
58 | super.onDraw(canvas);
59 | if (mListener != null) {
60 | mListener.onDraw();
61 | }
62 | }
63 |
64 | public void setListener(Listener listener) {
65 | mListener = listener;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/library/src/main/java/com/nshmura/snappyimageviewer/SnappyImageViewer.java:
--------------------------------------------------------------------------------
1 | package com.nshmura.snappyimageviewer;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Matrix;
7 | import android.graphics.RectF;
8 | import android.graphics.drawable.Drawable;
9 | import android.net.Uri;
10 | import android.os.Build;
11 | import android.util.AttributeSet;
12 | import android.view.MotionEvent;
13 | import android.view.VelocityTracker;
14 | import android.view.ViewConfiguration;
15 | import android.view.ViewGroup;
16 | import android.widget.FrameLayout;
17 | import android.widget.ImageView;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | public class SnappyImageViewer extends FrameLayout {
23 |
24 | public static final int INVALID_POINTER = -1;
25 |
26 | private static final int STATE_IDLE = 0;
27 | private static final int STATE_DRAGGING = 1;
28 | private static final int STATE_PINCHING = 2;
29 | private static final int STATE_ZOOMED = 5;
30 | private static final int STATE_SETTLING = 4;
31 | private static final int STATE_CLOSED = 5;
32 |
33 | private int state = STATE_IDLE;
34 |
35 | private SnappyImageView imageView;
36 | private List onClosedListeners = new ArrayList<>();
37 |
38 | //for drag
39 | private SnappyDragHelper snappyDragHelper;
40 | private int activePointerId;
41 | private VelocityTracker velocityTracker;
42 | private int maxVelocity;
43 | private float currentX;
44 | private float currentY;
45 |
46 | private RectF initialImageRect = new RectF();
47 | private Matrix imgInitMatrix = new Matrix();
48 |
49 | public interface OnClosedListener {
50 | void onClosed();
51 | }
52 |
53 | public SnappyImageViewer(Context context) {
54 | super(context);
55 | init();
56 | }
57 |
58 | public SnappyImageViewer(Context context, AttributeSet attrs) {
59 | super(context, attrs);
60 | init();
61 | }
62 |
63 | public SnappyImageViewer(Context context, AttributeSet attrs, int defStyleAttr) {
64 | super(context, attrs, defStyleAttr);
65 | init();
66 | }
67 |
68 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
69 | public SnappyImageViewer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
70 | super(context, attrs, defStyleAttr, defStyleRes);
71 | init();
72 | }
73 |
74 | private void init() {
75 | final ViewConfiguration vc = ViewConfiguration.get(getContext());
76 | maxVelocity = vc.getScaledMaximumFlingVelocity();
77 |
78 | imageView = new SnappyImageView(getContext());
79 | imageView.setScaleType(ImageView.ScaleType.MATRIX);
80 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
81 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
82 | imageView.setLayoutParams(layoutParams);
83 | addView(imageView);
84 |
85 | imageView.setListener(new SnappyImageView.Listener() {
86 | @Override
87 | public void onImageChanged() {
88 | updateSize();
89 | }
90 |
91 | @Override
92 | public void onDraw() {
93 | if (snappyDragHelper == null && imageView.getDrawable() != null) {
94 | updateSize();
95 | }
96 | }
97 | });
98 | }
99 |
100 | public void addOnClosedListener(OnClosedListener listener) {
101 | onClosedListeners.add(listener);
102 | }
103 |
104 | public void setImageBitmap(Bitmap bitmap) {
105 | imageView.setImageBitmap(bitmap);
106 | }
107 |
108 | public void setImageResource(int resId) {
109 | imageView.setImageResource(resId);
110 | }
111 |
112 | public void setImageURI(Uri uri) {
113 | imageView.setImageURI(uri);
114 | }
115 |
116 | public void setImageDrawable(Drawable drawable) {
117 | imageView.setImageDrawable(drawable);
118 | }
119 |
120 | public ImageView getImageView() {
121 | return imageView;
122 | }
123 |
124 | public void updateSize() {
125 | if (imageView.getDrawable() == null) {
126 | return;
127 | }
128 |
129 | float imageWidth = imageView.getDrawable().getIntrinsicWidth();
130 | float imageHeight = imageView.getDrawable().getIntrinsicHeight();
131 |
132 | float viewWidth = getWidth() - getPaddingLeft() - getPaddingRight();
133 | float viewHeight = getHeight() - getPaddingTop() - getPaddingBottom();
134 | float widthRatio = viewWidth / imageWidth;
135 | float heightRatio = viewHeight / imageHeight;
136 |
137 | float scale = widthRatio < heightRatio ? widthRatio : heightRatio;
138 | float imgWidth = imageWidth * scale;
139 | float imgHeight = imageHeight * scale;
140 |
141 | float x = (viewWidth - imgWidth) / 2f;
142 | float y = (viewHeight - imgHeight) / 2f;
143 |
144 | initialImageRect.set(x, y, x + imgWidth, y + imgHeight);
145 | imgInitMatrix.reset();
146 | imgInitMatrix.postScale(scale, scale);
147 | imgInitMatrix.postTranslate(x, y);
148 |
149 | if (snappyDragHelper != null) {
150 | snappyDragHelper.cancelAnimation();
151 | }
152 |
153 | snappyDragHelper = new SnappyDragHelper(getWidth(), getHeight(), (int) imageWidth, (int) imageHeight, imgInitMatrix,
154 | new SnappyDragHelper.Listener() {
155 | @Override
156 | public void onMove(Matrix bmpMatrix) {
157 | imageView.setImageMatrix(bmpMatrix);
158 | }
159 |
160 | @Override
161 | public void onRestored() {
162 | setState(STATE_IDLE);
163 | }
164 |
165 | @Override
166 | public void onCastAwayed() {
167 | setState(STATE_CLOSED);
168 | notifyClosed();
169 | }
170 | });
171 | imageView.setImageMatrix(imgInitMatrix);
172 | }
173 |
174 | @Override
175 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
176 | super.onSizeChanged(w, h, oldw, oldh);
177 | updateSize();
178 | }
179 |
180 | @Override
181 | public boolean onInterceptTouchEvent(MotionEvent event) {
182 | return true;
183 | }
184 |
185 | @Override
186 | public boolean onTouchEvent(MotionEvent event) {
187 | super.onTouchEvent(event);
188 |
189 | handleTouchEventForDrag(event);
190 |
191 | return true;
192 | }
193 |
194 | private void handleTouchEventForDrag(MotionEvent event) {
195 | if (imageView.getDrawable() == null) {
196 | return;
197 | }
198 | if (velocityTracker == null) {
199 | velocityTracker = VelocityTracker.obtain();
200 | }
201 | velocityTracker.addMovement(event);
202 |
203 | switch (event.getAction()) {
204 | case MotionEvent.ACTION_DOWN: {
205 | activePointerId = event.getPointerId(event.getActionIndex());
206 | currentX = event.getX();
207 | currentY = event.getY();
208 | snappyDragHelper.onTouch();
209 | break;
210 | }
211 |
212 | case MotionEvent.ACTION_MOVE: {
213 | float x = event.getX();
214 | float y = event.getY();
215 |
216 | if (activePointerId == event.getPointerId(event.getActionIndex())) {
217 | if ((state == STATE_IDLE && inImageView(x, y)) || state == STATE_DRAGGING) {
218 | snappyDragHelper.onMove(currentX, currentY, x, y);
219 | setState(STATE_DRAGGING);
220 | }
221 |
222 | currentX = x;
223 | currentY = y;
224 | }
225 | break;
226 | }
227 |
228 | case MotionEvent.ACTION_POINTER_UP:
229 | case MotionEvent.ACTION_UP:
230 | case MotionEvent.ACTION_CANCEL:
231 | if (activePointerId == event.getPointerId(event.getActionIndex())) {
232 | int xvel = 0;
233 | int yvel = 0;
234 | if (velocityTracker != null) {
235 | velocityTracker.computeCurrentVelocity(1000, maxVelocity);
236 | xvel = (int) velocityTracker.getXVelocity(activePointerId);
237 | yvel = (int) velocityTracker.getYVelocity(activePointerId);
238 |
239 | velocityTracker.recycle();
240 | velocityTracker = null;
241 | }
242 | if (state == STATE_DRAGGING) {
243 | snappyDragHelper.onRelease(xvel, yvel);
244 | setState(STATE_SETTLING);
245 | }
246 | activePointerId = INVALID_POINTER;
247 | }
248 | break;
249 | }
250 | }
251 |
252 | private boolean inImageView(float x, float y) {
253 | return initialImageRect.left <= x && x <= initialImageRect.right
254 | && initialImageRect.top <= y && y <= initialImageRect.bottom;
255 | }
256 |
257 | private void setState(int state) {
258 | this.state = state;
259 | }
260 |
261 | private void notifyClosed() {
262 | for (OnClosedListener listener : onClosedListeners) {
263 | listener.onClosed();
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library', ':demo'
2 |
--------------------------------------------------------------------------------