├── .classpath
├── .gitignore
├── .project
├── AndroidManifest.xml
├── License
├── README.md
├── default.properties
├── res
├── drawable
│ ├── icon.png
│ ├── image.png
│ └── sample.jpg
├── layout
│ └── main.xml
└── values
│ └── strings.xml
└── src
└── com
└── matabii
└── dev
└── scaleimageview
├── ScaleImageView.java
└── ScaleImageViewActivity.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | gen
3 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ScaleImageView
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Kenzo Ishii
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | scale-imageview-android
2 | =======================
3 |
4 | Add pinch-in and pinch-out function to Android ImageVIew.
5 |
6 | * Double-tap to enlarge
7 | * Can pinch to zoom in or out on the view
8 |
9 | Example Code
10 |
11 | Very similar ImageView
12 |
13 | Setting Layout XML
14 |
15 |
20 |
21 | or edit java source
22 |
23 | ScaleImageView image = (ScaleImageView) findViewById(R.id.image);
24 | Bitmap bitmap = BitmapFactory.decodeStream(is);
25 | image.setImageBitmap(bitmap);
26 |
27 | ======
28 | Download the sample application
29 |
30 | http://code.google.com/p/scale-imageview-android/downloads/detail?name=ScaleImageView.apk
31 |
--------------------------------------------------------------------------------
/default.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "build.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-10
12 |
--------------------------------------------------------------------------------
/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matabii/scale-imageview-android/46278621c8e80b51c7d05a33b92207a7d01f78d5/res/drawable/icon.png
--------------------------------------------------------------------------------
/res/drawable/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matabii/scale-imageview-android/46278621c8e80b51c7d05a33b92207a7d01f78d5/res/drawable/image.png
--------------------------------------------------------------------------------
/res/drawable/sample.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matabii/scale-imageview-android/46278621c8e80b51c7d05a33b92207a7d01f78d5/res/drawable/sample.jpg
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ScaleImageView
5 |
6 |
--------------------------------------------------------------------------------
/src/com/matabii/dev/scaleimageview/ScaleImageView.java:
--------------------------------------------------------------------------------
1 | package com.matabii.dev.scaleimageview;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Matrix;
6 | import android.graphics.drawable.Drawable;
7 | import android.util.AttributeSet;
8 | import android.util.FloatMath;
9 | import android.view.GestureDetector;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.view.View.OnTouchListener;
13 | import android.widget.ImageView;
14 |
15 | public class ScaleImageView extends ImageView implements OnTouchListener {
16 | private Context mContext;
17 | private float MAX_SCALE = 2f;
18 |
19 | private Matrix mMatrix;
20 | private final float[] mMatrixValues = new float[9];
21 |
22 | // display width height.
23 | private int mWidth;
24 | private int mHeight;
25 |
26 | private int mIntrinsicWidth;
27 | private int mIntrinsicHeight;
28 |
29 | private float mScale;
30 | private float mMinScale;
31 |
32 | private float mPrevDistance;
33 | private boolean isScaling;
34 |
35 | private int mPrevMoveX;
36 | private int mPrevMoveY;
37 | private GestureDetector mDetector;
38 |
39 | String TAG = "ScaleImageView";
40 |
41 | public ScaleImageView(Context context, AttributeSet attr) {
42 | super(context, attr);
43 | this.mContext = context;
44 | initialize();
45 | }
46 |
47 | public ScaleImageView(Context context) {
48 | super(context);
49 | this.mContext = context;
50 | initialize();
51 | }
52 |
53 | @Override
54 | public void setImageBitmap(Bitmap bm) {
55 | super.setImageBitmap(bm);
56 | this.initialize();
57 | }
58 |
59 | @Override
60 | public void setImageResource(int resId) {
61 | super.setImageResource(resId);
62 | this.initialize();
63 | }
64 |
65 | private void initialize() {
66 | this.setScaleType(ScaleType.MATRIX);
67 | this.mMatrix = new Matrix();
68 | Drawable d = getDrawable();
69 | if (d != null) {
70 | mIntrinsicWidth = d.getIntrinsicWidth();
71 | mIntrinsicHeight = d.getIntrinsicHeight();
72 | setOnTouchListener(this);
73 | }
74 | mDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() {
75 | @Override
76 | public boolean onDoubleTap(MotionEvent e) {
77 | maxZoomTo((int) e.getX(), (int) e.getY());
78 | cutting();
79 | return super.onDoubleTap(e);
80 | }
81 | });
82 |
83 | }
84 |
85 | @Override
86 | protected boolean setFrame(int l, int t, int r, int b) {
87 | mWidth = r - l;
88 | mHeight = b - t;
89 |
90 | mMatrix.reset();
91 | int r_norm = r - l;
92 | mScale = (float) r_norm / (float) mIntrinsicWidth;
93 |
94 | int paddingHeight = 0;
95 | int paddingWidth = 0;
96 | // scaling vertical
97 | if (mScale * mIntrinsicHeight > mHeight) {
98 | mScale = (float) mHeight / (float) mIntrinsicHeight;
99 | mMatrix.postScale(mScale, mScale);
100 | paddingWidth = (r - mWidth) / 2;
101 | paddingHeight = 0;
102 | // scaling horizontal
103 | } else {
104 | mMatrix.postScale(mScale, mScale);
105 | paddingHeight = (b - mHeight) / 2;
106 | paddingWidth = 0;
107 | }
108 | mMatrix.postTranslate(paddingWidth, paddingHeight);
109 |
110 | setImageMatrix(mMatrix);
111 | mMinScale = mScale;
112 | zoomTo(mScale, mWidth / 2, mHeight / 2);
113 | cutting();
114 | return super.setFrame(l, t, r, b);
115 | }
116 |
117 | protected float getValue(Matrix matrix, int whichValue) {
118 | matrix.getValues(mMatrixValues);
119 | return mMatrixValues[whichValue];
120 | }
121 |
122 | protected float getScale() {
123 | return getValue(mMatrix, Matrix.MSCALE_X);
124 | }
125 |
126 | public float getTranslateX() {
127 | return getValue(mMatrix, Matrix.MTRANS_X);
128 | }
129 |
130 | protected float getTranslateY() {
131 | return getValue(mMatrix, Matrix.MTRANS_Y);
132 | }
133 |
134 | protected void maxZoomTo(int x, int y) {
135 | if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) {
136 | // threshold 0.1f
137 | float scale = mMinScale / getScale();
138 | zoomTo(scale, x, y);
139 | } else {
140 | float scale = MAX_SCALE / getScale();
141 | zoomTo(scale, x, y);
142 | }
143 | }
144 |
145 | public void zoomTo(float scale, int x, int y) {
146 | if (getScale() * scale < mMinScale) {
147 | return;
148 | }
149 | if (scale >= 1 && getScale() * scale > MAX_SCALE) {
150 | return;
151 | }
152 | mMatrix.postScale(scale, scale);
153 | // move to center
154 | mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2, -(mHeight * scale - mHeight) / 2);
155 |
156 | // move x and y distance
157 | mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0);
158 | mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale);
159 | setImageMatrix(mMatrix);
160 | }
161 |
162 | public void cutting() {
163 | int width = (int) (mIntrinsicWidth * getScale());
164 | int height = (int) (mIntrinsicHeight * getScale());
165 | if (getTranslateX() < -(width - mWidth)) {
166 | mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0);
167 | }
168 | if (getTranslateX() > 0) {
169 | mMatrix.postTranslate(-getTranslateX(), 0);
170 | }
171 | if (getTranslateY() < -(height - mHeight)) {
172 | mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight));
173 | }
174 | if (getTranslateY() > 0) {
175 | mMatrix.postTranslate(0, -getTranslateY());
176 | }
177 | if (width < mWidth) {
178 | mMatrix.postTranslate((mWidth - width) / 2, 0);
179 | }
180 | if (height < mHeight) {
181 | mMatrix.postTranslate(0, (mHeight - height) / 2);
182 | }
183 | setImageMatrix(mMatrix);
184 | }
185 |
186 | private float distance(float x0, float x1, float y0, float y1) {
187 | float x = x0 - x1;
188 | float y = y0 - y1;
189 | return FloatMath.sqrt(x * x + y * y);
190 | }
191 |
192 | private float dispDistance() {
193 | return FloatMath.sqrt(mWidth * mWidth + mHeight * mHeight);
194 | }
195 |
196 | @Override
197 | public boolean onTouchEvent(MotionEvent event) {
198 | if (mDetector.onTouchEvent(event)) {
199 | return true;
200 | }
201 | int touchCount = event.getPointerCount();
202 | switch (event.getAction()) {
203 | case MotionEvent.ACTION_DOWN:
204 | case MotionEvent.ACTION_POINTER_1_DOWN:
205 | case MotionEvent.ACTION_POINTER_2_DOWN:
206 | if (touchCount >= 2) {
207 | float distance = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
208 | mPrevDistance = distance;
209 | isScaling = true;
210 | } else {
211 | mPrevMoveX = (int) event.getX();
212 | mPrevMoveY = (int) event.getY();
213 | }
214 | case MotionEvent.ACTION_MOVE:
215 | if (touchCount >= 2 && isScaling) {
216 | float dist = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
217 | float scale = (dist - mPrevDistance) / dispDistance();
218 | mPrevDistance = dist;
219 | scale += 1;
220 | scale = scale * scale;
221 | zoomTo(scale, mWidth / 2, mHeight / 2);
222 | cutting();
223 | } else if (!isScaling) {
224 | int distanceX = mPrevMoveX - (int) event.getX();
225 | int distanceY = mPrevMoveY - (int) event.getY();
226 | mPrevMoveX = (int) event.getX();
227 | mPrevMoveY = (int) event.getY();
228 | mMatrix.postTranslate(-distanceX, -distanceY);
229 | cutting();
230 | }
231 | break;
232 | case MotionEvent.ACTION_UP:
233 | case MotionEvent.ACTION_POINTER_UP:
234 | case MotionEvent.ACTION_POINTER_2_UP:
235 | if (event.getPointerCount() <= 1) {
236 | isScaling = false;
237 | }
238 | break;
239 | }
240 | return true;
241 | }
242 |
243 | @Override
244 | public boolean onTouch(View v, MotionEvent event) {
245 | return super.onTouchEvent(event);
246 | }
247 |
248 | }
249 |
--------------------------------------------------------------------------------
/src/com/matabii/dev/scaleimageview/ScaleImageViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.matabii.dev.scaleimageview;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class ScaleImageViewActivity extends Activity {
7 | @Override
8 | public void onCreate(Bundle savedInstanceState) {
9 | super.onCreate(savedInstanceState);
10 | setContentView(R.layout.main);
11 | }
12 | }
--------------------------------------------------------------------------------