1 /* 2 * Copyright (C) 2007 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 package com.example.android.apis.graphics; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.graphics.Bitmap; 22 import android.graphics.Canvas; 23 import android.graphics.Paint; 24 import android.graphics.Rect; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.view.Menu; 29 import android.view.MenuItem; 30 import android.view.MotionEvent; 31 import android.view.View; 32 33 //Need the following import to get access to the app resources, since this 34 //class is in a sub-package. 35 36 37 /** 38 * Demonstrates the handling of touch screen and trackball events to 39 * implement a simple painting app. 40 */ 41 public class TouchPaint extends GraphicsActivity { 42 /** Used as a pulse to gradually fade the contents of the window. */ 43 private static final int FADE_MSG = 1; 44 45 /** Menu ID for the command to clear the window. */ 46 private static final int CLEAR_ID = Menu.FIRST; 47 /** Menu ID for the command to toggle fading. */ 48 private static final int FADE_ID = Menu.FIRST+1; 49 50 /** How often to fade the contents of the window (in ms). */ 51 private static final int FADE_DELAY = 100; 52 53 /** The view responsible for drawing the window. */ 54 MyView mView; 55 /** Is fading mode enabled? */ 56 boolean mFading; 57 58 @Override protected void onCreate(Bundle savedInstanceState) { 59 super.onCreate(savedInstanceState); 60 61 // Create and attach the view that is responsible for painting. 62 mView = new MyView(this); 63 setContentView(mView); 64 mView.requestFocus(); 65 66 // Restore the fading option if we are being thawed from a 67 // previously saved state. Note that we are not currently remembering 68 // the contents of the bitmap. 69 mFading = savedInstanceState != null ? savedInstanceState.getBoolean("fading", true) : true; 70 } 71 72 @Override public boolean onCreateOptionsMenu(Menu menu) { 73 menu.add(0, CLEAR_ID, 0, "Clear"); 74 menu.add(0, FADE_ID, 0, "Fade").setCheckable(true); 75 return super.onCreateOptionsMenu(menu); 76 } 77 78 @Override public boolean onPrepareOptionsMenu(Menu menu) { 79 menu.findItem(FADE_ID).setChecked(mFading); 80 return super.onPrepareOptionsMenu(menu); 81 } 82 83 @Override public boolean onOptionsItemSelected(MenuItem item) { 84 switch (item.getItemId()) { 85 case CLEAR_ID: 86 mView.clear(); 87 return true; 88 case FADE_ID: 89 mFading = !mFading; 90 if (mFading) { 91 startFading(); 92 } else { 93 stopFading(); 94 } 95 return true; 96 default: 97 return super.onOptionsItemSelected(item); 98 } 99 } 100 101 @Override protected void onResume() { 102 super.onResume(); 103 // If fading mode is enabled, then as long as we are resumed we want 104 // to run pulse to fade the contents. 105 if (mFading) { 106 startFading(); 107 } 108 } 109 110 @Override protected void onSaveInstanceState(Bundle outState) { 111 super.onSaveInstanceState(outState); 112 // Save away the fading state to restore if needed later. Note that 113 // we do not currently save the contents of the display. 114 outState.putBoolean("fading", mFading); 115 } 116 117 @Override protected void onPause() { 118 super.onPause(); 119 // Make sure to never run the fading pulse while we are paused or 120 // stopped. 121 stopFading(); 122 } 123 124 /** 125 * Start up the pulse to fade the screen, clearing any existing pulse to 126 * ensure that we don't have multiple pulses running at a time. 127 */ 128 void startFading() { 129 mHandler.removeMessages(FADE_MSG); 130 mHandler.sendMessageDelayed( 131 mHandler.obtainMessage(FADE_MSG), FADE_DELAY); 132 } 133 134 /** 135 * Stop the pulse to fade the screen. 136 */ 137 void stopFading() { 138 mHandler.removeMessages(FADE_MSG); 139 } 140 141 private Handler mHandler = new Handler() { 142 @Override public void handleMessage(Message msg) { 143 switch (msg.what) { 144 // Upon receiving the fade pulse, we have the view perform a 145 // fade and then enqueue a new message to pulse at the desired 146 // next time. 147 case FADE_MSG: { 148 mView.fade(); 149 mHandler.sendMessageDelayed( 150 mHandler.obtainMessage(FADE_MSG), FADE_DELAY); 151 break; 152 } 153 default: 154 super.handleMessage(msg); 155 } 156 } 157 }; 158 159 public class MyView extends View { 160 private static final int FADE_ALPHA = 0x06; 161 private static final int MAX_FADE_STEPS = 256/FADE_ALPHA + 4; 162 private static final int TRACKBALL_SCALE = 10; 163 164 private Bitmap mBitmap; 165 private Canvas mCanvas; 166 private final Rect mRect = new Rect(); 167 private final Paint mPaint; 168 private final Paint mFadePaint; 169 private float mCurX; 170 private float mCurY; 171 private int mFadeSteps = MAX_FADE_STEPS; 172 173 public MyView(Context c) { 174 super(c); 175 setFocusable(true); 176 mPaint = new Paint(); 177 mPaint.setAntiAlias(true); 178 mPaint.setARGB(255, 255, 255, 255); 179 mFadePaint = new Paint(); 180 mFadePaint.setDither(true); 181 mFadePaint.setARGB(FADE_ALPHA, 0, 0, 0); 182 } 183 184 public void clear() { 185 if (mCanvas != null) { 186 mPaint.setARGB(0xff, 0, 0, 0); 187 mCanvas.drawPaint(mPaint); 188 invalidate(); 189 mFadeSteps = MAX_FADE_STEPS; 190 } 191 } 192 193 public void fade() { 194 if (mCanvas != null && mFadeSteps < MAX_FADE_STEPS) { 195 mCanvas.drawPaint(mFadePaint); 196 invalidate(); 197 mFadeSteps++; 198 } 199 } 200 201 @Override protected void onSizeChanged(int w, int h, int oldw, 202 int oldh) { 203 int curW = mBitmap != null ? mBitmap.getWidth() : 0; 204 int curH = mBitmap != null ? mBitmap.getHeight() : 0; 205 if (curW >= w && curH >= h) { 206 return; 207 } 208 209 if (curW < w) curW = w; 210 if (curH < h) curH = h; 211 212 Bitmap newBitmap = Bitmap.createBitmap(curW, curH, 213 Bitmap.Config.RGB_565); 214 Canvas newCanvas = new Canvas(); 215 newCanvas.setBitmap(newBitmap); 216 if (mBitmap != null) { 217 newCanvas.drawBitmap(mBitmap, 0, 0, null); 218 } 219 mBitmap = newBitmap; 220 mCanvas = newCanvas; 221 mFadeSteps = MAX_FADE_STEPS; 222 } 223 224 @Override protected void onDraw(Canvas canvas) { 225 if (mBitmap != null) { 226 canvas.drawBitmap(mBitmap, 0, 0, null); 227 } 228 } 229 230 @Override public boolean onTrackballEvent(MotionEvent event) { 231 int N = event.getHistorySize(); 232 final float scaleX = event.getXPrecision() * TRACKBALL_SCALE; 233 final float scaleY = event.getYPrecision() * TRACKBALL_SCALE; 234 for (int i=0; i<N; i++) { 235 //Log.i("TouchPaint", "Intermediate trackball #" + i 236 // + ": x=" + event.getHistoricalX(i) 237 // + ", y=" + event.getHistoricalY(i)); 238 mCurX += event.getHistoricalX(i) * scaleX; 239 mCurY += event.getHistoricalY(i) * scaleY; 240 drawPoint(mCurX, mCurY, 1.0f, 16.0f); 241 } 242 //Log.i("TouchPaint", "Trackball: x=" + event.getX() 243 // + ", y=" + event.getY()); 244 mCurX += event.getX() * scaleX; 245 mCurY += event.getY() * scaleY; 246 drawPoint(mCurX, mCurY, 1.0f, 16.0f); 247 return true; 248 } 249 250 @Override public boolean onTouchEvent(MotionEvent event) { 251 int action = event.getActionMasked(); 252 if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL) { 253 int N = event.getHistorySize(); 254 int P = event.getPointerCount(); 255 for (int i = 0; i < N; i++) { 256 for (int j = 0; j < P; j++) { 257 mCurX = event.getHistoricalX(j, i); 258 mCurY = event.getHistoricalY(j, i); 259 drawPoint(mCurX, mCurY, 260 event.getHistoricalPressure(j, i), 261 event.getHistoricalTouchMajor(j, i)); 262 } 263 } 264 for (int j = 0; j < P; j++) { 265 mCurX = event.getX(j); 266 mCurY = event.getY(j); 267 drawPoint(mCurX, mCurY, event.getPressure(j), event.getTouchMajor(j)); 268 } 269 } 270 return true; 271 } 272 273 private void drawPoint(float x, float y, float pressure, float width) { 274 //Log.i("TouchPaint", "Drawing: " + x + "x" + y + " p=" 275 // + pressure + " width=" + width); 276 if (width < 1) width = 1; 277 if (mBitmap != null) { 278 float radius = width / 2; 279 int pressureLevel = (int)(pressure * 255); 280 mPaint.setARGB(pressureLevel, 255, 255, 255); 281 mCanvas.drawCircle(x, y, radius, mPaint); 282 mRect.set((int) (x - radius - 2), (int) (y - radius - 2), 283 (int) (x + radius + 2), (int) (y + radius + 2)); 284 invalidate(mRect); 285 } 286 mFadeSteps = 0; 287 } 288 } 289 } 290