├── .idea ├── .name ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── vcs.xml ├── libraries │ ├── junit_4_12.xml │ ├── glide_3_6_1.xml │ ├── hamcrest_core_1_3.xml │ ├── support_annotations_23_0_1.xml │ ├── support_annotations_23_1_1.xml │ ├── appcompat_v7_23_1_1.xml │ ├── support_v4_23_1_1.xml │ └── support_v4_23_0_1.xml ├── modules.xml ├── runConfigurations.xml ├── compiler.xml ├── gradle.xml └── misc.xml ├── app ├── .gitignore ├── jarvan_180x320.gif ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.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 │ │ │ └── layout │ │ │ │ ├── fragment_picture_slide.xml │ │ │ │ └── activity_pic_viewer.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── trojx │ │ │ └── mytouchgallery │ │ │ ├── PictureSlideFragment.java │ │ │ └── PicViewerActivity.java │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── trojx │ │ │ └── mytouchgallery │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── trojx │ │ └── mytouchgallery │ │ └── ApplicationTest.java ├── proguard-rules.pro ├── build.gradle └── app.iml ├── settings.gradle ├── PhotoViewlibrary ├── gradle.properties ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── uk │ │ └── co │ │ └── senab │ │ └── photoview │ │ ├── gestures │ │ ├── OnGestureListener.java │ │ ├── GestureDetector.java │ │ ├── VersionedGestureDetector.java │ │ ├── FroyoGestureDetector.java │ │ ├── EclairGestureDetector.java │ │ └── CupcakeGestureDetector.java │ │ ├── scrollerproxy │ │ ├── IcsScroller.java │ │ ├── ScrollerProxy.java │ │ ├── PreGingerScroller.java │ │ └── GingerScroller.java │ │ ├── log │ │ ├── LogManager.java │ │ ├── Logger.java │ │ └── LoggerDefault.java │ │ ├── Compat.java │ │ ├── DefaultOnDoubleTapListener.java │ │ ├── PhotoView.java │ │ ├── IPhotoView.java │ │ └── PhotoViewAttacher.java ├── build.gradle ├── library.iml ├── PhotoViewlibrary.iml └── LICENSE ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── .gitignore ├── gradle.properties ├── MyTouchGallery.iml ├── gradlew.bat ├── gradlew └── LICENSE /.idea/.name: -------------------------------------------------------------------------------- 1 | MyTouchGallery -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':PhotoViewlibrary' 2 | -------------------------------------------------------------------------------- /app/jarvan_180x320.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JianxunRao/MyTouchGallery/HEAD/app/jarvan_180x320.gif -------------------------------------------------------------------------------- /PhotoViewlibrary/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=PhotoView Library 2 | POM_ARTIFACT_ID=library 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MyTouchGallery 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JianxunRao/MyTouchGallery/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###Simple TouchGallery demo based on ViewPager and Photoview. 2 | ![](http://7xla0x.com1.z0.glb.clouddn.com/picjarvan_180x320.gif) -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JianxunRao/MyTouchGallery/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JianxunRao/MyTouchGallery/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JianxunRao/MyTouchGallery/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JianxunRao/MyTouchGallery/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JianxunRao/MyTouchGallery/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 28 10:29:07 CST 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.10-all.zip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | -------------------------------------------------------------------------------- /app/src/test/java/com/trojx/mytouchgallery/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.trojx.mytouchgallery; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/trojx/mytouchgallery/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.trojx.mytouchgallery; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /.idea/libraries/junit_4_12.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_picture_slide.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/glide_3_6_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_core_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_23_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_23_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\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 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.trojx.mytouchgallery" 9 | minSdkVersion 17 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.1.1' 26 | compile project(':PhotoViewlibrary') 27 | compile 'com.github.bumptech.glide:glide:3.6.1' 28 | } 29 | -------------------------------------------------------------------------------- /.idea/libraries/appcompat_v7_23_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pic_viewer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /.idea/libraries/support_v4_23_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /MyTouchGallery.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_23_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /PhotoViewlibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | //apply plugin: 'com.novoda.bintray-release' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion '23.0.1' 7 | 8 | defaultConfig { 9 | minSdkVersion 4 10 | targetSdkVersion 23 11 | versionCode 124 12 | versionName "1.2.4" 13 | } 14 | } 15 | 16 | dependencies { 17 | compile "com.android.support:support-v4:23.0.1" 18 | } 19 | 20 | //./gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=false 21 | //publish { 22 | // userOrg = 'commit451' 23 | // groupId = 'com.commit451' 24 | // artifactId = 'PhotoView' 25 | // version = '1.2.4' 26 | // description = 'Implementation of ImageView for Android that supports zooming, by various touch gestures.' 27 | // website = 'https://github.com/chrisbanes/PhotoView' 28 | // issueTracker = "https://github.com/chrisbanes/PhotoView/issues" 29 | // repository = "https://github.com/chrisbanes/PhotoView.git" 30 | //} 31 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 27 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/gestures/OnGestureListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.gestures; 17 | 18 | public interface OnGestureListener { 19 | 20 | public void onDrag(float dx, float dy); 21 | 22 | public void onFling(float startX, float startY, float velocityX, 23 | float velocityY); 24 | 25 | public void onScale(float scaleFactor, float focusX, float focusY); 26 | 27 | } -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/gestures/GestureDetector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.gestures; 17 | 18 | import android.view.MotionEvent; 19 | 20 | public interface GestureDetector { 21 | 22 | public boolean onTouchEvent(MotionEvent ev); 23 | 24 | public boolean isScaling(); 25 | 26 | public boolean isDragging(); 27 | 28 | public void setOnGestureListener(OnGestureListener listener); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/scrollerproxy/IcsScroller.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.scrollerproxy; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | 21 | @TargetApi(14) 22 | public class IcsScroller extends GingerScroller { 23 | 24 | public IcsScroller(Context context) { 25 | super(context); 26 | } 27 | 28 | @Override 29 | public boolean computeScrollOffset() { 30 | return mScroller.computeScrollOffset(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/log/LogManager.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.log; 17 | 18 | import android.util.Log; 19 | 20 | /** 21 | * class that holds the {@link Logger} for this library, defaults to {@link LoggerDefault} to send logs to android {@link Log} 22 | */ 23 | public final class LogManager { 24 | 25 | private static Logger logger = new LoggerDefault(); 26 | 27 | public static void setLogger(Logger newLogger) { 28 | logger = newLogger; 29 | } 30 | 31 | public static Logger getLogger() { 32 | return logger; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/log/Logger.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.log; 17 | 18 | /** 19 | * interface for a logger class to replace the static calls to {@link android.util.Log} 20 | */ 21 | public interface Logger { 22 | 23 | int v(String tag, String msg); 24 | 25 | int v(String tag, String msg, Throwable tr); 26 | 27 | int d(String tag, String msg); 28 | 29 | int d(String tag, String msg, Throwable tr); 30 | 31 | int i(String tag, String msg); 32 | 33 | int i(String tag, String msg, Throwable tr); 34 | 35 | int w(String tag, String msg); 36 | 37 | int w(String tag, String msg, Throwable tr); 38 | 39 | int e(String tag, String msg); 40 | 41 | int e(String tag, String msg, Throwable tr); 42 | } 43 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/gestures/VersionedGestureDetector.java: -------------------------------------------------------------------------------- 1 | package uk.co.senab.photoview.gestures; 2 | 3 | /******************************************************************************* 4 | * Copyright 2011, 2012 Chris Banes. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | *******************************************************************************/ 18 | 19 | import android.content.Context; 20 | import android.os.Build; 21 | 22 | public final class VersionedGestureDetector { 23 | 24 | public static GestureDetector newInstance(Context context, 25 | OnGestureListener listener) { 26 | final int sdkVersion = Build.VERSION.SDK_INT; 27 | GestureDetector detector; 28 | 29 | if (sdkVersion < Build.VERSION_CODES.ECLAIR) { 30 | detector = new CupcakeGestureDetector(context); 31 | } else if (sdkVersion < Build.VERSION_CODES.FROYO) { 32 | detector = new EclairGestureDetector(context); 33 | } else { 34 | detector = new FroyoGestureDetector(context); 35 | } 36 | 37 | detector.setOnGestureListener(listener); 38 | 39 | return detector; 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/scrollerproxy/ScrollerProxy.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.scrollerproxy; 17 | 18 | import android.content.Context; 19 | import android.os.Build.VERSION; 20 | import android.os.Build.VERSION_CODES; 21 | 22 | public abstract class ScrollerProxy { 23 | 24 | public static ScrollerProxy getScroller(Context context) { 25 | if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) { 26 | return new PreGingerScroller(context); 27 | } else if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) { 28 | return new GingerScroller(context); 29 | } else { 30 | return new IcsScroller(context); 31 | } 32 | } 33 | 34 | public abstract boolean computeScrollOffset(); 35 | 36 | public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, 37 | int maxY, int overX, int overY); 38 | 39 | public abstract void forceFinished(boolean finished); 40 | 41 | public abstract boolean isFinished(); 42 | 43 | public abstract int getCurrX(); 44 | 45 | public abstract int getCurrY(); 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/scrollerproxy/PreGingerScroller.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.scrollerproxy; 17 | 18 | import android.content.Context; 19 | import android.widget.Scroller; 20 | 21 | public class PreGingerScroller extends ScrollerProxy { 22 | 23 | private final Scroller mScroller; 24 | 25 | public PreGingerScroller(Context context) { 26 | mScroller = new Scroller(context); 27 | } 28 | 29 | @Override 30 | public boolean computeScrollOffset() { 31 | return mScroller.computeScrollOffset(); 32 | } 33 | 34 | @Override 35 | public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, 36 | int overX, int overY) { 37 | mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY); 38 | } 39 | 40 | @Override 41 | public void forceFinished(boolean finished) { 42 | mScroller.forceFinished(finished); 43 | } 44 | 45 | public boolean isFinished() { 46 | return mScroller.isFinished(); 47 | } 48 | 49 | @Override 50 | public int getCurrX() { 51 | return mScroller.getCurrX(); 52 | } 53 | 54 | @Override 55 | public int getCurrY() { 56 | return mScroller.getCurrY(); 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/trojx/mytouchgallery/PictureSlideFragment.java: -------------------------------------------------------------------------------- 1 | package com.trojx.mytouchgallery; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | 11 | import com.bumptech.glide.Glide; 12 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 13 | import com.bumptech.glide.request.animation.GlideAnimation; 14 | import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; 15 | 16 | import uk.co.senab.photoview.PhotoViewAttacher; 17 | 18 | /** 19 | * Created by Administrator on 2016/1/3. 20 | */ 21 | public class PictureSlideFragment extends Fragment { 22 | private String url; 23 | private PhotoViewAttacher mAttacher; 24 | private ImageView imageView; 25 | 26 | public static PictureSlideFragment newInstance(String url) { 27 | PictureSlideFragment f = new PictureSlideFragment(); 28 | 29 | Bundle args = new Bundle(); 30 | args.putString("url", url); 31 | f.setArguments(args); 32 | 33 | return f; 34 | } 35 | 36 | @Override 37 | public void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | url = getArguments() != null ? getArguments().getString("url") : "http://www.zhagame.com/wp-content/uploads/2016/01/JarvanIV_6.jpg"; 40 | 41 | } 42 | 43 | @Nullable 44 | @Override 45 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 46 | View v=inflater.inflate(R.layout.fragment_picture_slide,container,false); 47 | 48 | imageView= (ImageView) v.findViewById(R.id.iv_main_pic); 49 | mAttacher = new PhotoViewAttacher(imageView); 50 | 51 | Glide.with(getActivity()).load(url).crossFade().into(new GlideDrawableImageViewTarget(imageView) { 52 | @Override 53 | public void onResourceReady(GlideDrawable resource, GlideAnimation animation) { 54 | super.onResourceReady(resource, animation); 55 | mAttacher.update(); 56 | } 57 | }); 58 | return v; 59 | } 60 | 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/log/LoggerDefault.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.log; 17 | 18 | import android.util.Log; 19 | 20 | /** 21 | * Helper class to redirect {@link LogManager#logger} to {@link Log} 22 | */ 23 | public class LoggerDefault implements Logger { 24 | 25 | @Override 26 | public int v(String tag, String msg) { 27 | return Log.v(tag, msg); 28 | } 29 | 30 | @Override 31 | public int v(String tag, String msg, Throwable tr) { 32 | return Log.v(tag, msg, tr); 33 | } 34 | 35 | @Override 36 | public int d(String tag, String msg) { 37 | return Log.d(tag, msg); 38 | } 39 | 40 | @Override 41 | public int d(String tag, String msg, Throwable tr) { 42 | return Log.d(tag, msg, tr); 43 | } 44 | 45 | @Override 46 | public int i(String tag, String msg) { 47 | return Log.i(tag, msg); 48 | } 49 | 50 | @Override 51 | public int i(String tag, String msg, Throwable tr) { 52 | return Log.i(tag, msg, tr); 53 | } 54 | 55 | @Override 56 | public int w(String tag, String msg) { 57 | return Log.w(tag, msg); 58 | } 59 | 60 | @Override 61 | public int w(String tag, String msg, Throwable tr) { 62 | return Log.w(tag, msg, tr); 63 | } 64 | 65 | @Override 66 | public int e(String tag, String msg) { 67 | return Log.e(tag, msg); 68 | } 69 | 70 | @Override 71 | public int e(String tag, String msg, Throwable tr) { 72 | return Log.e(tag, msg, tr); 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/Compat.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview; 17 | 18 | import android.annotation.TargetApi; 19 | import android.os.Build; 20 | import android.os.Build.VERSION; 21 | import android.os.Build.VERSION_CODES; 22 | import android.view.MotionEvent; 23 | import android.view.View; 24 | 25 | public class Compat { 26 | 27 | private static final int SIXTY_FPS_INTERVAL = 1000 / 60; 28 | 29 | public static void postOnAnimation(View view, Runnable runnable) { 30 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { 31 | postOnAnimationJellyBean(view, runnable); 32 | } else { 33 | view.postDelayed(runnable, SIXTY_FPS_INTERVAL); 34 | } 35 | } 36 | 37 | @TargetApi(16) 38 | private static void postOnAnimationJellyBean(View view, Runnable runnable) { 39 | view.postOnAnimation(runnable); 40 | } 41 | 42 | public static int getPointerIndex(int action) { 43 | if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) 44 | return getPointerIndexHoneyComb(action); 45 | else 46 | return getPointerIndexEclair(action); 47 | } 48 | 49 | @SuppressWarnings("deprecation") 50 | @TargetApi(Build.VERSION_CODES.ECLAIR) 51 | private static int getPointerIndexEclair(int action) { 52 | return (action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; 53 | } 54 | 55 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 56 | private static int getPointerIndexHoneyComb(int action) { 57 | return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/scrollerproxy/GingerScroller.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.scrollerproxy; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.widget.OverScroller; 21 | 22 | @TargetApi(9) 23 | public class GingerScroller extends ScrollerProxy { 24 | 25 | protected final OverScroller mScroller; 26 | private boolean mFirstScroll = false; 27 | 28 | public GingerScroller(Context context) { 29 | mScroller = new OverScroller(context); 30 | } 31 | 32 | @Override 33 | public boolean computeScrollOffset() { 34 | // Workaround for first scroll returning 0 for the direction of the edge it hits. 35 | // Simply recompute values. 36 | if (mFirstScroll) { 37 | mScroller.computeScrollOffset(); 38 | mFirstScroll = false; 39 | } 40 | return mScroller.computeScrollOffset(); 41 | } 42 | 43 | @Override 44 | public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, 45 | int overX, int overY) { 46 | mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY); 47 | } 48 | 49 | @Override 50 | public void forceFinished(boolean finished) { 51 | mScroller.forceFinished(finished); 52 | } 53 | 54 | @Override 55 | public boolean isFinished() { 56 | return mScroller.isFinished(); 57 | } 58 | 59 | @Override 60 | public int getCurrX() { 61 | return mScroller.getCurrX(); 62 | } 63 | 64 | @Override 65 | public int getCurrY() { 66 | return mScroller.getCurrY(); 67 | } 68 | } -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/gestures/FroyoGestureDetector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.gestures; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.view.MotionEvent; 21 | import android.view.ScaleGestureDetector; 22 | 23 | @TargetApi(8) 24 | public class FroyoGestureDetector extends EclairGestureDetector { 25 | 26 | protected final ScaleGestureDetector mDetector; 27 | 28 | public FroyoGestureDetector(Context context) { 29 | super(context); 30 | ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() { 31 | 32 | @Override 33 | public boolean onScale(ScaleGestureDetector detector) { 34 | float scaleFactor = detector.getScaleFactor(); 35 | 36 | if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) 37 | return false; 38 | 39 | mListener.onScale(scaleFactor, 40 | detector.getFocusX(), detector.getFocusY()); 41 | return true; 42 | } 43 | 44 | @Override 45 | public boolean onScaleBegin(ScaleGestureDetector detector) { 46 | return true; 47 | } 48 | 49 | @Override 50 | public void onScaleEnd(ScaleGestureDetector detector) { 51 | // NO-OP 52 | } 53 | }; 54 | mDetector = new ScaleGestureDetector(context, mScaleListener); 55 | } 56 | 57 | @Override 58 | public boolean isScaling() { 59 | return mDetector.isInProgress(); 60 | } 61 | 62 | @Override 63 | public boolean onTouchEvent(MotionEvent ev) { 64 | mDetector.onTouchEvent(ev); 65 | return super.onTouchEvent(ev); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/trojx/mytouchgallery/PicViewerActivity.java: -------------------------------------------------------------------------------- 1 | package com.trojx.mytouchgallery; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentStatePagerAdapter; 7 | import android.support.v4.view.ViewPager; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.widget.TextView; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | 14 | /**Simple TouchGallery demo based on ViewPager and Photoview. 15 | * Created by Trojx on 2016/1/3. 16 | */ 17 | public class PicViewerActivity extends AppCompatActivity { 18 | 19 | private ViewPager viewPager; 20 | private TextView tv_indicator; 21 | private ArrayList urlList; 22 | 23 | @Override 24 | public void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_pic_viewer); 27 | 28 | String [] urls={"http://7xla0x.com1.z0.glb.clouddn.com/picJarvanIV_0.jpg", 29 | "http://7xla0x.com1.z0.glb.clouddn.com/picJarvanIV_1.jpg", 30 | "http://7xla0x.com1.z0.glb.clouddn.com/picJarvanIV_2.jpg", 31 | "http://7xla0x.com1.z0.glb.clouddn.com/picJarvanIV_3.jpg", 32 | "http://7xla0x.com1.z0.glb.clouddn.com/picJarvanIV_4.jpg", 33 | "http://7xla0x.com1.z0.glb.clouddn.com/picJarvanIV_5.jpg", 34 | "http://7xla0x.com1.z0.glb.clouddn.com/picJarvanIV_6.jpg",}; 35 | 36 | urlList = new ArrayList<>(); 37 | Collections.addAll(urlList, urls); 38 | 39 | viewPager = (ViewPager) findViewById(R.id.viewpager); 40 | tv_indicator = (TextView) findViewById(R.id.tv_indicator); 41 | 42 | viewPager.setAdapter(new PictureSlidePagerAdapter(getSupportFragmentManager())); 43 | viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 44 | @Override 45 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 46 | tv_indicator.setText(String.valueOf(position+1)+"/"+urlList.size()); 47 | } 48 | 49 | @Override 50 | public void onPageSelected(int position) { 51 | } 52 | 53 | @Override 54 | public void onPageScrollStateChanged(int state) { 55 | 56 | } 57 | }); 58 | 59 | } 60 | private class PictureSlidePagerAdapter extends FragmentStatePagerAdapter{ 61 | 62 | public PictureSlidePagerAdapter(FragmentManager fm) { 63 | super(fm); 64 | } 65 | 66 | @Override 67 | public Fragment getItem(int position) { 68 | return PictureSlideFragment.newInstance(urlList.get(position)); 69 | } 70 | 71 | @Override 72 | public int getCount() { 73 | return urlList.size(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/gestures/EclairGestureDetector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.gestures; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.view.MotionEvent; 21 | 22 | import uk.co.senab.photoview.Compat; 23 | 24 | @TargetApi(5) 25 | public class EclairGestureDetector extends CupcakeGestureDetector { 26 | 27 | private static final int INVALID_POINTER_ID = -1; 28 | private int mActivePointerId = INVALID_POINTER_ID; 29 | private int mActivePointerIndex = 0; 30 | 31 | public EclairGestureDetector(Context context) { 32 | super(context); 33 | } 34 | 35 | @Override 36 | float getActiveX(MotionEvent ev) { 37 | try { 38 | return ev.getX(mActivePointerIndex); 39 | } catch (Exception e) { 40 | return ev.getX(); 41 | } 42 | } 43 | 44 | @Override 45 | float getActiveY(MotionEvent ev) { 46 | try { 47 | return ev.getY(mActivePointerIndex); 48 | } catch (Exception e) { 49 | return ev.getY(); 50 | } 51 | } 52 | 53 | @Override 54 | public boolean onTouchEvent(MotionEvent ev) { 55 | final int action = ev.getAction(); 56 | switch (action & MotionEvent.ACTION_MASK) { 57 | case MotionEvent.ACTION_DOWN: 58 | mActivePointerId = ev.getPointerId(0); 59 | break; 60 | case MotionEvent.ACTION_CANCEL: 61 | case MotionEvent.ACTION_UP: 62 | mActivePointerId = INVALID_POINTER_ID; 63 | break; 64 | case MotionEvent.ACTION_POINTER_UP: 65 | // Ignore deprecation, ACTION_POINTER_ID_MASK and 66 | // ACTION_POINTER_ID_SHIFT has same value and are deprecated 67 | // You can have either deprecation or lint target api warning 68 | final int pointerIndex = Compat.getPointerIndex(ev.getAction()); 69 | final int pointerId = ev.getPointerId(pointerIndex); 70 | if (pointerId == mActivePointerId) { 71 | // This was our active pointer going up. Choose a new 72 | // active pointer and adjust accordingly. 73 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 74 | mActivePointerId = ev.getPointerId(newPointerIndex); 75 | mLastTouchX = ev.getX(newPointerIndex); 76 | mLastTouchY = ev.getY(newPointerIndex); 77 | } 78 | break; 79 | } 80 | 81 | mActivePointerIndex = ev 82 | .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId 83 | : 0); 84 | return super.onTouchEvent(ev); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/DefaultOnDoubleTapListener.java: -------------------------------------------------------------------------------- 1 | package uk.co.senab.photoview; 2 | 3 | import android.graphics.RectF; 4 | import android.view.GestureDetector; 5 | import android.view.MotionEvent; 6 | import android.widget.ImageView; 7 | 8 | /** 9 | * Provided default implementation of GestureDetector.OnDoubleTapListener, to be overriden with custom behavior, if needed 10 | *

 

11 | * To be used via {@link uk.co.senab.photoview.PhotoViewAttacher#setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener)} 12 | */ 13 | public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener { 14 | 15 | private PhotoViewAttacher photoViewAttacher; 16 | 17 | /** 18 | * Default constructor 19 | * 20 | * @param photoViewAttacher PhotoViewAttacher to bind to 21 | */ 22 | public DefaultOnDoubleTapListener(PhotoViewAttacher photoViewAttacher) { 23 | setPhotoViewAttacher(photoViewAttacher); 24 | } 25 | 26 | /** 27 | * Allows to change PhotoViewAttacher within range of single instance 28 | * 29 | * @param newPhotoViewAttacher PhotoViewAttacher to bind to 30 | */ 31 | public void setPhotoViewAttacher(PhotoViewAttacher newPhotoViewAttacher) { 32 | this.photoViewAttacher = newPhotoViewAttacher; 33 | } 34 | 35 | @Override 36 | public boolean onSingleTapConfirmed(MotionEvent e) { 37 | if (this.photoViewAttacher == null) 38 | return false; 39 | 40 | ImageView imageView = photoViewAttacher.getImageView(); 41 | 42 | if (null != photoViewAttacher.getOnPhotoTapListener()) { 43 | final RectF displayRect = photoViewAttacher.getDisplayRect(); 44 | 45 | if (null != displayRect) { 46 | final float x = e.getX(), y = e.getY(); 47 | 48 | // Check to see if the user tapped on the photo 49 | if (displayRect.contains(x, y)) { 50 | 51 | float xResult = (x - displayRect.left) 52 | / displayRect.width(); 53 | float yResult = (y - displayRect.top) 54 | / displayRect.height(); 55 | 56 | photoViewAttacher.getOnPhotoTapListener().onPhotoTap(imageView, xResult, yResult); 57 | return true; 58 | } 59 | } 60 | } 61 | if (null != photoViewAttacher.getOnViewTapListener()) { 62 | photoViewAttacher.getOnViewTapListener().onViewTap(imageView, e.getX(), e.getY()); 63 | } 64 | 65 | return false; 66 | } 67 | 68 | @Override 69 | public boolean onDoubleTap(MotionEvent ev) { 70 | if (photoViewAttacher == null) 71 | return false; 72 | 73 | try { 74 | float scale = photoViewAttacher.getScale(); 75 | float x = ev.getX(); 76 | float y = ev.getY(); 77 | 78 | if (scale < photoViewAttacher.getMediumScale()) { 79 | photoViewAttacher.setScale(photoViewAttacher.getMediumScale(), x, y, true); 80 | } else if (scale >= photoViewAttacher.getMediumScale() && scale < photoViewAttacher.getMaximumScale()) { 81 | photoViewAttacher.setScale(photoViewAttacher.getMaximumScale(), x, y, true); 82 | } else { 83 | photoViewAttacher.setScale(photoViewAttacher.getMinimumScale(), x, y, true); 84 | } 85 | } catch (ArrayIndexOutOfBoundsException e) { 86 | // Can sometimes happen when getX() and getY() is called 87 | } 88 | 89 | return true; 90 | } 91 | 92 | @Override 93 | public boolean onDoubleTapEvent(MotionEvent e) { 94 | // Wait for the confirmed onDoubleTap() instead 95 | return false; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/gestures/CupcakeGestureDetector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview.gestures; 17 | 18 | import android.content.Context; 19 | import android.view.MotionEvent; 20 | import android.view.VelocityTracker; 21 | import android.view.ViewConfiguration; 22 | 23 | import uk.co.senab.photoview.log.LogManager; 24 | 25 | public class CupcakeGestureDetector implements GestureDetector { 26 | 27 | protected OnGestureListener mListener; 28 | private static final String LOG_TAG = "CupcakeGestureDetector"; 29 | float mLastTouchX; 30 | float mLastTouchY; 31 | final float mTouchSlop; 32 | final float mMinimumVelocity; 33 | 34 | @Override 35 | public void setOnGestureListener(OnGestureListener listener) { 36 | this.mListener = listener; 37 | } 38 | 39 | public CupcakeGestureDetector(Context context) { 40 | final ViewConfiguration configuration = ViewConfiguration 41 | .get(context); 42 | mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 43 | mTouchSlop = configuration.getScaledTouchSlop(); 44 | } 45 | 46 | private VelocityTracker mVelocityTracker; 47 | private boolean mIsDragging; 48 | 49 | float getActiveX(MotionEvent ev) { 50 | return ev.getX(); 51 | } 52 | 53 | float getActiveY(MotionEvent ev) { 54 | return ev.getY(); 55 | } 56 | 57 | public boolean isScaling() { 58 | return false; 59 | } 60 | 61 | public boolean isDragging() { 62 | return mIsDragging; 63 | } 64 | 65 | @Override 66 | public boolean onTouchEvent(MotionEvent ev) { 67 | switch (ev.getAction()) { 68 | case MotionEvent.ACTION_DOWN: { 69 | mVelocityTracker = VelocityTracker.obtain(); 70 | if (null != mVelocityTracker) { 71 | mVelocityTracker.addMovement(ev); 72 | } else { 73 | LogManager.getLogger().i(LOG_TAG, "Velocity tracker is null"); 74 | } 75 | 76 | mLastTouchX = getActiveX(ev); 77 | mLastTouchY = getActiveY(ev); 78 | mIsDragging = false; 79 | break; 80 | } 81 | 82 | case MotionEvent.ACTION_MOVE: { 83 | final float x = getActiveX(ev); 84 | final float y = getActiveY(ev); 85 | final float dx = x - mLastTouchX, dy = y - mLastTouchY; 86 | 87 | if (!mIsDragging) { 88 | // Use Pythagoras to see if drag length is larger than 89 | // touch slop 90 | mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; 91 | } 92 | 93 | if (mIsDragging) { 94 | mListener.onDrag(dx, dy); 95 | mLastTouchX = x; 96 | mLastTouchY = y; 97 | 98 | if (null != mVelocityTracker) { 99 | mVelocityTracker.addMovement(ev); 100 | } 101 | } 102 | break; 103 | } 104 | 105 | case MotionEvent.ACTION_CANCEL: { 106 | // Recycle Velocity Tracker 107 | if (null != mVelocityTracker) { 108 | mVelocityTracker.recycle(); 109 | mVelocityTracker = null; 110 | } 111 | break; 112 | } 113 | 114 | case MotionEvent.ACTION_UP: { 115 | if (mIsDragging) { 116 | if (null != mVelocityTracker) { 117 | mLastTouchX = getActiveX(ev); 118 | mLastTouchY = getActiveY(ev); 119 | 120 | // Compute velocity within the last 1000ms 121 | mVelocityTracker.addMovement(ev); 122 | mVelocityTracker.computeCurrentVelocity(1000); 123 | 124 | final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker 125 | .getYVelocity(); 126 | 127 | // If the velocity is greater than minVelocity, call 128 | // listener 129 | if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) { 130 | mListener.onFling(mLastTouchX, mLastTouchY, -vX, 131 | -vY); 132 | } 133 | } 134 | } 135 | 136 | // Recycle Velocity Tracker 137 | if (null != mVelocityTracker) { 138 | mVelocityTracker.recycle(); 139 | mVelocityTracker = null; 140 | } 141 | break; 142 | } 143 | } 144 | 145 | return true; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /PhotoViewlibrary/library.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /PhotoViewlibrary/PhotoViewlibrary.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/PhotoView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview; 17 | 18 | import android.content.Context; 19 | import android.graphics.Bitmap; 20 | import android.graphics.Matrix; 21 | import android.graphics.RectF; 22 | import android.graphics.drawable.Drawable; 23 | import android.net.Uri; 24 | import android.util.AttributeSet; 25 | import android.view.GestureDetector; 26 | import android.widget.ImageView; 27 | 28 | import uk.co.senab.photoview.PhotoViewAttacher.OnMatrixChangedListener; 29 | import uk.co.senab.photoview.PhotoViewAttacher.OnPhotoTapListener; 30 | import uk.co.senab.photoview.PhotoViewAttacher.OnViewTapListener; 31 | 32 | public class PhotoView extends ImageView implements IPhotoView { 33 | 34 | private PhotoViewAttacher mAttacher; 35 | 36 | private ScaleType mPendingScaleType; 37 | 38 | public PhotoView(Context context) { 39 | this(context, null); 40 | } 41 | 42 | public PhotoView(Context context, AttributeSet attr) { 43 | this(context, attr, 0); 44 | } 45 | 46 | public PhotoView(Context context, AttributeSet attr, int defStyle) { 47 | super(context, attr, defStyle); 48 | super.setScaleType(ScaleType.MATRIX); 49 | init(); 50 | } 51 | 52 | protected void init() { 53 | if (null == mAttacher || null == mAttacher.getImageView()) { 54 | mAttacher = new PhotoViewAttacher(this); 55 | } 56 | 57 | if (null != mPendingScaleType) { 58 | setScaleType(mPendingScaleType); 59 | mPendingScaleType = null; 60 | } 61 | } 62 | 63 | /** 64 | * @deprecated use {@link #setRotationTo(float)} 65 | */ 66 | @Override 67 | public void setPhotoViewRotation(float rotationDegree) { 68 | mAttacher.setRotationTo(rotationDegree); 69 | } 70 | 71 | @Override 72 | public void setRotationTo(float rotationDegree) { 73 | mAttacher.setRotationTo(rotationDegree); 74 | } 75 | 76 | @Override 77 | public void setRotationBy(float rotationDegree) { 78 | mAttacher.setRotationBy(rotationDegree); 79 | } 80 | 81 | @Override 82 | public boolean canZoom() { 83 | return mAttacher.canZoom(); 84 | } 85 | 86 | @Override 87 | public RectF getDisplayRect() { 88 | return mAttacher.getDisplayRect(); 89 | } 90 | 91 | @Override 92 | public Matrix getDisplayMatrix() { 93 | return mAttacher.getDisplayMatrix(); 94 | } 95 | 96 | @Override 97 | public boolean setDisplayMatrix(Matrix finalRectangle) { 98 | return mAttacher.setDisplayMatrix(finalRectangle); 99 | } 100 | 101 | @Override 102 | @Deprecated 103 | public float getMinScale() { 104 | return getMinimumScale(); 105 | } 106 | 107 | @Override 108 | public float getMinimumScale() { 109 | return mAttacher.getMinimumScale(); 110 | } 111 | 112 | @Override 113 | @Deprecated 114 | public float getMidScale() { 115 | return getMediumScale(); 116 | } 117 | 118 | @Override 119 | public float getMediumScale() { 120 | return mAttacher.getMediumScale(); 121 | } 122 | 123 | @Override 124 | @Deprecated 125 | public float getMaxScale() { 126 | return getMaximumScale(); 127 | } 128 | 129 | @Override 130 | public float getMaximumScale() { 131 | return mAttacher.getMaximumScale(); 132 | } 133 | 134 | @Override 135 | public float getScale() { 136 | return mAttacher.getScale(); 137 | } 138 | 139 | @Override 140 | public ScaleType getScaleType() { 141 | return mAttacher.getScaleType(); 142 | } 143 | 144 | @Override 145 | public void setAllowParentInterceptOnEdge(boolean allow) { 146 | mAttacher.setAllowParentInterceptOnEdge(allow); 147 | } 148 | 149 | @Override 150 | @Deprecated 151 | public void setMinScale(float minScale) { 152 | setMinimumScale(minScale); 153 | } 154 | 155 | @Override 156 | public void setMinimumScale(float minimumScale) { 157 | mAttacher.setMinimumScale(minimumScale); 158 | } 159 | 160 | @Override 161 | @Deprecated 162 | public void setMidScale(float midScale) { 163 | setMediumScale(midScale); 164 | } 165 | 166 | @Override 167 | public void setMediumScale(float mediumScale) { 168 | mAttacher.setMediumScale(mediumScale); 169 | } 170 | 171 | @Override 172 | @Deprecated 173 | public void setMaxScale(float maxScale) { 174 | setMaximumScale(maxScale); 175 | } 176 | 177 | @Override 178 | public void setMaximumScale(float maximumScale) { 179 | mAttacher.setMaximumScale(maximumScale); 180 | } 181 | 182 | @Override 183 | public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) { 184 | mAttacher.setScaleLevels(minimumScale, mediumScale, maximumScale); 185 | } 186 | 187 | @Override 188 | // setImageBitmap calls through to this method 189 | public void setImageDrawable(Drawable drawable) { 190 | super.setImageDrawable(drawable); 191 | if (null != mAttacher) { 192 | mAttacher.update(); 193 | } 194 | } 195 | 196 | @Override 197 | public void setImageResource(int resId) { 198 | super.setImageResource(resId); 199 | if (null != mAttacher) { 200 | mAttacher.update(); 201 | } 202 | } 203 | 204 | @Override 205 | public void setImageURI(Uri uri) { 206 | super.setImageURI(uri); 207 | if (null != mAttacher) { 208 | mAttacher.update(); 209 | } 210 | } 211 | 212 | @Override 213 | public void setOnMatrixChangeListener(OnMatrixChangedListener listener) { 214 | mAttacher.setOnMatrixChangeListener(listener); 215 | } 216 | 217 | @Override 218 | public void setOnLongClickListener(OnLongClickListener l) { 219 | mAttacher.setOnLongClickListener(l); 220 | } 221 | 222 | @Override 223 | public void setOnPhotoTapListener(OnPhotoTapListener listener) { 224 | mAttacher.setOnPhotoTapListener(listener); 225 | } 226 | 227 | @Override 228 | public OnPhotoTapListener getOnPhotoTapListener() { 229 | return mAttacher.getOnPhotoTapListener(); 230 | } 231 | 232 | @Override 233 | public void setOnViewTapListener(OnViewTapListener listener) { 234 | mAttacher.setOnViewTapListener(listener); 235 | } 236 | 237 | @Override 238 | public OnViewTapListener getOnViewTapListener() { 239 | return mAttacher.getOnViewTapListener(); 240 | } 241 | 242 | @Override 243 | public void setScale(float scale) { 244 | mAttacher.setScale(scale); 245 | } 246 | 247 | @Override 248 | public void setScale(float scale, boolean animate) { 249 | mAttacher.setScale(scale, animate); 250 | } 251 | 252 | @Override 253 | public void setScale(float scale, float focalX, float focalY, boolean animate) { 254 | mAttacher.setScale(scale, focalX, focalY, animate); 255 | } 256 | 257 | @Override 258 | public void setScaleType(ScaleType scaleType) { 259 | if (null != mAttacher) { 260 | mAttacher.setScaleType(scaleType); 261 | } else { 262 | mPendingScaleType = scaleType; 263 | } 264 | } 265 | 266 | @Override 267 | public void setZoomable(boolean zoomable) { 268 | mAttacher.setZoomable(zoomable); 269 | } 270 | 271 | @Override 272 | public Bitmap getVisibleRectangleBitmap() { 273 | return mAttacher.getVisibleRectangleBitmap(); 274 | } 275 | 276 | @Override 277 | public void setZoomTransitionDuration(int milliseconds) { 278 | mAttacher.setZoomTransitionDuration(milliseconds); 279 | } 280 | 281 | @Override 282 | public IPhotoView getIPhotoViewImplementation() { 283 | return mAttacher; 284 | } 285 | 286 | @Override 287 | public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) { 288 | mAttacher.setOnDoubleTapListener(newOnDoubleTapListener); 289 | } 290 | 291 | @Override 292 | public void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener) { 293 | mAttacher.setOnScaleChangeListener(onScaleChangeListener); 294 | } 295 | 296 | @Override 297 | protected void onDetachedFromWindow() { 298 | mAttacher.cleanup(); 299 | super.onDetachedFromWindow(); 300 | } 301 | 302 | @Override 303 | protected void onAttachedToWindow() { 304 | init(); 305 | super.onAttachedToWindow(); 306 | } 307 | } -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /PhotoViewlibrary/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/IPhotoView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview; 17 | 18 | import android.graphics.Bitmap; 19 | import android.graphics.Matrix; 20 | import android.graphics.RectF; 21 | import android.view.GestureDetector; 22 | import android.view.View; 23 | import android.widget.ImageView; 24 | 25 | 26 | public interface IPhotoView { 27 | 28 | public static final float DEFAULT_MAX_SCALE = 3.0f; 29 | public static final float DEFAULT_MID_SCALE = 1.75f; 30 | public static final float DEFAULT_MIN_SCALE = 1.0f; 31 | public static final int DEFAULT_ZOOM_DURATION = 200; 32 | 33 | /** 34 | * Returns true if the PhotoView is set to allow zooming of Photos. 35 | * 36 | * @return true if the PhotoView allows zooming. 37 | */ 38 | boolean canZoom(); 39 | 40 | /** 41 | * Gets the Display Rectangle of the currently displayed Drawable. The Rectangle is relative to 42 | * this View and includes all scaling and translations. 43 | * 44 | * @return - RectF of Displayed Drawable 45 | */ 46 | RectF getDisplayRect(); 47 | 48 | /** 49 | * Sets the Display Matrix of the currently displayed Drawable. The Rectangle is considered 50 | * relative to this View and includes all scaling and translations. 51 | * 52 | * @param finalMatrix target matrix to set PhotoView to 53 | * @return - true if rectangle was applied successfully 54 | */ 55 | boolean setDisplayMatrix(Matrix finalMatrix); 56 | 57 | /** 58 | * Gets the Display Matrix of the currently displayed Drawable. The Rectangle is considered 59 | * relative to this View and includes all scaling and translations. 60 | * 61 | * @return - true if rectangle was applied successfully 62 | */ 63 | Matrix getDisplayMatrix(); 64 | 65 | /** 66 | * Use {@link #getMinimumScale()} instead, this will be removed in future release 67 | * 68 | * @return The current minimum scale level. What this value represents depends on the current 69 | * {@link android.widget.ImageView.ScaleType}. 70 | */ 71 | @Deprecated 72 | float getMinScale(); 73 | 74 | /** 75 | * @return The current minimum scale level. What this value represents depends on the current 76 | * {@link android.widget.ImageView.ScaleType}. 77 | */ 78 | float getMinimumScale(); 79 | 80 | /** 81 | * Use {@link #getMediumScale()} instead, this will be removed in future release 82 | * 83 | * @return The current middle scale level. What this value represents depends on the current 84 | * {@link android.widget.ImageView.ScaleType}. 85 | */ 86 | @Deprecated 87 | float getMidScale(); 88 | 89 | /** 90 | * @return The current medium scale level. What this value represents depends on the current 91 | * {@link android.widget.ImageView.ScaleType}. 92 | */ 93 | float getMediumScale(); 94 | 95 | /** 96 | * Use {@link #getMaximumScale()} instead, this will be removed in future release 97 | * 98 | * @return The current maximum scale level. What this value represents depends on the current 99 | * {@link android.widget.ImageView.ScaleType}. 100 | */ 101 | @Deprecated 102 | float getMaxScale(); 103 | 104 | /** 105 | * @return The current maximum scale level. What this value represents depends on the current 106 | * {@link android.widget.ImageView.ScaleType}. 107 | */ 108 | float getMaximumScale(); 109 | 110 | /** 111 | * Returns the current scale value 112 | * 113 | * @return float - current scale value 114 | */ 115 | float getScale(); 116 | 117 | /** 118 | * Return the current scale type in use by the ImageView. 119 | * 120 | * @return current ImageView.ScaleType 121 | */ 122 | ImageView.ScaleType getScaleType(); 123 | 124 | /** 125 | * Whether to allow the ImageView's parent to intercept the touch event when the photo is scroll 126 | * to it's horizontal edge. 127 | * 128 | * @param allow whether to allow intercepting by parent element or not 129 | */ 130 | void setAllowParentInterceptOnEdge(boolean allow); 131 | 132 | /** 133 | * Use {@link #setMinimumScale(float minimumScale)} instead, this will be removed in future 134 | * release 135 | *

 

136 | * Sets the minimum scale level. What this value represents depends on the current {@link 137 | * android.widget.ImageView.ScaleType}. 138 | * 139 | * @param minScale minimum allowed scale 140 | */ 141 | @Deprecated 142 | void setMinScale(float minScale); 143 | 144 | /** 145 | * Sets the minimum scale level. What this value represents depends on the current {@link 146 | * android.widget.ImageView.ScaleType}. 147 | * 148 | * @param minimumScale minimum allowed scale 149 | */ 150 | void setMinimumScale(float minimumScale); 151 | 152 | /** 153 | * Use {@link #setMediumScale(float mediumScale)} instead, this will be removed in future 154 | * release 155 | *

 

156 | * Sets the middle scale level. What this value represents depends on the current {@link 157 | * android.widget.ImageView.ScaleType}. 158 | * 159 | * @param midScale medium scale preset 160 | */ 161 | @Deprecated 162 | void setMidScale(float midScale); 163 | 164 | /* 165 | * Sets the medium scale level. What this value represents depends on the current {@link android.widget.ImageView.ScaleType}. 166 | * 167 | * @param mediumScale medium scale preset 168 | */ 169 | void setMediumScale(float mediumScale); 170 | 171 | /** 172 | * Use {@link #setMaximumScale(float maximumScale)} instead, this will be removed in future 173 | * release 174 | *

 

175 | * Sets the maximum scale level. What this value represents depends on the current {@link 176 | * android.widget.ImageView.ScaleType}. 177 | * 178 | * @param maxScale maximum allowed scale preset 179 | */ 180 | @Deprecated 181 | void setMaxScale(float maxScale); 182 | 183 | /** 184 | * Sets the maximum scale level. What this value represents depends on the current {@link 185 | * android.widget.ImageView.ScaleType}. 186 | * 187 | * @param maximumScale maximum allowed scale preset 188 | */ 189 | void setMaximumScale(float maximumScale); 190 | 191 | /** 192 | * Allows to set all three scale levels at once, so you don't run into problem with setting 193 | * medium/minimum scale before the maximum one 194 | * 195 | * @param minimumScale minimum allowed scale 196 | * @param mediumScale medium allowed scale 197 | * @param maximumScale maximum allowed scale preset 198 | */ 199 | void setScaleLevels(float minimumScale, float mediumScale, float maximumScale); 200 | 201 | /** 202 | * Register a callback to be invoked when the Photo displayed by this view is long-pressed. 203 | * 204 | * @param listener - Listener to be registered. 205 | */ 206 | void setOnLongClickListener(View.OnLongClickListener listener); 207 | 208 | /** 209 | * Register a callback to be invoked when the Matrix has changed for this View. An example would 210 | * be the user panning or scaling the Photo. 211 | * 212 | * @param listener - Listener to be registered. 213 | */ 214 | void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener listener); 215 | 216 | /** 217 | * Register a callback to be invoked when the Photo displayed by this View is tapped with a 218 | * single tap. 219 | * 220 | * @param listener - Listener to be registered. 221 | */ 222 | void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener listener); 223 | 224 | /** 225 | * Returns a listener to be invoked when the Photo displayed by this View is tapped with a 226 | * single tap. 227 | * 228 | * @return PhotoViewAttacher.OnPhotoTapListener currently set, may be null 229 | */ 230 | PhotoViewAttacher.OnPhotoTapListener getOnPhotoTapListener(); 231 | 232 | /** 233 | * Register a callback to be invoked when the View is tapped with a single tap. 234 | * 235 | * @param listener - Listener to be registered. 236 | */ 237 | void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener listener); 238 | 239 | /** 240 | * Enables rotation via PhotoView internal functions. 241 | * 242 | * @param rotationDegree - Degree to rotate PhotoView to, should be in range 0 to 360 243 | */ 244 | void setRotationTo(float rotationDegree); 245 | 246 | /** 247 | * Enables rotation via PhotoView internal functions. 248 | * 249 | * @param rotationDegree - Degree to rotate PhotoView by, should be in range 0 to 360 250 | */ 251 | void setRotationBy(float rotationDegree); 252 | 253 | /** 254 | * Returns a callback listener to be invoked when the View is tapped with a single tap. 255 | * 256 | * @return PhotoViewAttacher.OnViewTapListener currently set, may be null 257 | */ 258 | PhotoViewAttacher.OnViewTapListener getOnViewTapListener(); 259 | 260 | /** 261 | * Changes the current scale to the specified value. 262 | * 263 | * @param scale - Value to scale to 264 | */ 265 | void setScale(float scale); 266 | 267 | /** 268 | * Changes the current scale to the specified value. 269 | * 270 | * @param scale - Value to scale to 271 | * @param animate - Whether to animate the scale 272 | */ 273 | void setScale(float scale, boolean animate); 274 | 275 | /** 276 | * Changes the current scale to the specified value, around the given focal point. 277 | * 278 | * @param scale - Value to scale to 279 | * @param focalX - X Focus Point 280 | * @param focalY - Y Focus Point 281 | * @param animate - Whether to animate the scale 282 | */ 283 | void setScale(float scale, float focalX, float focalY, boolean animate); 284 | 285 | /** 286 | * Controls how the image should be resized or moved to match the size of the ImageView. Any 287 | * scaling or panning will happen within the confines of this {@link 288 | * android.widget.ImageView.ScaleType}. 289 | * 290 | * @param scaleType - The desired scaling mode. 291 | */ 292 | void setScaleType(ImageView.ScaleType scaleType); 293 | 294 | /** 295 | * Allows you to enable/disable the zoom functionality on the ImageView. When disable the 296 | * ImageView reverts to using the FIT_CENTER matrix. 297 | * 298 | * @param zoomable - Whether the zoom functionality is enabled. 299 | */ 300 | void setZoomable(boolean zoomable); 301 | 302 | /** 303 | * Enables rotation via PhotoView internal functions. Name is chosen so it won't collide with 304 | * View.setRotation(float) in API since 11 305 | * 306 | * @param rotationDegree - Degree to rotate PhotoView to, should be in range 0 to 360 307 | * @deprecated use {@link #setRotationTo(float)} 308 | */ 309 | void setPhotoViewRotation(float rotationDegree); 310 | 311 | /** 312 | * Extracts currently visible area to Bitmap object, if there is no image loaded yet or the 313 | * ImageView is already destroyed, returns {@code null} 314 | * 315 | * @return currently visible area as bitmap or null 316 | */ 317 | Bitmap getVisibleRectangleBitmap(); 318 | 319 | /** 320 | * Allows to change zoom transition speed, default value is 200 (PhotoViewAttacher.DEFAULT_ZOOM_DURATION). 321 | * Will default to 200 if provided negative value 322 | * 323 | * @param milliseconds duration of zoom interpolation 324 | */ 325 | void setZoomTransitionDuration(int milliseconds); 326 | 327 | /** 328 | * Will return instance of IPhotoView (eg. PhotoViewAttacher), can be used to provide better 329 | * integration 330 | * 331 | * @return IPhotoView implementation instance if available, null if not 332 | */ 333 | IPhotoView getIPhotoViewImplementation(); 334 | 335 | /** 336 | * Sets custom double tap listener, to intercept default given functions. To reset behavior to 337 | * default, you can just pass in "null" or public field of PhotoViewAttacher.defaultOnDoubleTapListener 338 | * 339 | * @param newOnDoubleTapListener custom OnDoubleTapListener to be set on ImageView 340 | */ 341 | void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener); 342 | 343 | /** 344 | * Will report back about scale changes 345 | * 346 | * @param onScaleChangeListener OnScaleChangeListener instance 347 | */ 348 | void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener); 349 | } 350 | -------------------------------------------------------------------------------- /PhotoViewlibrary/src/main/java/uk/co/senab/photoview/PhotoViewAttacher.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 Chris Banes. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package uk.co.senab.photoview; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.content.Context; 20 | import android.graphics.Bitmap; 21 | import android.graphics.Matrix; 22 | import android.graphics.Matrix.ScaleToFit; 23 | import android.graphics.RectF; 24 | import android.graphics.drawable.Drawable; 25 | import android.util.Log; 26 | import android.view.GestureDetector; 27 | import android.view.MotionEvent; 28 | import android.view.View; 29 | import android.view.View.OnLongClickListener; 30 | import android.view.ViewParent; 31 | import android.view.ViewTreeObserver; 32 | import android.view.animation.AccelerateDecelerateInterpolator; 33 | import android.view.animation.Interpolator; 34 | import android.widget.ImageView; 35 | import android.widget.ImageView.ScaleType; 36 | 37 | import java.lang.ref.WeakReference; 38 | 39 | import uk.co.senab.photoview.gestures.OnGestureListener; 40 | import uk.co.senab.photoview.gestures.VersionedGestureDetector; 41 | import uk.co.senab.photoview.log.LogManager; 42 | import uk.co.senab.photoview.scrollerproxy.ScrollerProxy; 43 | 44 | import static android.view.MotionEvent.ACTION_CANCEL; 45 | import static android.view.MotionEvent.ACTION_DOWN; 46 | import static android.view.MotionEvent.ACTION_UP; 47 | 48 | public class PhotoViewAttacher implements IPhotoView, View.OnTouchListener, 49 | OnGestureListener, 50 | ViewTreeObserver.OnGlobalLayoutListener { 51 | 52 | private static final String LOG_TAG = "PhotoViewAttacher"; 53 | 54 | // let debug flag be dynamic, but still Proguard can be used to remove from 55 | // release builds 56 | private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); 57 | 58 | static final Interpolator sInterpolator = new AccelerateDecelerateInterpolator(); 59 | int ZOOM_DURATION = DEFAULT_ZOOM_DURATION; 60 | 61 | static final int EDGE_NONE = -1; 62 | static final int EDGE_LEFT = 0; 63 | static final int EDGE_RIGHT = 1; 64 | static final int EDGE_BOTH = 2; 65 | 66 | private float mMinScale = DEFAULT_MIN_SCALE; 67 | private float mMidScale = DEFAULT_MID_SCALE; 68 | private float mMaxScale = DEFAULT_MAX_SCALE; 69 | 70 | private boolean mAllowParentInterceptOnEdge = true; 71 | private boolean mBlockParentIntercept = false; 72 | 73 | private static void checkZoomLevels(float minZoom, float midZoom, 74 | float maxZoom) { 75 | if (minZoom >= midZoom) { 76 | throw new IllegalArgumentException( 77 | "MinZoom has to be less than MidZoom"); 78 | } else if (midZoom >= maxZoom) { 79 | throw new IllegalArgumentException( 80 | "MidZoom has to be less than MaxZoom"); 81 | } 82 | } 83 | 84 | /** 85 | * @return true if the ImageView exists, and it's Drawable existss 86 | */ 87 | private static boolean hasDrawable(ImageView imageView) { 88 | return null != imageView && null != imageView.getDrawable(); 89 | } 90 | 91 | /** 92 | * @return true if the ScaleType is supported. 93 | */ 94 | private static boolean isSupportedScaleType(final ScaleType scaleType) { 95 | if (null == scaleType) { 96 | return false; 97 | } 98 | 99 | switch (scaleType) { 100 | case MATRIX: 101 | throw new IllegalArgumentException(scaleType.name() 102 | + " is not supported in PhotoView"); 103 | 104 | default: 105 | return true; 106 | } 107 | } 108 | 109 | /** 110 | * Set's the ImageView's ScaleType to Matrix. 111 | */ 112 | private static void setImageViewScaleTypeMatrix(ImageView imageView) { 113 | /** 114 | * PhotoView sets it's own ScaleType to Matrix, then diverts all calls 115 | * setScaleType to this.setScaleType automatically. 116 | */ 117 | if (null != imageView && !(imageView instanceof IPhotoView)) { 118 | if (!ScaleType.MATRIX.equals(imageView.getScaleType())) { 119 | imageView.setScaleType(ScaleType.MATRIX); 120 | } 121 | } 122 | } 123 | 124 | private WeakReference mImageView; 125 | 126 | // Gesture Detectors 127 | private GestureDetector mGestureDetector; 128 | private uk.co.senab.photoview.gestures.GestureDetector mScaleDragDetector; 129 | 130 | // These are set so we don't keep allocating them on the heap 131 | private final Matrix mBaseMatrix = new Matrix(); 132 | private final Matrix mDrawMatrix = new Matrix(); 133 | private final Matrix mSuppMatrix = new Matrix(); 134 | private final RectF mDisplayRect = new RectF(); 135 | private final float[] mMatrixValues = new float[9]; 136 | 137 | // Listeners 138 | private OnMatrixChangedListener mMatrixChangeListener; 139 | private OnPhotoTapListener mPhotoTapListener; 140 | private OnViewTapListener mViewTapListener; 141 | private OnLongClickListener mLongClickListener; 142 | private OnScaleChangeListener mScaleChangeListener; 143 | 144 | private int mIvTop, mIvRight, mIvBottom, mIvLeft; 145 | private FlingRunnable mCurrentFlingRunnable; 146 | private int mScrollEdge = EDGE_BOTH; 147 | 148 | private boolean mZoomEnabled; 149 | private ScaleType mScaleType = ScaleType.FIT_CENTER; 150 | 151 | public PhotoViewAttacher(ImageView imageView) { 152 | this(imageView, true); 153 | } 154 | 155 | public PhotoViewAttacher(ImageView imageView, boolean zoomable) { 156 | mImageView = new WeakReference<>(imageView); 157 | 158 | imageView.setDrawingCacheEnabled(true); 159 | imageView.setOnTouchListener(this); 160 | 161 | ViewTreeObserver observer = imageView.getViewTreeObserver(); 162 | if (null != observer) 163 | observer.addOnGlobalLayoutListener(this); 164 | 165 | // Make sure we using MATRIX Scale Type 166 | setImageViewScaleTypeMatrix(imageView); 167 | 168 | if (imageView.isInEditMode()) { 169 | return; 170 | } 171 | // Create Gesture Detectors... 172 | mScaleDragDetector = VersionedGestureDetector.newInstance( 173 | imageView.getContext(), this); 174 | 175 | mGestureDetector = new GestureDetector(imageView.getContext(), 176 | new GestureDetector.SimpleOnGestureListener() { 177 | 178 | // forward long click listener 179 | @Override 180 | public void onLongPress(MotionEvent e) { 181 | if (null != mLongClickListener) { 182 | mLongClickListener.onLongClick(getImageView()); 183 | } 184 | } 185 | }); 186 | 187 | mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this)); 188 | 189 | // Finally, update the UI so that we're zoomable 190 | setZoomable(zoomable); 191 | } 192 | 193 | @Override 194 | public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) { 195 | if (newOnDoubleTapListener != null) { 196 | this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener); 197 | } else { 198 | this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this)); 199 | } 200 | } 201 | 202 | @Override 203 | public void setOnScaleChangeListener(OnScaleChangeListener onScaleChangeListener) { 204 | this.mScaleChangeListener = onScaleChangeListener; 205 | } 206 | 207 | @Override 208 | public boolean canZoom() { 209 | return mZoomEnabled; 210 | } 211 | 212 | /** 213 | * Clean-up the resources attached to this object. This needs to be called when the ImageView is 214 | * no longer used. A good example is from {@link android.view.View#onDetachedFromWindow()} or 215 | * from {@link android.app.Activity#onDestroy()}. This is automatically called if you are using 216 | * {@link uk.co.senab.photoview.PhotoView}. 217 | */ 218 | @SuppressWarnings("deprecation") 219 | public void cleanup() { 220 | if (null == mImageView) { 221 | return; // cleanup already done 222 | } 223 | 224 | final ImageView imageView = mImageView.get(); 225 | 226 | if (null != imageView) { 227 | // Remove this as a global layout listener 228 | ViewTreeObserver observer = imageView.getViewTreeObserver(); 229 | if (null != observer && observer.isAlive()) { 230 | observer.removeGlobalOnLayoutListener(this); 231 | } 232 | 233 | // Remove the ImageView's reference to this 234 | imageView.setOnTouchListener(null); 235 | 236 | // make sure a pending fling runnable won't be run 237 | cancelFling(); 238 | } 239 | 240 | if (null != mGestureDetector) { 241 | mGestureDetector.setOnDoubleTapListener(null); 242 | } 243 | 244 | // Clear listeners too 245 | mMatrixChangeListener = null; 246 | mPhotoTapListener = null; 247 | mViewTapListener = null; 248 | 249 | // Finally, clear ImageView 250 | mImageView = null; 251 | } 252 | 253 | @Override 254 | public RectF getDisplayRect() { 255 | checkMatrixBounds(); 256 | return getDisplayRect(getDrawMatrix()); 257 | } 258 | 259 | @Override 260 | public boolean setDisplayMatrix(Matrix finalMatrix) { 261 | if (finalMatrix == null) 262 | throw new IllegalArgumentException("Matrix cannot be null"); 263 | 264 | ImageView imageView = getImageView(); 265 | if (null == imageView) 266 | return false; 267 | 268 | if (null == imageView.getDrawable()) 269 | return false; 270 | 271 | mSuppMatrix.set(finalMatrix); 272 | setImageViewMatrix(getDrawMatrix()); 273 | checkMatrixBounds(); 274 | 275 | return true; 276 | } 277 | 278 | /** 279 | * @deprecated use {@link #setRotationTo(float)} 280 | */ 281 | @Override 282 | public void setPhotoViewRotation(float degrees) { 283 | mSuppMatrix.setRotate(degrees % 360); 284 | checkAndDisplayMatrix(); 285 | } 286 | 287 | @Override 288 | public void setRotationTo(float degrees) { 289 | mSuppMatrix.setRotate(degrees % 360); 290 | checkAndDisplayMatrix(); 291 | } 292 | 293 | @Override 294 | public void setRotationBy(float degrees) { 295 | mSuppMatrix.postRotate(degrees % 360); 296 | checkAndDisplayMatrix(); 297 | } 298 | 299 | public ImageView getImageView() { 300 | ImageView imageView = null; 301 | 302 | if (null != mImageView) { 303 | imageView = mImageView.get(); 304 | } 305 | 306 | // If we don't have an ImageView, call cleanup() 307 | if (null == imageView) { 308 | cleanup(); 309 | LogManager.getLogger().i(LOG_TAG, 310 | "ImageView no longer exists. You should not use this PhotoViewAttacher any more."); 311 | } 312 | 313 | return imageView; 314 | } 315 | 316 | @Override 317 | @Deprecated 318 | public float getMinScale() { 319 | return getMinimumScale(); 320 | } 321 | 322 | @Override 323 | public float getMinimumScale() { 324 | return mMinScale; 325 | } 326 | 327 | @Override 328 | @Deprecated 329 | public float getMidScale() { 330 | return getMediumScale(); 331 | } 332 | 333 | @Override 334 | public float getMediumScale() { 335 | return mMidScale; 336 | } 337 | 338 | @Override 339 | @Deprecated 340 | public float getMaxScale() { 341 | return getMaximumScale(); 342 | } 343 | 344 | @Override 345 | public float getMaximumScale() { 346 | return mMaxScale; 347 | } 348 | 349 | @Override 350 | public float getScale() { 351 | return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2)); 352 | } 353 | 354 | @Override 355 | public ScaleType getScaleType() { 356 | return mScaleType; 357 | } 358 | 359 | @Override 360 | public void onDrag(float dx, float dy) { 361 | if (mScaleDragDetector.isScaling()) { 362 | return; // Do not drag if we are already scaling 363 | } 364 | 365 | if (DEBUG) { 366 | LogManager.getLogger().d(LOG_TAG, 367 | String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy)); 368 | } 369 | 370 | ImageView imageView = getImageView(); 371 | mSuppMatrix.postTranslate(dx, dy); 372 | checkAndDisplayMatrix(); 373 | 374 | /** 375 | * Here we decide whether to let the ImageView's parent to start taking 376 | * over the touch event. 377 | * 378 | * First we check whether this function is enabled. We never want the 379 | * parent to take over if we're scaling. We then check the edge we're 380 | * on, and the direction of the scroll (i.e. if we're pulling against 381 | * the edge, aka 'overscrolling', let the parent take over). 382 | */ 383 | ViewParent parent = imageView.getParent(); 384 | if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) { 385 | if (mScrollEdge == EDGE_BOTH 386 | || (mScrollEdge == EDGE_LEFT && dx >= 1f) 387 | || (mScrollEdge == EDGE_RIGHT && dx <= -1f)) { 388 | if (null != parent) 389 | parent.requestDisallowInterceptTouchEvent(false); 390 | } 391 | } else { 392 | if (null != parent) { 393 | parent.requestDisallowInterceptTouchEvent(true); 394 | } 395 | } 396 | } 397 | 398 | @Override 399 | public void onFling(float startX, float startY, float velocityX, 400 | float velocityY) { 401 | if (DEBUG) { 402 | LogManager.getLogger().d( 403 | LOG_TAG, 404 | "onFling. sX: " + startX + " sY: " + startY + " Vx: " 405 | + velocityX + " Vy: " + velocityY); 406 | } 407 | ImageView imageView = getImageView(); 408 | mCurrentFlingRunnable = new FlingRunnable(imageView.getContext()); 409 | mCurrentFlingRunnable.fling(getImageViewWidth(imageView), 410 | getImageViewHeight(imageView), (int) velocityX, (int) velocityY); 411 | imageView.post(mCurrentFlingRunnable); 412 | } 413 | 414 | @Override 415 | public void onGlobalLayout() { 416 | ImageView imageView = getImageView(); 417 | 418 | if (null != imageView) { 419 | if (mZoomEnabled) { 420 | final int top = imageView.getTop(); 421 | final int right = imageView.getRight(); 422 | final int bottom = imageView.getBottom(); 423 | final int left = imageView.getLeft(); 424 | 425 | /** 426 | * We need to check whether the ImageView's bounds have changed. 427 | * This would be easier if we targeted API 11+ as we could just use 428 | * View.OnLayoutChangeListener. Instead we have to replicate the 429 | * work, keeping track of the ImageView's bounds and then checking 430 | * if the values change. 431 | */ 432 | if (top != mIvTop || bottom != mIvBottom || left != mIvLeft 433 | || right != mIvRight) { 434 | // Update our base matrix, as the bounds have changed 435 | updateBaseMatrix(imageView.getDrawable()); 436 | 437 | // Update values as something has changed 438 | mIvTop = top; 439 | mIvRight = right; 440 | mIvBottom = bottom; 441 | mIvLeft = left; 442 | } 443 | } else { 444 | updateBaseMatrix(imageView.getDrawable()); 445 | } 446 | } 447 | } 448 | 449 | @Override 450 | public void onScale(float scaleFactor, float focusX, float focusY) { 451 | if (DEBUG) { 452 | LogManager.getLogger().d( 453 | LOG_TAG, 454 | String.format("onScale: scale: %.2f. fX: %.2f. fY: %.2f", 455 | scaleFactor, focusX, focusY)); 456 | } 457 | 458 | if (getScale() < mMaxScale || scaleFactor < 1f) { 459 | if (null != mScaleChangeListener) { 460 | mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY); 461 | } 462 | mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); 463 | checkAndDisplayMatrix(); 464 | } 465 | } 466 | 467 | @SuppressLint("ClickableViewAccessibility") 468 | @Override 469 | public boolean onTouch(View v, MotionEvent ev) { 470 | boolean handled = false; 471 | 472 | if (mZoomEnabled && hasDrawable((ImageView) v)) { 473 | ViewParent parent = v.getParent(); 474 | switch (ev.getAction()) { 475 | case ACTION_DOWN: 476 | // First, disable the Parent from intercepting the touch 477 | // event 478 | if (null != parent) { 479 | parent.requestDisallowInterceptTouchEvent(true); 480 | } else { 481 | LogManager.getLogger().i(LOG_TAG, "onTouch getParent() returned null"); 482 | } 483 | 484 | // If we're flinging, and the user presses down, cancel 485 | // fling 486 | cancelFling(); 487 | break; 488 | 489 | case ACTION_CANCEL: 490 | case ACTION_UP: 491 | // If the user has zoomed less than min scale, zoom back 492 | // to min scale 493 | if (getScale() < mMinScale) { 494 | RectF rect = getDisplayRect(); 495 | if (null != rect) { 496 | v.post(new AnimatedZoomRunnable(getScale(), mMinScale, 497 | rect.centerX(), rect.centerY())); 498 | handled = true; 499 | } 500 | } 501 | break; 502 | } 503 | 504 | // Try the Scale/Drag detector 505 | if (null != mScaleDragDetector) { 506 | boolean wasScaling = mScaleDragDetector.isScaling(); 507 | boolean wasDragging = mScaleDragDetector.isDragging(); 508 | 509 | handled = mScaleDragDetector.onTouchEvent(ev); 510 | 511 | boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling(); 512 | boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging(); 513 | 514 | mBlockParentIntercept = didntScale && didntDrag; 515 | } 516 | 517 | // Check to see if the user double tapped 518 | if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) { 519 | handled = true; 520 | } 521 | 522 | } 523 | 524 | return handled; 525 | } 526 | 527 | @Override 528 | public void setAllowParentInterceptOnEdge(boolean allow) { 529 | mAllowParentInterceptOnEdge = allow; 530 | } 531 | 532 | @Override 533 | @Deprecated 534 | public void setMinScale(float minScale) { 535 | setMinimumScale(minScale); 536 | } 537 | 538 | @Override 539 | public void setMinimumScale(float minimumScale) { 540 | checkZoomLevels(minimumScale, mMidScale, mMaxScale); 541 | mMinScale = minimumScale; 542 | } 543 | 544 | @Override 545 | @Deprecated 546 | public void setMidScale(float midScale) { 547 | setMediumScale(midScale); 548 | } 549 | 550 | @Override 551 | public void setMediumScale(float mediumScale) { 552 | checkZoomLevels(mMinScale, mediumScale, mMaxScale); 553 | mMidScale = mediumScale; 554 | } 555 | 556 | @Override 557 | @Deprecated 558 | public void setMaxScale(float maxScale) { 559 | setMaximumScale(maxScale); 560 | } 561 | 562 | @Override 563 | public void setMaximumScale(float maximumScale) { 564 | checkZoomLevels(mMinScale, mMidScale, maximumScale); 565 | mMaxScale = maximumScale; 566 | } 567 | 568 | @Override 569 | public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) { 570 | checkZoomLevels(minimumScale, mediumScale, maximumScale); 571 | mMinScale = minimumScale; 572 | mMidScale = mediumScale; 573 | mMaxScale = maximumScale; 574 | } 575 | 576 | @Override 577 | public void setOnLongClickListener(OnLongClickListener listener) { 578 | mLongClickListener = listener; 579 | } 580 | 581 | @Override 582 | public void setOnMatrixChangeListener(OnMatrixChangedListener listener) { 583 | mMatrixChangeListener = listener; 584 | } 585 | 586 | @Override 587 | public void setOnPhotoTapListener(OnPhotoTapListener listener) { 588 | mPhotoTapListener = listener; 589 | } 590 | 591 | @Override 592 | public OnPhotoTapListener getOnPhotoTapListener() { 593 | return mPhotoTapListener; 594 | } 595 | 596 | @Override 597 | public void setOnViewTapListener(OnViewTapListener listener) { 598 | mViewTapListener = listener; 599 | } 600 | 601 | @Override 602 | public OnViewTapListener getOnViewTapListener() { 603 | return mViewTapListener; 604 | } 605 | 606 | @Override 607 | public void setScale(float scale) { 608 | setScale(scale, false); 609 | } 610 | 611 | @Override 612 | public void setScale(float scale, boolean animate) { 613 | ImageView imageView = getImageView(); 614 | 615 | if (null != imageView) { 616 | setScale(scale, 617 | (imageView.getRight()) / 2, 618 | (imageView.getBottom()) / 2, 619 | animate); 620 | } 621 | } 622 | 623 | @Override 624 | public void setScale(float scale, float focalX, float focalY, 625 | boolean animate) { 626 | ImageView imageView = getImageView(); 627 | 628 | if (null != imageView) { 629 | // Check to see if the scale is within bounds 630 | if (scale < mMinScale || scale > mMaxScale) { 631 | LogManager 632 | .getLogger() 633 | .i(LOG_TAG, 634 | "Scale must be within the range of minScale and maxScale"); 635 | return; 636 | } 637 | 638 | if (animate) { 639 | imageView.post(new AnimatedZoomRunnable(getScale(), scale, 640 | focalX, focalY)); 641 | } else { 642 | mSuppMatrix.setScale(scale, scale, focalX, focalY); 643 | checkAndDisplayMatrix(); 644 | } 645 | } 646 | } 647 | 648 | @Override 649 | public void setScaleType(ScaleType scaleType) { 650 | if (isSupportedScaleType(scaleType) && scaleType != mScaleType) { 651 | mScaleType = scaleType; 652 | 653 | // Finally update 654 | update(); 655 | } 656 | } 657 | 658 | @Override 659 | public void setZoomable(boolean zoomable) { 660 | mZoomEnabled = zoomable; 661 | update(); 662 | } 663 | 664 | public void update() { 665 | ImageView imageView = getImageView(); 666 | 667 | if (null != imageView) { 668 | if (mZoomEnabled) { 669 | // Make sure we using MATRIX Scale Type 670 | setImageViewScaleTypeMatrix(imageView); 671 | 672 | // Update the base matrix using the current drawable 673 | updateBaseMatrix(imageView.getDrawable()); 674 | } else { 675 | // Reset the Matrix... 676 | resetMatrix(); 677 | } 678 | } 679 | } 680 | 681 | @Override 682 | public Matrix getDisplayMatrix() { 683 | return new Matrix(getDrawMatrix()); 684 | } 685 | 686 | public Matrix getDrawMatrix() { 687 | mDrawMatrix.set(mBaseMatrix); 688 | mDrawMatrix.postConcat(mSuppMatrix); 689 | return mDrawMatrix; 690 | } 691 | 692 | private void cancelFling() { 693 | if (null != mCurrentFlingRunnable) { 694 | mCurrentFlingRunnable.cancelFling(); 695 | mCurrentFlingRunnable = null; 696 | } 697 | } 698 | 699 | /** 700 | * Helper method that simply checks the Matrix, and then displays the result 701 | */ 702 | private void checkAndDisplayMatrix() { 703 | if (checkMatrixBounds()) { 704 | setImageViewMatrix(getDrawMatrix()); 705 | } 706 | } 707 | 708 | private void checkImageViewScaleType() { 709 | ImageView imageView = getImageView(); 710 | 711 | /** 712 | * PhotoView's getScaleType() will just divert to this.getScaleType() so 713 | * only call if we're not attached to a PhotoView. 714 | */ 715 | if (null != imageView && !(imageView instanceof IPhotoView)) { 716 | if (!ScaleType.MATRIX.equals(imageView.getScaleType())) { 717 | throw new IllegalStateException( 718 | "The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher"); 719 | } 720 | } 721 | } 722 | 723 | private boolean checkMatrixBounds() { 724 | final ImageView imageView = getImageView(); 725 | if (null == imageView) { 726 | return false; 727 | } 728 | 729 | final RectF rect = getDisplayRect(getDrawMatrix()); 730 | if (null == rect) { 731 | return false; 732 | } 733 | 734 | final float height = rect.height(), width = rect.width(); 735 | float deltaX = 0, deltaY = 0; 736 | 737 | final int viewHeight = getImageViewHeight(imageView); 738 | if (height <= viewHeight) { 739 | switch (mScaleType) { 740 | case FIT_START: 741 | deltaY = -rect.top; 742 | break; 743 | case FIT_END: 744 | deltaY = viewHeight - height - rect.top; 745 | break; 746 | default: 747 | deltaY = (viewHeight - height) / 2 - rect.top; 748 | break; 749 | } 750 | } else if (rect.top > 0) { 751 | deltaY = -rect.top; 752 | } else if (rect.bottom < viewHeight) { 753 | deltaY = viewHeight - rect.bottom; 754 | } 755 | 756 | final int viewWidth = getImageViewWidth(imageView); 757 | if (width <= viewWidth) { 758 | switch (mScaleType) { 759 | case FIT_START: 760 | deltaX = -rect.left; 761 | break; 762 | case FIT_END: 763 | deltaX = viewWidth - width - rect.left; 764 | break; 765 | default: 766 | deltaX = (viewWidth - width) / 2 - rect.left; 767 | break; 768 | } 769 | mScrollEdge = EDGE_BOTH; 770 | } else if (rect.left > 0) { 771 | mScrollEdge = EDGE_LEFT; 772 | deltaX = -rect.left; 773 | } else if (rect.right < viewWidth) { 774 | deltaX = viewWidth - rect.right; 775 | mScrollEdge = EDGE_RIGHT; 776 | } else { 777 | mScrollEdge = EDGE_NONE; 778 | } 779 | 780 | // Finally actually translate the matrix 781 | mSuppMatrix.postTranslate(deltaX, deltaY); 782 | return true; 783 | } 784 | 785 | /** 786 | * Helper method that maps the supplied Matrix to the current Drawable 787 | * 788 | * @param matrix - Matrix to map Drawable against 789 | * @return RectF - Displayed Rectangle 790 | */ 791 | private RectF getDisplayRect(Matrix matrix) { 792 | ImageView imageView = getImageView(); 793 | 794 | if (null != imageView) { 795 | Drawable d = imageView.getDrawable(); 796 | if (null != d) { 797 | mDisplayRect.set(0, 0, d.getIntrinsicWidth(), 798 | d.getIntrinsicHeight()); 799 | matrix.mapRect(mDisplayRect); 800 | return mDisplayRect; 801 | } 802 | } 803 | return null; 804 | } 805 | 806 | public Bitmap getVisibleRectangleBitmap() { 807 | ImageView imageView = getImageView(); 808 | return imageView == null ? null : imageView.getDrawingCache(); 809 | } 810 | 811 | @Override 812 | public void setZoomTransitionDuration(int milliseconds) { 813 | if (milliseconds < 0) 814 | milliseconds = DEFAULT_ZOOM_DURATION; 815 | this.ZOOM_DURATION = milliseconds; 816 | } 817 | 818 | @Override 819 | public IPhotoView getIPhotoViewImplementation() { 820 | return this; 821 | } 822 | 823 | /** 824 | * Helper method that 'unpacks' a Matrix and returns the required value 825 | * 826 | * @param matrix - Matrix to unpack 827 | * @param whichValue - Which value from Matrix.M* to return 828 | * @return float - returned value 829 | */ 830 | private float getValue(Matrix matrix, int whichValue) { 831 | matrix.getValues(mMatrixValues); 832 | return mMatrixValues[whichValue]; 833 | } 834 | 835 | /** 836 | * Resets the Matrix back to FIT_CENTER, and then displays it.s 837 | */ 838 | private void resetMatrix() { 839 | mSuppMatrix.reset(); 840 | setImageViewMatrix(getDrawMatrix()); 841 | checkMatrixBounds(); 842 | } 843 | 844 | private void setImageViewMatrix(Matrix matrix) { 845 | ImageView imageView = getImageView(); 846 | if (null != imageView) { 847 | 848 | checkImageViewScaleType(); 849 | imageView.setImageMatrix(matrix); 850 | 851 | // Call MatrixChangedListener if needed 852 | if (null != mMatrixChangeListener) { 853 | RectF displayRect = getDisplayRect(matrix); 854 | if (null != displayRect) { 855 | mMatrixChangeListener.onMatrixChanged(displayRect); 856 | } 857 | } 858 | } 859 | } 860 | 861 | /** 862 | * Calculate Matrix for FIT_CENTER 863 | * 864 | * @param d - Drawable being displayed 865 | */ 866 | private void updateBaseMatrix(Drawable d) { 867 | ImageView imageView = getImageView(); 868 | if (null == imageView || null == d) { 869 | return; 870 | } 871 | 872 | final float viewWidth = getImageViewWidth(imageView); 873 | final float viewHeight = getImageViewHeight(imageView); 874 | final int drawableWidth = d.getIntrinsicWidth(); 875 | final int drawableHeight = d.getIntrinsicHeight(); 876 | 877 | mBaseMatrix.reset(); 878 | 879 | final float widthScale = viewWidth / drawableWidth; 880 | final float heightScale = viewHeight / drawableHeight; 881 | 882 | if (mScaleType == ScaleType.CENTER) { 883 | mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F, 884 | (viewHeight - drawableHeight) / 2F); 885 | 886 | } else if (mScaleType == ScaleType.CENTER_CROP) { 887 | float scale = Math.max(widthScale, heightScale); 888 | mBaseMatrix.postScale(scale, scale); 889 | mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, 890 | (viewHeight - drawableHeight * scale) / 2F); 891 | 892 | } else if (mScaleType == ScaleType.CENTER_INSIDE) { 893 | float scale = Math.min(1.0f, Math.min(widthScale, heightScale)); 894 | mBaseMatrix.postScale(scale, scale); 895 | mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, 896 | (viewHeight - drawableHeight * scale) / 2F); 897 | 898 | } else { 899 | RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight); 900 | RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight); 901 | 902 | switch (mScaleType) { 903 | case FIT_CENTER: 904 | mBaseMatrix 905 | .setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER); 906 | break; 907 | 908 | case FIT_START: 909 | mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START); 910 | break; 911 | 912 | case FIT_END: 913 | mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END); 914 | break; 915 | 916 | case FIT_XY: 917 | mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL); 918 | break; 919 | 920 | default: 921 | break; 922 | } 923 | } 924 | 925 | resetMatrix(); 926 | } 927 | 928 | private int getImageViewWidth(ImageView imageView) { 929 | if (null == imageView) 930 | return 0; 931 | return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); 932 | } 933 | 934 | private int getImageViewHeight(ImageView imageView) { 935 | if (null == imageView) 936 | return 0; 937 | return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); 938 | } 939 | 940 | /** 941 | * Interface definition for a callback to be invoked when the internal Matrix has changed for 942 | * this View. 943 | * 944 | * @author Chris Banes 945 | */ 946 | public static interface OnMatrixChangedListener { 947 | /** 948 | * Callback for when the Matrix displaying the Drawable has changed. This could be because 949 | * the View's bounds have changed, or the user has zoomed. 950 | * 951 | * @param rect - Rectangle displaying the Drawable's new bounds. 952 | */ 953 | void onMatrixChanged(RectF rect); 954 | } 955 | 956 | /** 957 | * Interface definition for callback to be invoked when attached ImageView scale changes 958 | * 959 | * @author Marek Sebera 960 | */ 961 | public static interface OnScaleChangeListener { 962 | /** 963 | * Callback for when the scale changes 964 | * 965 | * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in) 966 | * @param focusX focal point X position 967 | * @param focusY focal point Y position 968 | */ 969 | void onScaleChange(float scaleFactor, float focusX, float focusY); 970 | } 971 | 972 | /** 973 | * Interface definition for a callback to be invoked when the Photo is tapped with a single 974 | * tap. 975 | * 976 | * @author Chris Banes 977 | */ 978 | public static interface OnPhotoTapListener { 979 | 980 | /** 981 | * A callback to receive where the user taps on a photo. You will only receive a callback if 982 | * the user taps on the actual photo, tapping on 'whitespace' will be ignored. 983 | * 984 | * @param view - View the user tapped. 985 | * @param x - where the user tapped from the of the Drawable, as percentage of the 986 | * Drawable width. 987 | * @param y - where the user tapped from the top of the Drawable, as percentage of the 988 | * Drawable height. 989 | */ 990 | void onPhotoTap(View view, float x, float y); 991 | } 992 | 993 | /** 994 | * Interface definition for a callback to be invoked when the ImageView is tapped with a single 995 | * tap. 996 | * 997 | * @author Chris Banes 998 | */ 999 | public static interface OnViewTapListener { 1000 | 1001 | /** 1002 | * A callback to receive where the user taps on a ImageView. You will receive a callback if 1003 | * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored. 1004 | * 1005 | * @param view - View the user tapped. 1006 | * @param x - where the user tapped from the left of the View. 1007 | * @param y - where the user tapped from the top of the View. 1008 | */ 1009 | void onViewTap(View view, float x, float y); 1010 | } 1011 | 1012 | private class AnimatedZoomRunnable implements Runnable { 1013 | 1014 | private final float mFocalX, mFocalY; 1015 | private final long mStartTime; 1016 | private final float mZoomStart, mZoomEnd; 1017 | 1018 | public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, 1019 | final float focalX, final float focalY) { 1020 | mFocalX = focalX; 1021 | mFocalY = focalY; 1022 | mStartTime = System.currentTimeMillis(); 1023 | mZoomStart = currentZoom; 1024 | mZoomEnd = targetZoom; 1025 | } 1026 | 1027 | @Override 1028 | public void run() { 1029 | ImageView imageView = getImageView(); 1030 | if (imageView == null) { 1031 | return; 1032 | } 1033 | 1034 | float t = interpolate(); 1035 | float scale = mZoomStart + t * (mZoomEnd - mZoomStart); 1036 | float deltaScale = scale / getScale(); 1037 | 1038 | onScale(deltaScale, mFocalX, mFocalY); 1039 | 1040 | // We haven't hit our target scale yet, so post ourselves again 1041 | if (t < 1f) { 1042 | Compat.postOnAnimation(imageView, this); 1043 | } 1044 | } 1045 | 1046 | private float interpolate() { 1047 | float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION; 1048 | t = Math.min(1f, t); 1049 | t = sInterpolator.getInterpolation(t); 1050 | return t; 1051 | } 1052 | } 1053 | 1054 | private class FlingRunnable implements Runnable { 1055 | 1056 | private final ScrollerProxy mScroller; 1057 | private int mCurrentX, mCurrentY; 1058 | 1059 | public FlingRunnable(Context context) { 1060 | mScroller = ScrollerProxy.getScroller(context); 1061 | } 1062 | 1063 | public void cancelFling() { 1064 | if (DEBUG) { 1065 | LogManager.getLogger().d(LOG_TAG, "Cancel Fling"); 1066 | } 1067 | mScroller.forceFinished(true); 1068 | } 1069 | 1070 | public void fling(int viewWidth, int viewHeight, int velocityX, 1071 | int velocityY) { 1072 | final RectF rect = getDisplayRect(); 1073 | if (null == rect) { 1074 | return; 1075 | } 1076 | 1077 | final int startX = Math.round(-rect.left); 1078 | final int minX, maxX, minY, maxY; 1079 | 1080 | if (viewWidth < rect.width()) { 1081 | minX = 0; 1082 | maxX = Math.round(rect.width() - viewWidth); 1083 | } else { 1084 | minX = maxX = startX; 1085 | } 1086 | 1087 | final int startY = Math.round(-rect.top); 1088 | if (viewHeight < rect.height()) { 1089 | minY = 0; 1090 | maxY = Math.round(rect.height() - viewHeight); 1091 | } else { 1092 | minY = maxY = startY; 1093 | } 1094 | 1095 | mCurrentX = startX; 1096 | mCurrentY = startY; 1097 | 1098 | if (DEBUG) { 1099 | LogManager.getLogger().d( 1100 | LOG_TAG, 1101 | "fling. StartX:" + startX + " StartY:" + startY 1102 | + " MaxX:" + maxX + " MaxY:" + maxY); 1103 | } 1104 | 1105 | // If we actually can move, fling the scroller 1106 | if (startX != maxX || startY != maxY) { 1107 | mScroller.fling(startX, startY, velocityX, velocityY, minX, 1108 | maxX, minY, maxY, 0, 0); 1109 | } 1110 | } 1111 | 1112 | @Override 1113 | public void run() { 1114 | if (mScroller.isFinished()) { 1115 | return; // remaining post that should not be handled 1116 | } 1117 | 1118 | ImageView imageView = getImageView(); 1119 | if (null != imageView && mScroller.computeScrollOffset()) { 1120 | 1121 | final int newX = mScroller.getCurrX(); 1122 | final int newY = mScroller.getCurrY(); 1123 | 1124 | if (DEBUG) { 1125 | LogManager.getLogger().d( 1126 | LOG_TAG, 1127 | "fling run(). CurrentX:" + mCurrentX + " CurrentY:" 1128 | + mCurrentY + " NewX:" + newX + " NewY:" 1129 | + newY); 1130 | } 1131 | 1132 | mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY); 1133 | setImageViewMatrix(getDrawMatrix()); 1134 | 1135 | mCurrentX = newX; 1136 | mCurrentY = newY; 1137 | 1138 | // Post On animation 1139 | Compat.postOnAnimation(imageView, this); 1140 | } 1141 | } 1142 | } 1143 | } 1144 | --------------------------------------------------------------------------------