• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 
MyView(Context c)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 
clear()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 
fade()193         public void fade() {
194             if (mCanvas != null && mFadeSteps < MAX_FADE_STEPS) {
195                 mCanvas.drawPaint(mFadePaint);
196                 invalidate();
197                 mFadeSteps++;
198             }
199         }
200 
onSizeChanged(int w, int h, int oldw, int oldh)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 
onDraw(Canvas canvas)224         @Override protected void onDraw(Canvas canvas) {
225             if (mBitmap != null) {
226                 canvas.drawBitmap(mBitmap, 0, 0, null);
227             }
228         }
229 
onTrackballEvent(MotionEvent event)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 
onTouchEvent(MotionEvent event)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 
drawPoint(float x, float y, float pressure, float width)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