1145873331@qq.com 6 anni fa
parent
commit
f2882ec6f8
20 ha cambiato i file con 9 aggiunte e 2665 eliminazioni
  1. 3 3
      app/src/main/java/com/electric/chargingpile/activity/ImageDetailFragment.java
  2. 0 60
      app/src/main/java/com/electric/chargingpile/view/Compat.java
  3. 0 143
      app/src/main/java/com/electric/chargingpile/view/CupcakeGestureDetector.java
  4. 0 98
      app/src/main/java/com/electric/chargingpile/view/DefaultOnDoubleTapListener.java
  5. 0 86
      app/src/main/java/com/electric/chargingpile/view/EclairGestureDetector.java
  6. 0 68
      app/src/main/java/com/electric/chargingpile/view/FroyoGestureDetector.java
  7. 0 28
      app/src/main/java/com/electric/chargingpile/view/GestureDetector.java
  8. 0 68
      app/src/main/java/com/electric/chargingpile/view/GingerScroller.java
  9. 0 283
      app/src/main/java/com/electric/chargingpile/view/IPhotoView.java
  10. 0 33
      app/src/main/java/com/electric/chargingpile/view/IcsScroller.java
  11. 0 262
      app/src/main/java/com/electric/chargingpile/view/PhotoView.java
  12. 0 1078
      app/src/main/java/com/electric/chargingpile/view/PhotoViewAttacher.java
  13. 0 58
      app/src/main/java/com/electric/chargingpile/view/PreGingerScroller.java
  14. 0 196
      app/src/main/java/com/electric/chargingpile/view/RoundImageViewByXfermode.java
  15. 0 48
      app/src/main/java/com/electric/chargingpile/view/ScrollerProxy.java
  16. 0 1
      app/src/main/java/com/electric/chargingpile/view/SlideClaimView.java
  17. 0 42
      app/src/main/java/com/electric/chargingpile/view/VersionedGestureDetector.java
  18. 0 105
      app/src/main/java/com/electric/chargingpile/view/ZoomControlsLayout.java
  19. 5 1
      app/src/main/java/com/electric/chargingpile/zoom/PhotoViewAttacher.java
  20. 1 4
      app/src/main/res/values/attr.xml

+ 3 - 3
app/src/main/java/com/electric/chargingpile/activity/ImageDetailFragment.java

@ -1,8 +1,7 @@
1 1
package com.electric.chargingpile.activity;
2 2
3 3
import com.electric.chargingpile.R;
4
import com.electric.chargingpile.view.PhotoViewAttacher;
5
import com.electric.chargingpile.view.PhotoViewAttacher.OnPhotoTapListener;
4
6 5
import android.graphics.Bitmap;
7 6
import android.os.Bundle;
8 7
import android.support.v4.app.Fragment;
@ -12,6 +11,7 @@ import android.view.ViewGroup;
12 11
import android.widget.ImageView;
13 12
import android.widget.ProgressBar;
14 13
14
import com.electric.chargingpile.zoom.PhotoViewAttacher;
15 15
import com.nostra13.universalimageloader.core.ImageLoader;
16 16
import com.nostra13.universalimageloader.core.assist.FailReason;
17 17
import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener;
@ -45,7 +45,7 @@ public class ImageDetailFragment extends Fragment {
45 45
        mImageView = (ImageView) v.findViewById(R.id.image);
46 46
        mAttacher = new PhotoViewAttacher(mImageView);
47 47
48
        mAttacher.setOnPhotoTapListener(new OnPhotoTapListener() {
48
        mAttacher.setOnPhotoTapListener(new PhotoViewAttacher.OnPhotoTapListener() {
49 49
50 50
            @Override
51 51
            public void onPhotoTap(View arg0, float arg1, float arg2) {

+ 0 - 60
app/src/main/java/com/electric/chargingpile/view/Compat.java

@ -1,60 +0,0 @@
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.annotation.TargetApi;
19
import android.os.Build;
20
import android.os.Build.VERSION;
21
import android.os.Build.VERSION_CODES;
22
import android.view.MotionEvent;
23
import android.view.View;
24
25
public class Compat {
26
27
    private static final int SIXTY_FPS_INTERVAL = 1000 / 60;
28
29
    public static void postOnAnimation(View view, Runnable runnable) {
30
        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
31
            postOnAnimationJellyBean(view, runnable);
32
        } else {
33
            view.postDelayed(runnable, SIXTY_FPS_INTERVAL);
34
        }
35
    }
36
37
    @TargetApi(16)
38
    private static void postOnAnimationJellyBean(View view, Runnable runnable) {
39
        view.postOnAnimation(runnable);
40
    }
41
42
    public static int getPointerIndex(int action) {
43
        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)
44
            return getPointerIndexHoneyComb(action);
45
        else
46
            return getPointerIndexEclair(action);
47
    }
48
49
    @SuppressWarnings("deprecation")
50
    @TargetApi(VERSION_CODES.ECLAIR)
51
    private static int getPointerIndexEclair(int action) {
52
        return (action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
53
    }
54
55
    @TargetApi(VERSION_CODES.HONEYCOMB)
56
    private static int getPointerIndexHoneyComb(int action) {
57
        return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
58
    }
59
60
}

+ 0 - 143
app/src/main/java/com/electric/chargingpile/view/CupcakeGestureDetector.java

@ -1,143 +0,0 @@
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.util.FloatMath;
20
import android.util.Log;
21
import android.view.MotionEvent;
22
import android.view.VelocityTracker;
23
import android.view.ViewConfiguration;
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
    public boolean isScaling() {
58
        return false;
59
    }
60
61
    @Override
62
    public boolean onTouchEvent(MotionEvent ev) {
63
        switch (ev.getAction()) {
64
            case MotionEvent.ACTION_DOWN: {
65
                mVelocityTracker = VelocityTracker.obtain();
66
                if (null != mVelocityTracker) {
67
                    mVelocityTracker.addMovement(ev);
68
                } else {
69
                    Log.i(LOG_TAG, "Velocity tracker is null");
70
                }
71
72
                mLastTouchX = getActiveX(ev);
73
                mLastTouchY = getActiveY(ev);
74
                mIsDragging = false;
75
                break;
76
            }
77
78
            case MotionEvent.ACTION_MOVE: {
79
                final float x = getActiveX(ev);
80
                final float y = getActiveY(ev);
81
                final float dx = x - mLastTouchX, dy = y - mLastTouchY;
82
83
                if (!mIsDragging) {
84
                    // Use Pythagoras to see if drag length is larger than
85
                    // touch slop
86
                    mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
87
                }
88
89
                if (mIsDragging) {
90
                    mListener.onDrag(dx, dy);
91
                    mLastTouchX = x;
92
                    mLastTouchY = y;
93
94
                    if (null != mVelocityTracker) {
95
                        mVelocityTracker.addMovement(ev);
96
                    }
97
                }
98
                break;
99
            }
100
101
            case MotionEvent.ACTION_CANCEL: {
102
                // Recycle Velocity Tracker
103
                if (null != mVelocityTracker) {
104
                    mVelocityTracker.recycle();
105
                    mVelocityTracker = null;
106
                }
107
                break;
108
            }
109
110
            case MotionEvent.ACTION_UP: {
111
                if (mIsDragging) {
112
                    if (null != mVelocityTracker) {
113
                        mLastTouchX = getActiveX(ev);
114
                        mLastTouchY = getActiveY(ev);
115
116
                        // Compute velocity within the last 1000ms
117
                        mVelocityTracker.addMovement(ev);
118
                        mVelocityTracker.computeCurrentVelocity(1000);
119
120
                        final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
121
                                .getYVelocity();
122
123
                        // If the velocity is greater than minVelocity, call
124
                        // listener
125
                        if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
126
                            mListener.onFling(mLastTouchX, mLastTouchY, -vX,
127
                                    -vY);
128
                        }
129
                    }
130
                }
131
132
                // Recycle Velocity Tracker
133
                if (null != mVelocityTracker) {
134
                    mVelocityTracker.recycle();
135
                    mVelocityTracker = null;
136
                }
137
                break;
138
            }
139
        }
140
141
        return true;
142
    }
143
}

+ 0 - 98
app/src/main/java/com/electric/chargingpile/view/DefaultOnDoubleTapListener.java

@ -1,98 +0,0 @@
1
package com.electric.chargingpile.view;
2
3
import android.graphics.RectF;
4
import android.view.GestureDetector;
5
import android.view.MotionEvent;
6
import android.widget.ImageView;
7
8
/**
9
 * Provided default implementation of GestureDetector.OnDoubleTapListener, to be overriden with custom behavior, if needed
10
 * <p>&nbsp;</p>
11
// * To be used via {@link uk.co.senab.photoview.PhotoViewAttacher#setOnDoubleTapListener(GestureDetector.OnDoubleTapListener)}
12
 */
13
public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {
14
15
    private PhotoViewAttacher photoViewAttacher;
16
17
    /**
18
     * Default constructor
19
     *
20
     * @param photoViewAttacher PhotoViewAttacher to bind to
21
     */
22
    public DefaultOnDoubleTapListener(PhotoViewAttacher photoViewAttacher) {
23
        setPhotoViewAttacher(photoViewAttacher);
24
    }
25
26
    /**
27
     * Allows to change PhotoViewAttacher within range of single instance
28
     *
29
     * @param newPhotoViewAttacher PhotoViewAttacher to bind to
30
     */
31
    public void setPhotoViewAttacher(PhotoViewAttacher newPhotoViewAttacher) {
32
        this.photoViewAttacher = newPhotoViewAttacher;
33
    }
34
35
    @Override
36
    public boolean onSingleTapConfirmed(MotionEvent e) {
37
        if (this.photoViewAttacher == null)
38
            return false;
39
40
        ImageView imageView = photoViewAttacher.getImageView();
41
42
        if (null != photoViewAttacher.getOnPhotoTapListener()) {
43
            final RectF displayRect = photoViewAttacher.getDisplayRect();
44
45
            if (null != displayRect) {
46
                final float x = e.getX(), y = e.getY();
47
48
                // Check to see if the user tapped on the photo
49
                if (displayRect.contains(x, y)) {
50
51
                    float xResult = (x - displayRect.left)
52
                            / displayRect.width();
53
                    float yResult = (y - displayRect.top)
54
                            / displayRect.height();
55
56
                    photoViewAttacher.getOnPhotoTapListener().onPhotoTap(imageView, xResult, yResult);
57
                    return true;
58
                }
59
            }
60
        }
61
        if (null != photoViewAttacher.getOnViewTapListener()) {
62
            photoViewAttacher.getOnViewTapListener().onViewTap(imageView, e.getX(), e.getY());
63
        }
64
65
        return false;
66
    }
67
68
    @Override
69
    public boolean onDoubleTap(MotionEvent ev) {
70
        if (photoViewAttacher == null)
71
            return false;
72
73
        try {
74
            float scale = photoViewAttacher.getScale();
75
            float x = ev.getX();
76
            float y = ev.getY();
77
78
            if (scale < photoViewAttacher.getMediumScale()) {
79
                photoViewAttacher.setScale(photoViewAttacher.getMediumScale(), x, y, true);
80
            } else if (scale >= photoViewAttacher.getMediumScale() && scale < photoViewAttacher.getMaximumScale()) {
81
                photoViewAttacher.setScale(photoViewAttacher.getMaximumScale(), x, y, true);
82
            } else {
83
                photoViewAttacher.setScale(photoViewAttacher.getMinimumScale(), x, y, true);
84
            }
85
        } catch (ArrayIndexOutOfBoundsException e) {
86
            // Can sometimes happen when getX() and getY() is called
87
        }
88
89
        return true;
90
    }
91
92
    @Override
93
    public boolean onDoubleTapEvent(MotionEvent e) {
94
        // Wait for the confirmed onDoubleTap() instead
95
        return false;
96
    }
97
98
}

+ 0 - 86
app/src/main/java/com/electric/chargingpile/view/EclairGestureDetector.java

@ -1,86 +0,0 @@
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.annotation.TargetApi;
19
import android.content.Context;
20
import android.view.MotionEvent;
21
22
import com.electric.chargingpile.view.Compat;
23
24
@TargetApi(5)
25
public class EclairGestureDetector extends CupcakeGestureDetector {
26
27
    private static final int INVALID_POINTER_ID = -1;
28
    private int mActivePointerId = INVALID_POINTER_ID;
29
    private int mActivePointerIndex = 0;
30
31
    public EclairGestureDetector(Context context) {
32
        super(context);
33
    }
34
35
    @Override
36
    float getActiveX(MotionEvent ev) {
37
        try {
38
            return ev.getX(mActivePointerIndex);
39
        } catch (Exception e) {
40
            return ev.getX();
41
        }
42
    }
43
44
    @Override
45
    float getActiveY(MotionEvent ev) {
46
        try {
47
            return ev.getY(mActivePointerIndex);
48
        } catch (Exception e) {
49
            return ev.getY();
50
        }
51
    }
52
53
    @Override
54
    public boolean onTouchEvent(MotionEvent ev) {
55
        final int action = ev.getAction();
56
        switch (action & MotionEvent.ACTION_MASK) {
57
            case MotionEvent.ACTION_DOWN:
58
                mActivePointerId = ev.getPointerId(0);
59
                break;
60
            case MotionEvent.ACTION_CANCEL:
61
            case MotionEvent.ACTION_UP:
62
                mActivePointerId = INVALID_POINTER_ID;
63
                break;
64
            case MotionEvent.ACTION_POINTER_UP:
65
                // Ignore deprecation, ACTION_POINTER_ID_MASK and
66
                // ACTION_POINTER_ID_SHIFT has same value and are deprecated
67
                // You can have either deprecation or lint target api warning
68
                final int pointerIndex = Compat.getPointerIndex(ev.getAction());
69
                final int pointerId = ev.getPointerId(pointerIndex);
70
                if (pointerId == mActivePointerId) {
71
                    // This was our active pointer going up. Choose a new
72
                    // active pointer and adjust accordingly.
73
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
74
                    mActivePointerId = ev.getPointerId(newPointerIndex);
75
                    mLastTouchX = ev.getX(newPointerIndex);
76
                    mLastTouchY = ev.getY(newPointerIndex);
77
                }
78
                break;
79
        }
80
81
        mActivePointerIndex = ev
82
                .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
83
                        : 0);
84
        return super.onTouchEvent(ev);
85
    }
86
}

+ 0 - 68
app/src/main/java/com/electric/chargingpile/view/FroyoGestureDetector.java

@ -1,68 +0,0 @@
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.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
        mDetector.onTouchEvent(ev);
65
        return super.onTouchEvent(ev);
66
    }
67
68
}

+ 0 - 28
app/src/main/java/com/electric/chargingpile/view/GestureDetector.java

@ -1,28 +0,0 @@
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.view.MotionEvent;
19
20
public interface GestureDetector {
21
22
    public boolean onTouchEvent(MotionEvent ev);
23
24
    public boolean isScaling();
25
26
    public void setOnGestureListener(OnGestureListener listener);
27
28
}

+ 0 - 68
app/src/main/java/com/electric/chargingpile/view/GingerScroller.java

@ -1,68 +0,0 @@
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.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
    private boolean mFirstScroll = false;
27
28
    public GingerScroller(Context context) {
29
        mScroller = new OverScroller(context);
30
    }
31
32
    @Override
33
    public boolean computeScrollOffset() {
34
        // Workaround for first scroll returning 0 for the direction of the edge it hits.
35
        // Simply recompute values.
36
        if (mFirstScroll) {
37
            mScroller.computeScrollOffset();
38
            mFirstScroll = false;
39
        }
40
        return mScroller.computeScrollOffset();
41
    }
42
43
    @Override
44
    public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
45
                      int overX, int overY) {
46
        mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY);
47
    }
48
49
    @Override
50
    public void forceFinished(boolean finished) {
51
        mScroller.forceFinished(finished);
52
    }
53
54
    @Override
55
    public boolean isFinished() {
56
        return mScroller.isFinished();
57
    }
58
59
    @Override
60
    public int getCurrX() {
61
        return mScroller.getCurrX();
62
    }
63
64
    @Override
65
    public int getCurrY() {
66
        return mScroller.getCurrY();
67
    }
68
}

+ 0 - 283
app/src/main/java/com/electric/chargingpile/view/IPhotoView.java

@ -1,283 +0,0 @@
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.graphics.Bitmap;
19
import android.graphics.Matrix;
20
import android.graphics.RectF;
21
import android.view.View;
22
import android.widget.ImageView;
23
24
25
public interface IPhotoView {
26
27
    public static final float DEFAULT_MAX_SCALE = 3.0f;
28
    public static final float DEFAULT_MID_SCALE = 1.75f;
29
    public static final float DEFAULT_MIN_SCALE = 1.0f;
30
    public static final int DEFAULT_ZOOM_DURATION = 200;
31
32
    /**
33
     * Returns true if the PhotoView is set to allow zooming of Photos.
34
     *
35
     * @return true if the PhotoView allows zooming.
36
     */
37
    boolean canZoom();
38
39
    /**
40
     * Gets the Display Rectangle of the currently displayed Drawable. The Rectangle is relative to
41
     * this View and includes all scaling and translations.
42
     *
43
     * @return - RectF of Displayed Drawable
44
     */
45
    RectF getDisplayRect();
46
47
    /**
48
     * Sets the Display Matrix of the currently displayed Drawable. The Rectangle is considered
49
     * relative to this View and includes all scaling and translations.
50
     *
51
     * @return - true if rectangle was applied successfully
52
     */
53
    boolean setDisplayMatrix(Matrix finalMatrix);
54
55
    /**
56
     * Gets the Display Matrix of the currently displayed Drawable. The Rectangle is considered
57
     * relative to this View and includes all scaling and translations.
58
     *
59
     * @return - true if rectangle was applied successfully
60
     */
61
    Matrix getDisplayMatrix();
62
63
    /**
64
     * Use {@link #getMinimumScale()} instead, this will be removed in future release
65
     *
66
     * @return The current minimum scale level. What this value represents depends on the current
67
     * {@link ImageView.ScaleType}.
68
     */
69
    @Deprecated
70
    float getMinScale();
71
72
    /**
73
     * @return The current minimum scale level. What this value represents depends on the current
74
     * {@link ImageView.ScaleType}.
75
     */
76
    float getMinimumScale();
77
78
    /**
79
     * Use {@link #getMediumScale()} instead, this will be removed in future release
80
     *
81
     * @return The current middle scale level. What this value represents depends on the current
82
     * {@link ImageView.ScaleType}.
83
     */
84
    @Deprecated
85
    float getMidScale();
86
87
    /**
88
     * @return The current medium scale level. What this value represents depends on the current
89
     * {@link ImageView.ScaleType}.
90
     */
91
    float getMediumScale();
92
93
    /**
94
     * Use {@link #getMaximumScale()} instead, this will be removed in future release
95
     *
96
     * @return The current maximum scale level. What this value represents depends on the current
97
     * {@link ImageView.ScaleType}.
98
     */
99
    @Deprecated
100
    float getMaxScale();
101
102
    /**
103
     * @return The current maximum scale level. What this value represents depends on the current
104
     * {@link ImageView.ScaleType}.
105
     */
106
    float getMaximumScale();
107
108
    /**
109
     * Returns the current scale value
110
     *
111
     * @return float - current scale value
112
     */
113
    float getScale();
114
115
    /**
116
     * Return the current scale type in use by the ImageView.
117
     */
118
    ImageView.ScaleType getScaleType();
119
120
    /**
121
     * Whether to allow the ImageView's parent to intercept the touch event when the photo is scroll
122
     * to it's horizontal edge.
123
     */
124
    void setAllowParentInterceptOnEdge(boolean allow);
125
126
    /**
127
     * Use {@link #setMinimumScale(float minimumScale)} instead, this will be removed in future
128
     * release
129
     * <p/>
130
     * Sets the minimum scale level. What this value represents depends on the current {@link
131
     * ImageView.ScaleType}.
132
     */
133
    @Deprecated
134
    void setMinScale(float minScale);
135
136
    /**
137
     * Sets the minimum scale level. What this value represents depends on the current {@link
138
     * ImageView.ScaleType}.
139
     */
140
    void setMinimumScale(float minimumScale);
141
142
    /**
143
     * Use {@link #setMediumScale(float mediumScale)} instead, this will be removed in future
144
     * release
145
     * <p/>
146
     * Sets the middle scale level. What this value represents depends on the current {@link
147
     * ImageView.ScaleType}.
148
     */
149
    @Deprecated
150
    void setMidScale(float midScale);
151
152
    /*
153
     * Sets the medium scale level. What this value represents depends on the current {@link android.widget.ImageView.ScaleType}.
154
     */
155
    void setMediumScale(float mediumScale);
156
157
    /**
158
     * Use {@link #setMaximumScale(float maximumScale)} instead, this will be removed in future
159
     * release
160
     * <p/>
161
     * Sets the maximum scale level. What this value represents depends on the current {@link
162
     * ImageView.ScaleType}.
163
     */
164
    @Deprecated
165
    void setMaxScale(float maxScale);
166
167
    /**
168
     * Sets the maximum scale level. What this value represents depends on the current {@link
169
     * ImageView.ScaleType}.
170
     */
171
    void setMaximumScale(float maximumScale);
172
173
    /**
174
     * Register a callback to be invoked when the Photo displayed by this view is long-pressed.
175
     *
176
     * @param listener - Listener to be registered.
177
     */
178
    void setOnLongClickListener(View.OnLongClickListener listener);
179
180
    /**
181
     * Register a callback to be invoked when the Matrix has changed for this View. An example would
182
     * be the user panning or scaling the Photo.
183
     *
184
     * @param listener - Listener to be registered.
185
     */
186
    void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener listener);
187
188
    /**
189
     * Register a callback to be invoked when the Photo displayed by this View is tapped with a
190
     * single tap.
191
     *
192
     * @param listener - Listener to be registered.
193
     */
194
    void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener listener);
195
196
    /**
197
     * Returns a listener to be invoked when the Photo displayed by this View is tapped with a
198
     * single tap.
199
     *
200
     * @return PhotoViewAttacher.OnPhotoTapListener currently set, may be null
201
     */
202
    PhotoViewAttacher.OnPhotoTapListener getOnPhotoTapListener();
203
204
    /**
205
     * Register a callback to be invoked when the View is tapped with a single tap.
206
     *
207
     * @param listener - Listener to be registered.
208
     */
209
    void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener listener);
210
211
    /**
212
     * Returns a callback listener to be invoked when the View is tapped with a single tap.
213
     *
214
     * @return PhotoViewAttacher.OnViewTapListener currently set, may be null
215
     */
216
    PhotoViewAttacher.OnViewTapListener getOnViewTapListener();
217
218
    /**
219
     * Changes the current scale to the specified value.
220
     *
221
     * @param scale - Value to scale to
222
     */
223
    void setScale(float scale);
224
225
    /**
226
     * Changes the current scale to the specified value.
227
     *
228
     * @param scale   - Value to scale to
229
     * @param animate - Whether to animate the scale
230
     */
231
    void setScale(float scale, boolean animate);
232
233
    /**
234
     * Changes the current scale to the specified value, around the given focal point.
235
     *
236
     * @param scale   - Value to scale to
237
     * @param focalX  - X Focus Point
238
     * @param focalY  - Y Focus Point
239
     * @param animate - Whether to animate the scale
240
     */
241
    void setScale(float scale, float focalX, float focalY, boolean animate);
242
243
    /**
244
     * Controls how the image should be resized or moved to match the size of the ImageView. Any
245
     * scaling or panning will happen within the confines of this {@link
246
     * ImageView.ScaleType}.
247
     *
248
     * @param scaleType - The desired scaling mode.
249
     */
250
    void setScaleType(ImageView.ScaleType scaleType);
251
252
    /**
253
     * Allows you to enable/disable the zoom functionality on the ImageView. When disable the
254
     * ImageView reverts to using the FIT_CENTER matrix.
255
     *
256
     * @param zoomable - Whether the zoom functionality is enabled.
257
     */
258
    void setZoomable(boolean zoomable);
259
260
    /**
261
     * Enables rotation via PhotoView internal functions. Name is chosen so it won't collide with
262
     * View.setRotation(float) in API since 11
263
     *
264
     * @param rotationDegree - Degree to rotate PhotoView by, should be in range 0 to 360
265
     */
266
    void setPhotoViewRotation(float rotationDegree);
267
268
    /**
269
     * Extracts currently visible area to Bitmap object, if there is no image loaded yet or the
270
     * ImageView is already destroyed, returns {@code null}
271
     *
272
     * @return currently visible area as bitmap or null
273
     */
274
    Bitmap getVisibleRectangleBitmap();
275
276
    /**
277
     * Allows to change zoom transition speed, default value is 200 (PhotoViewAttacher.DEFAULT_ZOOM_DURATION).
278
     * Will default to 200 if provided negative value
279
     *
280
     * @param milliseconds duration of zoom interpolation
281
     */
282
    void setZoomTransitionDuration(int milliseconds);
283
}

+ 0 - 33
app/src/main/java/com/electric/chargingpile/view/IcsScroller.java

@ -1,33 +0,0 @@
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.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 - 262
app/src/main/java/com/electric/chargingpile/view/PhotoView.java

@ -1,262 +0,0 @@
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.graphics.Bitmap;
20
import android.graphics.Matrix;
21
import android.graphics.RectF;
22
import android.graphics.drawable.Drawable;
23
import android.net.Uri;
24
import android.util.AttributeSet;
25
import android.widget.ImageView;
26
27
import com.electric.chargingpile.view.PhotoViewAttacher.OnMatrixChangedListener;
28
import com.electric.chargingpile.view.PhotoViewAttacher.OnPhotoTapListener;
29
import com.electric.chargingpile.view.PhotoViewAttacher.OnViewTapListener;
30
31
public class PhotoView extends ImageView implements IPhotoView {
32
33
    private final PhotoViewAttacher mAttacher;
34
35
    private ScaleType mPendingScaleType;
36
37
    public PhotoView(Context context) {
38
        this(context, null);
39
    }
40
41
    public PhotoView(Context context, AttributeSet attr) {
42
        this(context, attr, 0);
43
    }
44
45
    public PhotoView(Context context, AttributeSet attr, int defStyle) {
46
        super(context, attr, defStyle);
47
        super.setScaleType(ScaleType.MATRIX);
48
        mAttacher = new PhotoViewAttacher(this);
49
50
        if (null != mPendingScaleType) {
51
            setScaleType(mPendingScaleType);
52
            mPendingScaleType = null;
53
        }
54
    }
55
56
    @Override
57
    public void setPhotoViewRotation(float rotationDegree) {
58
        mAttacher.setPhotoViewRotation(rotationDegree);
59
    }
60
61
    @Override
62
    public boolean canZoom() {
63
        return mAttacher.canZoom();
64
    }
65
66
    @Override
67
    public RectF getDisplayRect() {
68
        return mAttacher.getDisplayRect();
69
    }
70
71
    @Override
72
    public Matrix getDisplayMatrix() {
73
        return mAttacher.getDrawMatrix();
74
    }
75
76
    @Override
77
    public boolean setDisplayMatrix(Matrix finalRectangle) {
78
        return mAttacher.setDisplayMatrix(finalRectangle);
79
    }
80
81
    @Override
82
    @Deprecated
83
    public float getMinScale() {
84
        return getMinimumScale();
85
    }
86
87
    @Override
88
    public float getMinimumScale() {
89
        return mAttacher.getMinimumScale();
90
    }
91
92
    @Override
93
    @Deprecated
94
    public float getMidScale() {
95
        return getMediumScale();
96
    }
97
98
    @Override
99
    public float getMediumScale() {
100
        return mAttacher.getMediumScale();
101
    }
102
103
    @Override
104
    @Deprecated
105
    public float getMaxScale() {
106
        return getMaximumScale();
107
    }
108
109
    @Override
110
    public float getMaximumScale() {
111
        return mAttacher.getMaximumScale();
112
    }
113
114
    @Override
115
    public float getScale() {
116
        return mAttacher.getScale();
117
    }
118
119
    @Override
120
    public ScaleType getScaleType() {
121
        return mAttacher.getScaleType();
122
    }
123
124
    @Override
125
    public void setAllowParentInterceptOnEdge(boolean allow) {
126
        mAttacher.setAllowParentInterceptOnEdge(allow);
127
    }
128
129
    @Override
130
    @Deprecated
131
    public void setMinScale(float minScale) {
132
        setMinimumScale(minScale);
133
    }
134
135
    @Override
136
    public void setMinimumScale(float minimumScale) {
137
        mAttacher.setMinimumScale(minimumScale);
138
    }
139
140
    @Override
141
    @Deprecated
142
    public void setMidScale(float midScale) {
143
        setMediumScale(midScale);
144
    }
145
146
    @Override
147
    public void setMediumScale(float mediumScale) {
148
        mAttacher.setMediumScale(mediumScale);
149
    }
150
151
    @Override
152
    @Deprecated
153
    public void setMaxScale(float maxScale) {
154
        setMaximumScale(maxScale);
155
    }
156
157
    @Override
158
    public void setMaximumScale(float maximumScale) {
159
        mAttacher.setMaximumScale(maximumScale);
160
    }
161
162
    @Override
163
    // setImageBitmap calls through to this method
164
    public void setImageDrawable(Drawable drawable) {
165
        super.setImageDrawable(drawable);
166
        if (null != mAttacher) {
167
            mAttacher.update();
168
        }
169
    }
170
171
    @Override
172
    public void setImageResource(int resId) {
173
        super.setImageResource(resId);
174
        if (null != mAttacher) {
175
            mAttacher.update();
176
        }
177
    }
178
179
    @Override
180
    public void setImageURI(Uri uri) {
181
        super.setImageURI(uri);
182
        if (null != mAttacher) {
183
            mAttacher.update();
184
        }
185
    }
186
187
    @Override
188
    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
189
        mAttacher.setOnMatrixChangeListener(listener);
190
    }
191
192
    @Override
193
    public void setOnLongClickListener(OnLongClickListener l) {
194
        mAttacher.setOnLongClickListener(l);
195
    }
196
197
    @Override
198
    public void setOnPhotoTapListener(OnPhotoTapListener listener) {
199
        mAttacher.setOnPhotoTapListener(listener);
200
    }
201
202
    @Override
203
    public OnPhotoTapListener getOnPhotoTapListener() {
204
        return mAttacher.getOnPhotoTapListener();
205
    }
206
207
    @Override
208
    public void setOnViewTapListener(OnViewTapListener listener) {
209
        mAttacher.setOnViewTapListener(listener);
210
    }
211
212
    @Override
213
    public OnViewTapListener getOnViewTapListener() {
214
        return mAttacher.getOnViewTapListener();
215
    }
216
217
    @Override
218
    public void setScale(float scale) {
219
        mAttacher.setScale(scale);
220
    }
221
222
    @Override
223
    public void setScale(float scale, boolean animate) {
224
        mAttacher.setScale(scale, animate);
225
    }
226
227
    @Override
228
    public void setScale(float scale, float focalX, float focalY, boolean animate) {
229
        mAttacher.setScale(scale, focalX, focalY, animate);
230
    }
231
232
    @Override
233
    public void setScaleType(ScaleType scaleType) {
234
        if (null != mAttacher) {
235
            mAttacher.setScaleType(scaleType);
236
        } else {
237
            mPendingScaleType = scaleType;
238
        }
239
    }
240
241
    @Override
242
    public void setZoomable(boolean zoomable) {
243
        mAttacher.setZoomable(zoomable);
244
    }
245
246
    @Override
247
    public Bitmap getVisibleRectangleBitmap() {
248
        return mAttacher.getVisibleRectangleBitmap();
249
    }
250
251
    @Override
252
    public void setZoomTransitionDuration(int milliseconds) {
253
        mAttacher.setZoomTransitionDuration(milliseconds);
254
    }
255
256
    @Override
257
    protected void onDetachedFromWindow() {
258
        mAttacher.cleanup();
259
        super.onDetachedFromWindow();
260
    }
261
262
}

+ 0 - 1078
app/src/main/java/com/electric/chargingpile/view/PhotoViewAttacher.java

@ -1,1078 +0,0 @@
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.graphics.Bitmap;
20
import android.graphics.Matrix;
21
import android.graphics.Matrix.ScaleToFit;
22
import android.graphics.RectF;
23
import android.graphics.drawable.Drawable;
24
import android.util.Log;
25
import android.view.GestureDetector;
26
import android.view.MotionEvent;
27
import android.view.View;
28
import android.view.View.OnLongClickListener;
29
import android.view.ViewParent;
30
import android.view.ViewTreeObserver;
31
import android.view.animation.AccelerateDecelerateInterpolator;
32
import android.view.animation.Interpolator;
33
import android.widget.ImageView;
34
import android.widget.ImageView.ScaleType;
35
36
import java.lang.ref.WeakReference;
37
38
import static android.view.MotionEvent.ACTION_CANCEL;
39
import static android.view.MotionEvent.ACTION_DOWN;
40
import static android.view.MotionEvent.ACTION_UP;
41
42
public class PhotoViewAttacher implements IPhotoView, View.OnTouchListener,
43
        OnGestureListener,
44
        ViewTreeObserver.OnGlobalLayoutListener {
45
46
    private static final String LOG_TAG = "PhotoViewAttacher";
47
48
    // let debug flag be dynamic, but still Proguard can be used to remove from
49
    // release builds
50
    private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
51
52
    static final Interpolator sInterpolator = new AccelerateDecelerateInterpolator();
53
    int ZOOM_DURATION = DEFAULT_ZOOM_DURATION;
54
55
    static final int EDGE_NONE = -1;
56
    static final int EDGE_LEFT = 0;
57
    static final int EDGE_RIGHT = 1;
58
    static final int EDGE_BOTH = 2;
59
60
    private float mMinScale = DEFAULT_MIN_SCALE;
61
    private float mMidScale = DEFAULT_MID_SCALE;
62
    private float mMaxScale = DEFAULT_MAX_SCALE;
63
64
    private boolean mAllowParentInterceptOnEdge = true;
65
66
    private static void checkZoomLevels(float minZoom, float midZoom,
67
                                        float maxZoom) {
68
        if (minZoom >= midZoom) {
69
            throw new IllegalArgumentException(
70
                    "MinZoom has to be less than MidZoom");
71
        } else if (midZoom >= maxZoom) {
72
            throw new IllegalArgumentException(
73
                    "MidZoom has to be less than MaxZoom");
74
        }
75
    }
76
77
    /**
78
     * @return true if the ImageView exists, and it's Drawable existss
79
     */
80
    private static boolean hasDrawable(ImageView imageView) {
81
        return null != imageView && null != imageView.getDrawable();
82
    }
83
84
    /**
85
     * @return true if the ScaleType is supported.
86
     */
87
    private static boolean isSupportedScaleType(final ScaleType scaleType) {
88
        if (null == scaleType) {
89
            return false;
90
        }
91
92
        switch (scaleType) {
93
            case MATRIX:
94
                throw new IllegalArgumentException(scaleType.name()
95
                        + " is not supported in PhotoView");
96
97
            default:
98
                return true;
99
        }
100
    }
101
102
    /**
103
     * Set's the ImageView's ScaleType to Matrix.
104
     */
105
    private static void setImageViewScaleTypeMatrix(ImageView imageView) {
106
        /**
107
         * PhotoView sets it's own ScaleType to Matrix, then diverts all calls
108
         * setScaleType to this.setScaleType automatically.
109
         */
110
        if (null != imageView && !(imageView instanceof IPhotoView)) {
111
            if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
112
                imageView.setScaleType(ScaleType.MATRIX);
113
            }
114
        }
115
    }
116
117
    private WeakReference<ImageView> mImageView;
118
119
    // Gesture Detectors
120
    private GestureDetector mGestureDetector;
121
    private com.electric.chargingpile.view.GestureDetector mScaleDragDetector;
122
123
    // These are set so we don't keep allocating them on the heap
124
    private final Matrix mBaseMatrix = new Matrix();
125
    private final Matrix mDrawMatrix = new Matrix();
126
    private final Matrix mSuppMatrix = new Matrix();
127
    private final RectF mDisplayRect = new RectF();
128
    private final float[] mMatrixValues = new float[9];
129
130
    // Listeners
131
    private OnMatrixChangedListener mMatrixChangeListener;
132
    private OnPhotoTapListener mPhotoTapListener;
133
    private OnViewTapListener mViewTapListener;
134
    private OnLongClickListener mLongClickListener;
135
136
    private int mIvTop, mIvRight, mIvBottom, mIvLeft;
137
    private FlingRunnable mCurrentFlingRunnable;
138
    private int mScrollEdge = EDGE_BOTH;
139
140
    private boolean mRotationDetectionEnabled = false;
141
    private boolean mZoomEnabled;
142
    private ScaleType mScaleType = ScaleType.FIT_CENTER;
143
144
    public PhotoViewAttacher(ImageView imageView) {
145
        mImageView = new WeakReference<ImageView>(imageView);
146
147
        imageView.setDrawingCacheEnabled(true);
148
        imageView.setOnTouchListener(this);
149
150
        ViewTreeObserver observer = imageView.getViewTreeObserver();
151
        if (null != observer)
152
            observer.addOnGlobalLayoutListener(this);
153
154
        // Make sure we using MATRIX Scale Type
155
        setImageViewScaleTypeMatrix(imageView);
156
157
        if (imageView.isInEditMode()) {
158
            return;
159
        }
160
        // Create Gesture Detectors...
161
        mScaleDragDetector = VersionedGestureDetector.newInstance(
162
                imageView.getContext(), this);
163
164
        mGestureDetector = new GestureDetector(imageView.getContext(),
165
                new GestureDetector.SimpleOnGestureListener() {
166
167
                    // forward long click listener
168
                    @Override
169
                    public void onLongPress(MotionEvent e) {
170
                        if (null != mLongClickListener) {
171
                            mLongClickListener.onLongClick(getImageView());
172
                        }
173
                    }
174
                });
175
176
        mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
177
178
        // Finally, update the UI so that we're zoomable
179
        setZoomable(true);
180
    }
181
182
    /**
183
     * Sets custom double tap listener, to intercept default given functions. To reset behavior to
184
     * default, you can just pass in "null" or public field of PhotoViewAttacher.defaultOnDoubleTapListener
185
     *
186
     * @param newOnDoubleTapListener custom OnDoubleTapListener to be set on ImageView
187
     */
188
    public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
189
        if (newOnDoubleTapListener != null)
190
            this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);
191
        else
192
            this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
193
    }
194
195
    @Override
196
    public boolean canZoom() {
197
        return mZoomEnabled;
198
    }
199
200
    /**
201
     * Clean-up the resources attached to this object. This needs to be called when the ImageView is
202
     * no longer used. A good example is from {@link View#onDetachedFromWindow()} or
203
     * from {@link android.app.Activity#onDestroy()}. This is automatically called if you are using
204
     */
205
    @SuppressWarnings("deprecation")
206
    public void cleanup() {
207
        if (null == mImageView) {
208
            return; // cleanup already done
209
        }
210
211
        final ImageView imageView = mImageView.get();
212
213
        if (null != imageView) {
214
            // Remove this as a global layout listener
215
            ViewTreeObserver observer = imageView.getViewTreeObserver();
216
            if (null != observer && observer.isAlive()) {
217
                observer.removeGlobalOnLayoutListener(this);
218
            }
219
220
            // Remove the ImageView's reference to this
221
            imageView.setOnTouchListener(null);
222
223
            // make sure a pending fling runnable won't be run
224
            cancelFling();
225
        }
226
227
        if (null != mGestureDetector) {
228
            mGestureDetector.setOnDoubleTapListener(null);
229
        }
230
231
        // Clear listeners too
232
        mMatrixChangeListener = null;
233
        mPhotoTapListener = null;
234
        mViewTapListener = null;
235
236
        // Finally, clear ImageView
237
        mImageView = null;
238
    }
239
240
    @Override
241
    public RectF getDisplayRect() {
242
        checkMatrixBounds();
243
        return getDisplayRect(getDrawMatrix());
244
    }
245
246
    @Override
247
    public boolean setDisplayMatrix(Matrix finalMatrix) {
248
        if (finalMatrix == null)
249
            throw new IllegalArgumentException("Matrix cannot be null");
250
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,58 +0,0 @@
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,196 +0,0 @@
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,48 +0,0 @@
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,7 +48,6 @@ import com.umeng.analytics.MobclickAgent;
48 48
 */
49 49
public class SlideClaimView extends FrameLayout{
50 50
    // ???universal-image-loader????????????????????????universal-image-loader-1.8.6-with-sources.jar
51
    private ImageLoader imageLoader = ImageLoader.getInstance();
52 51
53 52
    //??????????
54 53
    private final static int IMAGE_COUNT = 5;

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

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

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

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

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