PointerLocationView.java revision f8d0f095e34f8d661ca5b7d555d8610272099bff
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.internal.widget; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.Paint; 22import android.graphics.Paint.FontMetricsInt; 23import android.util.Log; 24import android.view.MotionEvent; 25import android.view.VelocityTracker; 26import android.view.View; 27import android.view.ViewConfiguration; 28 29import java.util.ArrayList; 30 31public class PointerLocationView extends View { 32 public static class PointerState { 33 private final ArrayList<Float> mXs = new ArrayList<Float>(); 34 private final ArrayList<Float> mYs = new ArrayList<Float>(); 35 private boolean mCurDown; 36 private int mCurX; 37 private int mCurY; 38 private float mCurPressure; 39 private float mCurSize; 40 private int mCurWidth; 41 private VelocityTracker mVelocity; 42 } 43 44 private final ViewConfiguration mVC; 45 private final Paint mTextPaint; 46 private final Paint mTextBackgroundPaint; 47 private final Paint mTextLevelPaint; 48 private final Paint mPaint; 49 private final Paint mTargetPaint; 50 private final Paint mPathPaint; 51 private final FontMetricsInt mTextMetrics = new FontMetricsInt(); 52 private int mHeaderBottom; 53 private boolean mCurDown; 54 private int mCurNumPointers; 55 private int mMaxNumPointers; 56 private final ArrayList<PointerState> mPointers 57 = new ArrayList<PointerState>(); 58 59 private boolean mPrintCoords = true; 60 61 public PointerLocationView(Context c) { 62 super(c); 63 mVC = ViewConfiguration.get(c); 64 mTextPaint = new Paint(); 65 mTextPaint.setAntiAlias(true); 66 mTextPaint.setTextSize(10 67 * getResources().getDisplayMetrics().density); 68 mTextPaint.setARGB(255, 0, 0, 0); 69 mTextBackgroundPaint = new Paint(); 70 mTextBackgroundPaint.setAntiAlias(false); 71 mTextBackgroundPaint.setARGB(128, 255, 255, 255); 72 mTextLevelPaint = new Paint(); 73 mTextLevelPaint.setAntiAlias(false); 74 mTextLevelPaint.setARGB(192, 255, 0, 0); 75 mPaint = new Paint(); 76 mPaint.setAntiAlias(true); 77 mPaint.setARGB(255, 255, 255, 255); 78 mPaint.setStyle(Paint.Style.STROKE); 79 mPaint.setStrokeWidth(2); 80 mTargetPaint = new Paint(); 81 mTargetPaint.setAntiAlias(false); 82 mTargetPaint.setARGB(255, 0, 0, 192); 83 mPathPaint = new Paint(); 84 mPathPaint.setAntiAlias(false); 85 mPathPaint.setARGB(255, 0, 96, 255); 86 mPaint.setStyle(Paint.Style.STROKE); 87 mPaint.setStrokeWidth(1); 88 89 PointerState ps = new PointerState(); 90 ps.mVelocity = VelocityTracker.obtain(); 91 mPointers.add(ps); 92 } 93 94 public void setPrintCoords(boolean state) { 95 mPrintCoords = state; 96 } 97 98 @Override 99 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 100 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 101 mTextPaint.getFontMetricsInt(mTextMetrics); 102 mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2; 103 if (false) { 104 Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent 105 + " descent=" + mTextMetrics.descent 106 + " leading=" + mTextMetrics.leading 107 + " top=" + mTextMetrics.top 108 + " bottom=" + mTextMetrics.bottom); 109 } 110 } 111 112 @Override 113 protected void onDraw(Canvas canvas) { 114 synchronized (mPointers) { 115 final int w = getWidth(); 116 final int itemW = w/7; 117 final int base = -mTextMetrics.ascent+1; 118 final int bottom = mHeaderBottom; 119 120 final int NP = mPointers.size(); 121 122 if (NP > 0) { 123 final PointerState ps = mPointers.get(0); 124 canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint); 125 canvas.drawText("P: " + mCurNumPointers + " / " + mMaxNumPointers, 126 1, base, mTextPaint); 127 128 final int N = ps.mXs.size(); 129 if ((mCurDown && ps.mCurDown) || N == 0) { 130 canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint); 131 canvas.drawText("X: " + ps.mCurX, 1 + itemW, base, mTextPaint); 132 canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint); 133 canvas.drawText("Y: " + ps.mCurY, 1 + itemW * 2, base, mTextPaint); 134 } else { 135 float dx = ps.mXs.get(N-1) - ps.mXs.get(0); 136 float dy = ps.mYs.get(N-1) - ps.mYs.get(0); 137 canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, 138 Math.abs(dx) < mVC.getScaledTouchSlop() 139 ? mTextBackgroundPaint : mTextLevelPaint); 140 canvas.drawText("dX: " + String.format("%.1f", dx), 1 + itemW, base, mTextPaint); 141 canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, 142 Math.abs(dy) < mVC.getScaledTouchSlop() 143 ? mTextBackgroundPaint : mTextLevelPaint); 144 canvas.drawText("dY: " + String.format("%.1f", dy), 1 + itemW * 2, base, mTextPaint); 145 } 146 147 canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint); 148 int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000); 149 canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint); 150 151 canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint); 152 velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000); 153 canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint); 154 155 canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint); 156 canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCurPressure * itemW) - 1, 157 bottom, mTextLevelPaint); 158 canvas.drawText("Prs: " + String.format("%.2f", ps.mCurPressure), 1 + itemW * 5, 159 base, mTextPaint); 160 161 canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint); 162 canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCurSize * itemW) - 1, 163 bottom, mTextLevelPaint); 164 canvas.drawText("Size: " + String.format("%.2f", ps.mCurSize), 1 + itemW * 6, 165 base, mTextPaint); 166 } 167 168 for (int p=0; p<NP; p++) { 169 final PointerState ps = mPointers.get(p); 170 171 if (mCurDown && ps.mCurDown) { 172 canvas.drawLine(0, (int)ps.mCurY, getWidth(), (int)ps.mCurY, mTargetPaint); 173 canvas.drawLine((int)ps.mCurX, 0, (int)ps.mCurX, getHeight(), mTargetPaint); 174 int pressureLevel = (int)(ps.mCurPressure*255); 175 mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel); 176 canvas.drawPoint(ps.mCurX, ps.mCurY, mPaint); 177 canvas.drawCircle(ps.mCurX, ps.mCurY, ps.mCurWidth, mPaint); 178 } 179 } 180 181 for (int p=0; p<NP; p++) { 182 final PointerState ps = mPointers.get(p); 183 184 final int N = ps.mXs.size(); 185 float lastX=0, lastY=0; 186 boolean haveLast = false; 187 boolean drawn = false; 188 mPaint.setARGB(255, 128, 255, 255); 189 for (int i=0; i<N; i++) { 190 float x = ps.mXs.get(i); 191 float y = ps.mYs.get(i); 192 if (Float.isNaN(x)) { 193 haveLast = false; 194 continue; 195 } 196 if (haveLast) { 197 canvas.drawLine(lastX, lastY, x, y, mPathPaint); 198 canvas.drawPoint(lastX, lastY, mPaint); 199 drawn = true; 200 } 201 lastX = x; 202 lastY = y; 203 haveLast = true; 204 } 205 206 if (drawn) { 207 if (ps.mVelocity != null) { 208 mPaint.setARGB(255, 255, 64, 128); 209 float xVel = ps.mVelocity.getXVelocity() * (1000/60); 210 float yVel = ps.mVelocity.getYVelocity() * (1000/60); 211 canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint); 212 } else { 213 canvas.drawPoint(lastX, lastY, mPaint); 214 } 215 } 216 } 217 } 218 } 219 220 public void addTouchEvent(MotionEvent event) { 221 synchronized (mPointers) { 222 int action = event.getAction(); 223 224 //Log.i("Pointer", "Motion: action=0x" + Integer.toHexString(action) 225 // + " pointers=" + event.getPointerCount()); 226 227 int NP = mPointers.size(); 228 229 //mRect.set(0, 0, getWidth(), mHeaderBottom+1); 230 //invalidate(mRect); 231 //if (mCurDown) { 232 // mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3, 233 // mCurX+mCurWidth+3, mCurY+mCurWidth+3); 234 //} else { 235 // mRect.setEmpty(); 236 //} 237 if (action == MotionEvent.ACTION_DOWN) { 238 for (int p=0; p<NP; p++) { 239 final PointerState ps = mPointers.get(p); 240 ps.mXs.clear(); 241 ps.mYs.clear(); 242 ps.mVelocity = VelocityTracker.obtain(); 243 ps.mCurDown = false; 244 } 245 mPointers.get(0).mCurDown = true; 246 mMaxNumPointers = 0; 247 if (mPrintCoords) { 248 Log.i("Pointer", "Pointer 1: DOWN"); 249 } 250 } 251 252 if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) { 253 final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK) 254 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 255 final int id = event.getPointerId(index); 256 while (NP <= id) { 257 PointerState ps = new PointerState(); 258 ps.mVelocity = VelocityTracker.obtain(); 259 mPointers.add(ps); 260 NP++; 261 } 262 final PointerState ps = mPointers.get(id); 263 ps.mVelocity = VelocityTracker.obtain(); 264 ps.mCurDown = true; 265 if (mPrintCoords) { 266 Log.i("Pointer", "Pointer " + (id+1) + ": DOWN"); 267 } 268 } 269 270 final int NI = event.getPointerCount(); 271 272 mCurDown = action != MotionEvent.ACTION_UP 273 && action != MotionEvent.ACTION_CANCEL; 274 mCurNumPointers = mCurDown ? NI : 0; 275 if (mMaxNumPointers < mCurNumPointers) { 276 mMaxNumPointers = mCurNumPointers; 277 } 278 279 for (int i=0; i<NI; i++) { 280 final int id = event.getPointerId(i); 281 final PointerState ps = mPointers.get(id); 282 ps.mVelocity.addMovement(event); 283 ps.mVelocity.computeCurrentVelocity(1); 284 final int N = event.getHistorySize(); 285 for (int j=0; j<N; j++) { 286 if (mPrintCoords) { 287 Log.i("Pointer", "Pointer " + (id+1) + ": (" 288 + event.getHistoricalX(i, j) 289 + ", " + event.getHistoricalY(i, j) + ")" 290 + " Prs=" + event.getHistoricalPressure(i, j) 291 + " Size=" + event.getHistoricalSize(i, j)); 292 } 293 ps.mXs.add(event.getHistoricalX(i, j)); 294 ps.mYs.add(event.getHistoricalY(i, j)); 295 } 296 if (mPrintCoords) { 297 Log.i("Pointer", "Pointer " + (id+1) + ": (" 298 + event.getX(i) + ", " + event.getY(i) + ")" 299 + " Prs=" + event.getPressure(i) 300 + " Size=" + event.getSize(i)); 301 } 302 ps.mXs.add(event.getX(i)); 303 ps.mYs.add(event.getY(i)); 304 ps.mCurX = (int)event.getX(i); 305 ps.mCurY = (int)event.getY(i); 306 //Log.i("Pointer", "Pointer #" + p + ": (" + ps.mCurX 307 // + "," + ps.mCurY + ")"); 308 ps.mCurPressure = event.getPressure(i); 309 ps.mCurSize = event.getSize(i); 310 ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3)); 311 } 312 313 if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) { 314 final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK) 315 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 316 final int id = event.getPointerId(index); 317 final PointerState ps = mPointers.get(id); 318 ps.mXs.add(Float.NaN); 319 ps.mYs.add(Float.NaN); 320 ps.mCurDown = false; 321 if (mPrintCoords) { 322 Log.i("Pointer", "Pointer " + (id+1) + ": UP"); 323 } 324 } 325 326 if (action == MotionEvent.ACTION_UP) { 327 for (int i=0; i<NI; i++) { 328 final int id = event.getPointerId(i); 329 final PointerState ps = mPointers.get(id); 330 if (ps.mCurDown) { 331 ps.mCurDown = false; 332 if (mPrintCoords) { 333 Log.i("Pointer", "Pointer " + (id+1) + ": UP"); 334 } 335 } 336 } 337 } 338 339 //if (mCurDown) { 340 // mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3, 341 // mCurX+mCurWidth+3, mCurY+mCurWidth+3); 342 //} 343 //invalidate(mRect); 344 postInvalidate(); 345 } 346 } 347 348 @Override 349 public boolean onTouchEvent(MotionEvent event) { 350 addTouchEvent(event); 351 return true; 352 } 353} 354