td>
654
|
+ }
|
|
|
655
|
+
|
|
|
656
|
+ @Override
|
|
|
657
|
+ public void setOnLongClickListener(OnLongClickListener listener) {
|
|
|
658
|
+ mLongClickListener = listener;
|
|
|
659
|
+ }
|
|
|
660
|
+
|
|
|
661
|
+ @Override
|
|
|
662
|
+ public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
|
|
|
663
|
+ mMatrixChangeListener = listener;
|
|
|
664
|
+ }
|
|
|
665
|
+
|
|
|
666
|
+ @Override
|
|
|
667
|
+ public void setOnPhotoTapListener(OnPhotoTapListener listener) {
|
|
|
668
|
+ mPhotoTapListener = listener;
|
|
|
669
|
+ }
|
|
|
670
|
+
|
|
|
671
|
+ @Override
|
|
|
672
|
+ public void setOnRotateListener(OnRotateListener onRotateListener) {
|
|
|
673
|
+ mOnRotateListener = onRotateListener;
|
|
|
674
|
+ }
|
|
|
675
|
+
|
|
|
676
|
+ @Override
|
|
|
677
|
+ public OnViewTapListener getOnViewTapListener() {
|
|
|
678
|
+ return mViewTapListener;
|
|
|
679
|
+ }
|
|
|
680
|
+
|
|
|
681
|
+ @Nullable
|
|
|
682
|
+ OnPhotoTapListener getOnPhotoTapListener() {
|
|
|
683
|
+ return mPhotoTapListener;
|
|
|
684
|
+ }
|
|
|
685
|
+
|
|
|
686
|
+ @Override
|
|
|
687
|
+ public void setOnViewTapListener(OnViewTapListener listener) {
|
|
|
688
|
+ mViewTapListener = listener;
|
|
|
689
|
+ }
|
|
|
690
|
+
|
|
|
691
|
+ @Override
|
|
|
692
|
+ public void setScale(float scale) {
|
|
|
693
|
+ setScale(scale, false);
|
|
|
694
|
+ }
|
|
|
695
|
+
|
|
|
696
|
+ @Override
|
|
|
697
|
+ public void setScale(float scale, boolean animate) {
|
|
|
698
|
+ ImageView imageView = getImageView();
|
|
|
699
|
+
|
|
|
700
|
+ if (null != imageView) {
|
|
|
701
|
+ setScale(scale,
|
|
|
702
|
+ (imageView.getRight()) / 2,
|
|
|
703
|
+ (imageView.getBottom()) / 2,
|
|
|
704
|
+ animate);
|
|
|
705
|
+ }
|
|
|
706
|
+ }
|
|
|
707
|
+
|
|
|
708
|
+ @Override
|
|
|
709
|
+ public void setScale(float scale, float focalX, float focalY,
|
|
|
710
|
+ boolean animate) {
|
|
|
711
|
+ ImageView imageView = getImageView();
|
|
|
712
|
+
|
|
|
713
|
+ if (null != imageView) {
|
|
|
714
|
+ // Check to see if the scale is within bounds
|
|
|
715
|
+ if (scale < mMinScale || scale > mMaxScale) {
|
|
|
716
|
+ LogManager
|
|
|
717
|
+ .getLogger()
|
|
|
718
|
+ .i(LOG_TAG,
|
|
|
719
|
+ "Scale must be within the range of minScale and maxScale");
|
|
|
720
|
+ return;
|
|
|
721
|
+ }
|
|
|
722
|
+
|
|
|
723
|
+ if (animate) {
|
|
|
724
|
+ imageView.post(new AnimatedZoomRunnable(getScale(), scale,
|
|
|
725
|
+ focalX, focalY));
|
|
|
726
|
+ } else {
|
|
|
727
|
+ mSuppMatrix.setScale(scale, scale, focalX, focalY);
|
|
|
728
|
+ checkAndDisplayMatrix();
|
|
|
729
|
+ }
|
|
|
730
|
+ }
|
|
|
731
|
+ }
|
|
|
732
|
+
|
|
|
733
|
+ /**
|
|
|
734
|
+ * Set the zoom interpolator
|
|
|
735
|
+ * @param interpolator the zoom interpolator
|
|
|
736
|
+ */
|
|
|
737
|
+ public void setZoomInterpolator(Interpolator interpolator) {
|
|
|
738
|
+ mInterpolator = interpolator;
|
|
|
739
|
+ }
|
|
|
740
|
+
|
|
|
741
|
+ @Override
|
|
|
742
|
+ public void setScaleType(ScaleType scaleType) {
|
|
|
743
|
+ if (isSupportedScaleType(scaleType) && scaleType != mScaleType) {
|
|
|
744
|
+ mScaleType = scaleType;
|
|
|
745
|
+
|
|
|
746
|
+ // Finally update
|
|
|
747
|
+ update();
|
|
|
748
|
+ }
|
|
|
749
|
+ }
|
|
|
750
|
+
|
|
|
751
|
+ @Override
|
|
|
752
|
+ public void setZoomable(boolean zoomable) {
|
|
|
753
|
+ mZoomEnabled = zoomable;
|
|
|
754
|
+ update();
|
|
|
755
|
+ }
|
|
|
756
|
+
|
|
|
757
|
+ public void update() {
|
|
|
758
|
+ ImageView imageView = getImageView();
|
|
|
759
|
+
|
|
|
760
|
+ if (null != imageView) {
|
|
|
761
|
+ if (mZoomEnabled) {
|
|
|
762
|
+ // Make sure we using MATRIX Scale Type
|
|
|
763
|
+ setImageViewScaleTypeMatrix(imageView);
|
|
|
764
|
+
|
|
|
765
|
+ // Update the base matrix using the current drawable
|
|
|
766
|
+ updateBaseMatrix(imageView.getDrawable());
|
|
|
767
|
+ } else {
|
|
|
768
|
+ // Reset the Matrix...
|
|
|
769
|
+ resetMatrix();
|
|
|
770
|
+ }
|
|
|
771
|
+ }
|
|
|
772
|
+ }
|
|
|
773
|
+
|
|
|
774
|
+ /**
|
|
|
775
|
+ * Get the display matrix
|
|
|
776
|
+ * @param matrix target matrix to copy to
|
|
|
777
|
+ */
|
|
|
778
|
+ @Override
|
|
|
779
|
+ public void getDisplayMatrix(Matrix matrix) {
|
|
|
780
|
+ matrix.set(getDrawMatrix());
|
|
|
781
|
+ }
|
|
|
782
|
+
|
|
|
783
|
+ /**
|
|
|
784
|
+ * Get the current support matrix
|
|
|
785
|
+ */
|
|
|
786
|
+ public void getSuppMatrix(Matrix matrix) {
|
|
|
787
|
+ matrix.set(mSuppMatrix);
|
|
|
788
|
+ }
|
|
|
789
|
+
|
|
|
790
|
+ private Matrix getDrawMatrix() {
|
|
|
791
|
+ mDrawMatrix.set(mBaseMatrix);
|
|
|
792
|
+ mDrawMatrix.postConcat(mSuppMatrix);
|
|
|
793
|
+ return mDrawMatrix;
|
|
|
794
|
+ }
|
|
|
795
|
+
|
|
|
796
|
+ private void cancelFling() {
|
|
|
797
|
+ if (null != mCurrentFlingRunnable) {
|
|
|
798
|
+ mCurrentFlingRunnable.cancelFling();
|
|
|
799
|
+ mCurrentFlingRunnable = null;
|
|
|
800
|
+ }
|
|
|
801
|
+ }
|
|
|
802
|
+
|
|
|
803
|
+ public Matrix getImageMatrix() {
|
|
|
804
|
+ return mDrawMatrix;
|
|
|
805
|
+ }
|
|
|
806
|
+
|
|
|
807
|
+ /**
|
|
|
808
|
+ * Helper method that simply checks the Matrix, and then displays the result
|
|
|
809
|
+ */
|
|
|
810
|
+ private void checkAndDisplayMatrix() {
|
|
|
811
|
+ if (checkMatrixBounds()) {
|
|
|
812
|
+ setImageViewMatrix(getDrawMatrix());
|
|
|
813
|
+ }
|
|
|
814
|
+ }
|
|
|
815
|
+
|
|
|
816
|
+ private void checkImageViewScaleType() {
|
|
|
817
|
+ ImageView imageView = getImageView();
|
|
|
818
|
+
|
|
|
819
|
+ /**
|
|
|
820
|
+ * PhotoView's getScaleType() will just divert to this.getScaleType() so
|
|
|
821
|
+ * only call if we're not attached to a PhotoView.
|
|
|
822
|
+ */
|
|
|
823
|
+ if (null != imageView && !(imageView instanceof IPhotoView)) {
|
|
|
824
|
+ if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
|
|
|
825
|
+ throw new IllegalStateException(
|
|
|
826
|
+ "The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher. You should call setScaleType on the PhotoViewAttacher instead of on the ImageView" );
|
|
|
827
|
+ }
|
|
|
828
|
+ }
|
|
|
829
|
+ }
|
|
|
830
|
+
|
|
|
831
|
+ private boolean checkMatrixBounds() {
|
|
|
832
|
+ final ImageView imageView = getImageView();
|
|
|
833
|
+ if (null == imageView) {
|
|
|
834
|
+ return false;
|
|
|
835
|
+ }
|
|
|
836
|
+
|
|
|
837
|
+ final RectF rect = getDisplayRect(getDrawMatrix());
|
|
|
838
|
+ if (null == rect) {
|
|
|
839
|
+ return false;
|
|
|
840
|
+ }
|
|
|
841
|
+
|
|
|
842
|
+ final float height = rect.height(), width = rect.width();
|
|
|
843
|
+ float deltaX = 0, deltaY = 0;
|
|
|
844
|
+
|
|
|
845
|
+ final int viewHeight = getImageViewHeight(imageView);
|
|
|
846
|
+ if (height <= viewHeight) {
|
|
|
847
|
+ switch (mScaleType) {
|
|
|
848
|
+ case FIT_START:
|
|
|
849
|
+ deltaY = -rect.top;
|
|
|
850
|
+ break;
|
|
|
851
|
+ case FIT_END:
|
|
|
852
|
+ deltaY = viewHeight - height - rect.top;
|
|
|
853
|
+ break;
|
|
|
854
|
+ default:
|
|
|
855
|
+ deltaY = (viewHeight - height) / 2 - rect.top;
|
|
|
856
|
+ break;
|
|
|
857
|
+ }
|
|
|
858
|
+ } else if (rect.top > 0) {
|
|
|
859
|
+ deltaY = -rect.top;
|
|
|
860
|
+ } else if (rect.bottom < viewHeight) {
|
|
|
861
|
+ deltaY = viewHeight - rect.bottom;
|
|
|
862
|
+ }
|
|
|
863
|
+
|
|
|
864
|
+ final int viewWidth = getImageViewWidth(imageView);
|
|
|
865
|
+ if (width <= viewWidth) {
|
|
|
866
|
+ switch (mScaleType) {
|
|
|
867
|
+ case FIT_START:
|
|
|
868
|
+ deltaX = -rect.left;
|
|
|
869
|
+ break;
|
|
|
870
|
+ case FIT_END:
|
|
|
871
|
+ deltaX = viewWidth - width - rect.left;
|
|
|
872
|
+ break;
|
|
|
873
|
+ default:
|
|
|
874
|
+ deltaX = (viewWidth - width) / 2 - rect.left;
|
|
|
875
|
+ break;
|
|
|
876
|
+ }
|
|
|
877
|
+ mScrollEdge = EDGE_BOTH;
|
|
|
878
|
+ } else if (rect.left > 0) {
|
|
|
879
|
+ mScrollEdge = EDGE_LEFT;
|
|
|
880
|
+ deltaX = -rect.left;
|
|
|
881
|
+ } else if (rect.right < viewWidth) {
|
|
|
882
|
+ deltaX = viewWidth - rect.right;
|
|
|
883
|
+ mScrollEdge = EDGE_RIGHT;
|
|
|
884
|
+ } else {
|
|
|
885
|
+ mScrollEdge = EDGE_NONE;
|
|
|
886
|
+ }
|
|
|
887
|
+
|
|
|
888
|
+ // Finally actually translate the matrix
|
|
|
889
|
+ mSuppMatrix.postTranslate(deltaX, deltaY);
|
|
|
890
|
+
|
|
|
891
|
+ return true;
|
|
|
892
|
+ }
|
|
|
893
|
+
|
|
|
894
|
+ /**
|
|
|
895
|
+ * Helper method that maps the supplied Matrix to the current Drawable
|
|
|
896
|
+ *
|
|
|
897
|
+ * @param matrix - Matrix to map Drawable against
|
|
|
898
|
+ * @return RectF - Displayed Rectangle
|
|
|
899
|
+ */
|
|
|
900
|
+ private RectF getDisplayRect(Matrix matrix) {
|
|
|
901
|
+ ImageView imageView = getImageView();
|
|
|
902
|
+
|
|
|
903
|
+ if (null != imageView) {
|
|
|
904
|
+ Drawable d = imageView.getDrawable();
|
|
|
905
|
+ if (null != d) {
|
|
|
906
|
+ mDisplayRect.set(0, 0, d.getIntrinsicWidth(),
|
|
|
907
|
+ d.getIntrinsicHeight());
|
|
|
908
|
+ matrix.mapRect(mDisplayRect);
|
|
|
909
|
+ return mDisplayRect;
|
|
|
910
|
+ }
|
|
|
911
|
+ }
|
|
|
912
|
+ return null;
|
|
|
913
|
+ }
|
|
|
914
|
+
|
|
|
915
|
+ public Bitmap getVisibleRectangleBitmap() {
|
|
|
916
|
+ ImageView imageView = getImageView();
|
|
|
917
|
+ return imageView == null ? null : imageView.getDrawingCache();
|
|
|
918
|
+ }
|
|
|
919
|
+
|
|
|
920
|
+ @Override
|
|
|
921
|
+ public void setZoomTransitionDuration(int milliseconds) {
|
|
|
922
|
+ if (milliseconds < 0)
|
|
|
923
|
+ milliseconds = DEFAULT_ZOOM_DURATION;
|
|
|
924
|
+ this.ZOOM_DURATION = milliseconds;
|
|
|
925
|
+ }
|
|
|
926
|
+
|
|
|
927
|
+ @Override
|
|
|
928
|
+ public IPhotoView getIPhotoViewImplementation() {
|
|
|
929
|
+ return this;
|
|
|
930
|
+ }
|
|
|
931
|
+
|
|
|
932
|
+ /**
|
|
|
933
|
+ * Helper method that 'unpacks' a Matrix and returns the required value
|
|
|
934
|
+ *
|
|
|
935
|
+ * @param matrix - Matrix to unpack
|
|
|
936
|
+ * @param whichValue - Which value from Matrix.M* to return
|
|
|
937
|
+ * @return float - returned value
|
|
|
938
|
+ */
|
|
|
939
|
+ private float getValue(Matrix matrix, int whichValue) {
|
|
|
940
|
+ matrix.getValues(mMatrixValues);
|
|
|
941
|
+ return mMatrixValues[whichValue];
|
|
|
942
|
+ }
|
|
|
943
|
+
|
|
|
944
|
+ /**
|
|
|
945
|
+ * Resets the Matrix back to FIT_CENTER, and then displays it.s
|
|
|
946
|
+ */
|
|
|
947
|
+ private void resetMatrix() {
|
|
|
948
|
+ mSuppMatrix.reset();
|
|
|
949
|
+ setRotationBy(mBaseRotation);
|
|
|
950
|
+ setImageViewMatrix(getDrawMatrix());
|
|
|
951
|
+ checkMatrixBounds();
|
|
|
952
|
+ }
|
|
|
953
|
+
|
|
|
954
|
+ private void setImageViewMatrix(Matrix matrix) {
|
|
|
955
|
+ ImageView imageView = getImageView();
|
|
|
956
|
+ if (null != imageView) {
|
|
|
957
|
+
|
|
|
958
|
+ checkImageViewScaleType();
|
|
|
959
|
+ imageView.setImageMatrix(matrix);
|
|
|
960
|
+
|
|
|
961
|
+ // Call MatrixChangedListener if needed
|
|
|
962
|
+ if (null != mMatrixChangeListener) {
|
|
|
963
|
+ RectF displayRect = getDisplayRect(matrix);
|
|
|
964
|
+ if (null != displayRect) {
|
|
|
965
|
+ mMatrixChangeListener.onMatrixChanged(displayRect);
|
|
|
966
|
+ }
|
|
|
967
|
+ }
|
|
|
968
|
+ }
|
|
|
969
|
+ }
|
|
|
970
|
+
|
|
|
971
|
+ /**
|
|
|
972
|
+ * Calculate Matrix for FIT_CENTER
|
|
|
973
|
+ *
|
|
|
974
|
+ * @param d - Drawable being displayed
|
|
|
975
|
+ */
|
|
|
976
|
+ private void updateBaseMatrix(Drawable d) {
|
|
|
977
|
+ ImageView imageView = getImageView();
|
|
|
978
|
+ if (null == imageView || null == d) {
|
|
|
979
|
+ return;
|
|
|
980
|
+ }
|
|
|
981
|
+
|
|
|
982
|
+ final float viewWidth = getImageViewWidth(imageView);
|
|
|
983
|
+ final float viewHeight = getImageViewHeight(imageView);
|
|
|
984
|
+ final int drawableWidth = d.getIntrinsicWidth();
|
|
|
985
|
+ final int drawableHeight = d.getIntrinsicHeight();
|
|
|
986
|
+
|
|
|
987
|
+ mBaseMatrix.reset();
|
|
|
988
|
+
|
|
|
989
|
+ final float widthScale = viewWidth / drawableWidth;
|
|
|
990
|
+ final float heightScale = viewHeight / drawableHeight;
|
|
|
991
|
+
|
|
|
992
|
+ if (mScaleType == ScaleType.CENTER) {
|
|
|
993
|
+ mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,
|
|
|
994
|
+ (viewHeight - drawableHeight) / 2F);
|
|
|
995
|
+
|
|
|
996
|
+ } else if (mScaleType == ScaleType.CENTER_CROP) {
|
|
|
997
|
+ float scale = Math.max(widthScale, heightScale);
|
|
|
998
|
+ mBaseMatrix.postScale(scale, scale);
|
|
|
999
|
+ mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
|
|
|
1000
|
+ (viewHeight - drawableHeight * scale) / 2F);
|
|
|
1001
|
+
|
|
|
1002
|
+ } else if (mScaleType == ScaleType.CENTER_INSIDE) {
|
|
|
1003
|
+ float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
|
|
|
1004
|
+ mBaseMatrix.postScale(scale, scale);
|
|
|
1005
|
+ mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
|
|
|
1006
|
+ (viewHeight - drawableHeight * scale) / 2F);
|
|
|
1007
|
+
|
|
|
1008
|
+ } else {
|
|
|
1009
|
+ RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
|
|
|
1010
|
+ RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
|
|
|
1011
|
+
|
|
|
1012
|
+ if ((int) mBaseRotation % 180 != 0) {
|
|
|
1013
|
+ mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);
|
|
|
1014
|
+ }
|
|
|
1015
|
+
|
|
|
1016
|
+ switch (mScaleType) {
|
|
|
1017
|
+ case FIT_CENTER:
|
|
|
1018
|
+ mBaseMatrix
|
|
|
1019
|
+ .setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
|
|
|
1020
|
+ break;
|
|
|
1021
|
+
|
|
|
1022
|
+ case FIT_START:
|
|
|
1023
|
+ mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
|
|
|
1024
|
+ break;
|
|
|
1025
|
+
|
|
|
1026
|
+ case FIT_END:
|
|
|
1027
|
+ mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
|
|
|
1028
|
+ break;
|
|
|
1029
|
+
|
|
|
1030
|
+ case FIT_XY:
|
|
|
1031
|
+ mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
|
|
|
1032
|
+ break;
|
|
|
1033
|
+
|
|
|
1034
|
+ default:
|
|
|
1035
|
+ break;
|
|
|
1036
|
+ }
|
|
|
1037
|
+ }
|
|
|
1038
|
+
|
|
|
1039
|
+ resetMatrix();
|
|
|
1040
|
+ }
|
|
|
1041
|
+
|
|
|
1042
|
+ private int getImageViewWidth(ImageView imageView) {
|
|
|
1043
|
+ if (null == imageView)
|
|
|
1044
|
+ return 0;
|
|
|
1045
|
+ return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
|
|
|
1046
|
+ }
|
|
|
1047
|
+
|
|
|
1048
|
+ private int getImageViewHeight(ImageView imageView) {
|
|
|
1049
|
+ if (null == imageView)
|
|
|
1050
|
+ return 0;
|
|
|
1051
|
+ return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
|
|
|
1052
|
+ }
|
|
|
1053
|
+
|
|
|
1054
|
+ /**
|
|
|
1055
|
+ * Interface definition for a callback to be invoked when the internal Matrix has changed for
|
|
|
1056
|
+ * this View.
|
|
|
1057
|
+ *
|
|
|
1058
|
+ * @author Chris Banes
|
|
|
1059
|
+ */
|
|
|
1060
|
+ public interface OnMatrixChangedListener {
|
|
|
1061
|
+ /**
|
|
|
1062
|
+ * Callback for when the Matrix displaying the Drawable has changed. This could be because
|
|
|
1063
|
+ * the View's bounds have changed, or the user has zoomed.
|
|
|
1064
|
+ *
|
|
|
1065
|
+ * @param rect - Rectangle displaying the Drawable's new bounds.
|
|
|
1066
|
+ */
|
|
|
1067
|
+ void onMatrixChanged(RectF rect);
|
|
|
1068
|
+ }
|
|
|
1069
|
+
|
|
|
1070
|
+ /**
|
|
|
1071
|
+ * Interface definition for callback to be invoked when attached ImageView scale changes
|
|
|
1072
|
+ *
|
|
|
1073
|
+ * @author Marek Sebera
|
|
|
1074
|
+ */
|
|
|
1075
|
+ public interface OnScaleChangeListener {
|
|
|
1076
|
+ /**
|
|
|
1077
|
+ * Callback for when the scale changes
|
|
|
1078
|
+ *
|
|
|
1079
|
+ * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in)
|
|
|
1080
|
+ * @param focusX focal point X position
|
|
|
1081
|
+ * @param focusY focal point Y position
|
|
|
1082
|
+ */
|
|
|
1083
|
+ void onScaleChange(float scaleFactor, float focusX, float focusY);
|
|
|
1084
|
+ }
|
|
|
1085
|
+
|
|
|
1086
|
+ /**
|
|
|
1087
|
+ * Interface definition for a callback to be invoked when the Photo is tapped with a single
|
|
|
1088
|
+ * tap.
|
|
|
1089
|
+ *
|
|
|
1090
|
+ * @author Chris Banes
|
|
|
1091
|
+ */
|
|
|
1092
|
+ public interface OnPhotoTapListener {
|
|
|
1093
|
+
|
|
|
1094
|
+ /**
|
|
|
1095
|
+ * A callback to receive where the user taps on a photo. You will only receive a callback if
|
|
|
1096
|
+ * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
|
|
|
1097
|
+ *
|
|
|
1098
|
+ * @param view - View the user tapped.
|
|
|
1099
|
+ * @param x - where the user tapped from the of the Drawable, as percentage of the
|
|
|
1100
|
+ * Drawable width.
|
|
|
1101
|
+ * @param y - where the user tapped from the top of the Drawable, as percentage of the
|
|
|
1102
|
+ * Drawable height.
|
|
|
1103
|
+ */
|
|
|
1104
|
+ void onPhotoTap(View view, float x, float y);
|
|
|
1105
|
+
|
|
|
1106
|
+ /**
|
|
|
1107
|
+ * A simple callback where out of photo happened;
|
|
|
1108
|
+ * */
|
|
|
1109
|
+ void onOutsidePhotoTap();
|
|
|
1110
|
+ }
|
|
|
1111
|
+
|
|
|
1112
|
+ /**
|
|
|
1113
|
+ * Interface definition for a callback to be invoked when the ImageView is tapped with a single
|
|
|
1114
|
+ * tap.
|
|
|
1115
|
+ *
|
|
|
1116
|
+ * @author Chris Banes
|
|
|
1117
|
+ */
|
|
|
1118
|
+ public interface OnViewTapListener {
|
|
|
1119
|
+
|
|
|
1120
|
+ /**
|
|
|
1121
|
+ * A callback to receive where the user taps on a ImageView. You will receive a callback if
|
|
|
1122
|
+ * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
|
|
|
1123
|
+ *
|
|
|
1124
|
+ * @param view - View the user tapped.
|
|
|
1125
|
+ * @param x - where the user tapped from the left of the View.
|
|
|
1126
|
+ * @param y - where the user tapped from the top of the View.
|
|
|
1127
|
+ */
|
|
|
1128
|
+ void onViewTap(View view, float x, float y);
|
|
|
1129
|
+ }
|
|
|
1130
|
+
|
|
|
1131
|
+ /**
|
|
|
1132
|
+ * Interface definition for a callback to be invoked when the ImageView is roateted with two finger.
|
|
|
1133
|
+ *
|
|
|
1134
|
+ * @author ChenSL
|
|
|
1135
|
+ */
|
|
|
1136
|
+ public interface OnRotateListener {
|
|
|
1137
|
+ /**
|
|
|
1138
|
+ * A callBack to receive when the user rotate a ImageView.You will receive a callback
|
|
|
1139
|
+ * if the user rotate the ImageView
|
|
|
1140
|
+ *
|
|
|
1141
|
+ * @param degree rotate mOldDegree
|
|
|
1142
|
+ */
|
|
|
1143
|
+ void onRotate(int degree);
|
|
|
1144
|
+ }
|
|
|
1145
|
+
|
|
|
1146
|
+ /**
|
|
|
1147
|
+ * Interface definition for a callback to be invoked when the ImageView is fling with a single
|
|
|
1148
|
+ * touch
|
|
|
1149
|
+ *
|
|
|
1150
|
+ * @author tonyjs
|
|
|
1151
|
+ */
|
|
|
1152
|
+ public interface OnSingleFlingListener {
|
|
|
1153
|
+
|
|
|
1154
|
+ /**
|
|
|
1155
|
+ * A callback to receive where the user flings on a ImageView. You will receive a callback if
|
|
|
1156
|
+ * the user flings anywhere on the view.
|
|
|
1157
|
+ *
|
|
|
1158
|
+ * @param e1 - MotionEvent the user first touch.
|
|
|
1159
|
+ * @param e2 - MotionEvent the user last touch.
|
|
|
1160
|
+ * @param velocityX - distance of user's horizontal fling.
|
|
|
1161
|
+ * @param velocityY - distance of user's vertical fling.
|
|
|
1162
|
+ */
|
|
|
1163
|
+ boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
|
|
|
1164
|
+ }
|
|
|
1165
|
+
|
|
|
1166
|
+ private class AnimatedZoomRunnable implements Runnable {
|
|
|
1167
|
+
|
|
|
1168
|
+ private final float mFocalX, mFocalY;
|
|
|
1169
|
+ private final long mStartTime;
|
|
|
1170
|
+ private final float mZoomStart, mZoomEnd;
|
|
|
1171
|
+
|
|
|
1172
|
+ public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,
|
|
|
1173
|
+ final float focalX, final float focalY) {
|
|
|
1174
|
+ mFocalX = focalX;
|
|
|
1175
|
+ mFocalY = focalY;
|
|
|
1176
|
+ mStartTime = System.currentTimeMillis();
|
|
|
1177
|
+ mZoomStart = currentZoom;
|
|
|
1178
|
+ mZoomEnd = targetZoom;
|
|
|
1179
|
+ }
|
|
|
1180
|
+
|
|
|
1181
|
+ @Override
|
|
|
1182
|
+ public void run() {
|
|
|
1183
|
+ ImageView imageView = getImageView();
|
|
|
1184
|
+ if (imageView == null) {
|
|
|
1185
|
+ return;
|
|
|
1186
|
+ }
|
|
|
1187
|
+
|
|
|
1188
|
+ float t = interpolate();
|
|
|
1189
|
+ float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
|
|
|
1190
|
+ float deltaScale = scale / getScale();
|
|
|
1191
|
+
|
|
|
1192
|
+ onScale(deltaScale, mFocalX, mFocalY);
|
|
|
1193
|
+
|
|
|
1194
|
+ // We haven't hit our target scale yet, so post ourselves again
|
|
|
1195
|
+ if (t < 1f) {
|
|
|
1196
|
+ Compat.postOnAnimation(imageView, this);
|
|
|
1197
|
+ }
|
|
|
1198
|
+ }
|
|
|
1199
|
+
|
|
|
1200
|
+ private float interpolate() {
|
|
|
1201
|
+ float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION;
|
|
|
1202
|
+ t = Math.min(1f, t);
|
|
|
1203
|
+ t = mInterpolator.getInterpolation(t);
|
|
|
1204
|
+ return t;
|
|
|
1205
|
+ }
|
|
|
1206
|
+ }
|
|
|
1207
|
+
|
|
|
1208
|
+ private class FlingRunnable implements Runnable {
|
|
|
1209
|
+
|
|
|
1210
|
+ private final ScrollerProxy mScroller;
|
|
|
1211
|
+ private int mCurrentX, mCurrentY;
|
|
|
1212
|
+
|
|
|
1213
|
+ public FlingRunnable(Context context) {
|
|
|
1214
|
+ mScroller = ScrollerProxy.getScroller(context);
|
|
|
1215
|
+ }
|
|
|
1216
|
+
|
|
|
1217
|
+ public void cancelFling() {
|
|
|
1218
|
+ if (DEBUG) {
|
|
|
1219
|
+ LogManager.getLogger().d(LOG_TAG, "Cancel Fling");
|
|
|
1220
|
+ }
|
|
|
1221
|
+ mScroller.forceFinished(true);
|
|
|
1222
|
+ }
|
|
|
1223
|
+
|
|
|
1224
|
+ public void fling(int viewWidth, int viewHeight, int velocityX,
|
|
|
1225
|
+ int velocityY) {
|
|
|
1226
|
+ final RectF rect = getDisplayRect();
|
|
|
1227
|
+ if (null == rect) {
|
|
|
1228
|
+ return;
|
|
|
1229
|
+ }
|
|
|
1230
|
+
|
|
|
1231
|
+ final int startX = Math.round(-rect.left);
|
|
|
1232
|
+ final int minX, maxX, minY, maxY;
|
|
|
1233
|
+
|
|
|
1234
|
+ if (viewWidth < rect.width()) {
|
|
|
1235
|
+ minX = 0;
|
|
|
1236
|
+ maxX = Math.round(rect.width() - viewWidth);
|
|
|
1237
|
+ } else {
|
|
|
1238
|
+ minX = maxX = startX;
|
|
|
1239
|
+ }
|
|
|
1240
|
+
|
|
|
1241
|
+ final int startY = Math.round(-rect.top);
|
|
|
1242
|
+ if (viewHeight < rect.height()) {
|
|
|
1243
|
+ minY = 0;
|
|
|
1244
|
+ maxY = Math.round(rect.height() - viewHeight);
|
|
|
1245
|
+ } else {
|
|
|
1246
|
+ minY = maxY = startY;
|
|
|
1247
|
+ }
|
|
|
1248
|
+
|
|
|
1249
|
+ mCurrentX = startX;
|
|
|
1250
|
+ mCurrentY = startY;
|
|
|
1251
|
+
|
|
|
1252
|
+ if (DEBUG) {
|
|
|
1253
|
+ LogManager.getLogger().d(
|
|
|
1254
|
+ LOG_TAG,
|
|
|
1255
|
+ "fling. StartX:" + startX + " StartY:" + startY
|
|
|
1256
|
+ + " MaxX:" + maxX + " MaxY:" + maxY);
|
|
|
1257
|
+ }
|
|
|
1258
|
+
|
|
|
1259
|
+ // If we actually can move, fling the scroller
|
|
|
1260
|
+ if (startX != maxX || startY != maxY) {
|
|
|
1261
|
+ mScroller.fling(startX, startY, velocityX, velocityY, minX,
|
|
|
1262
|
+ maxX, minY, maxY, 0, 0);
|
|
|
1263
|
+ }
|
|
|
1264
|
+ }
|
|
|
1265
|
+
|
|
|
1266
|
+ @Override
|
|
|
1267
|
+ public void run() {
|
|
|
1268
|
+ if (mScroller.isFinished()) {
|
|
|
1269
|
+ return; // remaining post that should not be handled
|
|
|
1270
|
+ }
|
|
|
1271
|
+
|
|
|
1272
|
+ ImageView imageView = getImageView();
|
|
|
1273
|
+ if (null != imageView && mScroller.computeScrollOffset()) {
|
|
|
1274
|
+
|
|
|
1275
|
+ final int newX = mScroller.getCurrX();
|
|
|
1276
|
+ final int newY = mScroller.getCurrY();
|
|
|
1277
|
+
|
|
|
1278
|
+ if (DEBUG) {
|
|
|
1279
|
+ LogManager.getLogger().d(
|
|
|
1280
|
+ LOG_TAG,
|
|
|
1281
|
+ "fling run(). CurrentX:" + mCurrentX + " CurrentY:"
|
|
|
1282
|
+ + mCurrentY + " NewX:" + newX + " NewY:"
|
|
|
1283
|
+ + newY);
|
|
|
1284
|
+ }
|
|
|
1285
|
+
|
|
|
1286
|
+ mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
|
|
|
1287
|
+ setImageViewMatrix(getDrawMatrix());
|
|
|
1288
|
+
|
|
|
1289
|
+ mCurrentX = newX;
|
|
|
1290
|
+ mCurrentY = newY;
|
|
|
1291
|
+
|
|
|
1292
|
+ // Post On animation
|
|
|
1293
|
+ Compat.postOnAnimation(imageView, this);
|
|
|
1294
|
+ }
|
|
|
1295
|
+ }
|
|
|
1296
|
+ }
|
|
|
1297
|
+
|
|
|
1298
|
+ /**
|
|
|
1299
|
+ * a RightAngleRunnable that finger lift rotate to 0,90,180,270 degree
|
|
|
1300
|
+ */
|
|
|
1301
|
+ private class RightAngleRunnable implements Runnable {
|
|
|
1302
|
+ private static final int RECOVER_SPEED = 4;
|
|
|
1303
|
+ private int mOldDegree;
|
|
|
1304
|
+ private int mNeedToRotate;
|
|
|
1305
|
+ private int mRoPivotX;
|
|
|
1306
|
+ private int mRoPivotY;
|
|
|
1307
|
+
|
|
|
1308
|
+ RightAngleRunnable(int degree, int pivotX, int pivotY) {
|
|
|
1309
|
+ this.mOldDegree = degree;
|
|
|
1310
|
+ this.mNeedToRotate = calDegree(degree) - mOldDegree;
|
|
|
1311
|
+ this.mRoPivotX = pivotX;
|
|
|
1312
|
+ this.mRoPivotY = pivotY;
|
|
|
1313
|
+ }
|
|
|
1314
|
+
|
|
|
1315
|
+ /**
|
|
|
1316
|
+ * get right degree,when one finger lifts
|
|
|
1317
|
+ *
|
|
|
1318
|
+ * @param oldDegree current degree
|
|
|
1319
|
+ * @return 0, 90, 180, 270 according to oldDegree
|
|
|
1320
|
+ */
|
|
|
1321
|
+ private int calDegree(int oldDegree) {
|
|
|
1322
|
+ int result;
|
|
|
1323
|
+ float n = (float) oldDegree / 45;
|
|
|
1324
|
+ if (n >= 0 && n < 1) {
|
|
|
1325
|
+ result = 0;
|
|
|
1326
|
+ } else if (n >= 1 && n <= 2.5) {
|
|
|
1327
|
+ result = 90;
|
|
|
1328
|
+ } else if (n > 2.5 && n < 5.5) {
|
|
|
1329
|
+ result = 180;
|
|
|
1330
|
+ } else if (n >= 5.5 && n <= 7) {
|
|
|
1331
|
+ result = 270;
|
|
|
1332
|
+ } else {
|
|
|
1333
|
+ result = 360;
|
|
|
1334
|
+ }
|
|
|
1335
|
+ return result;
|
|
|
1336
|
+ }
|
|
|
1337
|
+
|
|
|
1338
|
+ @Override
|
|
|
1339
|
+ public void run() {
|
|
|
1340
|
+ if (mNeedToRotate == 0) {
|
|
|
1341
|
+ mIsToRighting = false;
|
|
|
1342
|
+ return;
|
|
|
1343
|
+ }
|
|
|
1344
|
+ ImageView imageView = getImageView();
|
|
|
1345
|
+ if (imageView == null) {
|
|
|
1346
|
+ mIsToRighting = false;
|
|
|
1347
|
+ return;
|
|
|
1348
|
+ }
|
|
|
1349
|
+ mIsToRighting = true;
|
|
|
1350
|
+ if (mNeedToRotate > 0) {
|
|
|
1351
|
+ //Clockwise rotation
|
|
|
1352
|
+ if (mNeedToRotate >= RECOVER_SPEED) {
|
|
|
1353
|
+ mSuppMatrix.postRotate(RECOVER_SPEED, mRoPivotX, mRoPivotY);
|
|
|
1354
|
+ mNeedToRotate -= RECOVER_SPEED;
|
|
|
1355
|
+ } else {
|
|
|
1356
|
+ mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
|
|
|
1357
|
+ mNeedToRotate = 0;
|
|
|
1358
|
+ }
|
|
|
1359
|
+ } else if (mNeedToRotate < 0) {
|
|
|
1360
|
+ //Counterclockwise rotation
|
|
|
1361
|
+ if (mNeedToRotate <= -RECOVER_SPEED) {
|
|
|
1362
|
+ mSuppMatrix.postRotate(-RECOVER_SPEED, mRoPivotX, mRoPivotY);
|
|
|
1363
|
+ mNeedToRotate += RECOVER_SPEED;
|
|
|
1364
|
+ } else {
|
|
|
1365
|
+ mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
|
|
|
1366
|
+ mNeedToRotate = 0;
|
|
|
1367
|
+ }
|
|
|
1368
|
+ }
|
|
|
1369
|
+ checkAndDisplayMatrix();
|
|
|
1370
|
+ Compat.postOnAnimation(imageView, this);
|
|
|
1371
|
+ }
|
|
|
1372
|
+ }
|
|
|
1373
|
+}
|
|
|
|
@@ -0,0 +1,149 @@
|
|
|
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.android.views.rotatephotoview.gestures;
|
|
|
17
|
+
|
|
|
18
|
+import android.content.Context;
|
|
|
19
|
+import android.view.MotionEvent;
|
|
|
20
|
+import android.view.VelocityTracker;
|
|
|
21
|
+import android.view.ViewConfiguration;
|
|
|
22
|
+
|
|
|
23
|
+import com.android.views.rotatephotoview.log.LogManager;
|
|
|
24
|
+
|
|
|
25
|
+public class CupcakeGestureDetector implements GestureDetector {
|
|
|
26
|
+
|
|
|
27
|
+ protected OnGestureListener mListener;
|
|
|
28
|
+ private static final String LOG_TAG = "CupcakeGestureDetector";
|
|
|
29
|
+ float mLastTouchX;
|
|
|
30
|
+ float mLastTouchY;
|
|
|
31
|
+ final float mTouchSlop;
|
|
|
32
|
+ final float mMinimumVelocity;
|
|
|
33
|
+
|
|
|
34
|
+ @Override
|
|
|
35
|
+ public void setOnGestureListener(OnGestureListener listener) {
|
|
|
36
|
+ this.mListener = listener;
|
|
|
37
|
+ }
|
|
|
38
|
+
|
|
|
39
|
+ public CupcakeGestureDetector(Context context) {
|
|
|
40
|
+ final ViewConfiguration configuration = ViewConfiguration
|
|
|
41
|
+ .get(context);
|
|
|
42
|
+ mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
|
|
|
43
|
+ mTouchSlop = configuration.getScaledTouchSlop();
|
|
|
44
|
+ }
|
|
|
45
|
+
|
|
|
46
|
+ private VelocityTracker mVelocityTracker;
|
|
|
47
|
+ private boolean mIsDragging;
|
|
|
48
|
+
|
|
|
49
|
+ float getActiveX(MotionEvent ev) {
|
|
|
50
|
+ return ev.getX();
|
|
|
51
|
+ }
|
|
|
52
|
+
|
|
|
53
|
+ float getActiveY(MotionEvent ev) {
|
|
|
54
|
+ return ev.getY();
|
|
|
55
|
+ }
|
|
|
56
|
+
|
|
|
57
|
+ @Override
|
|
|
58
|
+ public boolean isScaling() {
|
|
|
59
|
+ return false;
|
|
|
60
|
+ }
|
|
|
61
|
+
|
|
|
62
|
+ @Override
|
|
|
63
|
+ public boolean isDragging() {
|
|
|
64
|
+ return mIsDragging;
|
|
|
65
|
+ }
|
|
|
66
|
+
|
|
|
67
|
+ @Override
|
|
|
68
|
+ public boolean onTouchEvent(MotionEvent ev) {
|
|
|
69
|
+ switch (ev.getAction()) {
|
|
|
70
|
+ case MotionEvent.ACTION_DOWN: {
|
|
|
71
|
+ mVelocityTracker = VelocityTracker.obtain();
|
|
|
72
|
+ if (null != mVelocityTracker) {
|
|
|
73
|
+ mVelocityTracker.addMovement(ev);
|
|
|
74
|
+ } else {
|
|
|
75
|
+ LogManager.getLogger().i(LOG_TAG, "Velocity tracker is null");
|
|
|
76
|
+ }
|
|
|
77
|
+
|
|
|
78
|
+ mLastTouchX = getActiveX(ev);
|
|
|
79
|
+ mLastTouchY = getActiveY(ev);
|
|
|
80
|
+ mIsDragging = false;
|
|
|
81
|
+ break;
|
|
|
82
|
+ }
|
|
|
83
|
+
|
|
|
84
|
+ case MotionEvent.ACTION_MOVE: {
|
|
|
85
|
+ final float x = getActiveX(ev);
|
|
|
86
|
+ final float y = getActiveY(ev);
|
|
|
87
|
+ final float dx = x - mLastTouchX, dy = y - mLastTouchY;
|
|
|
88
|
+
|
|
|
89
|
+ if (!mIsDragging) {
|
|
|
90
|
+ // Use Pythagoras to see if drag length is larger than
|
|
|
91
|
+ // touch slop
|
|
|
92
|
+ mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
|
|
|
93
|
+ }
|
|
|
94
|
+
|
|
|
95
|
+ if (mIsDragging) {
|
|
|
96
|
+ mListener.onDrag(dx, dy);
|
|
|
97
|
+ mLastTouchX = x;
|
|
|
98
|
+ mLastTouchY = y;
|
|
|
99
|
+
|
|
|
100
|
+ if (null != mVelocityTracker) {
|
|
|
101
|
+ mVelocityTracker.addMovement(ev);
|
|
|
102
|
+ }
|
|
|
103
|
+ }
|
|
|
104
|
+ break;
|
|
|
105
|
+ }
|
|
|
106
|
+
|
|
|
107
|
+ case MotionEvent.ACTION_CANCEL: {
|
|
|
108
|
+ // Recycle Velocity Tracker
|
|
|
109
|
+ if (null != mVelocityTracker) {
|
|
|
110
|
+ mVelocityTracker.recycle();
|
|
|
111
|
+ mVelocityTracker = null;
|
|
|
112
|
+ }
|
|
|
113
|
+ break;
|
|
|
114
|
+ }
|
|
|
115
|
+
|
|
|
116
|
+ case MotionEvent.ACTION_UP: {
|
|
|
117
|
+ if (mIsDragging) {
|
|
|
118
|
+ if (null != mVelocityTracker) {
|
|
|
119
|
+ mLastTouchX = getActiveX(ev);
|
|
|
120
|
+ mLastTouchY = getActiveY(ev);
|
|
|
121
|
+
|
|
|
122
|
+ // Compute velocity within the last 1000ms
|
|
|
123
|
+ mVelocityTracker.addMovement(ev);
|
|
|
124
|
+ mVelocityTracker.computeCurrentVelocity(1000);
|
|
|
125
|
+
|
|
|
126
|
+ final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
|
|
|
127
|
+ .getYVelocity();
|
|
|
128
|
+
|
|
|
129
|
+ // If the velocity is greater than minVelocity, call
|
|
|
130
|
+ // listener
|
|
|
131
|
+ if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
|
|
|
132
|
+ mListener.onFling(mLastTouchX, mLastTouchY, -vX,
|
|
|
133
|
+ -vY);
|
|
|
134
|
+ }
|
|
|
135
|
+ }
|
|
|
136
|
+ }
|
|
|
137
|
+
|
|
|
138
|
+ // Recycle Velocity Tracker
|
|
|
139
|
+ if (null != mVelocityTracker) {
|
|
|
140
|
+ mVelocityTracker.recycle();
|
|
|
141
|
+ mVelocityTracker = null;
|
|
|
142
|
+ }
|
|
|
143
|
+ break;
|
|
|
144
|
+ }
|
|
|
145
|
+ }
|
|
|
146
|
+
|
|
|
147
|
+ return true;
|
|
|
148
|
+ }
|
|
|
149
|
+}
|
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+/**
|
|
|
2
|
+ * ****************************************************************************
|
|
|
3
|
+ * Copyright 2011, 2012 Chris Banes.
|
|
|
4
|
+ * <p>
|
|
|
5
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
6
|
+ * you may not use this file except in compliance with the License.
|
|
|
7
|
+ * You may obtain a copy of the License at
|
|
|
8
|
+ * <p>
|
|
|
9
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
10
|
+ * <p>
|
|
|
11
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
12
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
13
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
14
|
+ * See the License for the specific language governing permissions and
|
|
|
15
|
+ * limitations under the License.
|
|
|
16
|
+ *******************************************************************************/
|
|
|
17
|
+package com.android.views.rotatephotoview.gestures;
|
|
|
18
|
+
|
|
|
19
|
+import android.annotation.TargetApi;
|
|
|
20
|
+import android.content.Context;
|
|
|
21
|
+import android.view.MotionEvent;
|
|
|
22
|
+
|
|
|
23
|
+import com.android.views.rotatephotoview.Compat;
|
|
|
24
|
+
|
|
|
25
|
+@TargetApi(5)
|
|
|
26
|
+public class EclairGestureDetector extends CupcakeGestureDetector {
|
|
|
27
|
+
|
|
|
28
|
+ private static final int INVALID_POINTER_ID = -1;
|
|
|
29
|
+ private int mActivePointerId = INVALID_POINTER_ID;
|
|
|
30
|
+ private int mActivePointerIndex = 0;
|
|
|
31
|
+
|
|
|
32
|
+ public EclairGestureDetector(Context context) {
|
|
|
33
|
+ super(context);
|
|
|
34
|
+ }
|
|
|
35
|
+
|
|
|
36
|
+ @Override
|
|
|
37
|
+ float getActiveX(MotionEvent ev) {
|
|
|
38
|
+ try {
|
|
|
39
|
+ return ev.getX(mActivePointerIndex);
|
|
|
40
|
+ } catch (Exception e) {
|
|
|
41
|
+ return ev.getX();
|
|
|
42
|
+ }
|
|
|
43
|
+ }
|
|
|
44
|
+
|
|
|
45
|
+ @Override
|
|
|
46
|
+ float getActiveY(MotionEvent ev) {
|
|
|
47
|
+ try {
|
|
|
48
|
+ return ev.getY(mActivePointerIndex);
|
|
|
49
|
+ } catch (Exception e) {
|
|
|
50
|
+ return ev.getY();
|
|
|
51
|
+ }
|
|
|
52
|
+ }
|
|
|
53
|
+
|
|
|
54
|
+ @Override
|
|
|
55
|
+ public boolean onTouchEvent(MotionEvent ev) {
|
|
|
56
|
+ final int action = ev.getAction();
|
|
|
57
|
+ switch (action & MotionEvent.ACTION_MASK) {
|
|
|
58
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
59
|
+ mActivePointerId = ev.getPointerId(0);
|
|
|
60
|
+ break;
|
|
|
61
|
+ case MotionEvent.ACTION_CANCEL:
|
|
|
62
|
+ case MotionEvent.ACTION_UP:
|
|
|
63
|
+ mActivePointerId = INVALID_POINTER_ID;
|
|
|
64
|
+ break;
|
|
|
65
|
+ case MotionEvent.ACTION_POINTER_UP:
|
|
|
66
|
+ // Ignore deprecation, ACTION_POINTER_ID_MASK and
|
|
|
67
|
+ // ACTION_POINTER_ID_SHIFT has same value and are deprecated
|
|
|
68
|
+ // You can have either deprecation or lint target api warning
|
|
|
69
|
+ final int pointerIndex = Compat.getPointerIndex(ev.getAction());
|
|
|
70
|
+ final int pointerId = ev.getPointerId(pointerIndex);
|
|
|
71
|
+ if (pointerId == mActivePointerId) {
|
|
|
72
|
+ // This was our active pointer going up. Choose a new
|
|
|
73
|
+ // active pointer and adjust accordingly.
|
|
|
74
|
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
|
|
|
75
|
+ mActivePointerId = ev.getPointerId(newPointerIndex);
|
|
|
76
|
+ mLastTouchX = ev.getX(newPointerIndex);
|
|
|
77
|
+ mLastTouchY = ev.getY(newPointerIndex);
|
|
|
78
|
+ }
|
|
|
79
|
+ break;
|
|
|
80
|
+ }
|
|
|
81
|
+
|
|
|
82
|
+ mActivePointerIndex = ev
|
|
|
83
|
+ .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
|
|
|
84
|
+ : 0);
|
|
|
85
|
+ try {
|
|
|
86
|
+ return super.onTouchEvent(ev);
|
|
|
87
|
+ } catch (IllegalArgumentException e) {
|
|
|
88
|
+ // Fix for support lib bug, happening when onDestroy is
|
|
|
89
|
+ return true;
|
|
|
90
|
+ }
|
|
|
91
|
+ }
|
|
|
92
|
+}
|
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+/*******************************************************************************
|
|
|
2
|
+ * Copyright 2011, 2012 Chris Banes.
|
|
|
3
|
+ * <p/>
|
|
|
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
|
+ * <p/>
|
|
|
8
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
9
|
+ * <p/>
|
|
|
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.android.views.rotatephotoview.gestures;
|
|
|
17
|
+
|
|
|
18
|
+import android.annotation.TargetApi;
|
|
|
19
|
+import android.content.Context;
|
|
|
20
|
+import android.view.MotionEvent;
|
|
|
21
|
+import android.view.ScaleGestureDetector;
|
|
|
22
|
+
|
|
|
23
|
+@TargetApi(8)
|
|
|
24
|
+public class FroyoGestureDetector extends EclairGestureDetector {
|
|
|
25
|
+
|
|
|
26
|
+ protected final ScaleGestureDetector mDetector;
|
|
|
27
|
+
|
|
|
28
|
+ public FroyoGestureDetector(Context context) {
|
|
|
29
|
+ super(context);
|
|
|
30
|
+ ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
|
|
|
31
|
+
|
|
|
32
|
+ @Override
|
|
|
33
|
+ public boolean onScale(ScaleGestureDetector detector) {
|
|
|
34
|
+ float scaleFactor = detector.getScaleFactor();
|
|
|
35
|
+
|
|
|
36
|
+ if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
|
|
|
37
|
+ return false;
|
|
|
38
|
+
|
|
|
39
|
+ mListener.onScale(scaleFactor,
|
|
|
40
|
+ detector.getFocusX(), detector.getFocusY());
|
|
|
41
|
+ return true;
|
|
|
42
|
+ }
|
|
|
43
|
+
|
|
|
44
|
+ @Override
|
|
|
45
|
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
|
|
|
46
|
+ return true;
|
|
|
47
|
+ }
|
|
|
48
|
+
|
|
|
49
|
+ @Override
|
|
|
50
|
+ public void onScaleEnd(ScaleGestureDetector detector) {
|
|
|
51
|
+ // NO-OP
|
|
|
52
|
+ }
|
|
|
53
|
+ };
|
|
|
54
|
+ mDetector = new ScaleGestureDetector(context, mScaleListener);
|
|
|
55
|
+ }
|
|
|
56
|
+
|
|
|
57
|
+ @Override
|
|
|
58
|
+ public boolean isScaling() {
|
|
|
59
|
+ return mDetector.isInProgress();
|
|
|
60
|
+ }
|
|
|
61
|
+
|
|
|
62
|
+ @Override
|
|
|
63
|
+ public boolean onTouchEvent(MotionEvent ev) {
|
|
|
64
|
+ try {
|
|
|
65
|
+ mDetector.onTouchEvent(ev);
|
|
|
66
|
+ return super.onTouchEvent(ev);
|
|
|
67
|
+ } catch (IllegalArgumentException e) {
|
|
|
68
|
+ // Fix for support lib bug, happening when onDestroy is
|
|
|
69
|
+ return true;
|
|
|
70
|
+ }
|
|
|
71
|
+ }
|
|
|
72
|
+
|
|
|
73
|
+}
|
|
|
|
@@ -0,0 +1,30 @@
|
|
|
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.android.views.rotatephotoview.gestures;
|
|
|
17
|
+
|
|
|
18
|
+import android.view.MotionEvent;
|
|
|
19
|
+
|
|
|
20
|
+public interface GestureDetector {
|
|
|
21
|
+
|
|
|
22
|
+ boolean onTouchEvent(MotionEvent ev);
|
|
|
23
|
+
|
|
|
24
|
+ boolean isScaling();
|
|
|
25
|
+
|
|
|
26
|
+ boolean isDragging();
|
|
|
27
|
+
|
|
|
28
|
+ void setOnGestureListener(OnGestureListener listener);
|
|
|
29
|
+
|
|
|
30
|
+}
|
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+package com.android.views.rotatephotoview.gestures;
|
|
|
2
|
+
|
|
|
3
|
+import android.view.MotionEvent;
|
|
|
4
|
+
|
|
|
5
|
+/**
|
|
|
6
|
+ * Interface to detect rotation
|
|
|
7
|
+ * Created by ChenSL on 2015/9/16.
|
|
|
8
|
+ */
|
|
|
9
|
+public interface IRotateDetector {
|
|
|
10
|
+ /**
|
|
|
11
|
+ * handle rotation in onTouchEvent
|
|
|
12
|
+ *
|
|
|
13
|
+ * @param event The motion event.
|
|
|
14
|
+ * @return True if the event was handled, false otherwise.
|
|
|
15
|
+ */
|
|
|
16
|
+ boolean onTouchEvent(MotionEvent event);
|
|
|
17
|
+
|
|
|
18
|
+ /**
|
|
|
19
|
+ * is the Gesture Rotate
|
|
|
20
|
+ *
|
|
|
21
|
+ * @return true:rotating;false,otherwise
|
|
|
22
|
+ */
|
|
|
23
|
+ boolean isRotating();
|
|
|
24
|
+}
|
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+package com.android.views.rotatephotoview.gestures;
|
|
|
2
|
+
|
|
|
3
|
+/**
|
|
|
4
|
+ * Interface for a callback for rotation
|
|
|
5
|
+ * Created by ChenSL on 2015/9/16.
|
|
|
6
|
+ */
|
|
|
7
|
+public interface IRotateListener {
|
|
|
8
|
+ /**
|
|
|
9
|
+ * callback for rotation
|
|
|
10
|
+ *
|
|
|
11
|
+ * @param degree degree of rotation
|
|
|
12
|
+ */
|
|
|
13
|
+ void rotate(int degree, int pivotX, int pivotY);
|
|
|
14
|
+
|
|
|
15
|
+ /**
|
|
|
16
|
+ * MotionEvent.ACTION_POINTER_UP happens when two finger minus to only one
|
|
|
17
|
+ * change the ImageView to 0,90,180,270
|
|
|
18
|
+ */
|
|
|
19
|
+ void upRotate(int pivotX, int pivotY);
|
|
|
20
|
+}
|
|
|
|
@@ -0,0 +1,27 @@
|
|
|
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.android.views.rotatephotoview.gestures;
|
|
|
17
|
+
|
|
|
18
|
+public interface OnGestureListener {
|
|
|
19
|
+
|
|
|
20
|
+ void onDrag(float dx, float dy);
|
|
|
21
|
+
|
|
|
22
|
+ void onFling(float startX, float startY, float velocityX,
|
|
|
23
|
+ float velocityY);
|
|
|
24
|
+
|
|
|
25
|
+ void onScale(float scaleFactor, float focusX, float focusY);
|
|
|
26
|
+
|
|
|
27
|
+}
|
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+package com.android.views.rotatephotoview.gestures;
|
|
|
2
|
+
|
|
|
3
|
+import android.view.MotionEvent;
|
|
|
4
|
+
|
|
|
5
|
+/**
|
|
|
6
|
+ * Handle ImageView rotate event with two fingers
|
|
|
7
|
+ * Created by ChenSL on 2015/9/16.
|
|
|
8
|
+ */
|
|
|
9
|
+public class RotateGestureDetector implements IRotateDetector {
|
|
|
10
|
+ private int mLastAngle = 0;
|
|
|
11
|
+ private IRotateListener mListener;
|
|
|
12
|
+ private boolean mIsRotate;
|
|
|
13
|
+
|
|
|
14
|
+ /**
|
|
|
15
|
+ * set rotation listener for callback
|
|
|
16
|
+ *
|
|
|
17
|
+ * @param listener a rotation listener
|
|
|
18
|
+ */
|
|
|
19
|
+ public void setRotateListener(IRotateListener listener) {
|
|
|
20
|
+ this.mListener = listener;
|
|
|
21
|
+ }
|
|
|
22
|
+
|
|
|
23
|
+ @Override
|
|
|
24
|
+ public boolean onTouchEvent(MotionEvent event) {
|
|
|
25
|
+ return doRotate(event);
|
|
|
26
|
+ }
|
|
|
27
|
+
|
|
|
28
|
+ @Override
|
|
|
29
|
+ public boolean isRotating() {
|
|
|
30
|
+ return mIsRotate;
|
|
|
31
|
+ }
|
|
|
32
|
+
|
|
|
33
|
+ /**
|
|
|
34
|
+ * handle rotation
|
|
|
35
|
+ *
|
|
|
36
|
+ * @param ev Motion event
|
|
|
37
|
+ * @return always true.
|
|
|
38
|
+ */
|
|
|
39
|
+ private boolean doRotate(MotionEvent ev) {
|
|
|
40
|
+ if (ev.getPointerCount() != 2) {
|
|
|
41
|
+ return false;
|
|
|
42
|
+ }
|
|
|
43
|
+ //Calculate the angle between the two fingers
|
|
|
44
|
+ int pivotX = (int) (ev.getX(0) + ev.getX(1)) / 2;
|
|
|
45
|
+ int pivotY = (int) (ev.getY(0) + ev.getY(1)) / 2;
|
|
|
46
|
+ float deltaX = ev.getX(0) - ev.getX(1);
|
|
|
47
|
+ float deltaY = ev.getY(0) - ev.getY(1);
|
|
|
48
|
+ double radians = Math.atan(deltaY / deltaX);
|
|
|
49
|
+ //Convert to degrees
|
|
|
50
|
+ int degrees = (int) (radians * 180 / Math.PI);
|
|
|
51
|
+ /*
|
|
|
52
|
+ * Must use getActionMasked() for switching to pick up pointer events.
|
|
|
53
|
+ * These events have the pointer index encoded in them so the return
|
|
|
54
|
+ * from getAction() won't match the exact action constant.
|
|
|
55
|
+ */
|
|
|
56
|
+ switch (ev.getActionMasked()) {
|
|
|
57
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
58
|
+ mLastAngle = degrees;
|
|
|
59
|
+ mIsRotate = false;
|
|
|
60
|
+ break;
|
|
|
61
|
+ case MotionEvent.ACTION_UP:
|
|
|
62
|
+ mIsRotate = false;
|
|
|
63
|
+ break;
|
|
|
64
|
+ case MotionEvent.ACTION_POINTER_DOWN:
|
|
|
65
|
+ mLastAngle = degrees;
|
|
|
66
|
+ mIsRotate = false;
|
|
|
67
|
+ break;
|
|
|
68
|
+ case MotionEvent.ACTION_CANCEL:
|
|
|
69
|
+ case MotionEvent.ACTION_POINTER_UP:
|
|
|
70
|
+ mIsRotate = false;
|
|
|
71
|
+ upRotate(pivotX, pivotY);
|
|
|
72
|
+ mLastAngle = degrees;
|
|
|
73
|
+ break;
|
|
|
74
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
75
|
+ mIsRotate = true;
|
|
|
76
|
+ int degreesValue = degrees - mLastAngle;
|
|
|
77
|
+ if (degreesValue > 45) {
|
|
|
78
|
+ //Going CCW across the boundary
|
|
|
79
|
+ rotate(-5, pivotX, pivotY);
|
|
|
80
|
+ } else if (degreesValue < -45) {
|
|
|
81
|
+ //Going CW across the boundary
|
|
|
82
|
+ rotate(5, pivotX, pivotY);
|
|
|
83
|
+ } else {
|
|
|
84
|
+ //Normal rotation, rotate the difference
|
|
|
85
|
+ rotate(degreesValue, pivotX, pivotY);
|
|
|
86
|
+ }
|
|
|
87
|
+ //Save the current angle
|
|
|
88
|
+ mLastAngle = degrees;
|
|
|
89
|
+ break;
|
|
|
90
|
+ }
|
|
|
91
|
+ return true;
|
|
|
92
|
+ }
|
|
|
93
|
+
|
|
|
94
|
+ /**
|
|
|
95
|
+ * to invoke the callback
|
|
|
96
|
+ *
|
|
|
97
|
+ * @param degree degree to rotate
|
|
|
98
|
+ */
|
|
|
99
|
+ private void rotate(int degree, int pivotX, int pivotY) {
|
|
|
100
|
+ if (mListener != null) {
|
|
|
101
|
+ mListener.rotate(degree, pivotX, pivotY);
|
|
|
102
|
+ }
|
|
|
103
|
+ }
|
|
|
104
|
+
|
|
|
105
|
+ /**
|
|
|
106
|
+ * to invoke the finger up action
|
|
|
107
|
+ */
|
|
|
108
|
+ private void upRotate(int pivotX, int pivotY) {
|
|
|
109
|
+ if (mListener != null) {
|
|
|
110
|
+ mListener.upRotate(pivotX, pivotY);
|
|
|
111
|
+ }
|
|
|
112
|
+ }
|
|
|
113
|
+}
|
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+package com.android.views.rotatephotoview.gestures;
|
|
|
2
|
+
|
|
|
3
|
+/*******************************************************************************
|
|
|
4
|
+ * Copyright 2011, 2012 Chris Banes.
|
|
|
5
|
+ *
|
|
|
6
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
7
|
+ * you may not use this file except in compliance with the License.
|
|
|
8
|
+ * You may obtain a copy of the License at
|
|
|
9
|
+ *
|
|
|
10
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
11
|
+ *
|
|
|
12
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
13
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
14
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
15
|
+ * See the License for the specific language governing permissions and
|
|
|
16
|
+ * limitations under the License.
|
|
|
17
|
+ *******************************************************************************/
|
|
|
18
|
+
|
|
|
19
|
+import android.content.Context;
|
|
|
20
|
+import android.os.Build;
|
|
|
21
|
+
|
|
|
22
|
+public final class VersionedGestureDetector {
|
|
|
23
|
+
|
|
|
24
|
+ public static GestureDetector newInstance(Context context,
|
|
|
25
|
+ OnGestureListener listener) {
|
|
|
26
|
+ final int sdkVersion = Build.VERSION.SDK_INT;
|
|
|
27
|
+ GestureDetector detector;
|
|
|
28
|
+
|
|
|
29
|
+ if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
|
|
|
30
|
+ detector = new CupcakeGestureDetector(context);
|
|
|
31
|
+ } else if (sdkVersion < Build.VERSION_CODES.FROYO) {
|
|
|
32
|
+ detector = new EclairGestureDetector(context);
|
|
|
33
|
+ } else {
|
|
|
34
|
+ detector = new FroyoGestureDetector(context);
|
|
|
35
|
+ }
|
|
|
36
|
+
|
|
|
37
|
+ detector.setOnGestureListener(listener);
|
|
|
38
|
+
|
|
|
39
|
+ return detector;
|
|
|
40
|
+ }
|
|
|
41
|
+
|
|
|
42
|
+}
|
|
|
|
@@ -0,0 +1,35 @@
|
|
|
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.android.views.rotatephotoview.log;
|
|
|
17
|
+
|
|
|
18
|
+import android.util.Log;
|
|
|
19
|
+
|
|
|
20
|
+/**
|
|
|
21
|
+ * class that holds the {@link Logger} for this library, defaults to {@link LoggerDefault} to send logs to android {@link Log}
|
|
|
22
|
+ */
|
|
|
23
|
+public final class LogManager {
|
|
|
24
|
+
|
|
|
25
|
+ private static Logger logger = new LoggerDefault();
|
|
|
26
|
+
|
|
|
27
|
+ public static void setLogger(Logger newLogger) {
|
|
|
28
|
+ logger = newLogger;
|
|
|
29
|
+ }
|
|
|
30
|
+
|
|
|
31
|
+ public static Logger getLogger() {
|
|
|
32
|
+ return logger;
|
|
|
33
|
+ }
|
|
|
34
|
+
|
|
|
35
|
+}
|
|
|
|
@@ -0,0 +1,116 @@
|
|
|
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.android.views.rotatephotoview.log;
|
|
|
17
|
+
|
|
|
18
|
+/**
|
|
|
19
|
+ * interface for a logger class to replace the static calls to {@link android.util.Log}
|
|
|
20
|
+ */
|
|
|
21
|
+public interface Logger {
|
|
|
22
|
+ /**
|
|
|
23
|
+ * Send a {@link android.util.Log#VERBOSE} log message.
|
|
|
24
|
+ *
|
|
|
25
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
26
|
+ * the class or activity where the log call occurs.
|
|
|
27
|
+ * @param msg The message you would like logged.
|
|
|
28
|
+ */
|
|
|
29
|
+ int v(String tag, String msg);
|
|
|
30
|
+
|
|
|
31
|
+ /**
|
|
|
32
|
+ * Send a {@link android.util.Log#VERBOSE} log message and log the exception.
|
|
|
33
|
+ *
|
|
|
34
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
35
|
+ * the class or activity where the log call occurs.
|
|
|
36
|
+ * @param msg The message you would like logged.
|
|
|
37
|
+ * @param tr An exception to log
|
|
|
38
|
+ */
|
|
|
39
|
+ int v(String tag, String msg, Throwable tr);
|
|
|
40
|
+
|
|
|
41
|
+ /**
|
|
|
42
|
+ * Send a {@link android.util.Log#DEBUG} log message.
|
|
|
43
|
+ *
|
|
|
44
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
45
|
+ * the class or activity where the log call occurs.
|
|
|
46
|
+ * @param msg The message you would like logged.
|
|
|
47
|
+ */
|
|
|
48
|
+ int d(String tag, String msg);
|
|
|
49
|
+
|
|
|
50
|
+ /**
|
|
|
51
|
+ * Send a {@link android.util.Log#DEBUG} log message and log the exception.
|
|
|
52
|
+ *
|
|
|
53
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
54
|
+ * the class or activity where the log call occurs.
|
|
|
55
|
+ * @param msg The message you would like logged.
|
|
|
56
|
+ * @param tr An exception to log
|
|
|
57
|
+ */
|
|
|
58
|
+ int d(String tag, String msg, Throwable tr);
|
|
|
59
|
+
|
|
|
60
|
+ /**
|
|
|
61
|
+ * Send an {@link android.util.Log#INFO} log message.
|
|
|
62
|
+ *
|
|
|
63
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
64
|
+ * the class or activity where the log call occurs.
|
|
|
65
|
+ * @param msg The message you would like logged.
|
|
|
66
|
+ */
|
|
|
67
|
+ int i(String tag, String msg);
|
|
|
68
|
+
|
|
|
69
|
+ /**
|
|
|
70
|
+ * Send a {@link android.util.Log#INFO} log message and log the exception.
|
|
|
71
|
+ *
|
|
|
72
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
73
|
+ * the class or activity where the log call occurs.
|
|
|
74
|
+ * @param msg The message you would like logged.
|
|
|
75
|
+ * @param tr An exception to log
|
|
|
76
|
+ */
|
|
|
77
|
+ int i(String tag, String msg, Throwable tr);
|
|
|
78
|
+
|
|
|
79
|
+ /**
|
|
|
80
|
+ * Send a {@link android.util.Log#WARN} log message.
|
|
|
81
|
+ *
|
|
|
82
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
83
|
+ * the class or activity where the log call occurs.
|
|
|
84
|
+ * @param msg The message you would like logged.
|
|
|
85
|
+ */
|
|
|
86
|
+ int w(String tag, String msg);
|
|
|
87
|
+
|
|
|
88
|
+ /**
|
|
|
89
|
+ * Send a {@link android.util.Log#WARN} log message and log the exception.
|
|
|
90
|
+ *
|
|
|
91
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
92
|
+ * the class or activity where the log call occurs.
|
|
|
93
|
+ * @param msg The message you would like logged.
|
|
|
94
|
+ * @param tr An exception to log
|
|
|
95
|
+ */
|
|
|
96
|
+ int w(String tag, String msg, Throwable tr);
|
|
|
97
|
+
|
|
|
98
|
+ /**
|
|
|
99
|
+ * Send an {@link android.util.Log#ERROR} log message.
|
|
|
100
|
+ *
|
|
|
101
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
102
|
+ * the class or activity where the log call occurs.
|
|
|
103
|
+ * @param msg The message you would like logged.
|
|
|
104
|
+ */
|
|
|
105
|
+ int e(String tag, String msg);
|
|
|
106
|
+
|
|
|
107
|
+ /**
|
|
|
108
|
+ * Send a {@link android.util.Log#ERROR} log message and log the exception.
|
|
|
109
|
+ *
|
|
|
110
|
+ * @param tag Used to identify the source of a log message. It usually identifies
|
|
|
111
|
+ * the class or activity where the log call occurs.
|
|
|
112
|
+ * @param msg The message you would like logged.
|
|
|
113
|
+ * @param tr An exception to log
|
|
|
114
|
+ */
|
|
|
115
|
+ int e(String tag, String msg, Throwable tr);
|
|
|
116
|
+}
|
|
|
|
@@ -0,0 +1,76 @@
|
|
|
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.android.views.rotatephotoview.log;
|
|
|
17
|
+
|
|
|
18
|
+import android.util.Log;
|
|
|
19
|
+
|
|
|
20
|
+/**
|
|
|
21
|
+ * Helper class to redirect {@link LogManager#logger} to {@link Log}
|
|
|
22
|
+ */
|
|
|
23
|
+public class LoggerDefault implements Logger {
|
|
|
24
|
+
|
|
|
25
|
+ @Override
|
|
|
26
|
+ public int v(String tag, String msg) {
|
|
|
27
|
+ return Log.v(tag, msg);
|
|
|
28
|
+ }
|
|
|
29
|
+
|
|
|
30
|
+ @Override
|
|
|
31
|
+ public int v(String tag, String msg, Throwable tr) {
|
|
|
32
|
+ return Log.v(tag, msg, tr);
|
|
|
33
|
+ }
|
|
|
34
|
+
|
|
|
35
|
+ @Override
|
|
|
36
|
+ public int d(String tag, String msg) {
|
|
|
37
|
+ return Log.d(tag, msg);
|
|
|
38
|
+ }
|
|
|
39
|
+
|
|
|
40
|
+ @Override
|
|
|
41
|
+ public int d(String tag, String msg, Throwable tr) {
|
|
|
42
|
+ return Log.d(tag, msg, tr);
|
|
|
43
|
+ }
|
|
|
44
|
+
|
|
|
45
|
+ @Override
|
|
|
46
|
+ public int i(String tag, String msg) {
|
|
|
47
|
+ return Log.i(tag, msg);
|
|
|
48
|
+ }
|
|
|
49
|
+
|
|
|
50
|
+ @Override
|
|
|
51
|
+ public int i(String tag, String msg, Throwable tr) {
|
|
|
52
|
+ return Log.i(tag, msg, tr);
|
|
|
53
|
+ }
|
|
|
54
|
+
|
|
|
55
|
+ @Override
|
|
|
56
|
+ public int w(String tag, String msg) {
|
|
|
57
|
+ return Log.w(tag, msg);
|
|
|
58
|
+ }
|
|
|
59
|
+
|
|
|
60
|
+ @Override
|
|
|
61
|
+ public int w(String tag, String msg, Throwable tr) {
|
|
|
62
|
+ return Log.w(tag, msg, tr);
|
|
|
63
|
+ }
|
|
|
64
|
+
|
|
|
65
|
+ @Override
|
|
|
66
|
+ public int e(String tag, String msg) {
|
|
|
67
|
+ return Log.e(tag, msg);
|
|
|
68
|
+ }
|
|
|
69
|
+
|
|
|
70
|
+ @Override
|
|
|
71
|
+ public int e(String tag, String msg, Throwable tr) {
|
|
|
72
|
+ return Log.e(tag, msg, tr);
|
|
|
73
|
+ }
|
|
|
74
|
+
|
|
|
75
|
+
|
|
|
76
|
+}
|
|
|
|
@@ -0,0 +1,61 @@
|
|
|
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.android.views.rotatephotoview.scrollerproxy;
|
|
|
17
|
+
|
|
|
18
|
+import android.annotation.TargetApi;
|
|
|
19
|
+import android.content.Context;
|
|
|
20
|
+import android.widget.OverScroller;
|
|
|
21
|
+
|
|
|
22
|
+@TargetApi(9)
|
|
|
23
|
+public class GingerScroller extends ScrollerProxy {
|
|
|
24
|
+
|
|
|
25
|
+ protected final OverScroller mScroller;
|
|
|
26
|
+
|
|
|
27
|
+ public GingerScroller(Context context) {
|
|
|
28
|
+ mScroller = new OverScroller(context);
|
|
|
29
|
+ }
|
|
|
30
|
+
|
|
|
31
|
+ @Override
|
|
|
32
|
+ public boolean computeScrollOffset() {
|
|
|
33
|
+ return mScroller.computeScrollOffset();
|
|
|
34
|
+ }
|
|
|
35
|
+
|
|
|
36
|
+ @Override
|
|
|
37
|
+ public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
|
|
|
38
|
+ int overX, int overY) {
|
|
|
39
|
+ mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY);
|
|
|
40
|
+ }
|
|
|
41
|
+
|
|
|
42
|
+ @Override
|
|
|
43
|
+ public void forceFinished(boolean finished) {
|
|
|
44
|
+ mScroller.forceFinished(finished);
|
|
|
45
|
+ }
|
|
|
46
|
+
|
|
|
47
|
+ @Override
|
|
|
48
|
+ public boolean isFinished() {
|
|
|
49
|
+ return mScroller.isFinished();
|
|
|
50
|
+ }
|
|
|
51
|
+
|
|
|
52
|
+ @Override
|
|
|
53
|
+ public int getCurrX() {
|
|
|
54
|
+ return mScroller.getCurrX();
|
|
|
55
|
+ }
|
|
|
56
|
+
|
|
|
57
|
+ @Override
|
|
|
58
|
+ public int getCurrY() {
|
|
|
59
|
+ return mScroller.getCurrY();
|
|
|
60
|
+ }
|
|
|
61
|
+}
|
|
|
|
@@ -0,0 +1,33 @@
|
|
|
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.android.views.rotatephotoview.scrollerproxy;
|
|
|
17
|
+
|
|
|
18
|
+import android.annotation.TargetApi;
|
|
|
19
|
+import android.content.Context;
|
|
|
20
|
+
|
|
|
21
|
+@TargetApi(14)
|
|
|
22
|
+public class IcsScroller extends GingerScroller {
|
|
|
23
|
+
|
|
|
24
|
+ public IcsScroller(Context context) {
|
|
|
25
|
+ super(context);
|
|
|
26
|
+ }
|
|
|
27
|
+
|
|
|
28
|
+ @Override
|
|
|
29
|
+ public boolean computeScrollOffset() {
|
|
|
30
|
+ return mScroller.computeScrollOffset();
|
|
|
31
|
+ }
|
|
|
32
|
+
|
|
|
33
|
+}
|
|
|
|
@@ -0,0 +1,58 @@
|
|
|
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.android.views.rotatephotoview.scrollerproxy;
|
|
|
17
|
+
|
|
|
18
|
+import android.content.Context;
|
|
|
19
|
+import android.widget.Scroller;
|
|
|
20
|
+
|
|
|
21
|
+public class PreGingerScroller extends ScrollerProxy {
|
|
|
22
|
+
|
|
|
23
|
+ private final Scroller mScroller;
|
|
|
24
|
+
|
|
|
25
|
+ public PreGingerScroller(Context context) {
|
|
|
26
|
+ mScroller = new Scroller(context);
|
|
|
27
|
+ }
|
|
|
28
|
+
|
|
|
29
|
+ @Override
|
|
|
30
|
+ public boolean computeScrollOffset() {
|
|
|
31
|
+ return mScroller.computeScrollOffset();
|
|
|
32
|
+ }
|
|
|
33
|
+
|
|
|
34
|
+ @Override
|
|
|
35
|
+ public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
|
|
|
36
|
+ int overX, int overY) {
|
|
|
37
|
+ mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
|
|
|
38
|
+ }
|
|
|
39
|
+
|
|
|
40
|
+ @Override
|
|
|
41
|
+ public void forceFinished(boolean finished) {
|
|
|
42
|
+ mScroller.forceFinished(finished);
|
|
|
43
|
+ }
|
|
|
44
|
+
|
|
|
45
|
+ public boolean isFinished() {
|
|
|
46
|
+ return mScroller.isFinished();
|
|
|
47
|
+ }
|
|
|
48
|
+
|
|
|
49
|
+ @Override
|
|
|
50
|
+ public int getCurrX() {
|
|
|
51
|
+ return mScroller.getCurrX();
|
|
|
52
|
+ }
|
|
|
53
|
+
|
|
|
54
|
+ @Override
|
|
|
55
|
+ public int getCurrY() {
|
|
|
56
|
+ return mScroller.getCurrY();
|
|
|
57
|
+ }
|
|
|
58
|
+}
|
|
|
|
@@ -0,0 +1,48 @@
|
|
|
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.android.views.rotatephotoview.scrollerproxy;
|
|
|
17
|
+
|
|
|
18
|
+import android.content.Context;
|
|
|
19
|
+import android.os.Build.VERSION;
|
|
|
20
|
+import android.os.Build.VERSION_CODES;
|
|
|
21
|
+
|
|
|
22
|
+public abstract class ScrollerProxy {
|
|
|
23
|
+
|
|
|
24
|
+ public static ScrollerProxy getScroller(Context context) {
|
|
|
25
|
+ if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
|
|
|
26
|
+ return new PreGingerScroller(context);
|
|
|
27
|
+ } else if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {
|
|
|
28
|
+ return new GingerScroller(context);
|
|
|
29
|
+ } else {
|
|
|
30
|
+ return new IcsScroller(context);
|
|
|
31
|
+ }
|
|
|
32
|
+ }
|
|
|
33
|
+
|
|
|
34
|
+ public abstract boolean computeScrollOffset();
|
|
|
35
|
+
|
|
|
36
|
+ public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY,
|
|
|
37
|
+ int maxY, int overX, int overY);
|
|
|
38
|
+
|
|
|
39
|
+ public abstract void forceFinished(boolean finished);
|
|
|
40
|
+
|
|
|
41
|
+ public abstract boolean isFinished();
|
|
|
42
|
+
|
|
|
43
|
+ public abstract int getCurrX();
|
|
|
44
|
+
|
|
|
45
|
+ public abstract int getCurrY();
|
|
|
46
|
+
|
|
|
47
|
+
|
|
|
48
|
+}
|