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 */ DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY, int left, int top, int width, int height, final float initialScale)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 // Set the initial scale to avoid any jumps 79 setScaleX(initialScale); 80 setScaleY(initialScale); 81 82 // Animate the view into the correct position 83 mAnim = LauncherAnimUtils.ofFloat(this, 0f, 1f); 84 mAnim.setDuration(150); 85 mAnim.addUpdateListener(new AnimatorUpdateListener() { 86 @Override 87 public void onAnimationUpdate(ValueAnimator animation) { 88 final float value = (Float) animation.getAnimatedValue(); 89 90 final int deltaX = (int) ((value * offsetX) - mOffsetX); 91 final int deltaY = (int) ((value * offsetY) - mOffsetY); 92 93 mOffsetX += deltaX; 94 mOffsetY += deltaY; 95 setScaleX(initialScale + (value * (scale - initialScale))); 96 setScaleY(initialScale + (value * (scale - initialScale))); 97 if (sDragAlpha != 1f) { 98 setAlpha(sDragAlpha * value + (1f - value)); 99 } 100 101 if (getParent() == null) { 102 animation.cancel(); 103 } else { 104 setTranslationX(getTranslationX() + deltaX); 105 setTranslationY(getTranslationY() + deltaY); 106 } 107 } 108 }); 109 110 mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height); 111 setDragRegion(new Rect(0, 0, width, height)); 112 113 // The point in our scaled bitmap that the touch events are located 114 mRegistrationX = registrationX; 115 mRegistrationY = registrationY; 116 117 // Force a measure, because Workspace uses getMeasuredHeight() before the layout pass 118 int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 119 measure(ms, ms); 120 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 121 } 122 getOffsetY()123 public float getOffsetY() { 124 return mOffsetY; 125 } 126 getDragRegionLeft()127 public int getDragRegionLeft() { 128 return mDragRegion.left; 129 } 130 getDragRegionTop()131 public int getDragRegionTop() { 132 return mDragRegion.top; 133 } 134 getDragRegionWidth()135 public int getDragRegionWidth() { 136 return mDragRegion.width(); 137 } 138 getDragRegionHeight()139 public int getDragRegionHeight() { 140 return mDragRegion.height(); 141 } 142 setDragVisualizeOffset(Point p)143 public void setDragVisualizeOffset(Point p) { 144 mDragVisualizeOffset = p; 145 } 146 getDragVisualizeOffset()147 public Point getDragVisualizeOffset() { 148 return mDragVisualizeOffset; 149 } 150 setDragRegion(Rect r)151 public void setDragRegion(Rect r) { 152 mDragRegion = r; 153 } 154 getDragRegion()155 public Rect getDragRegion() { 156 return mDragRegion; 157 } 158 getInitialScale()159 public float getInitialScale() { 160 return mInitialScale; 161 } 162 updateInitialScaleToCurrentScale()163 public void updateInitialScaleToCurrentScale() { 164 mInitialScale = getScaleX(); 165 } 166 167 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)168 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 169 setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight()); 170 } 171 172 @Override onDraw(Canvas canvas)173 protected void onDraw(Canvas canvas) { 174 @SuppressWarnings("all") // suppress dead code warning 175 final boolean debug = false; 176 if (debug) { 177 Paint p = new Paint(); 178 p.setStyle(Paint.Style.FILL); 179 p.setColor(0x66ffffff); 180 canvas.drawRect(0, 0, getWidth(), getHeight(), p); 181 } 182 183 mHasDrawn = true; 184 boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null; 185 if (crossFade) { 186 int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255; 187 mPaint.setAlpha(alpha); 188 } 189 canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); 190 if (crossFade) { 191 mPaint.setAlpha((int) (255 * mCrossFadeProgress)); 192 canvas.save(); 193 float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth(); 194 float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight(); 195 canvas.scale(sX, sY); 196 canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint); 197 canvas.restore(); 198 } 199 } 200 setCrossFadeBitmap(Bitmap crossFadeBitmap)201 public void setCrossFadeBitmap(Bitmap crossFadeBitmap) { 202 mCrossFadeBitmap = crossFadeBitmap; 203 } 204 crossFade(int duration)205 public void crossFade(int duration) { 206 ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1f); 207 va.setDuration(duration); 208 va.setInterpolator(new DecelerateInterpolator(1.5f)); 209 va.addUpdateListener(new AnimatorUpdateListener() { 210 @Override 211 public void onAnimationUpdate(ValueAnimator animation) { 212 mCrossFadeProgress = animation.getAnimatedFraction(); 213 } 214 }); 215 va.start(); 216 } 217 setColor(int color)218 public void setColor(int color) { 219 if (mPaint == null) { 220 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 221 } 222 if (color != 0) { 223 mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); 224 } else { 225 mPaint.setColorFilter(null); 226 } 227 invalidate(); 228 } 229 hasDrawn()230 public boolean hasDrawn() { 231 return mHasDrawn; 232 } 233 234 @Override setAlpha(float alpha)235 public void setAlpha(float alpha) { 236 super.setAlpha(alpha); 237 mPaint.setAlpha((int) (255 * alpha)); 238 invalidate(); 239 } 240 241 /** 242 * Create a window containing this view and show it. 243 * 244 * @param windowToken obtained from v.getWindowToken() from one of your views 245 * @param touchX the x coordinate the user touched in DragLayer coordinates 246 * @param touchY the y coordinate the user touched in DragLayer coordinates 247 */ show(int touchX, int touchY)248 public void show(int touchX, int touchY) { 249 mDragLayer.addView(this); 250 251 // Start the pick-up animation 252 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0); 253 lp.width = mBitmap.getWidth(); 254 lp.height = mBitmap.getHeight(); 255 lp.customPosition = true; 256 setLayoutParams(lp); 257 setTranslationX(touchX - mRegistrationX); 258 setTranslationY(touchY - mRegistrationY); 259 // Post the animation to skip other expensive work happening on the first frame 260 post(new Runnable() { 261 public void run() { 262 mAnim.start(); 263 } 264 }); 265 } 266 cancelAnimation()267 public void cancelAnimation() { 268 if (mAnim != null && mAnim.isRunning()) { 269 mAnim.cancel(); 270 } 271 } 272 resetLayoutParams()273 public void resetLayoutParams() { 274 mOffsetX = mOffsetY = 0; 275 requestLayout(); 276 } 277 278 /** 279 * Move the window containing this view. 280 * 281 * @param touchX the x coordinate the user touched in DragLayer coordinates 282 * @param touchY the y coordinate the user touched in DragLayer coordinates 283 */ move(int touchX, int touchY)284 void move(int touchX, int touchY) { 285 setTranslationX(touchX - mRegistrationX + (int) mOffsetX); 286 setTranslationY(touchY - mRegistrationY + (int) mOffsetY); 287 } 288 remove()289 void remove() { 290 if (getParent() != null) { 291 mDragLayer.removeView(DragView.this); 292 } 293 } 294 } 295 296