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 onCreate(Bundle savedInstanceState)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 onCreateOptionsMenu(Menu menu)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 onPrepareOptionsMenu(Menu menu)78 @Override public boolean onPrepareOptionsMenu(Menu menu) { 79 menu.findItem(FADE_ID).setChecked(mFading); 80 return super.onPrepareOptionsMenu(menu); 81 } 82 onOptionsItemSelected(MenuItem item)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 onResume()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 onSaveInstanceState(Bundle outState)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 onPause()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 */ startFading()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 */ stopFading()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 Bitmap mBitmap; 163 private Canvas mCanvas; 164 private final Rect mRect = new Rect(); 165 private final Paint mPaint; 166 private final Paint mFadePaint; 167 private boolean mCurDown; 168 private int mCurX; 169 private int mCurY; 170 private float mCurPressure; 171 private float mCurSize; 172 private int mCurWidth; 173 private int mFadeSteps = MAX_FADE_STEPS; 174 MyView(Context c)175 public MyView(Context c) { 176 super(c); 177 mPaint = new Paint(); 178 mPaint.setAntiAlias(true); 179 mPaint.setARGB(255, 255, 255, 255); 180 mFadePaint = new Paint(); 181 mFadePaint.setDither(true); 182 mFadePaint.setARGB(FADE_ALPHA, 0, 0, 0); 183 } 184 clear()185 public void clear() { 186 if (mCanvas != null) { 187 mPaint.setARGB(0xff, 0, 0, 0); 188 mCanvas.drawPaint(mPaint); 189 invalidate(); 190 mFadeSteps = MAX_FADE_STEPS; 191 } 192 } 193 fade()194 public void fade() { 195 if (mCanvas != null && mFadeSteps < MAX_FADE_STEPS) { 196 mCanvas.drawPaint(mFadePaint); 197 invalidate(); 198 mFadeSteps++; 199 } 200 } 201 onSizeChanged(int w, int h, int oldw, int oldh)202 @Override protected void onSizeChanged(int w, int h, int oldw, 203 int oldh) { 204 int curW = mBitmap != null ? mBitmap.getWidth() : 0; 205 int curH = mBitmap != null ? mBitmap.getHeight() : 0; 206 if (curW >= w && curH >= h) { 207 return; 208 } 209 210 if (curW < w) curW = w; 211 if (curH < h) curH = h; 212 213 Bitmap newBitmap = Bitmap.createBitmap(curW, curH, 214 Bitmap.Config.RGB_565); 215 Canvas newCanvas = new Canvas(); 216 newCanvas.setBitmap(newBitmap); 217 if (mBitmap != null) { 218 newCanvas.drawBitmap(mBitmap, 0, 0, null); 219 } 220 mBitmap = newBitmap; 221 mCanvas = newCanvas; 222 mFadeSteps = MAX_FADE_STEPS; 223 } 224 onDraw(Canvas canvas)225 @Override protected void onDraw(Canvas canvas) { 226 if (mBitmap != null) { 227 canvas.drawBitmap(mBitmap, 0, 0, null); 228 } 229 } 230 onTrackballEvent(MotionEvent event)231 @Override public boolean onTrackballEvent(MotionEvent event) { 232 boolean oldDown = mCurDown; 233 mCurDown = true; 234 int N = event.getHistorySize(); 235 int baseX = mCurX; 236 int baseY = mCurY; 237 final float scaleX = event.getXPrecision(); 238 final float scaleY = event.getYPrecision(); 239 for (int i=0; i<N; i++) { 240 //Log.i("TouchPaint", "Intermediate trackball #" + i 241 // + ": x=" + event.getHistoricalX(i) 242 // + ", y=" + event.getHistoricalY(i)); 243 drawPoint(baseX+event.getHistoricalX(i)*scaleX, 244 baseY+event.getHistoricalY(i)*scaleY, 245 event.getHistoricalPressure(i), 246 event.getHistoricalSize(i)); 247 } 248 //Log.i("TouchPaint", "Trackball: x=" + event.getX() 249 // + ", y=" + event.getY()); 250 drawPoint(baseX+event.getX()*scaleX, baseY+event.getY()*scaleY, 251 event.getPressure(), event.getSize()); 252 mCurDown = oldDown; 253 return true; 254 } 255 onTouchEvent(MotionEvent event)256 @Override public boolean onTouchEvent(MotionEvent event) { 257 int action = event.getAction(); 258 mCurDown = action == MotionEvent.ACTION_DOWN 259 || action == MotionEvent.ACTION_MOVE; 260 int N = event.getHistorySize(); 261 for (int i=0; i<N; i++) { 262 //Log.i("TouchPaint", "Intermediate pointer #" + i); 263 drawPoint(event.getHistoricalX(i), event.getHistoricalY(i), 264 event.getHistoricalPressure(i), 265 event.getHistoricalSize(i)); 266 } 267 drawPoint(event.getX(), event.getY(), event.getPressure(), 268 event.getSize()); 269 return true; 270 } 271 drawPoint(float x, float y, float pressure, float size)272 private void drawPoint(float x, float y, float pressure, float size) { 273 //Log.i("TouchPaint", "Drawing: " + x + "x" + y + " p=" 274 // + pressure + " s=" + size); 275 mCurX = (int)x; 276 mCurY = (int)y; 277 mCurPressure = pressure; 278 mCurSize = size; 279 mCurWidth = (int)(mCurSize*(getWidth()/3)); 280 if (mCurWidth < 1) mCurWidth = 1; 281 if (mCurDown && mBitmap != null) { 282 int pressureLevel = (int)(mCurPressure*255); 283 mPaint.setARGB(pressureLevel, 255, 255, 255); 284 mCanvas.drawCircle(mCurX, mCurY, mCurWidth, mPaint); 285 mRect.set(mCurX-mCurWidth-2, mCurY-mCurWidth-2, 286 mCurX+mCurWidth+2, mCurY+mCurWidth+2); 287 invalidate(mRect); 288 } 289 mFadeSteps = 0; 290 } 291 } 292 } 293