ImageShow.java revision 253b0816b2e979a1e4fef1d99df3345483580655
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.gallery3d.filtershow.imageshow; 18 19import android.content.Context; 20import android.graphics.Bitmap; 21import android.graphics.Canvas; 22import android.graphics.Color; 23import android.graphics.Matrix; 24import android.graphics.Paint; 25import android.graphics.Point; 26import android.graphics.Rect; 27import android.graphics.RectF; 28import android.net.Uri; 29import android.os.Handler; 30import android.util.AttributeSet; 31import android.util.Log; 32import android.view.GestureDetector; 33import android.view.GestureDetector.OnDoubleTapListener; 34import android.view.GestureDetector.OnGestureListener; 35import android.view.MotionEvent; 36import android.view.ScaleGestureDetector; 37import android.view.View; 38import android.widget.LinearLayout; 39 40import com.android.gallery3d.filtershow.FilterShowActivity; 41import com.android.gallery3d.filtershow.PanelController; 42import com.android.gallery3d.filtershow.cache.ImageLoader; 43import com.android.gallery3d.filtershow.filters.ImageFilter; 44import com.android.gallery3d.filtershow.presets.ImagePreset; 45 46import java.io.File; 47 48public class ImageShow extends View implements OnGestureListener, 49 ScaleGestureDetector.OnScaleGestureListener, 50 OnDoubleTapListener { 51 52 private static final String LOGTAG = "ImageShow"; 53 private static final boolean ENABLE_HISTORY_SWAP = false; 54 private static final boolean ENABLE_COMPARISON = false; 55 56 protected Paint mPaint = new Paint(); 57 protected static int mTextSize = 24; 58 protected static int mTextPadding = 20; 59 60 protected ImageLoader mImageLoader = null; 61 private boolean mDirtyGeometry = false; 62 63 private Bitmap mBackgroundImage = null; 64 private final boolean USE_BACKGROUND_IMAGE = false; 65 private static int mBackgroundColor = Color.RED; 66 67 private GestureDetector mGestureDetector = null; 68 private ScaleGestureDetector mScaleGestureDetector = null; 69 70 protected Rect mImageBounds = new Rect(); 71 private boolean mOriginalDisabled = false; 72 private boolean mTouchShowOriginal = false; 73 private long mTouchShowOriginalDate = 0; 74 private final long mTouchShowOriginalDelayMin = 200; // 200ms 75 private final long mTouchShowOriginalDelayMax = 300; // 300ms 76 private int mShowOriginalDirection = 0; 77 private static int UNVEIL_HORIZONTAL = 1; 78 private static int UNVEIL_VERTICAL = 2; 79 80 private Point mTouchDown = new Point(); 81 private Point mTouch = new Point(); 82 private boolean mFinishedScalingOperation = false; 83 84 private static int mOriginalTextMargin = 8; 85 private static int mOriginalTextSize = 26; 86 private static String mOriginalText = "Original"; 87 private boolean mZoomIn = false; 88 89 protected GeometryMetadata getGeometry() { 90 return new GeometryMetadata(getImagePreset().mGeoData); 91 } 92 93 private String mToast = null; 94 private boolean mShowToast = false; 95 private boolean mImportantToast = false; 96 97 private PanelController mController = null; 98 99 private FilterShowActivity mActivity = null; 100 101 public static void setDefaultBackgroundColor(int value) { 102 mBackgroundColor = value; 103 } 104 105 public FilterShowActivity getActivity() { 106 return mActivity; 107 } 108 109 public int getDefaultBackgroundColor() { 110 return mBackgroundColor; 111 } 112 113 public static void setTextSize(int value) { 114 mTextSize = value; 115 } 116 117 public static void setTextPadding(int value) { 118 mTextPadding = value; 119 } 120 121 public static void setOriginalTextMargin(int value) { 122 mOriginalTextMargin = value; 123 } 124 125 public static void setOriginalTextSize(int value) { 126 mOriginalTextSize = value; 127 } 128 129 public static void setOriginalText(String text) { 130 mOriginalText = text; 131 } 132 133 private final Handler mHandler = new Handler(); 134 135 public void select() { 136 } 137 138 public void unselect() { 139 } 140 141 public boolean hasModifications() { 142 if (getImagePreset() == null) { 143 return false; 144 } 145 return getImagePreset().hasModifications(); 146 } 147 148 public void resetParameter() { 149 // TODO: implement reset 150 } 151 152 public void setPanelController(PanelController controller) { 153 mController = controller; 154 } 155 156 public PanelController getPanelController() { 157 return mController; 158 } 159 160 public void onNewValue(int parameter) { 161 if (getPanelController() != null) { 162 getPanelController().onNewValue(parameter); 163 } 164 invalidate(); 165 mActivity.enableSave(hasModifications()); 166 } 167 168 public Point getTouchPoint() { 169 return mTouch; 170 } 171 172 public ImageShow(Context context, AttributeSet attrs) { 173 super(context, attrs); 174 175 setupGestureDetector(context); 176 mActivity = (FilterShowActivity) context; 177 MasterImage.getImage().addObserver(this); 178 } 179 180 public ImageShow(Context context) { 181 super(context); 182 183 setupGestureDetector(context); 184 mActivity = (FilterShowActivity) context; 185 MasterImage.getImage().addObserver(this); 186 } 187 188 public void setupGestureDetector(Context context) { 189 mGestureDetector = new GestureDetector(context, this); 190 mScaleGestureDetector = new ScaleGestureDetector(context, this); 191 } 192 193 @Override 194 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 195 int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 196 int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 197 setMeasuredDimension(parentWidth, parentHeight); 198 } 199 200 public ImageFilter getCurrentFilter() { 201 return MasterImage.getImage().getCurrentFilter(); 202 } 203 204 public void showToast(String text) { 205 showToast(text, false); 206 } 207 208 public void showToast(String text, boolean important) { 209 mToast = text; 210 mShowToast = true; 211 mImportantToast = important; 212 invalidate(); 213 214 mHandler.postDelayed(new Runnable() { 215 @Override 216 public void run() { 217 mShowToast = false; 218 invalidate(); 219 } 220 }, 400); 221 } 222 223 public Rect getImageBounds() { 224 Rect dst = new Rect(); 225 getImagePreset().mGeoData.getPhotoBounds().roundOut(dst); 226 return dst; 227 } 228 229 public Rect getImageCropBounds() { 230 return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds()); 231 } 232 233 /* consider moving the following 2 methods into a subclass */ 234 /** 235 * This function calculates a Image to Screen Transformation matrix 236 * 237 * @param reflectRotation set true if you want the rotation encoded 238 * @return Image to Screen transformation matrix 239 */ 240 protected Matrix getImageToScreenMatrix(boolean reflectRotation) { 241 GeometryMetadata geo = getImagePreset().mGeoData; 242 if (geo == null || mImageLoader == null 243 || mImageLoader.getOriginalBounds() == null) { 244 return new Matrix(); 245 } 246 Matrix m = geo.getOriginalToScreen(reflectRotation, 247 mImageLoader.getOriginalBounds().width(), 248 mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); 249 Point translate = MasterImage.getImage().getTranslation(); 250 float scaleFactor = MasterImage.getImage().getScaleFactor(); 251 m.postTranslate(translate.x, translate.y); 252 m.postScale(scaleFactor, scaleFactor, getWidth() / 2.0f, getHeight() / 2.0f); 253 return m; 254 } 255 256 /** 257 * This function calculates a to Screen Image Transformation matrix 258 * 259 * @param reflectRotation set true if you want the rotation encoded 260 * @return Screen to Image transformation matrix 261 */ 262 protected Matrix getScreenToImageMatrix(boolean reflectRotation) { 263 Matrix m = getImageToScreenMatrix(reflectRotation); 264 Matrix invert = new Matrix(); 265 m.invert(invert); 266 return invert; 267 } 268 269 public Rect getDisplayedImageBounds() { 270 return mImageBounds; 271 } 272 273 public ImagePreset getImagePreset() { 274 return MasterImage.getImage().getPreset(); 275 } 276 277 public void drawToast(Canvas canvas) { 278 if (mShowToast && mToast != null) { 279 Paint paint = new Paint(); 280 paint.setTextSize(128); 281 float textWidth = paint.measureText(mToast); 282 int toastX = (int) ((getWidth() - textWidth) / 2.0f); 283 int toastY = (int) (getHeight() / 3.0f); 284 285 paint.setARGB(255, 0, 0, 0); 286 canvas.drawText(mToast, toastX - 2, toastY - 2, paint); 287 canvas.drawText(mToast, toastX - 2, toastY, paint); 288 canvas.drawText(mToast, toastX, toastY - 2, paint); 289 canvas.drawText(mToast, toastX + 2, toastY + 2, paint); 290 canvas.drawText(mToast, toastX + 2, toastY, paint); 291 canvas.drawText(mToast, toastX, toastY + 2, paint); 292 if (mImportantToast) { 293 paint.setARGB(255, 200, 0, 0); 294 } else { 295 paint.setARGB(255, 255, 255, 255); 296 } 297 canvas.drawText(mToast, toastX, toastY, paint); 298 } 299 } 300 301 @Override 302 public void onDraw(Canvas canvas) { 303 MasterImage.getImage().setImageShowSize(getWidth(), getHeight()); 304 305 float cx = canvas.getWidth()/2.0f; 306 float cy = canvas.getHeight()/2.0f; 307 float scaleFactor = MasterImage.getImage().getScaleFactor(); 308 Point translation = MasterImage.getImage().getTranslation(); 309 310 Matrix scalingMatrix = new Matrix(); 311 scalingMatrix.postScale(scaleFactor, scaleFactor, cx, cy); 312 scalingMatrix.preTranslate(translation.x, translation.y); 313 314 RectF unscaledClipRect = new RectF(mImageBounds); 315 scalingMatrix.mapRect(unscaledClipRect, unscaledClipRect); 316 317 canvas.save(); 318 if (!unscaledClipRect.isEmpty()) { 319 canvas.clipRect(unscaledClipRect); 320 } 321 322 canvas.save(); 323 // TODO: center scale on gesture 324 canvas.scale(scaleFactor, scaleFactor, cx, cy); 325 canvas.translate(translation.x, translation.y); 326 drawBackground(canvas); 327 drawImage(canvas, getFilteredImage()); 328 canvas.restore(); 329 330 if (showTitle() && getImagePreset() != null) { 331 mPaint.setARGB(200, 0, 0, 0); 332 mPaint.setTextSize(mTextSize); 333 334 Rect textRect = new Rect(0, 0, getWidth(), mTextSize + mTextPadding); 335 canvas.drawRect(textRect, mPaint); 336 mPaint.setARGB(255, 200, 200, 200); 337 canvas.drawText(getImagePreset().name(), mTextPadding, 338 1.5f * mTextPadding, mPaint); 339 } 340 341 Bitmap partialPreview = MasterImage.getImage().getPartialImage(); 342 if (partialPreview != null) { 343 Rect src = new Rect(0, 0, partialPreview.getWidth(), partialPreview.getHeight()); 344 Rect dest = new Rect(0, 0, getWidth(), getHeight()); 345 canvas.drawBitmap(partialPreview, src, dest, mPaint); 346 } 347 348 canvas.save(); 349 canvas.scale(scaleFactor, scaleFactor, cx, cy); 350 canvas.translate(translation.x, translation.y); 351 drawPartialImage(canvas, getGeometryOnlyImage()); 352 canvas.restore(); 353 354 canvas.restore(); 355 356 drawToast(canvas); 357 } 358 359 public void resetImageCaches(ImageShow caller) { 360 if (mImageLoader == null) { 361 return; 362 } 363 MasterImage.getImage().updatePresets(true); 364 } 365 366 public Bitmap getFiltersOnlyImage() { 367 return MasterImage.getImage().getFiltersOnlyImage(); 368 } 369 370 public Bitmap getGeometryOnlyImage() { 371 return MasterImage.getImage().getGeometryOnlyImage(); 372 } 373 374 public Bitmap getFilteredImage() { 375 return MasterImage.getImage().getFilteredImage(); 376 } 377 378 public void drawImage(Canvas canvas, Bitmap image) { 379 if (image != null) { 380 Rect s = new Rect(0, 0, image.getWidth(), 381 image.getHeight()); 382 383 float scale = GeometryMath.scale(image.getWidth(), image.getHeight(), getWidth(), 384 getHeight()); 385 386 float w = image.getWidth() * scale; 387 float h = image.getHeight() * scale; 388 float ty = (getHeight() - h) / 2.0f; 389 float tx = (getWidth() - w) / 2.0f; 390 391 Rect d = new Rect((int) tx, (int) ty, (int) (w + tx), 392 (int) (h + ty)); 393 mImageBounds = d; 394 canvas.drawBitmap(image, s, d, mPaint); 395 } 396 } 397 398 public void drawPartialImage(Canvas canvas, Bitmap image) { 399 if (!mTouchShowOriginal) 400 return; 401 canvas.save(); 402 if (image != null) { 403 if (mShowOriginalDirection == 0) { 404 if (Math.abs(mTouch.y - mTouchDown.y) > Math.abs(mTouch.x - mTouchDown.x)) { 405 mShowOriginalDirection = UNVEIL_VERTICAL; 406 } else { 407 mShowOriginalDirection = UNVEIL_HORIZONTAL; 408 } 409 } 410 411 int px = 0; 412 int py = 0; 413 if (mShowOriginalDirection == UNVEIL_VERTICAL) { 414 px = mImageBounds.width(); 415 py = (int) (mTouch.y - mImageBounds.top); 416 } else { 417 px = (int) (mTouch.x - mImageBounds.left); 418 py = mImageBounds.height(); 419 } 420 421 Rect d = new Rect(mImageBounds.left, mImageBounds.top, 422 mImageBounds.left + px, mImageBounds.top + py); 423 canvas.clipRect(d); 424 drawImage(canvas, image); 425 Paint paint = new Paint(); 426 paint.setColor(Color.BLACK); 427 paint.setStrokeWidth(3); 428 429 if (mShowOriginalDirection == UNVEIL_VERTICAL) { 430 canvas.drawLine(mImageBounds.left, mTouch.y, 431 mImageBounds.right, mTouch.y, paint); 432 } else { 433 canvas.drawLine(mTouch.x, mImageBounds.top, 434 mTouch.x, mImageBounds.bottom, paint); 435 } 436 437 Rect bounds = new Rect(); 438 paint.setAntiAlias(true); 439 paint.setTextSize(mOriginalTextSize); 440 paint.getTextBounds(mOriginalText, 0, mOriginalText.length(), bounds); 441 paint.setColor(Color.BLACK); 442 paint.setStyle(Paint.Style.STROKE); 443 paint.setStrokeWidth(3); 444 canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin, 445 mImageBounds.top + bounds.height() + mOriginalTextMargin, paint); 446 paint.setStyle(Paint.Style.FILL); 447 paint.setStrokeWidth(1); 448 paint.setColor(Color.WHITE); 449 canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin, 450 mImageBounds.top + bounds.height() + mOriginalTextMargin, paint); 451 } 452 canvas.restore(); 453 } 454 455 public void drawBackground(Canvas canvas) { 456 if (USE_BACKGROUND_IMAGE) { 457 if (mBackgroundImage == null) { 458 mBackgroundImage = mImageLoader.getBackgroundBitmap(getResources()); 459 } 460 if (mBackgroundImage != null) { 461 Rect s = new Rect(0, 0, mBackgroundImage.getWidth(), 462 mBackgroundImage.getHeight()); 463 Rect d = new Rect(0, 0, getWidth(), getHeight()); 464 canvas.drawBitmap(mBackgroundImage, s, d, mPaint); 465 } 466 } else { 467 canvas.drawColor(mBackgroundColor); 468 } 469 } 470 471 public boolean showTitle() { 472 return false; 473 } 474 475 public void setImageLoader(ImageLoader loader) { 476 mImageLoader = loader; 477 if (mImageLoader != null) { 478 mImageLoader.addListener(this); 479 MasterImage.getImage().setImageLoader(mImageLoader); 480 } 481 } 482 483 private void setDirtyGeometryFlag() { 484 mDirtyGeometry = true; 485 } 486 487 protected void clearDirtyGeometryFlag() { 488 mDirtyGeometry = false; 489 } 490 491 protected boolean getDirtyGeometryFlag() { 492 return mDirtyGeometry; 493 } 494 495 private void imageSizeChanged(Bitmap image) { 496 if (image == null || getImagePreset() == null) 497 return; 498 float w = image.getWidth(); 499 float h = image.getHeight(); 500 GeometryMetadata geo = getImagePreset().mGeoData; 501 RectF pb = geo.getPhotoBounds(); 502 if (w == pb.width() && h == pb.height()) { 503 return; 504 } 505 RectF r = new RectF(0, 0, w, h); 506 getImagePreset().mGeoData.setPhotoBounds(r); 507 getImagePreset().mGeoData.setCropBounds(r); 508 509 } 510 511 public boolean updateGeometryFlags() { 512 return true; 513 } 514 515 public void updateImage() { 516 invalidate(); 517 if (!updateGeometryFlags()) { 518 return; 519 } 520 Bitmap bitmap = mImageLoader.getOriginalBitmapLarge(); 521 if (bitmap != null) { 522 imageSizeChanged(bitmap); 523 } 524 } 525 526 public void imageLoaded() { 527 updateImage(); 528 invalidate(); 529 } 530 531 public void saveImage(FilterShowActivity filterShowActivity, File file) { 532 mImageLoader.saveImage(getImagePreset(), filterShowActivity, file); 533 } 534 535 public void saveToUri(Bitmap f, Uri u, String m, FilterShowActivity filterShowActivity) { 536 mImageLoader.saveToUri(f, u, m, filterShowActivity); 537 } 538 539 public void returnFilteredResult(FilterShowActivity filterShowActivity) { 540 mImageLoader.returnFilteredResult(getImagePreset(), filterShowActivity); 541 } 542 543 public boolean scaleInProgress() { 544 return mScaleGestureDetector.isInProgress(); 545 } 546 547 protected boolean isOriginalDisabled() { 548 return mOriginalDisabled; 549 } 550 551 protected void setOriginalDisabled(boolean originalDisabled) { 552 mOriginalDisabled = originalDisabled; 553 } 554 555 @Override 556 public boolean onTouchEvent(MotionEvent event) { 557 super.onTouchEvent(event); 558 mGestureDetector.onTouchEvent(event); 559 boolean scaleInProgress = scaleInProgress(); 560 mScaleGestureDetector.onTouchEvent(event); 561 if (!scaleInProgress() && scaleInProgress) { 562 // If we were scaling, the scale will stop but we will 563 // still issue an ACTION_UP. Let the subclasses know. 564 mFinishedScalingOperation = true; 565 } 566 567 int ex = (int) event.getX(); 568 int ey = (int) event.getY(); 569 if (event.getAction() == MotionEvent.ACTION_DOWN) { 570 mTouchDown.x = ex; 571 mTouchDown.y = ey; 572 mTouchShowOriginalDate = System.currentTimeMillis(); 573 mShowOriginalDirection = 0; 574 MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation()); 575 } 576 577 if (event.getAction() == MotionEvent.ACTION_MOVE) { 578 mTouch.x = ex; 579 mTouch.y = ey; 580 581 if (!ENABLE_COMPARISON || event.getPointerCount() == 2) { 582 float scaleFactor = MasterImage.getImage().getScaleFactor(); 583 if (scaleFactor >= 1) { 584 float translateX = (mTouch.x - mTouchDown.x) / scaleFactor; 585 float translateY = (mTouch.y - mTouchDown.y) / scaleFactor; 586 Point originalTranslation = MasterImage.getImage().getOriginalTranslation(); 587 Point translation = MasterImage.getImage().getTranslation(); 588 translation.x = (int) (originalTranslation.x + translateX); 589 translation.y = (int) (originalTranslation.y + translateY); 590 MasterImage.getImage().setTranslation(translation); 591 } 592 mTouchShowOriginal = false; 593 } else if (!mOriginalDisabled && !mActivity.isShowingHistoryPanel() 594 && (System.currentTimeMillis() - mTouchShowOriginalDate 595 > mTouchShowOriginalDelayMin) 596 && event.getPointerCount() == 1) { 597 mTouchShowOriginal = true; 598 } 599 } 600 601 if (event.getAction() == MotionEvent.ACTION_UP) { 602 mTouchShowOriginal = false; 603 mTouchDown.x = 0; 604 mTouchDown.y = 0; 605 mTouch.x = 0; 606 mTouch.y = 0; 607 if (MasterImage.getImage().getScaleFactor() <= 1) { 608 MasterImage.getImage().setScaleFactor(1); 609 MasterImage.getImage().resetTranslation(); 610 } 611 } 612 invalidate(); 613 return true; 614 } 615 616 // listview stuff 617 public void showOriginal(boolean show) { 618 invalidate(); 619 } 620 621 @Override 622 public boolean onDoubleTap(MotionEvent arg0) { 623 mZoomIn = !mZoomIn; 624 float scale = 1.0f; 625 if (mZoomIn) { 626 scale = MasterImage.getImage().getMaxScaleFactor(); 627 } 628 if (scale != MasterImage.getImage().getScaleFactor()) { 629 MasterImage.getImage().setScaleFactor(scale); 630 MasterImage.getImage().needsUpdateFullResPreview(); 631 invalidate(); 632 } 633 return true; 634 } 635 636 @Override 637 public boolean onDoubleTapEvent(MotionEvent arg0) { 638 // TODO Auto-generated method stub 639 return false; 640 } 641 642 @Override 643 public boolean onSingleTapConfirmed(MotionEvent arg0) { 644 // TODO Auto-generated method stub 645 return false; 646 } 647 648 @Override 649 public boolean onDown(MotionEvent arg0) { 650 // TODO Auto-generated method stub 651 return false; 652 } 653 654 @Override 655 public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) { 656 if (mActivity == null) { 657 return false; 658 } 659 if (endEvent.getPointerCount() == 2) { 660 return false; 661 } 662 if (ENABLE_HISTORY_SWAP && (!mActivity.isShowingHistoryPanel() && startEvent.getX() > endEvent.getX()) 663 || (mActivity.isShowingHistoryPanel() && endEvent.getX() > startEvent.getX())) { 664 if (!mTouchShowOriginal 665 || (mTouchShowOriginal && 666 (System.currentTimeMillis() - mTouchShowOriginalDate 667 < mTouchShowOriginalDelayMax))) { 668 mActivity.toggleHistoryPanel(); 669 } 670 } 671 return true; 672 } 673 674 @Override 675 public void onLongPress(MotionEvent arg0) { 676 // TODO Auto-generated method stub 677 } 678 679 @Override 680 public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { 681 // TODO Auto-generated method stub 682 return false; 683 } 684 685 @Override 686 public void onShowPress(MotionEvent arg0) { 687 // TODO Auto-generated method stub 688 } 689 690 @Override 691 public boolean onSingleTapUp(MotionEvent arg0) { 692 // TODO Auto-generated method stub 693 return false; 694 } 695 696 public boolean useUtilityPanel() { 697 return false; 698 } 699 700 public void openUtilityPanel(final LinearLayout accessoryViewList) { 701 // TODO Auto-generated method stub 702 } 703 704 @Override 705 public boolean onScale(ScaleGestureDetector detector) { 706 float scaleFactor = MasterImage.getImage().getScaleFactor(); 707 scaleFactor = scaleFactor * detector.getScaleFactor(); 708 if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) { 709 scaleFactor = MasterImage.getImage().getMaxScaleFactor(); 710 } 711 if (scaleFactor < 0.5) { 712 scaleFactor = 0.5f; 713 } 714 MasterImage.getImage().setScaleFactor(scaleFactor); 715 return true; 716 } 717 718 @Override 719 public boolean onScaleBegin(ScaleGestureDetector detector) { 720 return true; 721 } 722 723 @Override 724 public void onScaleEnd(ScaleGestureDetector detector) { 725 if (MasterImage.getImage().getScaleFactor() < 1) { 726 MasterImage.getImage().setScaleFactor(1); 727 invalidate(); 728 } 729 } 730 731 public boolean didFinishScalingOperation() { 732 if (mFinishedScalingOperation) { 733 mFinishedScalingOperation = false; 734 return true; 735 } 736 return false; 737 } 738 739} 740