OSDN Git Service

Fix b/5401109.
authorYuli Huang <yuli@google.com>
Thu, 20 Oct 2011 10:29:05 +0000 (18:29 +0800)
committerYuli Huang <yuli@google.com>
Thu, 20 Oct 2011 14:13:07 +0000 (22:13 +0800)
1. Add FlipView similar to existing RotateView.
2. Add flipPhoto() similar to existing rotatePhoto() in PhotoView, and
add setRenderToFlip() in RendererUtils.
3. Make FlipAction use FlipView/PhotoView similar to how RotateAction
uses RotateView/PhotoView.

Change-Id: I5642266adbc248c0b8eda48ddc29558ae9cbd21e

res/layout/photoeditor_flip_view.xml [new file with mode: 0644]
src/com/android/gallery3d/photoeditor/PhotoView.java
src/com/android/gallery3d/photoeditor/RendererUtils.java
src/com/android/gallery3d/photoeditor/actions/EffectToolFactory.java
src/com/android/gallery3d/photoeditor/actions/FlipAction.java
src/com/android/gallery3d/photoeditor/actions/FlipView.java [new file with mode: 0644]
src/com/android/gallery3d/photoeditor/actions/RotateAction.java
src/com/android/gallery3d/photoeditor/actions/RotateView.java
src/com/android/gallery3d/photoeditor/actions/StraightenAction.java

diff --git a/res/layout/photoeditor_flip_view.xml b/res/layout/photoeditor_flip_view.xml
new file mode 100644 (file)
index 0000000..0b7a7a7
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.gallery3d.photoeditor.actions.FlipView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/fullscreen_effect_tool"
+    style="@style/FullscreenToolView"/>
index edb3624..0d4caa8 100644 (file)
@@ -88,6 +88,13 @@ public class PhotoView extends GLSurfaceView {
     }
 
     /**
+     * Flips displayed photo; this method must be queued for GL thread.
+     */
+    public void flipPhoto(float horizontalDegrees, float verticalDegrees) {
+        renderer.flipPhoto(horizontalDegrees, verticalDegrees);
+    }
+
+    /**
      * Renderer that renders the GL surface-view and only be called from the GL thread.
      */
     private class PhotoRenderer implements GLSurfaceView.Renderer {
@@ -99,6 +106,8 @@ public class PhotoView extends GLSurfaceView {
         int viewWidth;
         int viewHeight;
         float rotatedDegrees;
+        float flippedHorizontalDegrees;
+        float flippedVerticalDegrees;
 
         void setPhoto(Photo photo, boolean clearTransform) {
             int width = (photo != null) ? photo.width() : 0;
@@ -115,18 +124,23 @@ public class PhotoView extends GLSurfaceView {
         }
 
         void updateSurface(boolean clearTransform, boolean sizeChanged) {
-            boolean transformed = (rotatedDegrees != 0);
+            boolean flipped = (flippedHorizontalDegrees != 0) || (flippedVerticalDegrees != 0);
+            boolean transformed = (rotatedDegrees != 0) || flipped;
             if ((clearTransform && transformed) || (sizeChanged && !transformed)) {
                 // Fit photo when clearing existing transforms or changing surface/photo sizes.
                 if (photo != null) {
                     RendererUtils.setRenderToFit(renderContext, photo.width(), photo.height(),
                             viewWidth, viewHeight);
                     rotatedDegrees = 0;
+                    flippedHorizontalDegrees = 0;
+                    flippedVerticalDegrees = 0;
                 }
             } else {
                 // Restore existing transformations for orientation changes or awaking from sleep.
                 if (rotatedDegrees != 0) {
                     rotatePhoto(rotatedDegrees);
+                } else if (flipped) {
+                    flipPhoto(flippedHorizontalDegrees, flippedVerticalDegrees);
                 }
             }
         }
@@ -139,6 +153,15 @@ public class PhotoView extends GLSurfaceView {
             }
         }
 
+        void flipPhoto(float horizontalDegrees, float verticalDegrees) {
+            if (photo != null) {
+                RendererUtils.setRenderToFlip(renderContext, photo.width(), photo.height(),
+                        viewWidth, viewHeight, horizontalDegrees, verticalDegrees);
+                flippedHorizontalDegrees = horizontalDegrees;
+                flippedVerticalDegrees = verticalDegrees;
+            }
+        }
+
         @Override
         public void onDrawFrame(GL10 gl) {
             Runnable r = null;
index ff593d4..b92907d 100644 (file)
@@ -121,13 +121,13 @@ public class RendererUtils {
         checkGlError("glDeleteTextures");
     }
 
-    public static void setRenderToFit(RenderContext context, int srcWidth, int srcHeight,
-            int dstWidth, int dstHeight) {
+    private static float[] getFitVertices(int srcWidth, int srcHeight, int dstWidth,
+            int dstHeight) {
         float srcAspectRatio = ((float) srcWidth) / srcHeight;
         float dstAspectRatio = ((float) dstWidth) / dstHeight;
         float relativeAspectRatio = dstAspectRatio / srcAspectRatio;
 
-        float vertices[] = new float[8];
+        float[] vertices = new float[8];
         System.arraycopy(POS_VERTICES, 0, vertices, 0, vertices.length);
         if (relativeAspectRatio > 1.0f) {
             // Screen is wider than the camera, scale down X
@@ -141,13 +141,20 @@ public class RendererUtils {
             vertices[5] *= relativeAspectRatio;
             vertices[7] *= relativeAspectRatio;
         }
-        context.posVertices = createVerticesBuffer(vertices);
+        return vertices;
+    }
+
+    public static void setRenderToFit(RenderContext context, int srcWidth, int srcHeight,
+            int dstWidth, int dstHeight) {
+        context.posVertices = createVerticesBuffer(
+                getFitVertices(srcWidth, srcHeight, dstWidth, dstHeight));
     }
 
     public static void setRenderToRotate(RenderContext context, int srcWidth, int srcHeight,
             int dstWidth, int dstHeight, float degrees) {
-        float cosTheta = (float) Math.cos(-degrees * DEGREE_TO_RADIAN);
-        float sinTheta = (float) Math.sin(-degrees * DEGREE_TO_RADIAN);
+        float radian = -degrees * DEGREE_TO_RADIAN;
+        float cosTheta = (float) Math.cos(radian);
+        float sinTheta = (float) Math.sin(radian);
         float cosWidth = cosTheta * srcWidth;
         float sinWidth = sinTheta * srcWidth;
         float cosHeight = cosTheta * srcHeight;
@@ -174,6 +181,66 @@ public class RendererUtils {
         context.posVertices = createVerticesBuffer(vertices);
     }
 
+    public static void setRenderToFlip(RenderContext context, int srcWidth, int srcHeight,
+            int dstWidth, int dstHeight, float horizontalDegrees, float verticalDegrees) {
+        // Calculate the base flip coordinates.
+        float[] base = getFitVertices(srcWidth, srcHeight, dstWidth, dstHeight);
+        int horizontalRounds = (int) horizontalDegrees / 180;
+        if (horizontalRounds % 2 != 0) {
+            base[0] = -base[0];
+            base[4] = base[0];
+            base[2] = -base[2];
+            base[6] = base[2];
+        }
+        int verticalRounds = (int) verticalDegrees / 180;
+        if (verticalRounds % 2 != 0) {
+            base[1] = -base[1];
+            base[3] = base[1];
+            base[5] = -base[5];
+            base[7] = base[5];
+        }
+
+        float length = 5;
+        float[] vertices = new float[8];
+        System.arraycopy(base, 0, vertices, 0, vertices.length);
+        if (horizontalDegrees % 180f != 0) {
+            float radian = (horizontalDegrees - horizontalRounds * 180) * DEGREE_TO_RADIAN;
+            float cosTheta = (float) Math.cos(radian);
+            float sinTheta = (float) Math.sin(radian);
+
+            float scale = length / (length + sinTheta * base[0]);
+            vertices[0] = cosTheta * base[0] * scale;
+            vertices[1] = base[1] * scale;
+            vertices[4] = vertices[0];
+            vertices[5] = base[5] * scale;
+
+            scale = length / (length + sinTheta * base[2]);
+            vertices[2] = cosTheta * base[2] * scale;
+            vertices[3] = base[3] * scale;
+            vertices[6] = vertices[2];
+            vertices[7] = base[7] * scale;
+        }
+
+        if (verticalDegrees % 180f != 0) {
+            float radian = (verticalDegrees - verticalRounds * 180) * DEGREE_TO_RADIAN;
+            float cosTheta = (float) Math.cos(radian);
+            float sinTheta = (float) Math.sin(radian);
+
+            float scale = length / (length + sinTheta * base[1]);
+            vertices[0] = base[0] * scale;
+            vertices[1] = cosTheta * base[1] * scale;
+            vertices[2] = base[2] * scale;
+            vertices[3] = vertices[1];
+
+            scale = length / (length + sinTheta * base[5]);
+            vertices[4] = base[4] * scale;
+            vertices[5] = cosTheta * base[5] * scale;
+            vertices[6] = base[6] * scale;
+            vertices[7] = vertices[5];
+        }
+        context.posVertices = createVerticesBuffer(vertices);
+    }
+
     public static void renderBackground() {
         GLES20.glClearColor(0, 0, 0, 1);
         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
index 2c69735..3641828 100644 (file)
@@ -90,6 +90,10 @@ public class EffectToolFactory {
         return (TouchView) createFullscreenTool(R.layout.photoeditor_touch_view);
     }
 
+    public FlipView createFlipView() {
+        return (FlipView) createFullscreenTool(R.layout.photoeditor_flip_view);
+    }
+
     public RotateView createRotateView() {
         return (RotateView) createFullscreenTool(R.layout.photoeditor_rotate_view);
     }
index 00abc60..da238ba 100644 (file)
@@ -19,6 +19,8 @@ package com.android.gallery3d.photoeditor.actions;
 import android.content.Context;
 import android.util.AttributeSet;
 
+import com.android.gallery3d.R;
+import com.android.gallery3d.photoeditor.PhotoView;
 import com.android.gallery3d.photoeditor.filters.FlipFilter;
 
 /**
@@ -26,9 +28,14 @@ import com.android.gallery3d.photoeditor.filters.FlipFilter;
  */
 public class FlipAction extends EffectAction {
 
-    private boolean flipHorizontal;
-    private boolean flipVertical;
-    private TouchView touchView;
+    private static final float DEFAULT_ANGLE = 0.0f;
+    private static final float DEFAULT_FLIP_SPAN = 180.0f;
+
+    private FlipFilter filter;
+    private float horizontalFlipDegrees;
+    private float verticalFlipDegrees;
+    private Runnable queuedFlipChange;
+    private FlipView flipView;
 
     public FlipAction(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -36,51 +43,86 @@ public class FlipAction extends EffectAction {
 
     @Override
     public void doBegin() {
-        final FlipFilter filter = new FlipFilter();
+        filter = new FlipFilter();
+
+        flipView = factory.createFlipView();
+        flipView.setOnFlipChangeListener(new FlipView.OnFlipChangeListener() {
 
-        touchView = factory.createTouchView();
-        touchView.setSwipeListener(new TouchView.SwipeListener() {
+            // Directly transform photo-view because running the flip filter isn't fast enough.
+            PhotoView photoView = (PhotoView) flipView.getRootView().findViewById(
+                    R.id.photo_view);
 
             @Override
-            public void onSwipeDown() {
-                flipFilterVertically(filter);
+            public void onAngleChanged(float horizontalDegrees, float verticalDegrees,
+                    boolean fromUser) {
+                if (fromUser) {
+                    horizontalFlipDegrees = horizontalDegrees;
+                    verticalFlipDegrees = verticalDegrees;
+                    updateFlipFilter(false);
+                    transformPhotoView(horizontalDegrees, verticalDegrees);
+                }
             }
 
             @Override
-            public void onSwipeLeft() {
-                flipFilterHorizontally(filter);
+            public void onStartTrackingTouch() {
+                // no-op
             }
 
             @Override
-            public void onSwipeRight() {
-                flipFilterHorizontally(filter);
+            public void onStopTrackingTouch() {
+                roundFlipDegrees();
+                updateFlipFilter(false);
+                transformPhotoView(horizontalFlipDegrees, verticalFlipDegrees);
+                flipView.setFlippedAngles(horizontalFlipDegrees, verticalFlipDegrees);
             }
 
-            @Override
-            public void onSwipeUp() {
-                flipFilterVertically(filter);
+            private void transformPhotoView(final float horizontalDegrees,
+                    final float verticalDegrees) {
+                // Remove the outdated flip change before queuing a new one.
+                if (queuedFlipChange != null) {
+                    photoView.remove(queuedFlipChange);
+                }
+                queuedFlipChange = new Runnable() {
+
+                    @Override
+                    public void run() {
+                        photoView.flipPhoto(horizontalDegrees, verticalDegrees);
+                    }
+                };
+                photoView.queue(queuedFlipChange);
             }
         });
-
-        flipHorizontal = false;
-        flipVertical = false;
-        flipFilterHorizontally(filter);
+        flipView.setFlippedAngles(DEFAULT_ANGLE, DEFAULT_ANGLE);
+        flipView.setFlipSpan(DEFAULT_FLIP_SPAN);
+        horizontalFlipDegrees = 0;
+        verticalFlipDegrees = 0;
+        queuedFlipChange = null;
     }
 
     @Override
     public void doEnd() {
-        touchView.setSwipeListener(null);
+        flipView.setOnFlipChangeListener(null);
+        // Round the current flip degrees in case flip tracking has not stopped yet.
+        roundFlipDegrees();
+        updateFlipFilter(true);
     }
 
-    private void flipFilterHorizontally(final FlipFilter filter) {
-        flipHorizontal = !flipHorizontal;
-        filter.setFlip(flipHorizontal, flipVertical);
-        notifyFilterChanged(filter, true);
+    /**
+     * Rounds flip degrees to multiples of 180 degrees.
+     */
+    private void roundFlipDegrees() {
+        if (horizontalFlipDegrees % 180 != 0) {
+            horizontalFlipDegrees = Math.round(horizontalFlipDegrees / 180) * 180;
+        }
+        if (verticalFlipDegrees % 180 != 0) {
+            verticalFlipDegrees = Math.round(verticalFlipDegrees / 180) * 180;
+        }
     }
 
-    private void flipFilterVertically(final FlipFilter filter) {
-        flipVertical = !flipVertical;
-        filter.setFlip(flipHorizontal, flipVertical);
-        notifyFilterChanged(filter, true);
+    private void updateFlipFilter(boolean outputFilter) {
+        // Flip the filter if the flipped degrees are at the opposite directions.
+        filter.setFlip(((int) horizontalFlipDegrees / 180) % 2 != 0,
+                ((int) verticalFlipDegrees / 180) % 2 != 0);
+        notifyFilterChanged(filter, outputFilter);
     }
 }
diff --git a/src/com/android/gallery3d/photoeditor/actions/FlipView.java b/src/com/android/gallery3d/photoeditor/actions/FlipView.java
new file mode 100644 (file)
index 0000000..17c4343
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.photoeditor.actions;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+/**
+ * View that handles touch-events to track flipping directions and angles.
+ */
+class FlipView extends FullscreenToolView {
+
+    /**
+     * Listens to flip changes.
+     */
+    public interface OnFlipChangeListener {
+
+        void onAngleChanged(float horizontalDegrees, float verticalDegrees, boolean fromUser);
+
+        void onStartTrackingTouch();
+
+        void onStopTrackingTouch();
+    }
+
+    private static final float FIXED_DIRECTION_THRESHOLD = 20;
+
+    private OnFlipChangeListener listener;
+    private float maxFlipSpan;
+    private float touchStartX;
+    private float touchStartY;
+    private float currentHorizontalDegrees;
+    private float currentVerticalDegrees;
+    private float lastHorizontalDegrees;
+    private float lastVerticalDegrees;
+    private boolean fixedDirection;
+    private boolean fixedDirectionHorizontal;
+
+    public FlipView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setOnFlipChangeListener(OnFlipChangeListener listener) {
+        this.listener = listener;
+    }
+
+    public void setFlippedAngles(float horizontalDegrees, float verticalDegrees) {
+        refreshAngle(horizontalDegrees, verticalDegrees, false);
+    }
+
+    /**
+     * Sets allowed degrees for every flip before flipping the view.
+     */
+    public void setFlipSpan(float degrees) {
+        // Flip-span limits allowed flipping degrees of every flip for usability purpose; the max.
+        // flipped angles could be accumulated and larger than allowed flip-span.
+        maxFlipSpan = degrees;
+    }
+
+    private float calculateAngle(boolean flipHorizontal, float x, float y) {
+        // Use partial length along the moving direction to calculate the flip angle.
+        float maxDistance = (flipHorizontal ? getWidth() : getHeight()) * 0.35f;
+        float moveDistance = flipHorizontal ? (x - touchStartX) : (touchStartY - y);
+
+        if (Math.abs(moveDistance) > maxDistance) {
+            moveDistance = (moveDistance > 0) ? maxDistance : -maxDistance;
+
+            if (flipHorizontal) {
+                touchStartX = x - moveDistance;
+            } else {
+                touchStartY = moveDistance + y;
+            }
+        }
+        return (moveDistance / maxDistance) * maxFlipSpan;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        super.onTouchEvent(ev);
+
+        if (isEnabled()) {
+            switch (ev.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    fixedDirection = false;
+                    lastHorizontalDegrees = currentHorizontalDegrees;
+                    lastVerticalDegrees = currentVerticalDegrees;
+                    touchStartX = ev.getX();
+                    touchStartY = ev.getY();
+
+                    if (listener != null) {
+                        listener.onStartTrackingTouch();
+                    }
+                    break;
+
+                case MotionEvent.ACTION_MOVE:
+                    // Allow only one direction for flipping during movements, and make the
+                    // direction fixed once it exceeds threshold.
+                    float x = ev.getX();
+                    float y = ev.getY();
+                    boolean flipHorizontal = fixedDirection ? fixedDirectionHorizontal
+                            : (Math.abs(x - touchStartX) >= Math.abs(y - touchStartY));
+                    float degrees = calculateAngle(flipHorizontal, x, y);
+                    if (!fixedDirection && (Math.abs(degrees) > FIXED_DIRECTION_THRESHOLD)) {
+                        fixedDirection = true;
+                        fixedDirectionHorizontal = flipHorizontal;
+                    }
+
+                    if (flipHorizontal) {
+                        refreshAngle(lastHorizontalDegrees + degrees, lastVerticalDegrees, true);
+                    } else {
+                        refreshAngle(lastHorizontalDegrees, lastVerticalDegrees + degrees, true);
+                    }
+                   break;
+
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP:
+                    if (listener != null) {
+                        listener.onStopTrackingTouch();
+                    }
+                    break;
+            }
+        }
+        return true;
+    }
+
+    private void refreshAngle(float horizontalDegrees, float verticalDegrees, boolean fromUser) {
+        currentHorizontalDegrees = horizontalDegrees;
+        currentVerticalDegrees = verticalDegrees;
+        if (listener != null) {
+            listener.onAngleChanged(horizontalDegrees, verticalDegrees, fromUser);
+        }
+    }
+}
index cc19038..36a09d9 100644 (file)
@@ -45,9 +45,9 @@ public class RotateAction extends EffectAction {
         filter = new RotateFilter();
 
         rotateView = factory.createRotateView();
-        rotateView.setOnAngleChangeListener(new RotateView.OnRotateChangeListener() {
+        rotateView.setOnRotateChangeListener(new RotateView.OnRotateChangeListener() {
 
-            // Directly transform photo-view because running the rotation filter isn't fast enough.
+            // Directly transform photo-view because running the rotate filter isn't fast enough.
             PhotoView photoView = (PhotoView) rotateView.getRootView().findViewById(
                     R.id.photo_view);
 
@@ -55,8 +55,7 @@ public class RotateAction extends EffectAction {
             public void onAngleChanged(float degrees, boolean fromUser){
                 if (fromUser) {
                     rotateDegrees = degrees;
-                    filter.setAngle(degrees);
-                    notifyFilterChanged(filter, false);
+                    updateRotateFilter(false);
                     transformPhotoView(degrees);
                 }
             }
@@ -68,11 +67,10 @@ public class RotateAction extends EffectAction {
 
             @Override
             public void onStopTrackingTouch() {
-                if (roundFilterRotationDegrees()) {
-                    notifyFilterChanged(filter, false);
-                    transformPhotoView(rotateDegrees);
-                    rotateView.setRotatedAngle(rotateDegrees);
-                }
+                roundRotateDegrees();
+                updateRotateFilter(false);
+                transformPhotoView(rotateDegrees);
+                rotateView.setRotatedAngle(rotateDegrees);
             }
 
             private void transformPhotoView(final float degrees) {
@@ -98,23 +96,23 @@ public class RotateAction extends EffectAction {
 
     @Override
     public void doEnd() {
-        rotateView.setOnAngleChangeListener(null);
+        rotateView.setOnRotateChangeListener(null);
         // Round the current rotation degrees in case rotation tracking has not stopped yet.
-        roundFilterRotationDegrees();
-        notifyFilterChanged(filter, true);
+        roundRotateDegrees();
+        updateRotateFilter(true);
     }
 
     /**
-     * Rounds filter rotation degrees to multiples of 90 degrees.
-     *
-     * @return true if the rotation degrees has been changed.
+     * Rounds rotate degrees to multiples of 90 degrees.
      */
-    private boolean roundFilterRotationDegrees() {
+    private void roundRotateDegrees() {
         if (rotateDegrees % 90 != 0) {
             rotateDegrees = Math.round(rotateDegrees / 90) * 90;
-            filter.setAngle(rotateDegrees);
-            return true;
         }
-        return false;
+    }
+
+    private void updateRotateFilter(boolean outputFilter) {
+        filter.setAngle(rotateDegrees);
+        notifyFilterChanged(filter, outputFilter);
     }
 }
index 3598e32..b4f63f0 100644 (file)
@@ -92,7 +92,7 @@ class RotateView extends FullscreenToolView {
         minRotatedAngle = -maxRotatedAngle;
     }
 
-    public void setOnAngleChangeListener(OnRotateChangeListener listener) {
+    public void setOnRotateChangeListener(OnRotateChangeListener listener) {
         this.listener = listener;
     }
 
index 2a8c549..42b384d 100644 (file)
@@ -40,7 +40,7 @@ public class StraightenAction extends EffectAction {
         final StraightenFilter filter = new StraightenFilter();
 
         rotateView = factory.createRotateView();
-        rotateView.setOnAngleChangeListener(new RotateView.OnRotateChangeListener() {
+        rotateView.setOnRotateChangeListener(new RotateView.OnRotateChangeListener() {
 
             @Override
             public void onAngleChanged(float degrees, boolean fromUser){
@@ -67,6 +67,6 @@ public class StraightenAction extends EffectAction {
 
     @Override
     public void doEnd() {
-        rotateView.setOnAngleChangeListener(null);
+        rotateView.setOnRotateChangeListener(null);
     }
 }