RotateImageView.java revision c5e37cbb96b8284203560c51dd5f41c705a21f58
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera.ui;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.Rect;
23import android.graphics.drawable.BitmapDrawable;
24import android.graphics.drawable.Drawable;
25import android.graphics.drawable.TransitionDrawable;
26import android.media.ThumbnailUtils;
27import android.util.AttributeSet;
28import android.view.ViewGroup.LayoutParams;
29import android.view.animation.AnimationUtils;
30import android.widget.ImageView;
31
32/**
33 * A @{code ImageView} which can rotate it's content.
34 */
35public class RotateImageView extends ImageView {
36
37    @SuppressWarnings("unused")
38    private static final String TAG = "RotateImageView";
39
40    private static final int ANIMATION_SPEED = 180; // 180 deg/sec
41
42    private int mCurrentDegree = 0; // [0, 359]
43    private int mStartDegree = 0;
44    private int mTargetDegree = 0;
45
46    private boolean mClockwise = false, mEnableAnimation = true;
47
48    private long mAnimationStartTime = 0;
49    private long mAnimationEndTime = 0;
50
51    public RotateImageView(Context context, AttributeSet attrs) {
52        super(context, attrs);
53    }
54
55    public void enableAnimation(boolean enable) {
56        mEnableAnimation = enable;
57    }
58
59    // Rotate the view counter-clockwise
60    public void setDegree(int degree) {
61        // make sure in the range of [0, 359]
62        degree = degree >= 0 ? degree % 360 : degree % 360 + 360;
63        if (degree == mTargetDegree) return;
64
65        mTargetDegree = degree;
66        mStartDegree = mCurrentDegree;
67        mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis();
68
69        int diff = mTargetDegree - mCurrentDegree;
70        diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359]
71
72        // Make it in range [-179, 180]. That's the shorted distance between the
73        // two angles
74        diff = diff > 180 ? diff - 360 : diff;
75
76        mClockwise = diff >= 0;
77        mAnimationEndTime = mAnimationStartTime
78                + Math.abs(diff) * 1000 / ANIMATION_SPEED;
79
80        invalidate();
81    }
82
83    @Override
84    protected void onDraw(Canvas canvas) {
85        Drawable drawable = getDrawable();
86        if (drawable == null) return;
87
88        Rect bounds = drawable.getBounds();
89        int w = bounds.right - bounds.left;
90        int h = bounds.bottom - bounds.top;
91
92        if (w == 0 || h == 0) return; // nothing to draw
93
94        if (mCurrentDegree != mTargetDegree) {
95            long time = AnimationUtils.currentAnimationTimeMillis();
96            if (time < mAnimationEndTime) {
97                int deltaTime = (int)(time - mAnimationStartTime);
98                int degree = mStartDegree + ANIMATION_SPEED
99                        * (mClockwise ? deltaTime : -deltaTime) / 1000;
100                degree = degree >= 0 ? degree % 360 : degree % 360 + 360;
101                mCurrentDegree = degree;
102                invalidate();
103            } else {
104                mCurrentDegree = mTargetDegree;
105            }
106        }
107
108        int left = getPaddingLeft();
109        int top = getPaddingTop();
110        int right = getPaddingRight();
111        int bottom = getPaddingBottom();
112        int width = getWidth() - left - right;
113        int height = getHeight() - top - bottom;
114
115        int saveCount = canvas.getSaveCount();
116        canvas.translate(left + width / 2, top + height / 2);
117        canvas.rotate(-mCurrentDegree);
118        canvas.translate(-w / 2, -h / 2);
119        drawable.draw(canvas);
120        canvas.restoreToCount(saveCount);
121    }
122
123    private Bitmap mThumb;
124    private Drawable[] mThumbs;
125    private TransitionDrawable mThumbTransition;
126
127    public void setBitmap(Bitmap bitmap) {
128        // Make sure uri and original are consistently both null or both
129        // non-null.
130        if (bitmap == null) {
131            mThumb = null;
132            mThumbs = null;
133            setImageDrawable(null);
134            setVisibility(GONE);
135            return;
136        }
137
138        LayoutParams param = getLayoutParams();
139        final int miniThumbWidth = param.width
140                - getPaddingLeft() - getPaddingRight();
141        final int miniThumbHeight = param.height
142                - getPaddingTop() - getPaddingBottom();
143        mThumb = ThumbnailUtils.extractThumbnail(
144                bitmap, miniThumbWidth, miniThumbHeight);
145        Drawable drawable;
146        if (mThumbs == null || !mEnableAnimation) {
147            mThumbs = new Drawable[2];
148            mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb);
149            setImageDrawable(mThumbs[1]);
150        } else {
151            mThumbs[0] = mThumbs[1];
152            mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb);
153            mThumbTransition = new TransitionDrawable(mThumbs);
154            setImageDrawable(mThumbTransition);
155            mThumbTransition.startTransition(500);
156        }
157        setVisibility(VISIBLE);
158    }
159}
160