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 17 package com.android.photoeditor.actions; 18 19 import android.content.Context; 20 import android.graphics.Canvas; 21 import android.graphics.Paint; 22 import android.graphics.Path; 23 import android.graphics.RectF; 24 import android.util.AttributeSet; 25 import android.view.MotionEvent; 26 import android.view.View; 27 28 import java.util.Vector; 29 30 /** 31 * A view that track touch motions as paths and paint them as doodles. 32 */ 33 class DoodleView extends View { 34 35 /** 36 * Listener of doodle paths. 37 */ 38 public interface OnDoodleChangeListener { 39 40 void onLastPathChanged(Path path); 41 } 42 43 private final Vector<ColorPath> colorPaths = new Vector<ColorPath>(); 44 private final Paint paint = ColorPath.createPaint(); 45 46 private OnDoodleChangeListener listener; 47 private RectF clipBounds; 48 private float lastX; 49 private float lastY; 50 51 public DoodleView(Context context, AttributeSet attrs) { 52 super(context, attrs); 53 } 54 55 public void setOnDoodleChangeListener(OnDoodleChangeListener listener) { 56 this.listener = listener; 57 } 58 59 public void startPath(int color) { 60 // Remove last empty path before adding a new path. 61 if (!colorPaths.isEmpty() && colorPaths.lastElement().path().isEmpty()) { 62 colorPaths.remove(colorPaths.size() - 1); 63 } 64 colorPaths.add(new ColorPath(color, new Path())); 65 colorPaths.lastElement().path().moveTo(lastX, lastY); 66 } 67 68 /** 69 * Clears clip bounds and paths drawn. 70 */ 71 public void clear() { 72 colorPaths.clear(); 73 clipBounds = null; 74 } 75 76 /** 77 * Clips bounds for paths being drawn. 78 */ 79 public void clipBounds(RectF bounds) { 80 clipBounds = bounds; 81 } 82 83 private void pathUpdated(Path path) { 84 invalidate(); 85 if (listener != null) { 86 listener.onLastPathChanged(path); 87 } 88 } 89 90 @Override 91 public boolean onTouchEvent(MotionEvent event) { 92 super.onTouchEvent(event); 93 94 if (isEnabled() && !colorPaths.isEmpty()) { 95 Path path = colorPaths.lastElement().path(); 96 float x = event.getX(); 97 float y = event.getY(); 98 99 switch (event.getAction()) { 100 case MotionEvent.ACTION_DOWN: 101 path.moveTo(x, y); 102 pathUpdated(path); 103 lastX = x; 104 lastY = y; 105 break; 106 107 case MotionEvent.ACTION_MOVE: 108 path.quadTo(lastX, lastY, (lastX + x) / 2, (lastY + y) / 2); 109 pathUpdated(path); 110 lastX = x; 111 lastY = y; 112 break; 113 114 case MotionEvent.ACTION_CANCEL: 115 case MotionEvent.ACTION_UP: 116 // Line to last position with offset to draw at least dots for single clicks. 117 path.lineTo(lastX + 1, lastY + 1); 118 pathUpdated(path); 119 break; 120 } 121 } 122 return true; 123 } 124 125 @Override 126 protected void onDraw(Canvas canvas) { 127 super.onDraw(canvas); 128 129 canvas.save(); 130 if (clipBounds != null) { 131 canvas.clipRect(clipBounds); 132 } 133 for (ColorPath colorPath : colorPaths) { 134 colorPath.draw(canvas, paint); 135 } 136 canvas.restore(); 137 } 138 } 139