="del-code nl-251 ol-251"> 251
        ImageView imageView = getImageView();
252
        if (null == imageView)
253
            return false;
254
255
        if (null == imageView.getDrawable())
256
            return false;
257
258
        mSuppMatrix.set(finalMatrix);
259
        setImageViewMatrix(getDrawMatrix());
260
        checkMatrixBounds();
261
262
        return true;
263
    }
264
265
    private float mLastRotation = 0;
266
267
    @Override
268
    public void setPhotoViewRotation(float degrees) {
269
        degrees %= 360;
270
        mSuppMatrix.postRotate(mLastRotation - degrees);
271
        mLastRotation = degrees;
272
        checkAndDisplayMatrix();
273
    }
274
275
    public ImageView getImageView() {
276
        ImageView imageView = null;
277
278
        if (null != mImageView) {
279
            imageView = mImageView.get();
280
        }
281
282
        // If we don't have an ImageView, call cleanup()
283
        if (null == imageView) {
284
            cleanup();
285
            Log.i(LOG_TAG,
286
                    "ImageView no longer exists. You should not use this PhotoViewAttacher any more.");
287
        }
288
289
        return imageView;
290
    }
291
292
    @Override
293
    @Deprecated
294
    public float getMinScale() {
295
        return getMinimumScale();
296
    }
297
298
    @Override
299
    public float getMinimumScale() {
300
        return mMinScale;
301
    }
302
303
    @Override
304
    @Deprecated
305
    public float getMidScale() {
306
        return getMediumScale();
307
    }
308
309
    @Override
310
    public float getMediumScale() {
311
        return mMidScale;
312
    }
313
314
    @Override
315
    @Deprecated
316
    public float getMaxScale() {
317
        return getMaximumScale();
318
    }
319
320
    @Override
321
    public float getMaximumScale() {
322
        return mMaxScale;
323
    }
324
325
    @Override
326
    public float getScale() {
327
        return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));
328
    }
329
330
    @Override
331
    public ScaleType getScaleType() {
332
        return mScaleType;
333
    }
334
335
    @Override
336
    public void onDrag(float dx, float dy) {
337
        if (mScaleDragDetector.isScaling()) {
338
            return; // Do not drag if we are already scaling
339
        }
340
341
        if (DEBUG) {
342
            LogManager.getLogger().d(LOG_TAG,
343
                    String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy));
344
        }
345
346
        ImageView imageView = getImageView();
347
        mSuppMatrix.postTranslate(dx, dy);
348
        checkAndDisplayMatrix();
349
350
        /**
351
         * Here we decide whether to let the ImageView's parent to start taking
352
         * over the touch event.
353
         *
354
         * First we check whether this function is enabled. We never want the
355
         * parent to take over if we're scaling. We then check the edge we're
356
         * on, and the direction of the scroll (i.e. if we're pulling against
357
         * the edge, aka 'overscrolling', let the parent take over).
358
         */
359
        ViewParent parent = imageView.getParent();
360
        if (mAllowParentInterceptOnEdge) {
361
            if (mScrollEdge == EDGE_BOTH
362
                    || (mScrollEdge == EDGE_LEFT && dx >= 1f)
363
                    || (mScrollEdge == EDGE_RIGHT && dx <= -1f)) {
364
                if (null != parent)
365
                    parent.requestDisallowInterceptTouchEvent(false);
366
            }
367
        } else {
368
            if (null != parent) {
369
                parent.requestDisallowInterceptTouchEvent(true);
370
            }
371
        }
372
    }
373
374
    @Override
375
    public void onFling(float startX, float startY, float velocityX,
376
                              float velocityY) {
377
        if (DEBUG) {
378
            LogManager.getLogger().d(
379
                    LOG_TAG,
380
                    "onFling. sX: " + startX + " sY: " + startY + " Vx: "
381
                            + velocityX + " Vy: " + velocityY);
382
        }
383
        ImageView imageView = getImageView();
384
        mCurrentFlingRunnable = new FlingRunnable(imageView.getContext());
385
        mCurrentFlingRunnable.fling(getImageViewWidth(imageView),
386
                getImageViewHeight(imageView), (int) velocityX, (int) velocityY);
387
        imageView.post(mCurrentFlingRunnable);
388
    }
389
390
    @Override
391
    public void onGlobalLayout() {
392
        ImageView imageView = getImageView();
393
394
        if (null != imageView) {
395
            if (mZoomEnabled) {
396
                final int top = imageView.getTop();
397
                final int right = imageView.getRight();
398
                final int bottom = imageView.getBottom();
399
                final int left = imageView.getLeft();
400
401
                /**
402
                 * We need to check whether the ImageView's bounds have changed.
403
                 * This would be easier if we targeted API 11+ as we could just use
404
                 * View.OnLayoutChangeListener. Instead we have to replicate the
405
                 * work, keeping track of the ImageView's bounds and then checking
406
                 * if the values change.
407
                 */
408
                if (top != mIvTop || bottom != mIvBottom || left != mIvLeft
409
                        || right != mIvRight) {
410
                    // Update our base matrix, as the bounds have changed
411
                    updateBaseMatrix(imageView.getDrawable());
412
413
                    // Update values as something has changed
414
                    mIvTop = top;
415
                    mIvRight = right;
416
                    mIvBottom = bottom;
417
                    mIvLeft = left;
418
                }
419
            } else {
420
                updateBaseMatrix(imageView.getDrawable());
421
            }
422
        }
423
    }
424
425
    @Override
426
    public void onScale(float scaleFactor, float focusX, float focusY) {
427
        if (DEBUG) {
428
            LogManager.getLogger().d(
429
                    LOG_TAG,
430
                    String.format("onScale: scale: %.2f. fX: %.2f. fY: %.2f",
431
                            scaleFactor, focusX, focusY));
432
        }
433
434
        if (getScale() < mMaxScale || scaleFactor < 1f) {
435
            mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
436
            checkAndDisplayMatrix();
437
        }
438
    }
439
440
    @Override
441
    public boolean onTouch(View v, MotionEvent ev) {
442
        boolean handled = false;
443
444
        if (mZoomEnabled && hasDrawable((ImageView) v)) {
445
            ViewParent parent = v.getParent();
446
            switch (ev.getAction()) {
447
                case ACTION_DOWN:
448
                    // First, disable the Parent from intercepting the touch
449
                    // event
450
                    if (null != parent)
451
                        parent.requestDisallowInterceptTouchEvent(true);
452
                    else
453
                        Log.i(LOG_TAG, "onTouch getParent() returned null");
454
455
                    // If we're flinging, and the user presses down, cancel
456
                    // fling
457
                    cancelFling();
458
                    break;
459
460
                case ACTION_CANCEL:
461
                case ACTION_UP:
462
                    // If the user has zoomed less than min scale, zoom back
463
                    // to min scale
464
                    if (getScale() < mMinScale) {
465
                        RectF rect = getDisplayRect();
466
                        if (null != rect) {
467
                            v.post(new AnimatedZoomRunnable(getScale(), mMinScale,
468
                                    rect.centerX(), rect.centerY()));
469
                            handled = true;
470
                        }
471
                    }
472
                    break;
473
            }
474
475
            // Try the Scale/Drag detector
476
            if (null != mScaleDragDetector
477
                    && mScaleDragDetector.onTouchEvent(ev)) {
478
                handled = true;
479
            }
480
481
            // Check to see if the user double tapped
482
            if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
483
                handled = true;
484
            }
485
        }
486
487
        return handled;
488
    }
489
490
    @Override
491
    public void setAllowParentInterceptOnEdge(boolean allow) {
492
        mAllowParentInterceptOnEdge = allow;
493
    }
494
495
    @Override
496
    @Deprecated
497
    public void setMinScale(float minScale) {
498
        setMinimumScale(minScale);
499
    }
500
501
    @Override
502
    public void setMinimumScale(float minimumScale) {
503
        checkZoomLevels(minimumScale, mMidScale, mMaxScale);
504
        mMinScale = minimumScale;
505
    }
506
507
    @Override
508
    @Deprecated
509
    public void setMidScale(float midScale) {
510
        setMediumScale(midScale);
511
    }
512
513
    @Override
514
    public void setMediumScale(float mediumScale) {
515
        checkZoomLevels(mMinScale, mediumScale, mMaxScale);
516
        mMidScale = mediumScale;
517
    }
518
519
    @Override
520
    @Deprecated
521
    public void setMaxScale(float maxScale) {
522
        setMaximumScale(maxScale);
523
    }
524
525
    @Override
526
    public void setMaximumScale(float maximumScale) {
527
        checkZoomLevels(mMinScale, mMidScale, maximumScale);
528
        mMaxScale = maximumScale;
529
    }
530
531
    @Override
532
    public void setOnLongClickListener(OnLongClickListener listener) {
533
        mLongClickListener = listener;
534
    }
535
536
    @Override
537
    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
538
        mMatrixChangeListener = listener;
539
    }
540
541
    @Override
542
    public void setOnPhotoTapListener(OnPhotoTapListener listener) {
543
        mPhotoTapListener = listener;
544
    }
545
546
    @Override
547
    public OnPhotoTapListener getOnPhotoTapListener() {
548
        return mPhotoTapListener;
549
    }
550
551
    @Override
552
    public void setOnViewTapListener(OnViewTapListener listener) {
553
        mViewTapListener = listener;
554
    }
555
556
    @Override
557
    public OnViewTapListener getOnViewTapListener() {
558
        return mViewTapListener;
559
    }
560
561
    @Override
562
    public void setScale(float scale) {
563
        setScale(scale, false);
564
    }
565
566
    @Override
567
    public void setScale(float scale, boolean animate) {
568
        ImageView imageView = getImageView();
569
570
        if (null != imageView) {
571
            setScale(scale,
572
                    (imageView.getRight()) / 2,
573
                    (imageView.getBottom()) / 2,
574
                    animate);
575
        }
576
    }
577
578
    @Override
579
    public void setScale(float scale, float focalX, float focalY,
580
                         boolean animate) {
581
        ImageView imageView = getImageView();
582
583
        if (null != imageView) {
584
            // Check to see if the scale is within bounds
585
            if (scale < mMinScale || scale > mMaxScale) {
586
                LogManager
587
                        .getLogger()
588
                        .i(LOG_TAG,
589
                                "Scale must be within the range of minScale and maxScale");
590
                return;
591
            }
592
593
            if (animate) {
594
                imageView.post(new AnimatedZoomRunnable(getScale(), scale,
595
                        focalX, focalY));
596
            } else {
597
                mSuppMatrix.setScale(scale, scale, focalX, focalY);
598
                checkAndDisplayMatrix();
599
            }
600
        }
601
    }
602
603
    @Override
604
    public void setScaleType(ScaleType scaleType) {
605
        if (isSupportedScaleType(scaleType) && scaleType != mScaleType) {
606
            mScaleType = scaleType;
607
608
            // Finally update
609
            update();
610
        }
611
    }
612
613
    @Override
614
    public void setZoomable(boolean zoomable) {
615
        mZoomEnabled = zoomable;
616
        update();
617
    }
618
619
    public void update() {
620
        ImageView imageView = getImageView();
621
622
        if (null != imageView) {
623
            if (mZoomEnabled) {
624
                // Make sure we using MATRIX Scale Type
625
                setImageViewScaleTypeMatrix(imageView);
626
627
                // Update the base matrix using the current drawable
628
                updateBaseMatrix(imageView.getDrawable());
629
            } else {
630
                // Reset the Matrix...
631
                resetMatrix();
632
            }
633
        }
634
    }
635
636
    @Override
637
    public Matrix getDisplayMatrix() {
638
        return new Matrix(getDrawMatrix());
639
    }
640
641
    public Matrix getDrawMatrix() {
642
        mDrawMatrix.set(mBaseMatrix);
643
        mDrawMatrix.postConcat(mSuppMatrix);
644
        return mDrawMatrix;
645
    }
646
647
    private void cancelFling() {
648
        if (null != mCurrentFlingRunnable) {
649
            mCurrentFlingRunnable.cancelFling();
650
            mCurrentFlingRunnable = null;
651
        }
652
    }
653
654
    /**
655
     * Helper method that simply checks the Matrix, and then displays the result
656
     */
657
    private void checkAndDisplayMatrix() {
658
        if (checkMatrixBounds()) {
659
            setImageViewMatrix(getDrawMatrix());
660
        }
661
    }
662
663
    private void checkImageViewScaleType() {
664
        ImageView imageView = getImageView();
665
666
        /**
667
         * PhotoView's getScaleType() will just divert to this.getScaleType() so
668
         * only call if we're not attached to a PhotoView.
669
         */
670
        if (null != imageView && !(imageView instanceof IPhotoView)) {
671
            if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
672
                throw new IllegalStateException(
673
                        "The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher");
674
            }
675
        }
676
    }
677
678
    private boolean checkMatrixBounds() {
679
        final ImageView imageView = getImageView();
680
        if (null == imageView) {
681
            return false;
682
        }
683
684
        final RectF rect = getDisplayRect(getDrawMatrix());
685
        if (null == rect) {
686
            return false;
687
        }
688
689
        final float height = rect.height(), width = rect.width();
690
        float deltaX = 0, deltaY = 0;
691
692
        final int viewHeight = getImageViewHeight(imageView);
693
        if (height <= viewHeight) {
694
            switch (mScaleType) {
695
                case FIT_START:
696
                    deltaY = -rect.top;
697
                    break;
698
                case FIT_END:
699
                    deltaY = viewHeight - height - rect.top;
700
                    break;
701
                default:
702
                    deltaY = (viewHeight - height) / 2 - rect.top;
703
                    break;
704
            }
705
        } else if (rect.top > 0) {
706
            deltaY = -rect.top;
707
        } else if (rect.bottom < viewHeight) {
708
            deltaY = viewHeight - rect.bottom;
709
        }
710
711
        final int viewWidth = getImageViewWidth(imageView);
712
        if (width <= viewWidth) {
713
            switch (mScaleType) {
714
                case FIT_START:
715
                    deltaX = -rect.left;
716
                    break;
717
                case FIT_END:
718
                    deltaX = viewWidth - width - rect.left;
719
                    break;
720
                default:
721
                    deltaX = (viewWidth - width) / 2 - rect.left;
722
                    break;
723
            }
724
            mScrollEdge = EDGE_BOTH;
725
        } else if (rect.left > 0) {
726
            mScrollEdge = EDGE_LEFT;
727
            deltaX = -rect.left;
728
        } else if (rect.right < viewWidth) {
729
            deltaX = viewWidth - rect.right;
730
            mScrollEdge = EDGE_RIGHT;
731
        } else {
732
            mScrollEdge = EDGE_NONE;
733
        }
734
735
        // Finally actually translate the matrix
736
        mSuppMatrix.postTranslate(deltaX, deltaY);
737
        return true;
738
    }
739
740
    /**
741
     * Helper method that maps the supplied Matrix to the current Drawable
742
     *
743
     * @param matrix - Matrix to map Drawable against
744
     * @return RectF - Displayed Rectangle
745
     */
746
    private RectF getDisplayRect(Matrix matrix) {
747
        ImageView imageView = getImageView();
748
749
        if (null != imageView) {
750
            Drawable d = imageView.getDrawable();
751
            if (null != d) {
752
                mDisplayRect.set(0, 0, d.getIntrinsicWidth(),
753
                        d.getIntrinsicHeight());
754
                matrix.mapRect(mDisplayRect);
755
                return mDisplayRect;
756
            }
757
        }
758
        return null;
759
    }
760
761
    public Bitmap getVisibleRectangleBitmap() {
762
        ImageView imageView = getImageView();
763
        return imageView == null ? null : imageView.getDrawingCache();
764
    }
765
766
    @Override
767
    public void setZoomTransitionDuration(int milliseconds) {
768
        if (milliseconds < 0)
769
            milliseconds = DEFAULT_ZOOM_DURATION;
770
        this.ZOOM_DURATION = milliseconds;
771
    }
772
773
    /**
774
     * Helper method that 'unpacks' a Matrix and returns the required value
775
     *
776
     * @param matrix     - Matrix to unpack
777
     * @param whichValue - Which value from Matrix.M* to return
778
     * @return float - returned value
779
     */
780
    private float getValue(Matrix matrix, int whichValue) {
781
        matrix.getValues(mMatrixValues);
782
        return mMatrixValues[whichValue];
783
    }
784
785
    /**
786
     * Resets the Matrix back to FIT_CENTER, and then displays it.s
787
     */
788
    private void resetMatrix() {
789
        mSuppMatrix.reset();
790
        setImageViewMatrix(getDrawMatrix());
791
        checkMatrixBounds();
792
    }
793
794
    private void setImageViewMatrix(Matrix matrix) {
795
        ImageView imageView = getImageView();
796
        if (null != imageView) {
797
798
            checkImageViewScaleType();
799
            imageView.setImageMatrix(matrix);
800
801
            // Call MatrixChangedListener if needed
802
            if (null != mMatrixChangeListener) {
803
                RectF displayRect = getDisplayRect(matrix);
804
                if (null != displayRect) {
805
                    mMatrixChangeListener.onMatrixChanged(displayRect);
806
                }
807
            }
808
        }
809
    }
810
811
    /**
812
     * Calculate Matrix for FIT_CENTER
813
     *
814
     * @param d - Drawable being displayed
815
     */
816
    private void updateBaseMatrix(Drawable d) {
817
        ImageView imageView = getImageView();
818
        if (null == imageView || null == d) {
819
            return;
820
        }
821
822
        final float viewWidth = getImageViewWidth(imageView);
823
        final float viewHeight = getImageViewHeight(imageView);
824
        final int drawableWidth = d.getIntrinsicWidth();
825
        final int drawableHeight = d.getIntrinsicHeight();
826
827
        mBaseMatrix.reset();
828
829
        final float widthScale = viewWidth / drawableWidth;
830
        final float heightScale = viewHeight / drawableHeight;
831
832
        if (mScaleType == ScaleType.CENTER) {
833
            mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,
834
                    (viewHeight - drawableHeight) / 2F);
835
836
        } else if (mScaleType == ScaleType.CENTER_CROP) {
837
            float scale = Math.max(widthScale, heightScale);
838
            mBaseMatrix.postScale(scale, scale);
839
            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
840
                    (viewHeight - drawableHeight * scale) / 2F);
841
842
        } else if (mScaleType == ScaleType.CENTER_INSIDE) {
843
            float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
844
            mBaseMatrix.postScale(scale, scale);
845
            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
846
                    (viewHeight - drawableHeight * scale) / 2F);
847
848
        } else {
849
            RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
850
            RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
851
852
            switch (mScaleType) {
853
                case FIT_CENTER:
854
                    mBaseMatrix
855
                            .setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
856
                    break;
857
858
                case FIT_START:
859
                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
860
                    break;
861
862
                case FIT_END:
863
                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
864
                    break;
865
866
                case FIT_XY:
867
                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
868
                    break;
869
870
                default:
871
                    break;
872
            }
873
        }
874
875
        resetMatrix();
876
    }
877
878
    private int getImageViewWidth(ImageView imageView) {
879
        if (null == imageView)
880
            return 0;
881
        return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
882
    }
883
884
    private int getImageViewHeight(ImageView imageView) {
885
        if (null == imageView)
886
            return 0;
887
        return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
888
    }
889
890
    /**
891
     * Interface definition for a callback to be invoked when the internal Matrix has changed for
892
     * this View.
893
     *
894
     * @author Chris Banes
895
     */
896
    public static interface OnMatrixChangedListener {
897
        /**
898
         * Callback for when the Matrix displaying the Drawable has changed. This could be because
899
         * the View's bounds have changed, or the user has zoomed.
900
         *
901
         * @param rect - Rectangle displaying the Drawable's new bounds.
902
         */
903
        void onMatrixChanged(RectF rect);
904
    }
905
906
    /**
907
     * Interface definition for a callback to be invoked when the Photo is tapped with a single
908
     * tap.
909
     *
910
     * @author Chris Banes
911
     */
912
    public static interface OnPhotoTapListener {
913
914
        /**
915
         * A callback to receive where the user taps on a photo. You will only receive a callback if
916
         * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
917
         *
918
         * @param view - View the user tapped.
919
         * @param x    - where the user tapped from the of the Drawable, as percentage of the
920
         *             Drawable width.
921
         * @param y    - where the user tapped from the top of the Drawable, as percentage of the
922
         *             Drawable height.
923
         */
924
        void onPhotoTap(View view, float x, float y);
925
    }
926
927
    /**
928
     * Interface definition for a callback to be invoked when the ImageView is tapped with a single
929
     * tap.
930
     *
931
     * @author Chris Banes
932
     */
933
    public static interface OnViewTapListener {
934
935
        /**
936
         * A callback to receive where the user taps on a ImageView. You will receive a callback if
937
         * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
938
         *
939
         * @param view - View the user tapped.
940
         * @param x    - where the user tapped from the left of the View.
941
         * @param y    - where the user tapped from the top of the View.
942
         */
943
        void onViewTap(View view, float x, float y);
944
    }
945
946
    private class AnimatedZoomRunnable implements Runnable {
947
948
        private final float mFocalX, mFocalY;
949
        private final long mStartTime;
950
        private final float mZoomStart, mZoomEnd;
951
952
        public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,
953
                                    final float focalX, final float focalY) {
954
            mFocalX = focalX;
955
            mFocalY = focalY;
956
            mStartTime = System.currentTimeMillis();
957
            mZoomStart = currentZoom;
958
            mZoomEnd = targetZoom;
959
        }
960
961
        @Override
962
        public void run() {
963
            ImageView imageView = getImageView();
964
            if (imageView == null) {
965
                return;
966
            }
967
968
            float t = interpolate();
969
            float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
970
            float deltaScale = scale / getScale();
971
972
            mSuppMatrix.postScale(deltaScale, deltaScale, mFocalX, mFocalY);
973
            checkAndDisplayMatrix();
974
975
            // We haven't hit our target scale yet, so post ourselves again
976
            if (t < 1f) {
977
                Compat.postOnAnimation(imageView, this);
978
            }
979
        }
980
981
        private float interpolate() {
982
            float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION;
983
            t = Math.min(1f, t);
984
            t = sInterpolator.getInterpolation(t);
985
            return t;
986
        }
987
    }
988
989
    private class FlingRunnable implements Runnable {
990
991
        private final ScrollerProxy mScroller;
992
        private int mCurrentX, mCurrentY;
993
994
        public FlingRunnable(Context context) {
995
            mScroller = ScrollerProxy.getScroller(context);
996
        }
997
998
        public void cancelFling() {
999
            if (DEBUG) {
1000
                LogManager.getLogger().d(LOG_TAG, "Cancel Fling");
1001
            }
1002
            mScroller.forceFinished(true);
1003
        }
1004
1005
        public void fling(int viewWidth, int viewHeight, int velocityX,
1006
                          int velocityY) {
1007
            final RectF rect = getDisplayRect();
1008
            if (null == rect) {
1009
                return;
1010
            }
1011
1012
            final int startX = Math.round(-rect.left);
1013
            final int minX, maxX, minY, maxY;
1014
1015
            if (viewWidth < rect.width()) {
1016
                minX = 0;
1017
                maxX = Math.round(rect.width() - viewWidth);
1018
            } else {
1019
                minX = maxX = startX;
1020
            }
1021
1022
            final int startY = Math.round(-rect.top);
1023
            if (viewHeight < rect.height()) {
1024
                minY = 0;
1025
                maxY = Math.round(rect.height() - viewHeight);
1026
            } else {
1027
                minY = maxY = startY;
1028
            }
1029
1030
            mCurrentX = startX;
1031
            mCurrentY = startY;
1032
1033
            if (DEBUG) {
1034
                LogManager.getLogger().d(
1035
                        LOG_TAG,
1036
                        "fling. StartX:" + startX + " StartY:" + startY
1037
                                + " MaxX:" + maxX + " MaxY:" + maxY);
1038
            }
1039
1040
            // If we actually can move, fling the scroller
1041
            if (startX != maxX || startY != maxY) {
1042
                mScroller.fling(startX, startY, velocityX, velocityY, minX,
1043
                        maxX, minY, maxY, 0, 0);
1044
            }
1045
        }
1046
1047
        @Override
1048
        public void run() {
1049
            if (mScroller.isFinished()) {
1050
                return; // remaining post that should not be handled
1051
            }
1052
1053
            ImageView imageView = getImageView();
1054
            if (null != imageView && mScroller.computeScrollOffset()) {
1055
1056
                final int newX = mScroller.getCurrX();
1057
                final int newY = mScroller.getCurrY();
1058
1059
                if (DEBUG) {
1060
                    LogManager.getLogger().d(
1061
                            LOG_TAG,
1062
                            "fling run(). CurrentX:" + mCurrentX + " CurrentY:"
1063
                                    + mCurrentY + " NewX:" + newX + " NewY:"
1064
                                    + newY);
1065
                }
1066
1067
                mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
1068
                setImageViewMatrix(getDrawMatrix());
1069
1070
                mCurrentX = newX;
1071
                mCurrentY = newY;
1072
1073
                // Post On animation
1074
                Compat.postOnAnimation(imageView, this);
1075
            }
1076
        }
1077
    }
1078
}

+ 0 - 58
app/src/main/java/com/electric/chargingpile/view/PreGingerScroller.java

1
/*******************************************************************************
2
 * Copyright 2011, 2012 Chris Banes.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 *******************************************************************************/
16
package com.electric.chargingpile.view;
17
18
import android.content.Context;
19
import android.widget.Scroller;
20
21
public class PreGingerScroller extends ScrollerProxy {
22
23
    private final Scroller mScroller;
24
25
    public PreGingerScroller(Context context) {
26
        mScroller = new Scroller(context);
27
    }
28
29
    @Override
30
    public boolean computeScrollOffset() {
31
        return mScroller.computeScrollOffset();
32
    }
33
34
    @Override
35
    public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
36
                      int overX, int overY) {
37
        mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
38
    }
39
40
    @Override
41
    public void forceFinished(boolean finished) {
42
        mScroller.forceFinished(finished);
43
    }
44
45
    public boolean isFinished() {
46
        return mScroller.isFinished();
47
    }
48
49
    @Override
50
    public int getCurrX() {
51
        return mScroller.getCurrX();
52
    }
53
54
    @Override
55
    public int getCurrY() {
56
        return mScroller.getCurrY();
57
    }
58
}

+ 0 - 196
app/src/main/java/com/electric/chargingpile/view/RoundImageViewByXfermode.java

1
package com.electric.chargingpile.view;
2

3
import java.lang.ref.WeakReference;
4

5
import com.electric.chargingpile.R;
6

7
import android.annotation.SuppressLint;
8
import android.content.Context;
9
import android.content.res.TypedArray;
10
import android.graphics.Bitmap;
11
import android.graphics.Bitmap.Config;
12
import android.graphics.BitmapShader;
13
import android.graphics.Canvas;
14
import android.graphics.Color;
15
import android.graphics.Matrix;
16
import android.graphics.Paint;
17
import android.graphics.Path;
18
import android.graphics.PorterDuff.Mode;
19
import android.graphics.PorterDuffXfermode;
20
import android.graphics.RectF;
21
import android.graphics.Xfermode;
22
import android.graphics.drawable.Drawable;
23
import android.util.AttributeSet;
24
import android.util.Log;
25
import android.util.TypedValue;
26
import android.widget.ImageView;
27

28
public class RoundImageViewByXfermode extends ImageView
29
{
30

31
	private Paint mPaint;
32
	private Xfermode mXfermode = new PorterDuffXfermode(Mode.DST_IN);
33
	private Bitmap mMaskBitmap;
34

35
	private WeakReference<Bitmap> mWeakBitmap;
36

37
	/**
38
	 * 图片的类型,圆形or圆角
39
	 */
40
	private int type;
41
	public static final int TYPE_CIRCLE = 0;
42
	public static final int TYPE_ROUND = 1;
43
	/**
44
	 * 圆角大小的默认值
45
	 */
46
	private static final int BODER_RADIUS_DEFAULT = 10;
47
	/**
48
	 * 圆角的大小
49
	 */
50
	private int mBorderRadius;
51

52
	public RoundImageViewByXfermode(Context context)
53
	{
54
		this(context,null);
55

56
		mPaint = new Paint();
57
		mPaint.setAntiAlias(true);
58
	}
59

60
	public RoundImageViewByXfermode(Context context, AttributeSet attrs)
61
	{
62
		super(context, attrs);
63

64
		mPaint = new Paint();
65
		mPaint.setAntiAlias(true);
66

67
		TypedArray a = context.obtainStyledAttributes(attrs,
68
				R.styleable.RoundImageViewByXfermode);
69

70
		mBorderRadius = a.getDimensionPixelSize(
71
				R.styleable.RoundImageViewByXfermode_borderRadius, (int) TypedValue
72
						.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
73
								BODER_RADIUS_DEFAULT, getResources()
74
										.getDisplayMetrics()));// 默认为10dp
75
		Log.e("TAG", mBorderRadius+"");
76
		type = a.getInt(R.styleable.RoundImageViewByXfermode_type, TYPE_CIRCLE);// 默认为Circle
77

78
		a.recycle();
79
	}
80

81
	@Override
82
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
83
	{
84
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
85

86
		/**
87
		 * 如果类型是圆形,则强制改变view的宽高一致,以小值为准
88
		 */
89
		if (type == TYPE_CIRCLE)
90
		{
91
			int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
92
			setMeasuredDimension(width, width);
93
		}
94

95
	}
96

97
	@Override
98
	public void invalidate()
99
	{
100
		mWeakBitmap = null;
101
		if (mMaskBitmap != null)
102
		{
103
			mMaskBitmap.recycle();
104
			mMaskBitmap = null;
105
		}
106
		super.invalidate();
107
	}
108

109
	@SuppressLint("DrawAllocation")
110
	@Override
111
	protected void onDraw(Canvas canvas)
112
	{
113
		//在缓存中取出bitmap
114
		Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
115

116
		if (null == bitmap || bitmap.isRecycled())
117
		{
118
			//拿到Drawable
119
			Drawable drawable = getDrawable();
120
			//获取drawable的宽和高
121
			int dWidth = drawable.getIntrinsicWidth();
122
			int dHeight = drawable.getIntrinsicHeight();
123

124
			if (drawable != null)
125
			{
126
				//创建bitmap
127
				bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
128
						Config.ARGB_8888);
129
				float scale = 1.0f;
130
				//创建画布
131
				Canvas drawCanvas = new Canvas(bitmap);
132
				//按照bitmap的宽高,以及view的宽高,计算缩放比例;因为设置的src宽高比例可能和imageview的宽高比例不同,这里我们不希望图片失真;
133
				if (type == TYPE_ROUND)
134
				{
135
					// 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
136
					scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
137
							* 1.0f / dHeight);
138
				} else
139
				{
140
					scale = getWidth() * 1.0F / Math.min(dWidth, dHeight);
141
				}
142
				//根据缩放比例,设置bounds,相当于缩放图片了
143
				drawable.setBounds(0, 0, (int) (scale * dWidth),
144
						(int) (scale * dHeight));
145
				drawable.draw(drawCanvas);
146
				if (mMaskBitmap == null || mMaskBitmap.isRecycled())
147
				{
148
					mMaskBitmap = getBitmap();
149
				}
150
				// Draw Bitmap.
151
				mPaint.reset();
152
				mPaint.setFilterBitmap(false);
153
				mPaint.setXfermode(mXfermode);
154
				//绘制形状
155
				drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);
156
				mPaint.setXfermode(null);
157
				//将准备好的bitmap绘制出来
158
				canvas.drawBitmap(bitmap, 0, 0, null);
159
				//bitmap缓存起来,避免每次调用onDraw,分配内存
160
				mWeakBitmap = new WeakReference<Bitmap>(bitmap);
161
			}
162
		}
163
		//如果bitmap还存在,则直接绘制即可
164
		if (bitmap != null)
165
		{
166
			mPaint.setXfermode(null);
167
			canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
168
			return;
169
		}
170

171
	}
172
	/**
173
	 * 绘制形状
174
	 * @return
175
	 */
176
	public Bitmap getBitmap()
177
	{
178
		Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
179
				Config.ARGB_8888);
180
		Canvas canvas = new Canvas(bitmap);
181
		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
182
		paint.setColor(Color.BLACK);
183

184
		if (type == TYPE_ROUND)
185
		{
186
			canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()),
187
					mBorderRadius, mBorderRadius, paint);
188
		} else
189
		{
190
			canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2,
191
					paint);
192
		}
193

194
		return bitmap;
195
	}
196
}

+ 0 - 48
app/src/main/java/com/electric/chargingpile/view/ScrollerProxy.java

1
/*******************************************************************************
2
 * Copyright 2011, 2012 Chris Banes.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 *******************************************************************************/
16
package com.electric.chargingpile.view;
17
18
import android.content.Context;
19
import android.os.Build.VERSION;
20
import android.os.Build.VERSION_CODES;
21
22
public abstract class ScrollerProxy {
23
24
    public static ScrollerProxy getScroller(Context context) {
25
        if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
26
            return new PreGingerScroller(context);
27
        } else if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {
28
            return new GingerScroller(context);
29
        } else {
30
            return new IcsScroller(context);
31
        }
32
    }
33
34
    public abstract boolean computeScrollOffset();
35
36
    public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY,
37
                               int maxY, int overX, int overY);
38
39
    public abstract void forceFinished(boolean finished);
40
41
    public abstract boolean isFinished();
42
43
    public abstract int getCurrX();
44
45
    public abstract int getCurrY();
46
47
48
}

+ 0 - 1
app/src/main/java/com/electric/chargingpile/view/SlideClaimView.java

48
 */
48
 */
49
public class SlideClaimView extends FrameLayout{
49
public class SlideClaimView extends FrameLayout{
50
    // ???universal-image-loader????????????????????????universal-image-loader-1.8.6-with-sources.jar
50
    // ???universal-image-loader????????????????????????universal-image-loader-1.8.6-with-sources.jar
51
    private ImageLoader imageLoader = ImageLoader.getInstance();
52
51
53
    //??????????
52
    //??????????
54
    private final static int IMAGE_COUNT = 5;
53
    private final static int IMAGE_COUNT = 5;

+ 0 - 42
app/src/main/java/com/electric/chargingpile/view/VersionedGestureDetector.java

1
package com.electric.chargingpile.view;
2
3
/*******************************************************************************
4
 * Copyright 2011, 2012 Chris Banes.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 *******************************************************************************/
18
19
import android.content.Context;
20
import android.os.Build;
21
22
public final class VersionedGestureDetector {
23
24
    public static GestureDetector newInstance(Context context,
25
                                              OnGestureListener listener) {
26
        final int sdkVersion = Build.VERSION.SDK_INT;
27
        GestureDetector detector;
28
29
        if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
30
            detector = new CupcakeGestureDetector(context);
31
        } else if (sdkVersion < Build.VERSION_CODES.FROYO) {
32
            detector = new EclairGestureDetector(context);
33
        } else {
34
            detector = new FroyoGestureDetector(context);
35
        }
36
37
        detector.setOnGestureListener(listener);
38
39
        return detector;
40
    }
41
42
}

+ 0 - 105
app/src/main/java/com/electric/chargingpile/view/ZoomControlsLayout.java

1
//package com.electric.chargingpile.view;
2
//
3
//
4
//import com.electric.chargingpile.R;
5
//
6
//import android.content.Context;
7
//import android.util.AttributeSet;
8
//import android.widget.ImageButton;
9
//import android.widget.LinearLayout;
10
//import android.view.LayoutInflater;
11
//import android.view.View;
12
//import android.view.View.OnClickListener;
13
//
14
//public class ZoomControlsLayout extends LinearLayout implements OnClickListener {
15
//	private ImageButton inBtn;
16
//	private ImageButton outBtn;
17
//
18
//	private float minZoomLevel;
19
//	private float maxZoomLevel;
20
//
21
//	public ZoomControlsLayout(Context context, AttributeSet attrs) {
22
//		super(context, attrs);
23
//		init();
24
//	}
25
//
26
//	public ZoomControlsLayout(Context context, AttributeSet attrs, int defStyle) {
27
//		super(context, attrs);
28
//	}
29
//
30
//	private void init() {
31
//		LinearLayout view = (LinearLayout) LayoutInflater.from(getContext())
32
//				.inflate(R.layout.zoom_controls_layout, null);
33
//		inBtn = (ImageButton) view.findViewById(R.id.zoomin);
34
//		outBtn = (ImageButton) view.findViewById(R.id.zoomout);
35
//		inBtn.setOnClickListener(this);
36
//		outBtn.setOnClickListener(this);
37
//		addView(view);
38
//	}
39
//
40
//	@Override
41
//	public void onClick(View v) {
42
//		this.mapStatus = this.baiduMap.getMapStatus();
43
//		switch (v.getId()) {
44
//		case R.id.zoomin:
45
//			this.baiduMap.setMapStatus(MapStatusUpdateFactory
46
//					.zoomTo(mapStatus.zoom + 1));
47
//			controlZoomShow();
48
//			break;
49
//		case R.id.zoomout:
50
//			this.baiduMap.setMapStatus(MapStatusUpdateFactory
51
//					.zoomTo(mapStatus.zoom - 1));
52
//			controlZoomShow();
53
//			break;
54
//		default:
55
//			break;
56
//		}
57
//		mapStatus = this.baiduMap.getMapStatus();
58
//	}
59
//
60
//	public void setMapView(MapView mapView) {
61
//		this.baiduMap = mapView.getMap();
62
//		this.baiduMap.setOnMapStatusChangeListener(onMapStatusChangeListener);
63
//		maxZoomLevel = baiduMap.getMaxZoomLevel();
64
//		minZoomLevel = baiduMap.getMinZoomLevel();
65
//		controlZoomShow();
66
//	}
67
//
68
//	private void controlZoomShow() {
69
//		float zoom = this.baiduMap.getMapStatus().zoom;
70
//		if (zoom >= maxZoomLevel) {
71
//			inBtn.setImageResource(R.drawable.map_add_nor);
72
//			inBtn.setEnabled(false);
73
//		} else {
74
//			inBtn.setImageResource(R.drawable.map_add_press);
75
//			inBtn.setEnabled(true);
76
//		}
77
//
78
//		if (zoom <= minZoomLevel) {
79
//			outBtn.setImageResource(R.drawable.map_cut_nor);
80
//			outBtn.setEnabled(false);
81
//		} else {
82
//			outBtn.setImageResource(R.drawable.map_cut_press);
83
//			outBtn.setEnabled(true);
84
//		}
85
//	}
86
//
87
//	BaiduMap.OnMapStatusChangeListener onMapStatusChangeListener = new BaiduMap.OnMapStatusChangeListener() {
88
//
89
//		@Override
90
//		public void onMapStatusChangeStart(MapStatus arg0) {
91
//
92
//		}
93
//
94
//		@Override
95
//		public void onMapStatusChangeFinish(MapStatus arg0) {
96
//
97
//		}
98
//
99
//		@Override
100
//		public void onMapStatusChange(MapStatus arg0) {
101
//			controlZoomShow();
102
//		}
103
//	};
104
//
105
//}

+ 5 - 1
app/src/main/java/com/electric/chargingpile/zoom/PhotoViewAttacher.java

169
	 * when the ImageView is no longer used. A good example is from
169
	 * when the ImageView is no longer used. A good example is from
170
	 * {@link View#onDetachedFromWindow()} or from
170
	 * {@link View#onDetachedFromWindow()} or from
171
	 * {@link android.app.Activity#onDestroy()}. This is automatically called if
171
	 * {@link android.app.Activity#onDestroy()}. This is automatically called if
172
	 * you are using {@link uk.co.senab.photoview.PhotoView}.
173
	 */
172
	 */
174
	@SuppressLint("NewApi")
173
	@SuppressLint("NewApi")
175
	// @SuppressWarnings("deprecation")
174
	// @SuppressWarnings("deprecation")
277
		return mScaleType;
276
		return mScaleType;
278
	}
277
	}
279
278
279
	@Override
280
	public final boolean onDoubleTap(MotionEvent ev) {
280
	public final boolean onDoubleTap(MotionEvent ev) {
281
		try {
281
		try {
282
			float scale = getScale();
282
			float scale = getScale();
297
		return true;
297
		return true;
298
	}
298
	}
299
299
300
	@Override
300
	public final boolean onDoubleTapEvent(MotionEvent e) {
301
	public final boolean onDoubleTapEvent(MotionEvent e) {
301
		// Wait for the confirmed onDoubleTap() instead
302
		// Wait for the confirmed onDoubleTap() instead
302
		return false;
303
		return false;
303
	}
304
	}
304
305
306
	@Override
305
	public final void onDrag(float dx, float dy) {
307
	public final void onDrag(float dx, float dy) {
306
		if (DEBUG) {
308
		if (DEBUG) {
307
			Log.d(LOG_TAG, String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy));
309
			Log.d(LOG_TAG, String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy));
381
		}
383
		}
382
	}
384
	}
383
385
386
	@Override
384
	public final void onScale(float scaleFactor, float focusX, float focusY) {
387
	public final void onScale(float scaleFactor, float focusX, float focusY) {
385
		if (DEBUG) {
388
		if (DEBUG) {
386
			Log.d(LOG_TAG, String.format(
389
			Log.d(LOG_TAG, String.format(
395
		}
398
		}
396
	}
399
	}
397
400
401
	@Override
398
	public final boolean onSingleTapConfirmed(MotionEvent e) {
402
	public final boolean onSingleTapConfirmed(MotionEvent e) {
399
		ImageView imageView = getImageView();
403
		ImageView imageView = getImageView();
400
404

+ 1 - 4
app/src/main/res/values/attr.xml

72
        <enum name="circle" value="0" />
72
        <enum name="circle" value="0" />
73
        <enum name="round" value="1" />
73
        <enum name="round" value="1" />
74
    </attr>
74
    </attr>
75
    <declare-styleable name="RoundImageViewByXfermode">
76
        <attr name="borderRadius" />
77
        <attr name="type" />
78
    </declare-styleable>
75

79
    <declare-styleable name="RoundImageView">
76
    <declare-styleable name="RoundImageView">
80
        <attr name="borderRadius" />
77
        <attr name="borderRadius" />
81
        <attr name="type" />
78
        <attr name="type" />

cdzApp - Gogs: Go Git Service

充电桩app代码

hy 3e008ac643 修复安全漏洞,将分享的SDK初始化所需的值,配置相应Mob的后台 4 years ago
..
ShareSDK.xml 3e008ac643 修复安全漏洞,将分享的SDK初始化所需的值,配置相应Mob的后台 4 years ago