add gesture library

This commit is contained in:
weiyixiong 2015-11-17 17:00:12 +08:00
parent 0de0c3cd0e
commit 802d9c7ab8
101 changed files with 3532 additions and 700 deletions

View File

@ -10,6 +10,7 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/PdfView" />
<option value="$PROJECT_DIR$/gestureimageview" />
<option value="$PROJECT_DIR$/pdfviewsample" />
</set>
</option>

View File

@ -4,6 +4,7 @@
<modules>
<module fileurl="file://$PROJECT_DIR$/Android-Pdf-Viewer-Library.iml" filepath="$PROJECT_DIR$/Android-Pdf-Viewer-Library.iml" />
<module fileurl="file://$PROJECT_DIR$/PdfView/PdfView.iml" filepath="$PROJECT_DIR$/PdfView/PdfView.iml" />
<module fileurl="file://$PROJECT_DIR$/gestureimageview/gestureimageview.iml" filepath="$PROJECT_DIR$/gestureimageview/gestureimageview.iml" />
<module fileurl="file://$PROJECT_DIR$/pdfviewsample/pdfviewsample.iml" filepath="$PROJECT_DIR$/pdfviewsample/pdfviewsample.iml" />
</modules>
</component>

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />

View File

@ -80,5 +80,6 @@
</content>
<orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="gestureimageview" exported="" />
</component>
</module>

View File

@ -1,18 +1,21 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 21
buildToolsVersion "23.0.2"
compileSdkVersion 21
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 4
targetSdkVersion 4
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
defaultConfig {
minSdkVersion 14
targetSdkVersion 4
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
}
dependencies {
compile project(':gestureimageview')
}

View File

@ -5,7 +5,7 @@
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="4"
android:minSdkVersion="14"
android:targetSdkVersion="4" />
<application />

View File

@ -5,7 +5,7 @@
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="4"
android:minSdkVersion="14"
android:targetSdkVersion="4" />
<application />

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.polites.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="7" />
</manifest>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.polites.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="7" />
</manifest>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="unspecified"><source path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/res"/></dataSet><dataSet config="main"><source path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res"><file name="slide_in" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/anim/slide_in.xml" qualifiers="" type="anim"/><file name="slide_out" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/anim/slide_out.xml" qualifiers="" type="anim"/><file name="back01" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/back01.png" qualifiers="" type="drawable"/><file name="back02" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/back02.png" qualifiers="" type="drawable"/><file name="doc" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/doc.png" qualifiers="" type="drawable"/><file name="folder" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/folder.png" qualifiers="" type="drawable"/><file name="icon" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/icon.png" qualifiers="" type="drawable"/><file name="left_arrow" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/left_arrow.png" qualifiers="" type="drawable"/><file name="pdf" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/pdf.png" qualifiers="" type="drawable"/><file name="right_arrow" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/right_arrow.png" qualifiers="" type="drawable"/><file name="zoom_in" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/zoom_in.png" qualifiers="" type="drawable"/><file name="zoom_out" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable/zoom_out.png" qualifiers="" type="drawable"/><file name="icon" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable-hdpi/icon.png" qualifiers="hdpi" type="drawable"/><file name="icon" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable-ldpi/icon.png" qualifiers="ldpi" type="drawable"/><file name="icon" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/drawable-mdpi/icon.png" qualifiers="mdpi" type="drawable"/><file name="dialog_pagenumber" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/dialog_pagenumber.xml" qualifiers="" type="layout"/><file name="graphics_view" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/graphics_view.xml" qualifiers="" type="layout"/><file name="main" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/main.xml" qualifiers="" type="layout"/><file name="navigation_overlay" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/navigation_overlay.xml" qualifiers="" type="layout"/><file name="pdf_file_password" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/pdf_file_password.xml" qualifiers="" type="layout"/><file name="scroll_layout" path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/scroll_layout.xml" qualifiers="" type="layout"/><file path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/values/color.xml" qualifiers=""><drawable name="violet">#9370DB</drawable><drawable name="white">#FFFFFFFF</drawable><drawable name="black">#000000</drawable><drawable name="blue">#000000FF</drawable></file><file path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/values/strings.xml" qualifiers=""><string name="hello">Hello World, PdfViewerActivity!</string><string name="app_name">PdfViewer</string></file></source><source path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/build/generated/res/rs/release"/><source path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/build/generated/res/resValues/release"/></dataSet><dataSet config="release"><source path="/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/release/res"/></dataSet><mergedItems/></merger>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2008 Romain Guy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="200"
android:fromYDelta="100%"
android:toYDelta="0%" />
<alpha
android:duration="200"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/anim/slide_in.xml -->

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2008 Romain Guy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="200"
android:fromYDelta="0%"
android:toYDelta="100%" />
<alpha
android:duration="200"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/anim/slide_out.xml -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/username_view"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="Page number:"
android:inputType="number"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/pagenum_edit"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:inputType="phone"
android:gravity="fill_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/dialog_pagenumber.xml -->

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:id="@+id/pdf_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="5px"/>
</LinearLayout>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/graphics_view.xml -->

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/main.xml -->

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:paddingTop="5dip"
android:paddingRight="10dip"
android:paddingBottom="4dip"
android:background="#99000000" >
<ImageButton android:id="@+id/zoom_out"
android:text="Zoom Out"
android:src="@drawable/zoom_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
<ImageButton android:id="@+id/zoom_in"
android:text="Zoom In"
android:src="@drawable/zoom_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
<TextView android:id="@+id/navigation_page_position_label"
android:text="1/1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="60"
android:gravity="center"
android:layout_gravity="center" />
<ImageButton android:id="@+id/navigation_previous"
android:text="Previous"
android:src="@drawable/left_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
<ImageButton android:id="@+id/navigation_next"
android:text="Next"
android:src="@drawable/right_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
</LinearLayout>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/navigation_overlay.xml -->

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2009 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
This file is part of the Android PDF Viewer
The Android PDF Viewer is free software;
you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
The Android PDF Viewer is distributed
in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Android PDF Viewer
if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- filename -->
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="0dip"
android:text="Password protected PDF-File:"/>
<EditText
android:id="@+id/etPassword"
android:layout_width="fill_parent"
android:layout_height="60px"
android:text=""
android:layout_weight="10"
android:hint="please enter password for PDF">
<requestFocus />
</EditText>
<!-- Buttons 'OK' + 'Exit' -->
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="horizontal" >
<Button android:id="@+id/btExit" android:layout_width="120px" android:layout_height="40px" android:text="Exit"></Button>
<Button android:id="@+id/btOK" android:layout_width="120px" android:layout_height="40px" android:text="OK"></Button>
</LinearLayout>
</LinearLayout>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/pdf_file_password.xml -->

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2009 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
This file is part of the Android PDF Viewer
The Android PDF Viewer is free software;
you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
The Android PDF Viewer is distributed
in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Android PDF Viewer
if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</LinearLayout>
</ScrollView>
</ScrollView>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/layout/scroll_layout.xml -->

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/values/color.xml -->
<eat-comment/>
<drawable name="black">#000000</drawable>
<drawable name="blue">#000000FF</drawable>
<drawable name="violet">#9370DB</drawable>
<drawable name="white">#FFFFFFFF</drawable>
<!-- From: file:/Users/winney/git/Android-Pdf-Viewer-Library/PdfView/src/main/res/values/strings.xml -->
<eat-comment/>
<string name="app_name">PdfViewer</string>
<string name="hello">Hello World, PdfViewerActivity!</string>
</resources>

View File

@ -6,11 +6,14 @@ import android.graphics.Color;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;
import com.polites.android.GestureImageView;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFImage;
import com.sun.pdfview.PDFPage;
@ -35,7 +38,7 @@ public class PdfView extends FullScrollView {
private static final float ZOOM_INCREMENT = 1.5f;
private Bitmap mBi;
private ImageView mImageView;
private GestureImageView mImageView;
private Handler uiHandler;
ImageButton bZoomOut;
ImageButton bZoomIn;
@ -59,11 +62,12 @@ public class PdfView extends FullScrollView {
PDFPaint.s_doAntiAlias = true;
uiHandler = new Handler();
LayoutParams matchLp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mImageView = new ImageView(context);
mImageView = new GestureImageView(context);
setPageBitmap(null);
updateImage();
addView(mImageView, matchLp);
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
setBackgroundColor(Color.LTGRAY);
setHorizontalScrollBarEnabled(true);
setHorizontalFadingEdgeEnabled(true);
@ -79,6 +83,21 @@ public class PdfView extends FullScrollView {
this.mPdfFile = mPdfFile;
}
private int getDeviceWidth() {
DisplayMetrics metric = new DisplayMetrics();
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metric);
return metric.widthPixels; // 屏幕宽度像素
}
private int getDeviceHeight() {
DisplayMetrics metric = new DisplayMetrics();
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metric);
return metric.heightPixels; // 屏幕高度像素
}
public void showPage(int page, float zoom) throws Exception {
try {
// free memory from previous page
@ -90,6 +109,13 @@ public class PdfView extends FullScrollView {
}
float width = mPdfPage.getWidth();
float height = mPdfPage.getHeight();
if (getLayoutParams().height == ViewGroup.LayoutParams.MATCH_PARENT) {
height *= getDeviceWidth() / width;
}
if (getLayoutParams().width == LayoutParams.MATCH_PARENT) {
width = getDeviceWidth();
}
RectF clip = null;
Bitmap bi = mPdfPage.getImage((int) (width * zoom), (int) (height * zoom), clip, true, true);
setPageBitmap(bi);

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.andpdf.utils;
import android.graphics.PointF;
import android.util.FloatMath;
import android.view.MotionEvent;
public class MathUtils {
public static float distance(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
public static float distance(PointF p1, PointF p2) {
float x = p1.x - p2.x;
float y = p1.y - p2.y;
return FloatMath.sqrt(x * x + y * y);
}
public static float distance(float x1, float y1, float x2, float y2) {
float x = x1 - x2;
float y = y1 - y2;
return FloatMath.sqrt(x * x + y * y);
}
public static void midpoint(MotionEvent event, PointF point) {
float x1 = event.getX(0);
float y1 = event.getY(0);
float x2 = event.getX(1);
float y2 = event.getY(1);
midpoint(x1, y1, x2, y2, point);
}
public static void midpoint(float x1, float y1, float x2, float y2, PointF point) {
point.x = (x1 + x2) / 2.0f;
point.y = (y1 + y2) / 2.0f;
}
/**
* Rotates p1 around p2 by angle degrees.
*
* @param p1
* @param p2
* @param angle
*/
public void rotate(PointF p1, PointF p2, float angle) {
float px = p1.x;
float py = p1.y;
float ox = p2.x;
float oy = p2.y;
p1.x = (FloatMath.cos(angle) * (px - ox) - FloatMath.sin(angle) * (py - oy) + ox);
p1.y = (FloatMath.sin(angle) * (px - ox) + FloatMath.cos(angle) * (py - oy) + oy);
}
public static float angle(PointF p1, PointF p2) {
return angle(p1.x, p1.y, p2.x, p2.y);
}
public static float angle(float x1, float y1, float x2, float y2) {
return (float) Math.atan2(y2 - y1, x2 - x1);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.andpdf.utils;
import android.graphics.PointF;
import android.util.FloatMath;
import android.view.MotionEvent;
public class VectorF {
public float angle;
public float length;
public final PointF start = new PointF();
public final PointF end = new PointF();
public void calculateEndPoint() {
end.x = FloatMath.cos(angle) * length + start.x;
end.y = FloatMath.sin(angle) * length + start.y;
}
public void setStart(PointF p) {
this.start.x = p.x;
this.start.y = p.y;
}
public void setEnd(PointF p) {
this.end.x = p.x;
this.end.y = p.y;
}
public void set(MotionEvent event) {
this.start.x = event.getX(0);
this.start.y = event.getY(0);
this.end.x = event.getX(1);
this.end.y = event.getY(1);
}
public float calculateLength() {
length = MathUtils.distance(start, end);
return length;
}
public float calculateAngle() {
angle = MathUtils.angle(start, end);
return angle;
}
}

View File

@ -5,7 +5,7 @@
jar="/Users/winney/git/Android-Pdf-Viewer-Library/pdfviewsample/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/PdfView/unspecified/jars/classes.jar"
jumboMode="false"
revision="23.0.2"
sha1="933be2f099767838f02cd4a28fc5ac4c219b327f">
sha1="e7d6dd07e1cac71ea7611e16c5dcd8b3cba978dc">
<dex dex="/Users/winney/git/Android-Pdf-Viewer-Library/pdfviewsample/build/intermediates/pre-dexed/debug/classes-dbc371ee905b36ce5cd278976d41c35b063e55b0.jar" />
</item>

View File

@ -0,0 +1,18 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 19
buildToolsVersion "19.1.0"
defaultConfig {
minSdkVersion 7
targetSdkVersion 7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}

View File

@ -0,0 +1,13 @@
/**
* Automatically generated file. DO NOT MODIFY
*/
package com.polites.android;
public final class BuildConfig {
public static final boolean DEBUG = false;
public static final String APPLICATION_ID = "com.polites.android";
public static final String BUILD_TYPE = "release";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "";
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.polites.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="7" />
</manifest>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.polites.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="7" />
</manifest>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="/Users/winney/git/Android-Pdf-Viewer-Library/gestureimageview/src/main/assets"/></dataSet><dataSet config="release"><source path="/Users/winney/git/Android-Pdf-Viewer-Library/gestureimageview/src/release/assets"/></dataSet></merger>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="/Users/winney/git/Android-Pdf-Viewer-Library/gestureimageview/src/main/res"/><source path="/Users/winney/git/Android-Pdf-Viewer-Library/gestureimageview/build/generated/res/rs/release"/><source path="/Users/winney/git/Android-Pdf-Viewer-Library/gestureimageview/build/generated/res/resValues/release"/></dataSet><dataSet config="release"><source path="/Users/winney/git/Android-Pdf-Viewer-Library/gestureimageview/src/release/res"/></dataSet><mergedItems/></merger>

View File

@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":gestureimageview" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="Android-Pdf-Viewer-Library" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":gestureimageview" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
<afterSyncTasks>
<task>generateDebugAndroidTestSources</task>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
<option name="LIBRARY_PROJECT" value="true" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.polites.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="7" />
</manifest>

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author Jason Polites
*
*/
public interface Animation {
/**
* Transforms the view.
* @param view
* @param diffTime
* @return true if this animation should remain active. False otherwise.
*/
public boolean update(GestureImageView view, long time);
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author Jason Polites
*
*/
public class Animator extends Thread {
private GestureImageView view;
private Animation animation;
private boolean running = false;
private boolean active = false;
private long lastTime = -1L;
public Animator(GestureImageView view, String threadName) {
super(threadName);
this.view = view;
}
@Override
public void run() {
running = true;
while(running) {
while(active && animation != null) {
long time = System.currentTimeMillis();
active = animation.update(view, time - lastTime);
view.redraw();
lastTime = time;
while(active) {
try {
if(view.waitForDraw(32)) { // 30Htz
break;
}
}
catch (InterruptedException ignore) {
active = false;
}
}
}
synchronized(this) {
if(running) {
try {
wait();
}
catch (InterruptedException ignore) {}
}
}
}
}
public synchronized void finish() {
running = false;
active = false;
notifyAll();
}
public void play(Animation transformer) {
if(active) {
cancel();
}
this.animation = transformer;
activate();
}
public synchronized void activate() {
lastTime = System.currentTimeMillis();
active = true;
notifyAll();
}
public void cancel() {
active = false;
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author Jason Polites
*
*/
public class FlingAnimation implements Animation {
private float velocityX;
private float velocityY;
private float factor = 0.95f;
private float threshold = 10;
private FlingAnimationListener listener;
/* (non-Javadoc)
* @see com.polites.android.Transformer#update(com.polites.android.GestureImageView, long)
*/
@Override
public boolean update(GestureImageView view, long time) {
float seconds = (float) time / 1000.0f;
float dx = velocityX * seconds;
float dy = velocityY * seconds;
velocityX *= factor;
velocityY *= factor;
boolean active = (Math.abs(velocityX) > threshold && Math.abs(velocityY) > threshold);
if(listener != null) {
listener.onMove(dx, dy);
if(!active) {
listener.onComplete();
}
}
return active;
}
public void setVelocityX(float velocityX) {
this.velocityX = velocityX;
}
public void setVelocityY(float velocityY) {
this.velocityY = velocityY;
}
public void setFactor(float factor) {
this.factor = factor;
}
public void setListener(FlingAnimationListener listener) {
this.listener = listener;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author Jason Polites
*
*/
public interface FlingAnimationListener {
public void onMove(float x, float y);
public void onComplete();
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
/**
* @author Jason Polites
*
*/
public class FlingListener extends SimpleOnGestureListener {
private float velocityX;
private float velocityY;
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
this.velocityX = velocityX;
this.velocityY = velocityY;
return true;
}
public float getVelocityX() {
return velocityX;
}
public float getVelocityY() {
return velocityY;
}
}

View File

@ -0,0 +1,713 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
import android.content.Context;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import java.io.InputStream;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class GestureImageView extends ImageView {
public static final String GLOBAL_NS = "http://schemas.android.com/apk/res/android";
public static final String LOCAL_NS = "http://schemas.polites.com/android";
private final Semaphore drawLock = new Semaphore(0);
private Animator animator;
private Drawable drawable;
private float x = 0, y = 0;
private boolean layout = false;
private float scaleAdjust = 1.0f;
private float startingScale = -1.0f;
private float scale = 1.0f;
private float maxScale = 5.0f;
private float minScale = 0.75f;
private float fitScaleHorizontal = 1.0f;
private float fitScaleVertical = 1.0f;
private float rotation = 0.0f;
private float centerX;
private float centerY;
private Float startX, startY;
private int hWidth;
private int hHeight;
private int resId = -1;
private boolean recycle = false;
private boolean strict = false;
private int displayHeight;
private int displayWidth;
private int alpha = 255;
private ColorFilter colorFilter;
private int deviceOrientation = -1;
private int imageOrientation;
private GestureImageViewListener gestureImageViewListener;
private GestureImageViewTouchListener gestureImageViewTouchListener;
private OnTouchListener customOnTouchListener;
private OnClickListener onClickListener;
public GestureImageView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs);
}
public GestureImageView(Context context, AttributeSet attrs) {
super(context, attrs);
String scaleType = attrs.getAttributeValue(GLOBAL_NS, "scaleType");
if (scaleType == null || scaleType.trim().length() == 0) {
setScaleType(ScaleType.CENTER_INSIDE);
}
String strStartX = attrs.getAttributeValue(LOCAL_NS, "start-x");
String strStartY = attrs.getAttributeValue(LOCAL_NS, "start-y");
if (strStartX != null && strStartX.trim().length() > 0) {
startX = Float.parseFloat(strStartX);
}
if (strStartY != null && strStartY.trim().length() > 0) {
startY = Float.parseFloat(strStartY);
}
setStartingScale(attrs.getAttributeFloatValue(LOCAL_NS, "start-scale", startingScale));
setMinScale(attrs.getAttributeFloatValue(LOCAL_NS, "min-scale", minScale));
setMaxScale(attrs.getAttributeFloatValue(LOCAL_NS, "max-scale", maxScale));
setStrict(attrs.getAttributeBooleanValue(LOCAL_NS, "strict", strict));
setRecycle(attrs.getAttributeBooleanValue(LOCAL_NS, "recycle", recycle));
initImage();
}
public GestureImageView(Context context) {
super(context);
setScaleType(ScaleType.CENTER_INSIDE);
initImage();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (drawable != null) {
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
displayHeight = MeasureSpec.getSize(heightMeasureSpec);
if (getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
float ratio = (float) getImageWidth() / (float) getImageHeight();
displayWidth = Math.round((float) displayHeight * ratio);
} else {
displayWidth = MeasureSpec.getSize(widthMeasureSpec);
}
} else {
displayWidth = MeasureSpec.getSize(widthMeasureSpec);
if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
float ratio = (float) getImageHeight() / (float) getImageWidth();
displayHeight = Math.round((float) displayWidth * ratio);
} else {
displayHeight = MeasureSpec.getSize(heightMeasureSpec);
}
}
} else {
displayHeight = MeasureSpec.getSize(heightMeasureSpec);
displayWidth = MeasureSpec.getSize(widthMeasureSpec);
}
setMeasuredDimension(displayWidth, displayHeight);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed || !layout) {
setupCanvas(displayWidth, displayHeight, getResources().getConfiguration().orientation);
}
}
protected void setupCanvas(int measuredWidth, int measuredHeight, int orientation) {
if (deviceOrientation != orientation) {
layout = false;
deviceOrientation = orientation;
}
if (drawable != null && !layout) {
int imageWidth = getImageWidth();
int imageHeight = getImageHeight();
hWidth = Math.round(((float) imageWidth / 2.0f));
hHeight = Math.round(((float) imageHeight / 2.0f));
measuredWidth -= (getPaddingLeft() + getPaddingRight());
measuredHeight -= (getPaddingTop() + getPaddingBottom());
computeCropScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
if (startingScale <= 0.0f) {
computeStartingScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
}
scaleAdjust = startingScale;
this.centerX = (float) measuredWidth / 2.0f;
this.centerY = (float) measuredHeight / 2.0f;
if (startX == null) {
x = centerX;
} else {
x = startX;
}
if (startY == null) {
y = centerY;
} else {
y = startY;
}
gestureImageViewTouchListener = new GestureImageViewTouchListener(this, measuredWidth, measuredHeight);
if (isLandscape()) {
gestureImageViewTouchListener.setMinScale(minScale * fitScaleHorizontal);
} else {
gestureImageViewTouchListener.setMinScale(minScale * fitScaleVertical);
}
gestureImageViewTouchListener.setMaxScale(maxScale * startingScale);
gestureImageViewTouchListener.setFitScaleHorizontal(fitScaleHorizontal);
gestureImageViewTouchListener.setFitScaleVertical(fitScaleVertical);
gestureImageViewTouchListener.setCanvasWidth(measuredWidth);
gestureImageViewTouchListener.setCanvasHeight(measuredHeight);
gestureImageViewTouchListener.setOnClickListener(onClickListener);
drawable.setBounds(-hWidth, -hHeight, hWidth, hHeight);
super.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (customOnTouchListener != null) {
customOnTouchListener.onTouch(v, event);
}
return gestureImageViewTouchListener.onTouch(v, event);
}
});
layout = true;
}
}
protected void computeCropScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
fitScaleHorizontal = (float) measuredWidth / (float) imageWidth;
fitScaleVertical = (float) measuredHeight / (float) imageHeight;
}
protected void computeStartingScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
switch (getScaleType()) {
case CENTER:
// Center the image in the view, but perform no scaling.
startingScale = 1.0f;
break;
case CENTER_CROP:
// Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
// (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).
startingScale = Math.max((float) measuredHeight / (float) imageHeight, (float) measuredWidth / (float) imageWidth);
break;
case CENTER_INSIDE:
// Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
// (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
float wRatio = (float) imageWidth / (float) measuredWidth;
float hRatio = (float) imageHeight / (float) measuredHeight;
if (wRatio > hRatio) {
startingScale = fitScaleHorizontal;
} else {
startingScale = fitScaleVertical;
}
break;
}
}
protected boolean isRecycled() {
if (drawable != null && drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (bitmap != null) {
return bitmap.isRecycled();
}
}
return false;
}
protected void recycle() {
if (recycle && drawable != null && drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (bitmap != null) {
bitmap.recycle();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
if (layout) {
if (drawable != null && !isRecycled()) {
canvas.save();
float adjustedScale = scale * scaleAdjust;
canvas.translate(x, y);
if (rotation != 0.0f) {
canvas.rotate(rotation);
}
if (adjustedScale != 1.0f) {
canvas.scale(adjustedScale, adjustedScale);
}
drawable.draw(canvas);
canvas.restore();
}
if (drawLock.availablePermits() <= 0) {
drawLock.release();
}
}
}
/**
* Waits for a draw
*
* @param max time to wait for draw (ms)
* @throws InterruptedException
*/
public boolean waitForDraw(long timeout) throws InterruptedException {
return drawLock.tryAcquire(timeout, TimeUnit.MILLISECONDS);
}
@Override
protected void onAttachedToWindow() {
animator = new Animator(this, "GestureImageViewAnimator");
animator.start();
if (resId >= 0 && drawable == null) {
setImageResource(resId);
}
super.onAttachedToWindow();
}
public void animationStart(Animation animation) {
if (animator != null) {
animator.play(animation);
}
}
public void animationStop() {
if (animator != null) {
animator.cancel();
}
}
@Override
protected void onDetachedFromWindow() {
if (animator != null) {
animator.finish();
}
if (recycle && drawable != null && !isRecycled()) {
recycle();
drawable = null;
}
super.onDetachedFromWindow();
}
protected void initImage() {
if (this.drawable != null) {
this.drawable.setAlpha(alpha);
this.drawable.setFilterBitmap(true);
if (colorFilter != null) {
this.drawable.setColorFilter(colorFilter);
}
}
if (!layout) {
requestLayout();
redraw();
}
}
public void setImageBitmap(Bitmap image) {
this.drawable = new BitmapDrawable(getResources(), image);
initImage();
}
@Override
public void setImageDrawable(Drawable drawable) {
this.drawable = drawable;
initImage();
}
public void setImageResource(int id) {
if (this.drawable != null) {
this.recycle();
}
if (id >= 0) {
this.resId = id;
setImageDrawable(getContext().getResources().getDrawable(id));
}
}
public int getScaledWidth() {
return Math.round(getImageWidth() * getScale());
}
public int getScaledHeight() {
return Math.round(getImageHeight() * getScale());
}
public int getImageWidth() {
if (drawable != null) {
return drawable.getIntrinsicWidth();
}
return 0;
}
public int getImageHeight() {
if (drawable != null) {
return drawable.getIntrinsicHeight();
}
return 0;
}
public void moveBy(float x, float y) {
this.x += x;
this.y += y;
}
public void setPosition(float x, float y) {
this.x = x;
this.y = y;
}
public void redraw() {
postInvalidate();
}
public void setMinScale(float min) {
this.minScale = min;
if (gestureImageViewTouchListener != null) {
gestureImageViewTouchListener.setMinScale(min * fitScaleHorizontal);
}
}
public void setMaxScale(float max) {
this.maxScale = max;
if (gestureImageViewTouchListener != null) {
gestureImageViewTouchListener.setMaxScale(max * startingScale);
}
}
public void setScale(float scale) {
scaleAdjust = scale;
}
public float getScale() {
return scaleAdjust;
}
public float getImageX() {
return x;
}
public float getImageY() {
return y;
}
public boolean isStrict() {
return strict;
}
public void setStrict(boolean strict) {
this.strict = strict;
}
public boolean isRecycle() {
return recycle;
}
public void setRecycle(boolean recycle) {
this.recycle = recycle;
}
public void reset() {
x = centerX;
y = centerY;
scaleAdjust = startingScale;
if (gestureImageViewTouchListener != null) {
gestureImageViewTouchListener.reset();
}
redraw();
}
public void setRotation(float rotation) {
this.rotation = rotation;
}
public void setGestureImageViewListener(GestureImageViewListener pinchImageViewListener) {
this.gestureImageViewListener = pinchImageViewListener;
}
public GestureImageViewListener getGestureImageViewListener() {
return gestureImageViewListener;
}
@Override
public Drawable getDrawable() {
return drawable;
}
@Override
public void setAlpha(int alpha) {
this.alpha = alpha;
if (drawable != null) {
drawable.setAlpha(alpha);
}
}
@Override
public void setColorFilter(ColorFilter cf) {
this.colorFilter = cf;
if (drawable != null) {
drawable.setColorFilter(cf);
}
}
@Override
public void setImageURI(Uri mUri) {
if ("content".equals(mUri.getScheme())) {
try {
String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
Cursor cur = getContext().getContentResolver().query(mUri, orientationColumn, null, null, null);
if (cur != null && cur.moveToFirst()) {
imageOrientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
}
InputStream in = null;
try {
in = getContext().getContentResolver().openInputStream(mUri);
Bitmap bmp = BitmapFactory.decodeStream(in);
if (imageOrientation != 0) {
Matrix m = new Matrix();
m.postRotate(imageOrientation);
Bitmap rotated = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
bmp.recycle();
setImageDrawable(new BitmapDrawable(getResources(), rotated));
} else {
setImageDrawable(new BitmapDrawable(getResources(), bmp));
}
} finally {
if (in != null) {
in.close();
}
if (cur != null) {
cur.close();
}
}
} catch (Exception e) {
Log.w("GestureImageView", "Unable to open content: " + mUri, e);
}
} else {
setImageDrawable(Drawable.createFromPath(mUri.toString()));
}
if (drawable == null) {
Log.e("GestureImageView", "resolveUri failed on bad bitmap uri: " + mUri);
// Don't try again.
mUri = null;
}
}
@Override
public Matrix getImageMatrix() {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
return super.getImageMatrix();
}
@Override
public void setScaleType(ScaleType scaleType) {
if (scaleType == ScaleType.CENTER ||
scaleType == ScaleType.CENTER_CROP ||
scaleType == ScaleType.CENTER_INSIDE) {
super.setScaleType(scaleType);
} else if (strict) {
throw new UnsupportedOperationException("Not supported");
}
}
@Override
public void invalidateDrawable(Drawable dr) {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
super.invalidateDrawable(dr);
}
@Override
public int[] onCreateDrawableState(int extraSpace) {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
return super.onCreateDrawableState(extraSpace);
}
@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
super.setAdjustViewBounds(adjustViewBounds);
}
@Override
public void setImageLevel(int level) {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
super.setImageLevel(level);
}
@Override
public void setImageMatrix(Matrix matrix) {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
}
@Override
public void setImageState(int[] state, boolean merge) {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
}
@Override
public void setSelected(boolean selected) {
if (strict) {
throw new UnsupportedOperationException("Not supported");
}
super.setSelected(selected);
}
@Override
public void setOnTouchListener(OnTouchListener l) {
this.customOnTouchListener = l;
}
public float getCenterX() {
return centerX;
}
public float getCenterY() {
return centerY;
}
public boolean isLandscape() {
return getImageWidth() >= getImageHeight();
}
public boolean isPortrait() {
return getImageWidth() <= getImageHeight();
}
public void setStartingScale(float startingScale) {
this.startingScale = startingScale;
}
public void setStartingPosition(float x, float y) {
this.startX = x;
this.startY = y;
}
@Override
public void setOnClickListener(OnClickListener l) {
this.onClickListener = l;
if (gestureImageViewTouchListener != null) {
gestureImageViewTouchListener.setOnClickListener(l);
}
}
/**
* Returns true if the image dimensions are aligned with the orientation of the device.
*
* @return
*/
public boolean isOrientationAligned() {
if (deviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
return isLandscape();
} else if (deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
return isPortrait();
}
return true;
}
public int getDeviceOrientation() {
return deviceOrientation;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author jasonpolites
*
*/
public interface GestureImageViewListener {
public void onTouch(float x, float y);
public void onScale(float scale);
public void onPosition(float x, float y);
}

View File

@ -0,0 +1,540 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
import android.content.res.Configuration;
import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
public class GestureImageViewTouchListener implements OnTouchListener {
private GestureImageView image;
private OnClickListener onClickListener;
private final PointF current = new PointF();
private final PointF last = new PointF();
private final PointF next = new PointF();
private final PointF midpoint = new PointF();
private final VectorF scaleVector = new VectorF();
private final VectorF pinchVector = new VectorF();
private boolean touched = false;
private boolean inZoom = false;
private float initialDistance;
private float lastScale = 1.0f;
private float currentScale = 1.0f;
private float boundaryLeft = 0;
private float boundaryTop = 0;
private float boundaryRight = 0;
private float boundaryBottom = 0;
private float maxScale = 5.0f;
private float minScale = 0.25f;
private float fitScaleHorizontal = 1.0f;
private float fitScaleVertical = 1.0f;
private int canvasWidth = 0;
private int canvasHeight = 0;
private float centerX = 0;
private float centerY = 0;
private float startingScale = 0;
private boolean canDragX = false;
private boolean canDragY = false;
private boolean multiTouch = false;
private int displayWidth;
private int displayHeight;
private int imageWidth;
private int imageHeight;
private FlingListener flingListener;
private FlingAnimation flingAnimation;
private ZoomAnimation zoomAnimation;
private MoveAnimation moveAnimation;
private GestureDetector tapDetector;
private GestureDetector flingDetector;
private GestureImageViewListener imageListener;
public GestureImageViewTouchListener(final GestureImageView image, int displayWidth, int displayHeight) {
super();
this.image = image;
this.displayWidth = displayWidth;
this.displayHeight = displayHeight;
this.centerX = (float) displayWidth / 2.0f;
this.centerY = (float) displayHeight / 2.0f;
this.imageWidth = image.getImageWidth();
this.imageHeight = image.getImageHeight();
startingScale = image.getScale();
currentScale = startingScale;
lastScale = startingScale;
boundaryRight = displayWidth;
boundaryBottom = displayHeight;
boundaryLeft = 0;
boundaryTop = 0;
next.x = image.getImageX();
next.y = image.getImageY();
flingListener = new FlingListener();
flingAnimation = new FlingAnimation();
zoomAnimation = new ZoomAnimation();
moveAnimation = new MoveAnimation();
flingAnimation.setListener(new FlingAnimationListener() {
@Override
public void onMove(float x, float y) {
handleDrag(current.x + x, current.y + y);
}
@Override
public void onComplete() {}
});
zoomAnimation.setZoom(2.0f);
zoomAnimation.setZoomAnimationListener(new ZoomAnimationListener() {
@Override
public void onZoom(float scale, float x, float y) {
if(scale <= maxScale && scale >= minScale) {
handleScale(scale, x, y);
}
}
@Override
public void onComplete() {
inZoom = false;
handleUp();
}
});
moveAnimation.setMoveAnimationListener(new MoveAnimationListener() {
@Override
public void onMove(float x, float y) {
image.setPosition(x, y);
image.redraw();
}
});
tapDetector = new GestureDetector(image.getContext(), new SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
startZoom(e);
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if(!inZoom) {
if(onClickListener != null) {
onClickListener.onClick(image);
return true;
}
}
return false;
}
});
flingDetector = new GestureDetector(image.getContext(), flingListener);
imageListener = image.getGestureImageViewListener();
calculateBoundaries();
}
private void startFling() {
flingAnimation.setVelocityX(flingListener.getVelocityX());
flingAnimation.setVelocityY(flingListener.getVelocityY());
image.animationStart(flingAnimation);
}
private void startZoom(MotionEvent e) {
inZoom = true;
zoomAnimation.reset();
float zoomTo;
if(image.isLandscape()) {
if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
int scaledHeight = image.getScaledHeight();
if(scaledHeight < canvasHeight) {
zoomTo = fitScaleVertical / currentScale;
zoomAnimation.setTouchX(e.getX());
zoomAnimation.setTouchY(image.getCenterY());
}
else {
zoomTo = fitScaleHorizontal / currentScale;
zoomAnimation.setTouchX(image.getCenterX());
zoomAnimation.setTouchY(image.getCenterY());
}
}
else {
int scaledWidth = image.getScaledWidth();
if(scaledWidth == canvasWidth) {
zoomTo = currentScale*4.0f;
zoomAnimation.setTouchX(e.getX());
zoomAnimation.setTouchY(e.getY());
}
else if(scaledWidth < canvasWidth) {
zoomTo = fitScaleHorizontal / currentScale;
zoomAnimation.setTouchX(image.getCenterX());
zoomAnimation.setTouchY(e.getY());
}
else {
zoomTo = fitScaleHorizontal / currentScale;
zoomAnimation.setTouchX(image.getCenterX());
zoomAnimation.setTouchY(image.getCenterY());
}
}
}
else {
if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
int scaledHeight = image.getScaledHeight();
if(scaledHeight == canvasHeight) {
zoomTo = currentScale*4.0f;
zoomAnimation.setTouchX(e.getX());
zoomAnimation.setTouchY(e.getY());
}
else if(scaledHeight < canvasHeight) {
zoomTo = fitScaleVertical / currentScale;
zoomAnimation.setTouchX(e.getX());
zoomAnimation.setTouchY(image.getCenterY());
}
else {
zoomTo = fitScaleVertical / currentScale;
zoomAnimation.setTouchX(image.getCenterX());
zoomAnimation.setTouchY(image.getCenterY());
}
}
else {
int scaledWidth = image.getScaledWidth();
if(scaledWidth < canvasWidth) {
zoomTo = fitScaleHorizontal / currentScale;
zoomAnimation.setTouchX(image.getCenterX());
zoomAnimation.setTouchY(e.getY());
}
else {
zoomTo = fitScaleVertical / currentScale;
zoomAnimation.setTouchX(image.getCenterX());
zoomAnimation.setTouchY(image.getCenterY());
}
}
}
zoomAnimation.setZoom(zoomTo);
image.animationStart(zoomAnimation);
}
private void stopAnimations() {
image.animationStop();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if(!inZoom) {
if(!tapDetector.onTouchEvent(event)) {
if(event.getPointerCount() == 1 && flingDetector.onTouchEvent(event)) {
startFling();
}
if(event.getAction() == MotionEvent.ACTION_UP) {
handleUp();
}
else if(event.getAction() == MotionEvent.ACTION_DOWN) {
stopAnimations();
last.x = event.getX();
last.y = event.getY();
if(imageListener != null) {
imageListener.onTouch(last.x, last.y);
}
touched = true;
}
else if(event.getAction() == MotionEvent.ACTION_MOVE) {
if(event.getPointerCount() > 1) {
multiTouch = true;
if(initialDistance > 0) {
pinchVector.set(event);
pinchVector.calculateLength();
float distance = pinchVector.length;
if(initialDistance != distance) {
float newScale = (distance / initialDistance) * lastScale;
if(newScale <= maxScale) {
scaleVector.length *= newScale;
scaleVector.calculateEndPoint();
scaleVector.length /= newScale;
float newX = scaleVector.end.x;
float newY = scaleVector.end.y;
handleScale(newScale, newX, newY);
}
}
}
else {
initialDistance = MathUtils.distance(event);
MathUtils.midpoint(event, midpoint);
scaleVector.setStart(midpoint);
scaleVector.setEnd(next);
scaleVector.calculateLength();
scaleVector.calculateAngle();
scaleVector.length /= lastScale;
}
}
else {
if(!touched) {
touched = true;
last.x = event.getX();
last.y = event.getY();
next.x = image.getImageX();
next.y = image.getImageY();
}
else if(!multiTouch) {
if(handleDrag(event.getX(), event.getY())) {
image.redraw();
}
}
}
}
}
}
return true;
}
protected void handleUp() {
multiTouch = false;
initialDistance = 0;
lastScale = currentScale;
if(!canDragX) {
next.x = centerX;
}
if(!canDragY) {
next.y = centerY;
}
boundCoordinates();
if(!canDragX && !canDragY) {
if(image.isLandscape()) {
currentScale = fitScaleHorizontal;
lastScale = fitScaleHorizontal;
}
else {
currentScale = fitScaleVertical;
lastScale = fitScaleVertical;
}
}
image.setScale(currentScale);
image.setPosition(next.x, next.y);
if(imageListener != null) {
imageListener.onScale(currentScale);
imageListener.onPosition(next.x, next.y);
}
image.redraw();
}
protected void handleScale(float scale, float x, float y) {
currentScale = scale;
if(currentScale > maxScale) {
currentScale = maxScale;
}
else if (currentScale < minScale) {
currentScale = minScale;
}
else {
next.x = x;
next.y = y;
}
calculateBoundaries();
image.setScale(currentScale);
image.setPosition(next.x, next.y);
if(imageListener != null) {
imageListener.onScale(currentScale);
imageListener.onPosition(next.x, next.y);
}
image.redraw();
}
protected boolean handleDrag(float x, float y) {
current.x = x;
current.y = y;
float diffX = (current.x - last.x);
float diffY = (current.y - last.y);
if(diffX != 0 || diffY != 0) {
if(canDragX) next.x += diffX;
if(canDragY) next.y += diffY;
boundCoordinates();
last.x = current.x;
last.y = current.y;
if(canDragX || canDragY) {
image.setPosition(next.x, next.y);
if(imageListener != null) {
imageListener.onPosition(next.x, next.y);
}
return true;
}
}
return false;
}
public void reset() {
currentScale = startingScale;
next.x = centerX;
next.y = centerY;
calculateBoundaries();
image.setScale(currentScale);
image.setPosition(next.x, next.y);
image.redraw();
}
public float getMaxScale() {
return maxScale;
}
public void setMaxScale(float maxScale) {
this.maxScale = maxScale;
}
public float getMinScale() {
return minScale;
}
public void setMinScale(float minScale) {
this.minScale = minScale;
}
public void setOnClickListener(OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
protected void setCanvasWidth(int canvasWidth) {
this.canvasWidth = canvasWidth;
}
protected void setCanvasHeight(int canvasHeight) {
this.canvasHeight = canvasHeight;
}
protected void setFitScaleHorizontal(float fitScale) {
this.fitScaleHorizontal = fitScale;
}
protected void setFitScaleVertical(float fitScaleVertical) {
this.fitScaleVertical = fitScaleVertical;
}
protected void boundCoordinates() {
if(next.x < boundaryLeft) {
next.x = boundaryLeft;
}
else if(next.x > boundaryRight) {
next.x = boundaryRight;
}
if(next.y < boundaryTop) {
next.y = boundaryTop;
}
else if(next.y > boundaryBottom) {
next.y = boundaryBottom;
}
}
protected void calculateBoundaries() {
int effectiveWidth = Math.round( (float) imageWidth * currentScale );
int effectiveHeight = Math.round( (float) imageHeight * currentScale );
canDragX = effectiveWidth > displayWidth;
canDragY = effectiveHeight > displayHeight;
if(canDragX) {
float diff = (float)(effectiveWidth - displayWidth) / 2.0f;
boundaryLeft = centerX - diff;
boundaryRight = centerX + diff;
}
if(canDragY) {
float diff = (float)(effectiveHeight - displayHeight) / 2.0f;
boundaryTop = centerY - diff;
boundaryBottom = centerY + diff;
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
import android.graphics.PointF;
import android.util.FloatMath;
import android.view.MotionEvent;
public class MathUtils {
public static float distance(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
public static float distance(PointF p1, PointF p2) {
float x = p1.x - p2.x;
float y = p1.y - p2.y;
return FloatMath.sqrt(x * x + y * y);
}
public static float distance(float x1, float y1, float x2, float y2) {
float x = x1 - x2;
float y = y1 - y2;
return FloatMath.sqrt(x * x + y * y);
}
public static void midpoint(MotionEvent event, PointF point) {
float x1 = event.getX(0);
float y1 = event.getY(0);
float x2 = event.getX(1);
float y2 = event.getY(1);
midpoint(x1, y1, x2, y2, point);
}
public static void midpoint(float x1, float y1, float x2, float y2, PointF point) {
point.x = (x1 + x2) / 2.0f;
point.y = (y1 + y2) / 2.0f;
}
/**
* Rotates p1 around p2 by angle degrees.
*
* @param p1
* @param p2
* @param angle
*/
public void rotate(PointF p1, PointF p2, float angle) {
float px = p1.x;
float py = p1.y;
float ox = p2.x;
float oy = p2.y;
p1.x = (FloatMath.cos(angle) * (px - ox) - FloatMath.sin(angle) * (py - oy) + ox);
p1.y = (FloatMath.sin(angle) * (px - ox) + FloatMath.cos(angle) * (py - oy) + oy);
}
public static float angle(PointF p1, PointF p2) {
return angle(p1.x, p1.y, p2.x, p2.y);
}
public static float angle(float x1, float y1, float x2, float y2) {
return (float) Math.atan2(y2 - y1, x2 - x1);
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author Jason Polites
*
*/
public class MoveAnimation implements Animation {
private boolean firstFrame = true;
private float startX;
private float startY;
private float targetX;
private float targetY;
private long animationTimeMS = 100;
private long totalTime = 0;
private MoveAnimationListener moveAnimationListener;
/* (non-Javadoc)
* @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
*/
@Override
public boolean update(GestureImageView view, long time) {
totalTime += time;
if(firstFrame) {
firstFrame = false;
startX = view.getImageX();
startY = view.getImageY();
}
if(totalTime < animationTimeMS) {
float ratio = (float) totalTime / animationTimeMS;
float newX = ((targetX - startX) * ratio) + startX;
float newY = ((targetY - startY) * ratio) + startY;
if(moveAnimationListener != null) {
moveAnimationListener.onMove(newX, newY);
}
return true;
}
else {
if(moveAnimationListener != null) {
moveAnimationListener.onMove(targetX, targetY);
}
}
return false;
}
public void reset() {
firstFrame = true;
totalTime = 0;
}
public float getTargetX() {
return targetX;
}
public void setTargetX(float targetX) {
this.targetX = targetX;
}
public float getTargetY() {
return targetY;
}
public void setTargetY(float targetY) {
this.targetY = targetY;
}
public long getAnimationTimeMS() {
return animationTimeMS;
}
public void setAnimationTimeMS(long animationTimeMS) {
this.animationTimeMS = animationTimeMS;
}
public void setMoveAnimationListener(MoveAnimationListener moveAnimationListener) {
this.moveAnimationListener = moveAnimationListener;
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author Jason Polites
*
*/
public interface MoveAnimationListener {
public void onMove(float x, float y);
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
import android.graphics.PointF;
import android.util.FloatMath;
import android.view.MotionEvent;
public class VectorF {
public float angle;
public float length;
public final PointF start = new PointF();
public final PointF end = new PointF();
public void calculateEndPoint() {
end.x = FloatMath.cos(angle) * length + start.x;
end.y = FloatMath.sin(angle) * length + start.y;
}
public void setStart(PointF p) {
this.start.x = p.x;
this.start.y = p.y;
}
public void setEnd(PointF p) {
this.end.x = p.x;
this.end.y = p.y;
}
public void set(MotionEvent event) {
this.start.x = event.getX(0);
this.start.y = event.getY(0);
this.end.x = event.getX(1);
this.end.y = event.getY(1);
}
public float calculateLength() {
length = MathUtils.distance(start, end);
return length;
}
public float calculateAngle() {
angle = MathUtils.angle(start, end);
return angle;
}
}

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
import android.graphics.PointF;
/**
* @author Jason Polites
*
*/
public class ZoomAnimation implements Animation {
private boolean firstFrame = true;
private float touchX;
private float touchY;
private float zoom;
private float startX;
private float startY;
private float startScale;
private float xDiff;
private float yDiff;
private float scaleDiff;
private long animationLengthMS = 200;
private long totalTime = 0;
private ZoomAnimationListener zoomAnimationListener;
/* (non-Javadoc)
* @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
*/
@Override
public boolean update(GestureImageView view, long time) {
if(firstFrame) {
firstFrame = false;
startX = view.getImageX();
startY = view.getImageY();
startScale = view.getScale();
scaleDiff = (zoom * startScale) - startScale;
if(scaleDiff > 0) {
// Calculate destination for midpoint
VectorF vector = new VectorF();
// Set the touch point as start because we want to move the end
vector.setStart(new PointF(touchX, touchY));
vector.setEnd(new PointF(startX, startY));
vector.calculateAngle();
// Get the current length
float length = vector.calculateLength();
// Multiply length by zoom to get the new length
vector.length = length*zoom;
// Now deduce the new endpoint
vector.calculateEndPoint();
xDiff = vector.end.x - startX;
yDiff = vector.end.y - startY;
}
else {
// Zoom out to center
xDiff = view.getCenterX() - startX;
yDiff = view.getCenterY() - startY;
}
}
totalTime += time;
float ratio = (float) totalTime / (float) animationLengthMS;
if(ratio < 1) {
if(ratio > 0) {
// we still have time left
float newScale = (ratio * scaleDiff) + startScale;
float newX = (ratio * xDiff) + startX;
float newY = (ratio * yDiff) + startY;
if(zoomAnimationListener != null) {
zoomAnimationListener.onZoom(newScale, newX, newY);
}
}
return true;
}
else {
float newScale = scaleDiff + startScale;
float newX = xDiff + startX;
float newY = yDiff + startY;
if(zoomAnimationListener != null) {
zoomAnimationListener.onZoom(newScale, newX, newY);
zoomAnimationListener.onComplete();
}
return false;
}
}
public void reset() {
firstFrame = true;
totalTime = 0;
}
public float getZoom() {
return zoom;
}
public void setZoom(float zoom) {
this.zoom = zoom;
}
public float getTouchX() {
return touchX;
}
public void setTouchX(float touchX) {
this.touchX = touchX;
}
public float getTouchY() {
return touchY;
}
public void setTouchY(float touchY) {
this.touchY = touchY;
}
public long getAnimationLengthMS() {
return animationLengthMS;
}
public void setAnimationLengthMS(long animationLengthMS) {
this.animationLengthMS = animationLengthMS;
}
public ZoomAnimationListener getZoomAnimationListener() {
return zoomAnimationListener;
}
public void setZoomAnimationListener(ZoomAnimationListener zoomAnimationListener) {
this.zoomAnimationListener = zoomAnimationListener;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2012 Jason Polites
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.polites.android;
/**
* @author Jason Polites
*
*/
public interface ZoomAnimationListener {
public void onZoom(float scale, float x, float y);
public void onComplete();
}

View File

@ -7,22 +7,10 @@ The following files were *not* copied into the new Gradle project; you
should evaluate whether these are still needed in your project and if
so manually move them:
* .idea/
* .idea/.name
* .idea/compiler.xml
* .idea/copyright/
* .idea/copyright/profiles_settings.xml
* .idea/libraries/
* .idea/libraries/PdfViewer.xml
* .idea/misc.xml
* .idea/modules.xml
* .idea/vcs.xml
* .idea/workspace.xml
* Android-Pdf-Viewer-Library.iml
* PdfViewer.jar
* README.txt
* .DS_Store
* .gitignore
* build.xml
* default.properties
* gesture-imageview.iml
* proguard.cfg
Moved Files:
@ -30,28 +18,11 @@ Moved Files:
Android Gradle projects use a different directory structure than ADT
Eclipse projects. Here's how the projects were restructured:
* AndroidManifest.xml => app/src/main/AndroidManifest.xml
* crypto-src/ => app/src/main/java/
* res/ => app/src/main/res/
* src/ => app/src/main/java/
* src/com/sun/pdfview/.cvsignore => app/src/main/resources/com/sun/pdfview/.cvsignore
* src/com/sun/pdfview/decode/CCITTCodes => app/src/main/resources/com/sun/pdfview/decode/CCITTCodes
* src/com/sun/pdfview/font/res/BaseFonts.properties => app/src/main/resources/com/sun/pdfview/font/res/BaseFonts.properties
* src/com/sun/pdfview/font/res/d050000l.pfb => app/src/main/resources/com/sun/pdfview/font/res/d050000l.pfb
* src/com/sun/pdfview/font/res/n019003l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019003l.pfb
* src/com/sun/pdfview/font/res/n019004l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019004l.pfb
* src/com/sun/pdfview/font/res/n019023l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019023l.pfb
* src/com/sun/pdfview/font/res/n019024l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019024l.pfb
* src/com/sun/pdfview/font/res/n021003l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021003l.pfb
* src/com/sun/pdfview/font/res/n021004l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021004l.pfb
* src/com/sun/pdfview/font/res/n021023l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021023l.pfb
* src/com/sun/pdfview/font/res/n021024l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021024l.pfb
* src/com/sun/pdfview/font/res/n022003l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022003l.pfb
* src/com/sun/pdfview/font/res/n022004l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022004l.pfb
* src/com/sun/pdfview/font/res/n022023l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022023l.pfb
* src/com/sun/pdfview/font/res/n022024l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022024l.pfb
* src/com/sun/pdfview/font/res/s050000l.pfb => app/src/main/resources/com/sun/pdfview/font/res/s050000l.pfb
* src/com/sun/pdfview/font/ttf/resource/glyphlist.txt => app/src/main/resources/com/sun/pdfview/font/ttf/resource/glyphlist.txt
* AndroidManifest.xml => gestureimageview/src/main/AndroidManifest.xml
* src/ => gestureimageview/src/main/java/
* src/.DS_Store => gestureimageview/src/main/resources/.DS_Store
* src/com/.DS_Store => gestureimageview/src/main/resources/com/.DS_Store
* src/com/polites/.DS_Store => gestureimageview/src/main/resources/com/polites/.DS_Store
Next Steps:
-----------

View File

@ -7,8 +7,8 @@
<net.sf.andpdf.pdfviewer.gui.PdfView
android:id="@+id/pdf_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp" />
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More