1 /* 2 * Copyright (C) 2008 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 17 18 package com.android.launcher2; 19 20 import android.animation.ValueAnimator; 21 import android.animation.ValueAnimator.AnimatorUpdateListener; 22 import android.content.res.Resources; 23 import android.graphics.Bitmap; 24 import android.graphics.Canvas; 25 import android.graphics.Paint; 26 import android.graphics.Point; 27 import android.graphics.PorterDuff; 28 import android.graphics.PorterDuffColorFilter; 29 import android.graphics.Rect; 30 import android.view.View; 31 import android.view.animation.DecelerateInterpolator; 32 33 import com.android.launcher.R; 34 35 public class DragView extends View { 36 private static float sDragAlpha = 1f; 37 38 private Bitmap mBitmap; 39 private Bitmap mCrossFadeBitmap; 40 private Paint mPaint; 41 private int mRegistrationX; 42 private int mRegistrationY; 43 44 private Point mDragVisualizeOffset = null; 45 private Rect mDragRegion = null; 46 private DragLayer mDragLayer = null; 47 private boolean mHasDrawn = false; 48 private float mCrossFadeProgress = 0f; 49 50 ValueAnimator mAnim; 51 private float mOffsetX = 0.0f; 52 private float mOffsetY = 0.0f; 53 private float mInitialScale = 1f; 54 55 /** 56 * Construct the drag view. 57 * <p> 58 * The registration point is the point inside our view that the touch events should 59 * be centered upon. 60 * 61 * @param launcher The Launcher instance 62 * @param bitmap The view that we're dragging around. We scale it up when we draw it. 63 * @param registrationX The x coordinate of the registration point. 64 * @param registrationY The y coordinate of the registration point. 65 */ 66 public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY, 67 int left, int top, int width, int height, final float initialScale) { 68 super(launcher); 69 mDragLayer = launcher.getDragLayer(); 70 mInitialScale = initialScale; 71 72 final Resources res = getResources(); 73 final float offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX); 74 final float offsetY = res.getDimensionPixelSize(R.dimen.dragViewOffsetY); 75 final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); 76 final float scale = (width + scaleDps) / width; 77 78 // Animate the view into the correct position 79 mAnim = ValueAnimator.ofFloat(0.0f, 1.0f); 80 mAnim.setDuration(150); 81 mAnim.addUpdateListener(new AnimatorUpdateListener() { 82 @Override 83 public void onAnimationUpdate(ValueAnimator animation) { 84 final float value = (Float) animation.getAnimatedValue(); 85 86 final int deltaX = (int) ((value * offsetX) - mOffsetX); 87 final int deltaY = (int) ((value * offsetY) - mOffsetY); 88 89 mOffsetX += deltaX; 90 mOffsetY += deltaY; 91 setScaleX(initialScale + (value * (scale - initialScale))); 92 setScaleY(initialScale + (value * (scale - initialScale))); 93 if (sDragAlpha != 1f) { 94 setAlpha(sDragAlpha * value + (1f - value)); 95 } 96 97 if (getParent() == null) { 98 animation.cancel(); 99 } else { 100 setTranslationX(getTranslationX() + deltaX); 101 setTranslationY(getTranslationY() + deltaY); 102 } 103 } 104 }); 105 106 mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height); 107 setDragRegion(new Rect(0, 0, width, height)); 108 109 // The point in our scaled bitmap that the touch events are located 110 mRegistrationX = registrationX; 111 mRegistrationY = registrationY; 112 113 // Force a measure, because Workspace uses getMeasuredHeight() before the layout pass 114 int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 115 measure(ms, ms); 116 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 117 } 118 119 public float getOffsetY() { 120 return mOffsetY; 121 } 122 123 public int getDragRegionLeft() { 124 return mDragRegion.left; 125 } 126 127 public int getDragRegionTop() { 128 return mDragRegion.top; 129 } 130 131 public int getDragRegionWidth() { 132 return mDragRegion.width(); 133 } 134 135 public int getDragRegionHeight() { 136 return mDragRegion.height(); 137 } 138 139 public void setDragVisualizeOffset(Point p) { 140 mDragVisualizeOffset = p; 141 } 142 143 public Point getDragVisualizeOffset() { 144 return mDragVisualizeOffset; 145 } 146 147 public void setDragRegion(Rect r) { 148 mDragRegion = r; 149 } 150 151 public Rect getDragRegion() { 152 return mDragRegion; 153 } 154 155 public float getInitialScale() { 156 return mInitialScale; 157 } 158 159 public void updateInitialScaleToCurrentScale() { 160 mInitialScale = getScaleX(); 161 } 162 163 @Override 164 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 165 setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight()); 166 } 167 168 @Override 169 protected void onDraw(Canvas canvas) { 170 @SuppressWarnings("all") // suppress dead code warning 171 final boolean debug = false; 172 if (debug) { 173 Paint p = new Paint(); 174 p.setStyle(Paint.Style.FILL); 175 p.setColor(0x66ffffff); 176 canvas.drawRect(0, 0, getWidth(), getHeight(), p); 177 } 178 179 mHasDrawn = true; 180 boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null; 181 if (crossFade) { 182 int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255; 183 mPaint.setAlpha(alpha); 184 } 185 canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); 186 if (crossFade) { 187 mPaint.setAlpha((int) (255 * mCrossFadeProgress)); 188 canvas.save(); 189 float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth(); 190 float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight(); 191 canvas.scale(sX, sY); 192 canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint); 193 canvas.restore(); 194 } 195 } 196 197 public void setCrossFadeBitmap(Bitmap crossFadeBitmap) { 198 mCrossFadeBitmap = crossFadeBitmap; 199 } 200 201 public void crossFade(int duration) { 202 ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); 203 va.setDuration(duration); 204 va.setInterpolator(new DecelerateInterpolator(1.5f)); 205 va.addUpdateListener(new AnimatorUpdateListener() { 206 @Override 207 public void onAnimationUpdate(ValueAnimator animation) { 208 mCrossFadeProgress = animation.getAnimatedFraction(); 209 } 210 }); 211 va.start(); 212 } 213 214 public void setColor(int color) { 215 if (mPaint == null) { 216 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 217 } 218 if (color != 0) { 219 mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); 220 } else { 221 mPaint.setColorFilter(null); 222 } 223 invalidate(); 224 } 225 226 public boolean hasDrawn() { 227 return mHasDrawn; 228 } 229 230 @Override 231 public void setAlpha(float alpha) { 232 super.setAlpha(alpha); 233 mPaint.setAlpha((int) (255 * alpha)); 234 invalidate(); 235 } 236 237 /** 238 * Create a window containing this view and show it. 239 * 240 * @param windowToken obtained from v.getWindowToken() from one of your views 241 * @param touchX the x coordinate the user touched in DragLayer coordinates 242 * @param touchY the y coordinate the user touched in DragLayer coordinates 243 */ 244 public void show(int touchX, int touchY) { 245 mDragLayer.addView(this); 246 247 // Enable hw-layers on this view 248 setLayerType(View.LAYER_TYPE_HARDWARE, null); 249 250 // Start the pick-up animation 251 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0); 252 lp.width = mBitmap.getWidth(); 253 lp.height = mBitmap.getHeight(); 254 lp.customPosition = true; 255 setLayoutParams(lp); 256 setTranslationX(touchX - mRegistrationX); 257 setTranslationY(touchY - mRegistrationY); 258 mAnim.start(); 259 } 260 261 public void cancelAnimation() { 262 if (mAnim != null && mAnim.isRunning()) { 263 mAnim.cancel(); 264 } 265 } 266 267 public void resetLayoutParams() { 268 mOffsetX = mOffsetY = 0; 269 requestLayout(); 270 } 271 272 /** 273 * Move the window containing this view. 274 * 275 * @param touchX the x coordinate the user touched in DragLayer coordinates 276 * @param touchY the y coordinate the user touched in DragLayer coordinates 277 */ 278 void move(int touchX, int touchY) { 279 setTranslationX(touchX - mRegistrationX + (int) mOffsetX); 280 setTranslationY(touchY - mRegistrationY + (int) mOffsetY); 281 } 282 283 void remove() { 284 if (getParent() != null) { 285 // Disable hw-layers on this view 286 setLayerType(View.LAYER_TYPE_NONE, null); 287 288 mDragLayer.removeView(DragView.this); 289 } 290 } 291 } 292 293