RotateImageView.java revision 94ca8baae7e6e3885d895c095890eabf30d922e2
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 com.android.camera.ui.Rotatable; 20 21import android.content.Context; 22import android.graphics.Bitmap; 23import android.graphics.Canvas; 24import android.graphics.Rect; 25import android.graphics.drawable.BitmapDrawable; 26import android.graphics.drawable.Drawable; 27import android.graphics.drawable.TransitionDrawable; 28import android.media.ThumbnailUtils; 29import android.util.AttributeSet; 30import android.view.ViewGroup.LayoutParams; 31import android.view.animation.AnimationUtils; 32import android.widget.ImageView; 33 34/** 35 * A @{code ImageView} which can rotate it's content. 36 */ 37public class RotateImageView extends ColorFilterImageView implements Rotatable { 38 39 @SuppressWarnings("unused") 40 private static final String TAG = "RotateImageView"; 41 42 private static final int ANIMATION_SPEED = 270; // 270 deg/sec 43 44 private int mCurrentDegree = 0; // [0, 359] 45 private int mStartDegree = 0; 46 private int mTargetDegree = 0; 47 48 private boolean mClockwise = false, mEnableAnimation = true; 49 50 private long mAnimationStartTime = 0; 51 private long mAnimationEndTime = 0; 52 53 public RotateImageView(Context context, AttributeSet attrs) { 54 super(context, attrs); 55 } 56 57 public RotateImageView(Context context) { 58 super(context); 59 } 60 61 public void enableAnimation(boolean enable) { 62 mEnableAnimation = enable; 63 } 64 65 protected int getDegree() { 66 return mTargetDegree; 67 } 68 69 public void setOrientation(int orientation) { 70 setDegree(orientation); 71 } 72 73 // Rotate the view counter-clockwise 74 public void setDegree(int degree) { 75 // make sure in the range of [0, 359] 76 degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 77 if (degree == mTargetDegree) return; 78 79 mTargetDegree = degree; 80 mStartDegree = mCurrentDegree; 81 mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis(); 82 83 int diff = mTargetDegree - mCurrentDegree; 84 diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359] 85 86 // Make it in range [-179, 180]. That's the shorted distance between the 87 // two angles 88 diff = diff > 180 ? diff - 360 : diff; 89 90 mClockwise = diff >= 0; 91 mAnimationEndTime = mAnimationStartTime 92 + Math.abs(diff) * 1000 / ANIMATION_SPEED; 93 94 invalidate(); 95 } 96 97 @Override 98 protected void onDraw(Canvas canvas) { 99 Drawable drawable = getDrawable(); 100 if (drawable == null) return; 101 102 Rect bounds = drawable.getBounds(); 103 int w = bounds.right - bounds.left; 104 int h = bounds.bottom - bounds.top; 105 106 if (w == 0 || h == 0) return; // nothing to draw 107 108 if (mCurrentDegree != mTargetDegree) { 109 long time = AnimationUtils.currentAnimationTimeMillis(); 110 if (time < mAnimationEndTime) { 111 int deltaTime = (int)(time - mAnimationStartTime); 112 int degree = mStartDegree + ANIMATION_SPEED 113 * (mClockwise ? deltaTime : -deltaTime) / 1000; 114 degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 115 mCurrentDegree = degree; 116 invalidate(); 117 } else { 118 mCurrentDegree = mTargetDegree; 119 } 120 } 121 122 int left = getPaddingLeft(); 123 int top = getPaddingTop(); 124 int right = getPaddingRight(); 125 int bottom = getPaddingBottom(); 126 int width = getWidth() - left - right; 127 int height = getHeight() - top - bottom; 128 129 int saveCount = canvas.getSaveCount(); 130 131 // Scale down the image first if required. 132 if ((getScaleType() == ImageView.ScaleType.FIT_CENTER) && 133 ((width < w) || (height < h))) { 134 float ratio = Math.min((float) width / w, (float) height / h); 135 canvas.scale(ratio, ratio, width / 2.0f, height / 2.0f); 136 } 137 canvas.translate(left + width / 2, top + height / 2); 138 canvas.rotate(-mCurrentDegree); 139 canvas.translate(-w / 2, -h / 2); 140 drawable.draw(canvas); 141 canvas.restoreToCount(saveCount); 142 } 143 144 private Bitmap mThumb; 145 private Drawable[] mThumbs; 146 private TransitionDrawable mThumbTransition; 147 148 public void setBitmap(Bitmap bitmap) { 149 // Make sure uri and original are consistently both null or both 150 // non-null. 151 if (bitmap == null) { 152 mThumb = null; 153 mThumbs = null; 154 setImageDrawable(null); 155 setVisibility(GONE); 156 return; 157 } 158 159 LayoutParams param = getLayoutParams(); 160 final int miniThumbWidth = param.width 161 - getPaddingLeft() - getPaddingRight(); 162 final int miniThumbHeight = param.height 163 - getPaddingTop() - getPaddingBottom(); 164 mThumb = ThumbnailUtils.extractThumbnail( 165 bitmap, miniThumbWidth, miniThumbHeight); 166 Drawable drawable; 167 if (mThumbs == null || !mEnableAnimation) { 168 mThumbs = new Drawable[2]; 169 mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 170 setImageDrawable(mThumbs[1]); 171 } else { 172 mThumbs[0] = mThumbs[1]; 173 mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 174 mThumbTransition = new TransitionDrawable(mThumbs); 175 setImageDrawable(mThumbTransition); 176 mThumbTransition.startTransition(500); 177 } 178 setVisibility(VISIBLE); 179 } 180} 181