1/* 2 * Copyright (C) 2010 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.annotation.TargetApi; 20import android.content.Context; 21import android.graphics.Canvas; 22import android.graphics.Matrix; 23import android.graphics.Rect; 24import android.util.AttributeSet; 25import android.view.MotionEvent; 26import android.view.View; 27import android.view.ViewGroup; 28import android.view.ViewParent; 29 30import com.android.gallery3d.common.ApiHelper; 31import com.android.gallery3d.util.MotionEventHelper; 32 33// A RotateLayout is designed to display a single item and provides the 34// capabilities to rotate the item. 35public class RotateLayout extends ViewGroup implements Rotatable { 36 @SuppressWarnings("unused") 37 private static final String TAG = "RotateLayout"; 38 private int mOrientation; 39 private Matrix mMatrix = new Matrix(); 40 protected View mChild; 41 42 public RotateLayout(Context context, AttributeSet attrs) { 43 super(context, attrs); 44 // The transparent background here is a workaround of the render issue 45 // happened when the view is rotated as the device's orientation 46 // changed. The view looks fine in landscape. After rotation, the view 47 // is invisible. 48 setBackgroundResource(android.R.color.transparent); 49 } 50 51 @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) 52 @Override 53 protected void onFinishInflate() { 54 mChild = getChildAt(0); 55 if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) { 56 mChild.setPivotX(0); 57 mChild.setPivotY(0); 58 } 59 } 60 61 @Override 62 protected void onLayout( 63 boolean change, int left, int top, int right, int bottom) { 64 int width = right - left; 65 int height = bottom - top; 66 switch (mOrientation) { 67 case 0: 68 case 180: 69 mChild.layout(0, 0, width, height); 70 break; 71 case 90: 72 case 270: 73 mChild.layout(0, 0, height, width); 74 break; 75 } 76 } 77 78 @Override 79 public boolean dispatchTouchEvent(MotionEvent event) { 80 if (!ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) { 81 final int w = getMeasuredWidth(); 82 final int h = getMeasuredHeight(); 83 switch (mOrientation) { 84 case 0: 85 mMatrix.setTranslate(0, 0); 86 break; 87 case 90: 88 mMatrix.setTranslate(0, -h); 89 break; 90 case 180: 91 mMatrix.setTranslate(-w, -h); 92 break; 93 case 270: 94 mMatrix.setTranslate(-w, 0); 95 break; 96 } 97 mMatrix.postRotate(mOrientation); 98 event = MotionEventHelper.transformEvent(event, mMatrix); 99 } 100 return super.dispatchTouchEvent(event); 101 } 102 103 @Override 104 protected void dispatchDraw(Canvas canvas) { 105 if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) { 106 super.dispatchDraw(canvas); 107 } else { 108 canvas.save(); 109 int w = getMeasuredWidth(); 110 int h = getMeasuredHeight(); 111 switch (mOrientation) { 112 case 0: 113 canvas.translate(0, 0); 114 break; 115 case 90: 116 canvas.translate(0, h); 117 break; 118 case 180: 119 canvas.translate(w, h); 120 break; 121 case 270: 122 canvas.translate(w, 0); 123 break; 124 } 125 canvas.rotate(-mOrientation, 0, 0); 126 super.dispatchDraw(canvas); 127 canvas.restore(); 128 } 129 } 130 131 @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) 132 @Override 133 protected void onMeasure(int widthSpec, int heightSpec) { 134 int w = 0, h = 0; 135 switch(mOrientation) { 136 case 0: 137 case 180: 138 measureChild(mChild, widthSpec, heightSpec); 139 w = mChild.getMeasuredWidth(); 140 h = mChild.getMeasuredHeight(); 141 break; 142 case 90: 143 case 270: 144 measureChild(mChild, heightSpec, widthSpec); 145 w = mChild.getMeasuredHeight(); 146 h = mChild.getMeasuredWidth(); 147 break; 148 } 149 setMeasuredDimension(w, h); 150 151 if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) { 152 switch (mOrientation) { 153 case 0: 154 mChild.setTranslationX(0); 155 mChild.setTranslationY(0); 156 break; 157 case 90: 158 mChild.setTranslationX(0); 159 mChild.setTranslationY(h); 160 break; 161 case 180: 162 mChild.setTranslationX(w); 163 mChild.setTranslationY(h); 164 break; 165 case 270: 166 mChild.setTranslationX(w); 167 mChild.setTranslationY(0); 168 break; 169 } 170 mChild.setRotation(-mOrientation); 171 } 172 } 173 174 @Override 175 public boolean shouldDelayChildPressedState() { 176 return false; 177 } 178 179 // Rotate the view counter-clockwise 180 @Override 181 public void setOrientation(int orientation, boolean animation) { 182 orientation = orientation % 360; 183 if (mOrientation == orientation) return; 184 mOrientation = orientation; 185 requestLayout(); 186 } 187 188 public int getOrientation() { 189 return mOrientation; 190 } 191 192 @Override 193 public ViewParent invalidateChildInParent(int[] location, Rect r) { 194 if (!ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES && mOrientation != 0) { 195 // The workaround invalidates the entire rotate layout. After 196 // rotation, the correct area to invalidate may be larger than the 197 // size of the child. Ex: ListView. There is no way to invalidate 198 // only the necessary area. 199 r.set(0, 0, getWidth(), getHeight()); 200 } 201 return super.invalidateChildInParent(location, r); 202 } 203} 204