I found this code on stack and it works well. However, there is an issue. While I'm able to set its background color, the color changes to black as soon as the clearSignature() function is called.
Why is that happening?
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* A simple view to capture a path traced onto the screen. Initially intended to
* be used to captures signatures.
*
* #author Andrew Crichton
* #version 0.1
*/
public class SignatureView extends View
{
private Path mPath;
private Paint mPaint;
private Paint bgPaint = new Paint(Color.TRANSPARENT);
private Bitmap mBitmap;
private Canvas mCanvas;
private float curX, curY;
private static final int TOUCH_TOLERANCE = 4;
private static final int STROKE_WIDTH = 4;
boolean modified = false;
public SignatureView(Context context)
{
super(context);
init();
}
public SignatureView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public SignatureView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
}
private void init()
{
setFocusable(true);
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(STROKE_WIDTH);
}
public void setSigColor(int color)
{
mPaint.setColor(color);
}
public void setSigColor(int a, int red, int green, int blue)
{
mPaint.setARGB(a, red, green, blue);
}
public boolean clearSignature()
{
if (mBitmap != null)
createFakeMotionEvents();
if (mCanvas != null)
{
mCanvas.drawColor(Color.BLACK);
mCanvas.drawPaint(bgPaint);
mPath.reset();
invalidate();
}
else
{
return false;
}
return true;
}
public Bitmap getImage()
{
return this.mBitmap;
}
public void setImage(Bitmap bitmap)
{
this.mBitmap = bitmap;
this.invalidate();
}
public boolean hasChanged()
{
return modified;
}
#Override protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
int bitmapWidth = mBitmap != null ? mBitmap.getWidth() : 0;
int bitmapHeight = mBitmap != null ? mBitmap.getWidth() : 0;
if (bitmapWidth >= width && bitmapHeight >= height)
return;
if (bitmapWidth < width)
bitmapWidth = width;
if (bitmapHeight < height)
bitmapHeight = height;
Bitmap newBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
if (mBitmap != null)
newCanvas.drawBitmap(mBitmap, 0, 0, null);
mBitmap = newBitmap;
mCanvas = newCanvas;
}
private void createFakeMotionEvents()
{
MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_DOWN,
1f, 1f, 0);
MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_UP, 1f,
1f, 0);
onTouchEvent(downEvent);
onTouchEvent(upEvent);
}
#Override protected void onDraw(Canvas canvas)
{
modified = true;
canvas.drawColor(Color.RED);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.drawPath(mPath, mPaint);
}
#Override public boolean onTouchEvent(MotionEvent event)
{
float x = event.getX();
float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
touchDown(x, y);
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
break;
case MotionEvent.ACTION_UP:
touchUp();
break;
}
invalidate();
return true;
}
/**
* ---------------------------------------------------------- Private
* methods ---------------------------------------------------------
*/
private void touchDown(float x, float y)
{
mPath.reset();
mPath.moveTo(x, y);
curX = x;
curY = y;
}
private void touchMove(float x, float y)
{
float dx = Math.abs(x - curX);
float dy = Math.abs(y - curY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
mPath.quadTo(curX, curY, (x + curX) / 2, (y + curY) / 2);
curX = x;
curY = y;
}
}
private void touchUp()
{
mPath.lineTo(curX, curY);
if (mCanvas == null)
{
mCanvas = new Canvas();
mCanvas.setBitmap(mBitmap);
}
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
}
Well I have updated the original SignatureView code, now it supports a custom signature background color. This color is different from the view's background color!
setSigBackgroundColor()
I also made some other optimizations, use on your own risk as this is minimal tested!
Small list of optimizations:
Better bitmap recycling etc.
Recycling of MotionEvents
Added signature background color set method
Optimizations
Changed setImage method, although still not very safe to use!
New code:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* A simple view to capture a path traced onto the screen. Initially intended to
* be used to captures signatures.
*
* #author Andrew Crichton
* #version 0.1.1
*
* Modified by Rolf Smit
* -Recycle bitmaps
* -Recycle MotionEvents
* -Signature Background color changes
* -Optimizations
* -Changed setImage method, although still unsafe to use!
*/
public class SignatureView extends View {
private Path mPath;
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private int sigBackgroundColor = Color.TRANSPARENT;
private float curX, curY;
private static final int TOUCH_TOLERANCE = 4;
private static final int STROKE_WIDTH = 4;
boolean modified = false;
public SignatureView(Context context) {
super(context);
init();
}
public SignatureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SignatureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setFocusable(true);
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(STROKE_WIDTH);
}
public void setSigColor(int color) {
mPaint.setColor(color);
}
public void setSigColor(int alpha, int red, int green, int blue) {
mPaint.setARGB(alpha, red, green, blue);
}
public void setSigBackgroundColor(int color){
sigBackgroundColor = color;
}
public void setSigBackgroundColor(int alpha, int red, int green, int blue){
sigBackgroundColor = Color.argb(alpha, red, green, blue);
}
public boolean clearSignature() {
if (mBitmap != null) {
createFakeMotionEvents();
}
if (mCanvas != null) {
mCanvas.drawColor(sigBackgroundColor);
mPath.reset();
invalidate();
} else {
return false;
}
return true;
}
public Bitmap getImage() {
return Bitmap.createBitmap(mBitmap);
}
public void setImage(Bitmap bitmap){
this.mBitmap = bitmap;
if(mCanvas != null){
mCanvas.setBitmap(mBitmap);
}
}
public boolean hasChanged() {
return modified;
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
int bitmapWidth = mBitmap != null ? mBitmap.getWidth() : 0;
int bitmapHeight = mBitmap != null ? mBitmap.getWidth() : 0;
if (bitmapWidth >= width && bitmapHeight >= height) {
return;
}
if (bitmapWidth < width) {
bitmapWidth = width;
}
if (bitmapHeight < height) {
bitmapHeight = height;
}
Bitmap newBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
mCanvas = newCanvas;
if (mBitmap != null) {
newCanvas.drawBitmap(mBitmap, 0, 0, null);
mBitmap.recycle();
} else {
newCanvas.drawColor(sigBackgroundColor);
}
mBitmap = newBitmap;
}
private void createFakeMotionEvents() {
MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_DOWN, 1f, 1f, 0);
MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_UP, 1f, 1f, 0);
onTouchEvent(downEvent);
onTouchEvent(upEvent);
downEvent.recycle();
upEvent.recycle();
}
#Override
protected void onDraw(Canvas canvas) {
modified = true;
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.drawPath(mPath, mPaint);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDown(x, y);
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
break;
case MotionEvent.ACTION_UP:
touchUp();
break;
}
invalidate();
return true;
}
private void touchDown(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
curX = x;
curY = y;
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - curX);
float dy = Math.abs(y - curY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(curX, curY, (x + curX) / 2, (y + curY) / 2);
curX = x;
curY = y;
}
}
private void touchUp() {
mPath.lineTo(curX, curY);
if (mCanvas == null) {
mCanvas = new Canvas();
mCanvas.setBitmap(mBitmap);
}
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
}
Related
I wanted to draw on the canvas with the code below with my custom brush, but as you can see in the picture, the background of my brush is black, albeit without color.
Although I specified the brush color as Color.TRANSPARENT or Color.parseColor ("# 00000000"), the brush background still turns black.
How can I make the background color of my brush transparent?
click to see the picture
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import androidx.annotation.ColorInt;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import java.util.Stack;
public class BrushDrawingView extends View {
static final float DEFAULT_BRUSH_SIZE = 50.0f;
static final float DEFAULT_ERASER_SIZE = 50.0f;
static final int DEFAULT_OPACITY = 255;
private float mBrushSize = DEFAULT_BRUSH_SIZE;
private float mBrushEraserSize = DEFAULT_ERASER_SIZE;
private int mOpacity = DEFAULT_OPACITY;
private final Stack<BrushLinePath> mDrawnPaths = new Stack<>();
private final Stack<BrushLinePath> mRedoPaths = new Stack<>();
private final Paint mDrawPaint = new Paint();
private Canvas mDrawCanvas;
private boolean mBrushDrawMode;
private Bitmap brushBitmap;
private Path mPath;
private float mTouchX, mTouchY;
private static final float TOUCH_TOLERANCE = 4;
private BrushViewChangeListener mBrushViewChangeListener;
public BrushDrawingView(Context context) {
this(context, null);
}
public BrushDrawingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BrushDrawingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setupBrushDrawing();
}
private void setupBrushDrawing() {
//Caution: This line is to disable hardware acceleration to make eraser feature work properly
setupPathAndPaint();
setVisibility(View.GONE);
}
private void setupPathAndPaint() {
mPath = new Path();
mDrawPaint.setAntiAlias(true);
mDrawPaint.setStyle(Paint.Style.STROKE);
mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
mDrawPaint.setStrokeWidth(mBrushSize);
mDrawPaint.setAlpha(mOpacity);
}
private void refreshBrushDrawing() {
mBrushDrawMode = true;
setupPathAndPaint();
}
void brushEraser() {
mBrushDrawMode = true;
mDrawPaint.setStrokeWidth(mBrushEraserSize);
mDrawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
public void setBrushDrawingMode(boolean brushDrawMode) {
this.mBrushDrawMode = brushDrawMode;
if (brushDrawMode) {
this.setVisibility(View.VISIBLE);
refreshBrushDrawing();
}
}
public Bitmap getBrushBitmap() {
return brushBitmap;
}
public void setBrushBitmap(Bitmap brushBitmap) {
this.brushBitmap = brushBitmap;
}
public void setOpacity(#IntRange(from = 0, to = 255) int opacity) {
this.mOpacity = (int) (opacity * 2.55f);
setBrushDrawingMode(true);
}
public int getOpacity() {
return mOpacity;
}
boolean getBrushDrawingMode() {
return mBrushDrawMode;
}
public void setBrushSize(float size) {
mBrushSize = 5 + (int) (size);
setBrushDrawingMode(true);
}
void setBrushColor(#ColorInt int color) {
mDrawPaint.setColor(color);
setBrushDrawingMode(true);
}
void setBrushEraserSize(float brushEraserSize) {
this.mBrushEraserSize = brushEraserSize;
setBrushDrawingMode(true);
}
void setBrushEraserColor(#ColorInt int color) {
mDrawPaint.setColor(color);
setBrushDrawingMode(true);
}
float getEraserSize() {
return mBrushEraserSize;
}
public float getBrushSize() {
return mBrushSize;
}
int getBrushColor() {
return mDrawPaint.getColor();
}
public void clearAll() {
mDrawnPaths.clear();
mRedoPaths.clear();
if (mDrawCanvas != null) {
mDrawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
invalidate();
}
void setBrushViewChangeListener(BrushViewChangeListener brushViewChangeListener) {
mBrushViewChangeListener = brushViewChangeListener;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Bitmap canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mDrawCanvas = new Canvas(canvasBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
for (BrushLinePath linePath : mDrawnPaths) {
canvas.drawPath(linePath.getDrawPath(), linePath.getDrawPaint());
}
canvas.drawPath(mPath, mDrawPaint);
/////
final Bitmap scaledBitmap = getScaledBitmap();
final float centerX = scaledBitmap.getWidth() / 2;
final float centerY = scaledBitmap.getHeight() / 2;
final PathMeasure pathMeasure = new PathMeasure(mPath, false);
float distance = scaledBitmap.getWidth() / 2;
float[] position = new float[2];
float[] slope = new float[2];
float slopeDegree;
while (distance < pathMeasure.getLength())
{
pathMeasure.getPosTan(distance, position, slope);
slopeDegree = (float)((Math.atan2(slope[1], slope[0]) * 180f) / Math.PI);
canvas.save();
canvas.translate(position[0] - centerX, position[1] - centerY);
canvas.rotate(slopeDegree, centerX, centerY);
canvas.drawBitmap(scaledBitmap, 0, 0, mDrawPaint);
canvas.restore();
distance += scaledBitmap.getWidth() + 10;
}
}
/////
private Bitmap getScaledBitmap()
{
// width / height of the bitmap[
float width = brushBitmap.getWidth();
float height = brushBitmap.getHeight();
// ratio of the bitmap
float ratio = width / height;
// set the height of the bitmap to the width of the path (from the paint object).
float scaledHeight = mDrawPaint.getStrokeWidth();
// to maintain aspect ratio of the bitmap, use the height * ratio for the width.
float scaledWidth = scaledHeight * ratio;
// return the generated bitmap, scaled to the correct size.
return Bitmap.createScaledBitmap(brushBitmap, (int)scaledWidth, (int)scaledHeight, true);
}
/**
* Handle touch event to draw paint on canvas i.e brush drawing
*
* #param event points having touch info
* #return true if handling touch events
*/
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
if (mBrushDrawMode) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStart(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
touchMove(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
touchUp();
break;
}
invalidate();
return true;
} else {
return false;
}
}
boolean undo() {
if (!mDrawnPaths.empty()) {
mRedoPaths.push(mDrawnPaths.pop());
invalidate();
}
if (mBrushViewChangeListener != null) {
mBrushViewChangeListener.onViewRemoved(this);
}
return !mDrawnPaths.empty();
}
boolean redo() {
if (!mRedoPaths.empty()) {
mDrawnPaths.push(mRedoPaths.pop());
invalidate();
}
if (mBrushViewChangeListener != null) {
mBrushViewChangeListener.onViewAdd(this);
}
return !mRedoPaths.empty();
}
private void touchStart(float x, float y) {
mRedoPaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mTouchX = x;
mTouchY = y;
if (mBrushViewChangeListener != null) {
mBrushViewChangeListener.onStartDrawing();
}
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - mTouchX);
float dy = Math.abs(y - mTouchY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mTouchX, mTouchY, (x + mTouchX) / 2, (y + mTouchY) / 2);
mTouchX = x;
mTouchY = y;
}
}
private void touchUp() {
mPath.lineTo(mTouchX, mTouchY);
// Commit the path to our offscreen
mDrawCanvas.drawPath(mPath, mDrawPaint);
// kill this so we don't double draw
mDrawnPaths.push(new BrushLinePath(mPath, mDrawPaint));
/////
final Bitmap scaledBitmap = getScaledBitmap();
final float centerX = scaledBitmap.getWidth() / 2;
final float centerY = scaledBitmap.getHeight() / 2;
final PathMeasure pathMeasure = new PathMeasure(mPath, false);
float distance = scaledBitmap.getWidth() / 2;
float[] position = new float[2];
float[] slope = new float[2];
float slopeDegree;
while (distance < pathMeasure.getLength())
{
pathMeasure.getPosTan(distance, position, slope);
slopeDegree = (float)((Math.atan2(slope[1], slope[0]) * 180f) / Math.PI);
mDrawCanvas.save();
mDrawCanvas.translate(position[0] - centerX, position[1] - centerY);
mDrawCanvas.rotate(slopeDegree, centerX, centerY);
mDrawCanvas.drawBitmap(scaledBitmap, 0, 0, mDrawPaint);
mDrawCanvas.restore();
distance += scaledBitmap.getWidth() + 10;
}
/////
mPath = new Path();
if (mBrushViewChangeListener != null) {
mBrushViewChangeListener.onStopDrawing();
mBrushViewChangeListener.onViewAdd(this);
}
}
#VisibleForTesting
Paint getDrawingPaint() {
return mDrawPaint;
}
#VisibleForTesting
Pair<Stack<BrushLinePath>, Stack<BrushLinePath>> getDrawingPath() {
return new Pair<>(mDrawnPaths, mRedoPaths);
}
}
public interface BrushViewChangeListener {
void onViewAdd(BrushDrawingView brushDrawingView);
void onViewRemoved(BrushDrawingView brushDrawingView);
void onStartDrawing();
void onStopDrawing();
}
class BrushLinePath {
private final Paint mDrawPaint;
private final Path mDrawPath;
BrushLinePath(final Path drawPath, final Paint drawPaints) {
mDrawPaint = new Paint(drawPaints);
mDrawPath = new Path(drawPath);
}
Paint getDrawPaint() {
return mDrawPaint;
}
Path getDrawPath() {
return mDrawPath;
}
}
The reason it happens is because Paint doesn't have an alpha composing mode set by default. Thus, when you're trying to paint a bitmap over your canvas it will replace the destination pixels with your brush pixels, which in your case is #00000000. And that will result in pixel being displayed as black. Have a look into this documentation: https://developer.android.com/reference/android/graphics/PorterDuff.Mode
By the first glance it seems you're looking for PorterDuff.Mode.SRC_OVER or PorterDuff.Mode.SRC_ATOP - this way transparent pixels from your source image (your brush) will not over-draw the pixels from your destination (canvas). In case your background is always non-transparent, you will see no difference between SRC_OVER and SRC_ATOP, but if it isn't - choose the one which fits your needs. Then you can modify setupPathAndPaint method by adding this line to its end:
mDrawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
I want to create android application that contain some points and user can connect the points with drawing line between them (similar to android lock pattern but there is points in custom position and user can add line after drawing a pattern).
I don't know where should I start to search about and what to search for this requirement.
How should I design and code for this?
any suggestion would be appreciated.
I finally find the solution that use a custom view. In a similar case please use the code below:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class ConnectDotsView extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private Paint mCirclePaint;
private Point startPoint;
private List<Point> selectPoints;
private static final int TOUCH_TOLERANCE_DP = 30;
private static final int BACKGROUND = 0xFFFFFF;
private List<Point> mPoints = new ArrayList<Point>();
private int mTouchTolerance;
public ConnectDotsView(Context context) {
super(context);
mCanvas = new Canvas();
mPath = new Path();
selectPoints = new ArrayList<Point>();
initPaint();
}
public ConnectDotsView(Context context, AttributeSet attrs) {
super(context, attrs);
mCanvas = new Canvas();
mPath = new Path();
selectPoints = new ArrayList<Point>();
initPaint();
}
public ConnectDotsView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mCanvas = new Canvas();
mPath = new Path();
selectPoints = new ArrayList<Point>();
initPaint();
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
clear();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(BACKGROUND);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawPath(mPath, mPaint);
//Draw Points
for (Point point : mPoints) {
canvas.drawCircle(point.x, point.y, 16, mCirclePaint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up(x, y);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
touch_up(x, y);
invalidate();
break;
}
return true;
}
private void touch_start(float x, float y) {
startPoint = getFeasiblePoint(x, y);
}
private void touch_move(float x, float y) {
clear();
Point p = getFeasiblePoint(x, y);
if (startPoint == null) {
startPoint = p;
} else if (p != null) {
if (p != startPoint) {
mPath.moveTo(startPoint.x, startPoint.y);
mPath.lineTo(p.x, p.y);
selectPoints.add(startPoint);
selectPoints.add(p);
startPoint = p;
}
} else {
mCanvas.drawLine(startPoint.x, startPoint.y, x, y, mPaint);
}
}
private void touch_up(float x, float y) {
clear();
Point p = getFeasiblePoint(x, y);
if (startPoint == null) {
startPoint = p;
} else if (p != null) {
if (p != startPoint) {
mPath.moveTo(startPoint.x, startPoint.y);
mPath.lineTo(p.x, p.y);
selectPoints.add(startPoint);
selectPoints.add(p);
startPoint = p;
}
}
}
public void setPaint(Paint paint) {
this.mPaint = paint;
}
public Bitmap getBitmap() {
return mBitmap;
}
private void clear() {
mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(BACKGROUND);
mCanvas.setBitmap(mBitmap);
invalidate();
}
private Point getFeasiblePoint(float x, float y) {
for (Point point : mPoints) {
if (x > (point.x - mTouchTolerance) && x < (point.x + mTouchTolerance)) {
if (y > (point.y - mTouchTolerance) && y < (point.y + mTouchTolerance)) {
return point;
}
}
}
return null;
}
public List<Point> getPoints() {
return mPoints;
}
public void setPoints(List<Point> points) {
this.mPoints = points;
}
public List<Point> getSelectPoints() {
return selectPoints;
}
public void Reset() {
selectPoints = new ArrayList<Point>();
mPath.reset();
clear();
}
private void initPaint() {
//Paint
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
//CirclePaint
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setDither(true);
mCirclePaint.setColor(Color.RED);
mCirclePaint.setStyle(Paint.Style.FILL);
mCirclePaint.setStrokeJoin(Paint.Join.ROUND);
mCirclePaint.setStrokeCap(Paint.Cap.ROUND);
mCirclePaint.setStrokeWidth(12);
//Others
mTouchTolerance = Utility.dp2px(getContext(), TOUCH_TOLERANCE_DP);
}
}
this library will helps you, created app with this
https://github.com/Pi-Developers/Pi-Locker
more reference : https://github.com/n3tr/DrawPoints
I'm trying to implement a simple "scratch card". The java code is taken by an android demo about painting (WHERE to find Android "Fingerpaint" demo? (Android Studio era)) and what i want is simply to show the image below the surfaceview.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/android"
android:scaleType="fitXY"
/>
<com.example.jammtup.ErasableView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/></RelativeLayout>
the java code :
public class ErasableView extends SurfaceView {
private Bitmap mBitmap;
private Canvas mCanvas;
private Paint mPaint;
private Path mPath;
private Paint mBitmapPaint;
public ErasableView(Context context) {
super(context);
mPaint = new Paint();
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
public void init() {
setFocusableInTouchMode(true);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.TRANSPARENT);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(64);
mPaint.setColor(0xFFFF0000);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
} }
what i get is a black view not erasable. That does not allow to show the image below.
i need your help !!
Try to use this instead of your view If you want the whole project just check out my Github repository https://github.com/amarvadla/ScratchCoupon
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.support.v4.content.ContextCompat;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.widget.TextView;
public class ScratchTextView extends TextView {
Bitmap scratchBitmap;
public interface IRevealListener {
public void onRevealed(ScratchTextView tv);
public void onRevealPercentChangedListener(ScratchTextView stv, float percent);
}
public static final float STROKE_WIDTH = 12f;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
/**
* Bitmap holding the scratch region.
*/
private Bitmap mScratchBitmap;
/**
* Drawable canvas area through which the scratchable area is drawn.
*/
public static Canvas mCanvas;
/**
* Path holding the erasing path done by the user.
*/
private Path mErasePath;
/**
* Path to indicate where the user have touched.
*/
private Path mTouchPath;
/**
* Paint properties for drawing the scratch area.
*/
private Paint mBitmapPaint;
/**
* Paint properties for erasing the scratch region.
*/
private Paint mErasePaint;
/**
* Gradient paint properties that lies as a background for scratch region.
*/
private Paint mGradientBgPaint;
/**
* Sample Drawable bitmap having the scratch pattern.
*/
private BitmapDrawable mDrawable;
/**
* Listener object callback reference to send back the callback when the text has been revealed.
*/
private IRevealListener mRevealListener;
/**
* Reveal percent value.
*/
private float mRevealPercent;
/**
* Thread Count
*/
private int mThreadCount = 0;
public ScratchTextView(Context context) {
super(context);
init();
}
public ScratchTextView(Context context, AttributeSet set) {
super(context, set);
init();
}
public ScratchTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* Set the strokes width based on the parameter multiplier.
* #param multiplier can be 1,2,3 and so on to set the stroke width of the paint.
*/
public void setStrokeWidth(int multiplier) {
mErasePaint.setStrokeWidth(multiplier * STROKE_WIDTH);
}
/**
* Initialises the paint drawing elements.
*/
private void init() {
mTouchPath = new Path();
mErasePaint = new Paint();
mErasePaint.setAntiAlias(true);
mErasePaint.setDither(true);
mErasePaint.setColor(0xFFFF0000);
mErasePaint.setStyle(Paint.Style.STROKE);
mErasePaint.setStrokeJoin(Paint.Join.BEVEL);
mErasePaint.setStrokeCap(Paint.Cap.ROUND);
mErasePaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
setStrokeWidth(6);
mGradientBgPaint = new Paint();
mErasePath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
scratchBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.spiderman);
mDrawable = new BitmapDrawable(getResources(), scratchBitmap);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mScratchBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mScratchBitmap);
Rect rect = new Rect(0, 0, mScratchBitmap.getWidth(), mScratchBitmap.getHeight());
mDrawable.setBounds(rect);
int startGradientColor = ContextCompat.getColor(getContext(), R.color.colorPrimary);
int endGradientColor = ContextCompat.getColor(getContext(), R.color.colorAccent);
mCanvas.drawRect(rect, mGradientBgPaint);
mDrawable.draw(mCanvas);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mScratchBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mErasePath, mErasePaint);
}
private void touch_start(float x, float y) {
mErasePath.reset();
mErasePath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mErasePath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
drawPath();
}
mTouchPath.reset();
mTouchPath.addCircle(mX, mY, 30, Path.Direction.CW);
}
private void drawPath() {
mErasePath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mErasePath, mErasePaint);
// kill this so we don't double draw
mTouchPath.reset();
mErasePath.reset();
mErasePath.moveTo(mX, mY);
checkRevealed();
}
/**
* Reveals the hidden text by erasing the scratch area.
*/
public void reveal() {
int[] bounds = getTextBounds(1.5f);
int left = bounds[0];
int top = bounds[1];
int right = bounds[2];
int bottom = bounds[3];
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
mCanvas.drawRect(left, top, right, bottom, paint);
checkRevealed();
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
drawPath();
invalidate();
break;
default:
break;
}
return true;
}
public int getColor() {
return mErasePaint.getColor();
}
public void setRevealListener(IRevealListener listener) {
this.mRevealListener = listener;
}
public boolean isRevealed() {
return mRevealPercent == 1;
}
private void checkRevealed() {
if(! isRevealed() && mRevealListener != null) {
int[] bounds = getTextBounds();
int left = bounds[0];
int top = bounds[1];
int width = bounds[2] - left;
int height = bounds[3] - top;
// Do not create multiple calls to compare.
if(mThreadCount > 1) {
return;
}
mThreadCount++;
new AsyncTask<Integer, Void, Float>() {
#Override
protected Float doInBackground(Integer... params) {
try {
int left = params[0];
int top = params[1];
int width = params[2];
int height = params[3];
Bitmap croppedBitmap = Bitmap.createBitmap(mScratchBitmap, left, top, width, height);
return BitmapUtils.getTransparentPixelPercent(croppedBitmap);
} finally {
mThreadCount--;
}
}
public void onPostExecute(Float percentRevealed) {
// check if not revealed before.
if( ! isRevealed()) {
float oldValue = mRevealPercent;
mRevealPercent = percentRevealed;
if(oldValue != percentRevealed) {
mRevealListener.onRevealPercentChangedListener(ScratchTextView.this, percentRevealed);
}
// if now revealed.
if( isRevealed()) {
mRevealListener.onRevealed(ScratchTextView.this);
}
}
}
}.execute(left, top, width, height);
}
}
private static int[] getTextDimens(String text, Paint paint) {
int end = text.length();
Rect bounds = new Rect();
paint.getTextBounds(text, 0, end, bounds);
int width = bounds.left + bounds.width();
int height = bounds.bottom + bounds.height();
return new int[] { width, height};
}
private int[] getTextBounds() {
return getTextBounds(1f);
}
private int[] getTextBounds(float scale) {
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int vwidth = getWidth();
int vheight = getHeight();
int centerX = vwidth/2;
int centerY = vheight/2;
TextPaint paint = getPaint();
String text = getText().toString();
int[] dimens = getTextDimens(text, paint);
int width = dimens[0];
int height = dimens[1];
int lines = getLineCount();
height = height * lines;
width = width / lines;
int left = 0;
int top = 0;
if(height > vheight) {
height = vheight - ( paddingBottom + paddingTop);
}
else {
height = (int) (height * scale);
}
if(width > vwidth) {
width = vwidth - (paddingLeft + paddingRight);
}
else {
width = (int) (width * scale);
}
int gravity = getGravity();
//todo Gravity.START
if((gravity & Gravity.LEFT) == Gravity.LEFT) {
left = paddingLeft;
}
//todo Gravity.END
else if((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
left = (vwidth - paddingRight) - width;
}
else if((gravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
left = centerX - width / 2;
}
if((gravity & Gravity.TOP) == Gravity.TOP) {
top = paddingTop;
}
else if((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
top = (vheight - paddingBottom) - height;
}
else if((gravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
top = centerY - height / 2;
}
return new int[] {left, top, left + width, top + height};
}
I have a canvas and I am able to zoom out and scroll with boundaries, the code for that is here
package com.mypackage.ui.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.provider.SyncStateContract;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
*
*/
public class MyDrawingView extends View {
private static final int INVALID_POINTER_ID = -1;
private int state = 0; //0 se dibuja - 1 se mueve
//drawing path
private Path drawPath;
//drawing and canvas paint
private Paint drawPaint, canvasPaint;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap bitmap;
protected String tool;
protected String color;
protected float brushSize;
//erase flag
private boolean erase=false;
public String getTool() {
return tool;
}
public void setTool(String tool) {
this.tool = tool;
}
public String getColor() {
return color;
}
public float getBrushSize() {
return brushSize;
}
public boolean isErase() {
return erase;
}
private String drawId;
// public OnSaveActionListener listener;
private float mPosX;
private float mPosY;
public float getMPosX() { return mPosX; }
public float getMPosY() { return mPosY; }
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
private float viewHeight;
private float viewWidth;
float canvasWidth, canvasHeight;
private float minScaleFactor;
private boolean panEnabled = true;
private boolean zoomEnabled = true;
public MyDrawingView(Context context) {
super(context);
setup();
}
public MyDrawingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setup();
}
public MyDrawingView(Context context, AttributeSet attrs){
super(context, attrs);
setup();
}
//setup drawing
private void setup(){
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
//prepare for drawing and setup paint stroke properties
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
public void setState(int state) {
this.state = state;
}
public void setImageBitmap(Bitmap bmp) {
bitmap = bmp;
resetZoom();
resetPan();
invalidate();
}
public void setImageDrawable(Drawable drawable) {
setImageBitmap(((BitmapDrawable) drawable).getBitmap());
}
public void setBitmap(Bitmap bmp) {
setImageBitmap(bmp);
}
public void resetZoom() {
mScaleFactor = 1.0f;
}
public void resetPan() {
mPosX = 0f;
mPosY = 0f;
}
public BitmapDrawable getImageDrawable() {
BitmapDrawable bd = new BitmapDrawable(getContext().getResources(), bitmap);
return bd;
}
public BitmapDrawable getDrawable() {
return getImageDrawable();
}
//size assigned to view
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(bitmap);
}
//draw the view - will be called after touch event
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mScaleFactor = Math.max(mScaleFactor, minScaleFactor);
canvasHeight = canvas.getHeight();
canvasWidth = canvas.getWidth();
//Save the canvas without translating (panning) or scaling (zooming)
//After each change, restore to this state, instead of compounding
//changes upon changes
canvas.save();
int maxX, minX, maxY, minY;
//Regardless of the screen density (HDPI, MDPI) or the scale factor,
//The image always consists of bitmap width divided by 2 pixels. If an image
//is 200 pixels wide and you scroll right 100 pixels, you just scrolled the image
//off the screen to the left.
minX = (int) (((viewWidth / mScaleFactor) - bitmap.getWidth()) / 2);
maxX = 0;
//How far can we move the image vertically without having a gap between image and frame?
minY = (int) (((viewHeight / mScaleFactor) - bitmap.getHeight()) / 2);
maxY = 0;
//Do not go beyond the boundaries of the image
if (mPosX > maxX) {
mPosX = maxX;
}
if (mPosX < minX) {
mPosX = minX;
}
if (mPosY > maxY) {
mPosY = maxY;
}
if (mPosY < minY) {
mPosY = minY;
}
if (state == 0) {
} else if (state == 1) {
canvas.scale(this.mScaleFactor, this.mScaleFactor, this.mScaleDetector.getFocusX(), this.mScaleDetector.getFocusY());
}
canvas.translate(mPosX, mPosY);
canvas.drawBitmap(bitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
canvas.restore();
invalidate();
}
//register user touches as drawing action
#Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
float touchX = event.getX();
float touchY = event.getY();
if(isErase()){
drawCanvas.drawPath(drawPath, drawPaint);
invalidate();
}
//respond to down, move and up events
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastTouchX = touchX;
mLastTouchY = touchY;
mActivePointerId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if (state == 0) {
} else if (state == 1) {
final int pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
}
break;
case MotionEvent.ACTION_UP:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_CANCEL:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = event.getX(newPointerIndex);
mLastTouchY = event.getY(newPointerIndex);
mActivePointerId = event.getPointerId(newPointerIndex);
}
break;
default:
return false;
}
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
The code is above is an adaptation that worked for me from the answer here
The problem I have is when I zoom in I can't scroll to any place of the image, is like the corners are unreacheable. I know I am making a mistake with the boundaries but I can't realize how to solve it
Thank you
I'm making an application to create signature on file image. Here, I will load the image file from the memory card to the application and use Canvas Paint can create a signature by hand through the draw.
The problem here is the application requirements for customers to sign in a specific location. My code allows customers can sign in anywhere the customer wants, like paint on the windows. Now, I want to be signed in a specific area only... Help me...
This my code
Main.java
public class Longvan extends Activity implements OnClickListener, OnTouchListener {
ImageView choosenImageView;
Button choosePicture;
Button savePicture;
Bitmap bmp;
Bitmap alteredBitmap;
Canvas canvas;
Paint paint;
Matrix matrix;
float downx = 0;
float downy = 0;
float upx = 0;
float upy = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_longvan);
int currentOrientation = getResources().getConfiguration().orientation;
if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
choosenImageView = (ImageView) this.findViewById(R.id.ChoosenImageView);
choosePicture = (Button) this.findViewById(R.id.ChoosePictureButton);
savePicture = (Button) this.findViewById(R.id.SavePictureButton);
savePicture.setOnClickListener(this);
choosePicture.setOnClickListener(this);
choosenImageView.setOnTouchListener(this);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_longvan, menu);
return true;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = event.getX();
downy = event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = event.getX();
upy = event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
choosenImageView.invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
upx = event.getX();
upy = event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
choosenImageView.invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v == choosePicture) {
Intent choosePictureIntent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(choosePictureIntent, 0);
} else if (v == savePicture) {
if (alteredBitmap != null) {
ContentValues contentValues = new ContentValues(3);
contentValues.put(Media.DISPLAY_NAME, "Draw On Me");
Uri imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, contentValues);
try {
OutputStream imageFileOS = getContentResolver().openOutputStream(imageFileUri);
alteredBitmap.compress(CompressFormat.JPEG, 90, imageFileOS);
Toast t = Toast.makeText(this, "Thank you! Image Saved!", Toast.LENGTH_LONG);
t.show();
} catch (Exception e) {
Log.v("EXCEPTION", e.getMessage());
}
}
}
}
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (resultCode == RESULT_OK) {
Uri imageFileUri = intent.getData();
try {
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(
imageFileUri), null, bmpFactoryOptions);
bmpFactoryOptions.inJustDecodeBounds = false;
bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(
imageFileUri), null, bmpFactoryOptions);
alteredBitmap = Bitmap.createBitmap(bmp.getWidth(),bmp.getHeight(),bmp.getConfig());
canvas = new Canvas(alteredBitmap);
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(2);
matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
choosenImageView.setImageBitmap(alteredBitmap);
choosenImageView.setOnTouchListener(this);
} catch (Exception e) {
Log.v("ERROR", e.toString());
}
}
}
}
How about a specific View for inputting Signatures? Something like this: https://github.com/CoatedMoose/CustomViews/tree/master/library/src/com/coatedmoose/customviews
Then, add this setter for the Bitmap:
public void setImage(Bitmap bitmap) {
this.mBitmap = bitmap;
this.invalidate();
}
Or, if you dont want the applied bitmap as part of your signatureview (use it as background, and not as canvas), replace the onDraw method with this:
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.drawPath(mPath, mPaint);
}
and then just use mSignatureView.setBackgroundResource(R.drawable.background);
Edit:
Just create a Class with the code from coatedMoose, and use it in your layout XML:
<com.example.myapp.gui.views.SignatureView
android:id="#+id/signature"
android:layout_width="match_parent"
android:layout_height="80dp"/>
Somewhere in your code, or even as an XML attribute, you can set the background.
Edit: signatureView class:
package me.other;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* A simple view to capture a path traced onto the screen. Initially intended to be used to captures signatures.
*
* #author Andrew Crichton
* #version 0.1
*/
public class SignatureView extends View {
#SuppressWarnings("unused")
private Path mPath;
private Paint mPaint;
private Paint bgPaint = new Paint(Color.TRANSPARENT);
private Bitmap mBitmap;
private Canvas mCanvas;
private float curX, curY;
private static final int TOUCH_TOLERANCE = 4;
private static final int STROKE_WIDTH = 4;
public SignatureView(Context context) {
super(context);
init();
}
public SignatureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SignatureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setFocusable(true);
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(STROKE_WIDTH);
}
public void setSigColor(int color) {
mPaint.setColor(color);
}
public void setSigColor(int a, int red, int green, int blue) {
mPaint.setARGB(a, red, green, blue);
}
public boolean clearSignature() {
if (mBitmap != null)
createFakeMotionEvents();
if (mCanvas != null) {
mCanvas.drawColor(Color.TRANSPARENT);
mCanvas.drawPaint(bgPaint);
mPath.reset();
invalidate();
}
else {
return false;
}
return true;
}
public Bitmap getImage() {
return this.mBitmap;
}
public void setImage(Bitmap bitmap) {
this.mBitmap = bitmap;
this.invalidate();
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
int bitmapWidth = mBitmap != null ? mBitmap.getWidth() : 0;
int bitmapHeight = mBitmap != null ? mBitmap.getWidth() : 0;
if (bitmapWidth >= width && bitmapHeight >= height)
return;
if (bitmapWidth < width)
bitmapWidth = width;
if (bitmapHeight < height)
bitmapHeight = height;
Bitmap newBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
if (mBitmap != null)
newCanvas.drawBitmap(mBitmap, 0, 0, null);
mBitmap = newBitmap;
mCanvas = newCanvas;
}
private void createFakeMotionEvents() {
MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(),SystemClock.uptimeMillis()+100, MotionEvent.ACTION_DOWN, 1f, 1f ,0);
MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(),SystemClock.uptimeMillis()+100, MotionEvent.ACTION_UP, 1f, 1f ,0);
onTouchEvent(downEvent);
onTouchEvent(upEvent);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.drawPath(mPath, mPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDown(x, y);
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
break;
case MotionEvent.ACTION_UP:
touchUp();
break;
}
invalidate();
return true;
}
/**----------------------------------------------------------
* Private methods
**---------------------------------------------------------*/
private void touchDown(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
curX = x;
curY = y;
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - curX);
float dy = Math.abs(y - curY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(curX, curY, (x + curX)/2, (y + curY)/2);
curX = x;
curY = y;
}
}
private void touchUp() {
mPath.lineTo(curX, curY);
if (mCanvas == null) {
mCanvas = new Canvas();
mCanvas.setBitmap(mBitmap);
}
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
}
Some random layout XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.mysignatureviewapp.views.SignatureView
android:id="#+id/sign"
android:layout_width="match_parent"
android:layout_height="204dp">
</com.example.mysignatureviewapp.views.SignatureView>
</RelativeLayout>
In the onCreate() method:
private SignatureView sign;
//
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mysignatureviewlayout);
sign = (SignatureView) findViewById(R.id.sign);
sign.setBackgroundResource(R.drawable.mybackgrounddrawable);
}
//get your signature when you press, say, the back button
#Override
public void onBackPressed() {
Bitmap mySignature = sign.getImage();
//do something with bitmap
}
Getting both the background and the signature:
BitmapDrawable mySignature = new BitmapDrawable(sign.getImage());
Drawable background = sign.getBackground();
LayerDrawable ld = new LayerDrawable(new Drawable[] { mySignature, background});