I am working on an application which captures an applicant's signature and saves it to the phone memory. The class used for the signature view is given below
public class SignatureView extends View {
private static final float STROKE_WIDTH = 5f;
/** Need to track this so the dirty region can accommodate the stroke. **/
private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
private Paint paint = new Paint();
private Path path = new Path();
/**
* Optimizes painting by invalidating the smallest possible area.
*/
private float lastTouchX;
private float lastTouchY;
private final RectF dirtyRect = new RectF();
public SignatureView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(STROKE_WIDTH);
}
/**
* Erases the signature.
*/
public void clear() {
path.reset();
// Repaints the entire view.
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(255, 233, 255, 255);
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
lastTouchX = eventX;
lastTouchY = eventY;
// There is no end point yet, so don't waste cycles invalidating.
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
// Start tracking the dirty region.
resetDirtyRect(eventX, eventY);
// When the hardware tracks events faster than they are delivered, the
// event will contain a history of those skipped points.
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY);
path.lineTo(historicalX, historicalY);
}
// After replaying history, connect the line to the touch point.
path.lineTo(eventX, eventY);
break;
default:
//debug("Ignored touch event: " + event.toString());
return false;
}
// Include half the stroke width to avoid clipping.
invalidate(
(int) (dirtyRect.left - HALF_STROKE_WIDTH),
(int) (dirtyRect.top - HALF_STROKE_WIDTH),
(int) (dirtyRect.right + HALF_STROKE_WIDTH),
(int) (dirtyRect.bottom + HALF_STROKE_WIDTH));
lastTouchX = eventX;
lastTouchY = eventY;
return true;
}
/**
* Called when replaying history to ensure the dirty region includes all
* points.
*/
private void expandDirtyRect(float historicalX, float historicalY) {
if (historicalX < dirtyRect.left) {
dirtyRect.left = historicalX;
} else if (historicalX > dirtyRect.right) {
dirtyRect.right = historicalX;
}
if (historicalY < dirtyRect.top) {
dirtyRect.top = historicalY;
} else if (historicalY > dirtyRect.bottom) {
dirtyRect.bottom = historicalY;
}
}
/**
* Resets the dirty region when the motion event occurs.
*/
private void resetDirtyRect(float eventX, float eventY) {
// The lastTouchX and lastTouchY were set when the ACTION_DOWN
// motion event occurred.
dirtyRect.left = Math.min(lastTouchX, eventX);
dirtyRect.right = Math.max(lastTouchX, eventX);
dirtyRect.top = Math.min(lastTouchY, eventY);
dirtyRect.bottom = Math.max(lastTouchY, eventY);
}
// get drawn image as bitmap
public Bitmap getBitmap() {
this.setDrawingCacheEnabled(true);
this.buildDrawingCache();
Bitmap bmp = Bitmap.createBitmap(this.getDrawingCache());
this.setDrawingCacheEnabled(false);
return bmp;
}
public boolean isDrawn(){
return ! path.isEmpty();
}
}
This class returns the signature bitmap.This view works fine for devices with Android 8.0 and below, but its not working for Android 8.0 plus devices. For Devices above Android 8.0, the method getBitmap() returns a bitmap of the signature, but for Devices above Android 8.0, it returns empty.
Another Solution I tried which did not work either
public class SignatureViewType3 extends View {
private Bitmap _Bitmap;
private Canvas _Canvas;
private Path _Path;
private Paint _BitmapPaint;
private Paint _paint;
private float _mX;
private float _mY;
private float TouchTolerance = 4;
private float LineThickness = 4;
private boolean signatureAdded;
public SignatureViewType3(Context context, AttributeSet attr) {
super(context, attr);
_Path = new Path();
_BitmapPaint = new Paint(Paint.DITHER_FLAG);
_paint = new Paint();
_paint.setAntiAlias(true);
_paint.setDither(true);
_paint.setColor(Color.argb(255, 0, 0, 0));
_paint.setStyle(Paint.Style.STROKE);
_paint.setStrokeJoin(Paint.Join.ROUND);
_paint.setStrokeCap(Paint.Cap.ROUND);
_paint.setStrokeWidth(LineThickness);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
_Bitmap = Bitmap.createBitmap(w, (h > 0 ? h : ((View) this.getParent()).getHeight()), Bitmap.Config.ARGB_8888);
_Canvas = new Canvas(_Bitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getParent().requestDisallowInterceptTouchEvent(true);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(_Bitmap, 0, 0, _BitmapPaint);
canvas.drawPath(_Path, _paint);
}
private void TouchStart(float x, float y) {
_Path.reset();
_Path.moveTo(x, y);
_mX = x;
_mY = y;
}
private void TouchMove(float x, float y) {
float dx = Math.abs(x - _mX);
float dy = Math.abs(y - _mY);
if (dx >= TouchTolerance || dy >= TouchTolerance) {
_Path.quadTo(_mX, _mY, (x + _mX) / 2, (y + _mY) / 2);
_mX = x;
_mY = y;
}
}
private void TouchUp() {
if (!_Path.isEmpty()) {
_Path.lineTo(_mX, _mY);
_Canvas.drawPath(_Path, _paint);
} else {
_Canvas.drawPoint(_mX, _mY, _paint);
}
signatureAdded = true;
_Path.reset();
}
#Override
public boolean onTouchEvent(MotionEvent e) {
super.onTouchEvent(e);
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
TouchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
TouchMove(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
TouchUp();
invalidate();
break;
}
return true;
}
public void clear() {
_Canvas.drawColor(Color.WHITE);
invalidate();
}
public byte[] getBytes() {
Bitmap b = getBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 100, baos);
if(signatureAdded)
return baos.toByteArray();
else
return null;
}
public Bitmap getBitmap() {
View v = (View) this.getParent();
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
v.draw(c);
return b;
}
public boolean isDrawn(){
// return ! _Path.isEmpty();
return true;
}
}
getDrawingCache() is deprecated in Android API 28, so you should use Canvas like in this answer of Ashvin solanki
RelativeLayout view = (RelativeLayout)findViewById(R.id.relativelayout);
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Drawable bgDrawable = view.getBackground();
if (bgDrawable != null) {
bgDrawable.draw(canvas);
} else {
canvas.drawColor(Color.WHITE);
}
view.draw(canvas);
or PixelCopy like in that article of Shivesh Karan Mehta:
// for api level 28
fun getScreenShotFromView(view: View, activity: Activity, callback: (Bitmap) -> Unit) {
activity.window?.let { window ->
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val locationOfViewInWindow = IntArray(2)
view.getLocationInWindow(locationOfViewInWindow)
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PixelCopy.request(
window,
Rect(
locationOfViewInWindow[0],
locationOfViewInWindow[1],
locationOfViewInWindow[0] + view.width,
locationOfViewInWindow[1] + view.height
), bitmap, { copyResult ->
if (copyResult == PixelCopy.SUCCESS) {
callback(bitmap) }
else {
}
// possible to handle other result codes ...
},
Handler()
)
}
} catch (e: IllegalArgumentException) {
// PixelCopy may throw IllegalArgumentException, make sure to handle it
e.printStackTrace()
}
}
}
Related
How to draw a circler path by swiping, it's draw with edges.
I wanted to draw path with smooth edges.
this is my code.
this.mPaint = new Paint();
this.mPaint.setStyle(Paint.Style.STROKE);
this.mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen._5sdp));
this.mPaint.setStrokeCap(Paint.Cap.ROUND);
this.mPaint.setStrokeJoin(Paint.Join.ROUND);
this.mPaint.setColor(Color.BLUE);
this.mPaint.setAntiAlias(true);
this.mPaint.setDither(true);
this code is in OnTouchListener
public boolean onTouch(View view, MotionEvent motionEvent) {
int action = motionEvent.getAction();
float x = motionEvent.getX();
float y = motionEvent.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onT:-> x " + x + " y-> " + y);
mPath.lineTo(x, y);
view.invalidate();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mPaths.add(new CurrentDraw(mPath, mPaint));
mPath.reset();
view.invalidate();
// updateLocalSS();
return false;
}
return true;
}
this code is in ondraw() method
if (mPaths != null) {
for (int i = 0; i < mPaths.size(); i++) {
canvas.drawPath(mPaths.get(i).path, mPaths.get(i).paint);
}
}
if (mPath != null) {
canvas.drawPath(mPath, mPaint);
}
Please just try this way may help you
RectF area;
private Paint bgPaint;
private Paint initpaint() {
int bColor = Color.WHITE;
if (!isInEditMode()) {
bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
} else {
bgPaint = new Paint();
}
bgPaint.setColor(bColor);
bgPaint.setStyle(Paint.Style.STROKE);
bgPaint.setStrokeWidth(sizeBg);
}
Public void initArea(){
float drawPadding = (size / 2);
float width = getWidth();
float height = getHeight();
float left = drawPadding;
float top = drawPadding;
float right = width - drawPadding;
float bottom = height - drawPadding;
area = new RectF(left, top, right, bottom);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path path = new Path();
path.addCircle(area.centerX(), area.centerY(), getRadius(), Path.Direction.CCW);
canvas.drawPath(path, bgPaint);
}
private float getRadius() {
if (area != null) {
return (area.width() / 2);
} else {
return 0;
}
}
Here is my Code i have an sticker on an image View after chose it from Recycler View all functions perform accurate rather when I want to lock a sticker on image view it did not work how can i use MOTION EVENT to lock sticker on image view and then save it
i did not know what method i can use kindly please answer my question.
private float[] mOriginPoints;
private float[] mPoints;
private RectF mOriginContentRect;
private RectF mContentRect;
private RectF mViewRect;
private float mLastPointX, mLastPointY;
private Bitmap mBitmap;
private Bitmap mControllerBitmap, mDeleteBitmap;
private Matrix mMatrix;
private Paint mPaint, mBorderPaint;
private float mControllerWidth, mControllerHeight, mDeleteWidth, mDeleteHeight;
private boolean mInController, mInMove;
private boolean mDrawController = true;
//private boolean mCanTouch;
private float mStickerScaleSize = 1.0f;
private OnStickerDeleteListener mOnStickerDeleteListener;
public StickerView(Context context) {
this(context, null);
}
public StickerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public StickerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setFilterBitmap(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(4.0f);
mPaint.setColor(Color.WHITE);
mBorderPaint = new Paint(mPaint);
mBorderPaint.setColor(Color.parseColor("#B2ffffff"));
mBorderPaint.setShadowLayer(DisplayUtil.dip2px(getContext(), 2.0f), 0, 0, Color.parseColor("#33000000"));
mControllerBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_sticker_control);
mControllerWidth = mControllerBitmap.getWidth();
mControllerHeight = mControllerBitmap.getHeight();
mDeleteBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_sticker_delete);
mDeleteWidth = mDeleteBitmap.getWidth();
mDeleteHeight = mDeleteBitmap.getHeight();
}
public void setWaterMark(#NonNull Bitmap bitmap) {
mBitmap = bitmap;
mStickerScaleSize = 1.0f;
setFocusable(true);
try {
float px = mBitmap.getWidth();
float py = mBitmap.getHeight();
//mOriginPoints = new float[]{px, py, px + bitmap.getWidth(), py, bitmap.getWidth() + px, bitmap.getHeight() + py, px, py + bitmap.getHeight()};
mOriginPoints = new float[]{0, 0, px, 0, px, py, 0, py, px / 2, py / 2};
mOriginContentRect = new RectF(0, 0, px, py);
mPoints = new float[10];
mContentRect = new RectF();
mMatrix = new Matrix();
float transtLeft = ((float)DisplayUtil.getDisplayWidthPixels(getContext()) - mBitmap.getWidth()) / 2;
float transtTop = ((float)DisplayUtil.getDisplayWidthPixels(getContext()) - mBitmap.getHeight()) / 2;
mMatrix.postTranslate(transtLeft, transtTop);
} catch (Exception e) {
e.printStackTrace();
}
postInvalidate();
}
public Matrix getMarkMatrix() {
return mMatrix;
}
#Override
public void setFocusable(boolean focusable) {
super.setFocusable(focusable);
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmap == null || mMatrix == null) {
return;
}
mMatrix.mapPoints(mPoints, mOriginPoints);
mMatrix.mapRect(mContentRect, mOriginContentRect);
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
if (mDrawController && isFocusable()) {
canvas.drawLine(mPoints[0], mPoints[1], mPoints[2], mPoints[3], mBorderPaint);
canvas.drawLine(mPoints[2], mPoints[3], mPoints[4], mPoints[5], mBorderPaint);
canvas.drawLine(mPoints[4], mPoints[5], mPoints[6], mPoints[7], mBorderPaint);
canvas.drawLine(mPoints[6], mPoints[7], mPoints[0], mPoints[1], mBorderPaint);
canvas.drawBitmap(mControllerBitmap, mPoints[4] - mControllerWidth / 2, mPoints[5] - mControllerHeight / 2, mBorderPaint);
canvas.drawBitmap(mDeleteBitmap, mPoints[0] - mDeleteWidth / 2, mPoints[1] - mDeleteHeight / 2, mBorderPaint);
}
}
public Bitmap getBitmap() {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
mDrawController = false;
draw(canvas);
mDrawController = true;
canvas.save();
return bitmap;
}
public void setShowDrawController(boolean show) {
mDrawController = show;
}
private boolean isInController(float x, float y) {
int position = 4;
//while (position < 8) {
float rx = mPoints[position];
float ry = mPoints[position + 1];
RectF rectF = new RectF(rx - mControllerWidth / 2,
ry - mControllerHeight / 2,
rx + mControllerWidth / 2,
ry + mControllerHeight / 2);
if (rectF.contains(x, y)) {
return true;
}
// position += 2;
//}
return false;
}
private boolean isInDelete(float x, float y) {
int position = 0;
//while (position < 8) {
float rx = mPoints[position];
float ry = mPoints[position + 1];
RectF rectF = new RectF(rx - mDeleteWidth / 2,
ry - mDeleteHeight / 2,
rx + mDeleteWidth / 2,
ry + mDeleteHeight / 2);
if (rectF.contains(x, y)) {
return true;
}
// position += 2;
//}
return false;
}
private boolean mInDelete = false;
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (!isFocusable()) {
return super.dispatchTouchEvent(event);
}
if (mViewRect == null) {
mViewRect = new RectF(0f, 0f, getMeasuredWidth(), getMeasuredHeight());
}
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isInController(x, y)) {
mInController = true;
mLastPointY = y;
mLastPointX = x;
break;
}
if (isInDelete(x, y)) {
mInDelete = true;
break;
}
if (mContentRect.contains(x, y)) {
mLastPointY = y;
mLastPointX = x;
mInMove = true;
}
break;
case MotionEvent.ACTION_UP:
if (isInDelete(x, y) && mInDelete) {
doDeleteSticker();
}
case MotionEvent.ACTION_OUTSIDE:
Log.d(TAG, "Movement occurred outside bounds of current screen element");
return true;
case MotionEvent.ACTION_CANCEL:
mLastPointX = 0;
mLastPointY = 0;
mInController = false;
mInMove = false;
mInDelete = false;
break;
case MotionEvent.ACTION_MOVE:
if (mInController) {
mMatrix.postRotate(rotation(event), mPoints[8], mPoints[9]);
float nowLenght = caculateLength(mPoints[0], mPoints[1]);
float touchLenght = caculateLength(event.getX(), event.getY());
if ((float)Math.sqrt((nowLenght - touchLenght) * (nowLenght - touchLenght)) > 0.0f) {
float scale = touchLenght / nowLenght;
float nowsc = mStickerScaleSize * scale;
if (nowsc >= MIN_SCALE_SIZE && nowsc <= MAX_SCALE_SIZE) {
mMatrix.postScale(scale, scale, mPoints[8], mPoints[9]);
mStickerScaleSize = nowsc;
}
}
invalidate();
mLastPointX = x;
mLastPointY = y;
break;
}
if (mInMove == true) { //拖动的操作
float cX = x - mLastPointX;
float cY = y - mLastPointY;
mInController = false;
//Log.i("MATRIX_OK", "ma_jiaodu:" + a(cX, cY));
if ((float)Math.sqrt(cX * cX + cY * cY) > 2.0f && canStickerMove(cX, cY)) {
//Log.i("MATRIX_OK", "is true to move");
mMatrix.postTranslate(cX, cY);
postInvalidate();
mLastPointX = x;
mLastPointY = y;
}
break;
}
return true;
}
return true;
}
private void doDeleteSticker() {
setVisibility(View.GONE);
if (mOnStickerDeleteListener != null) {
mOnStickerDeleteListener.onDelete(this);
}
}
private boolean canStickerMove(float cx, float cy) {
float px = cx + mPoints[8];
float py = cy + mPoints[9];
if (mViewRect.contains(px, py)) {
return true;
} else {
return false;
}
}
private float caculateLength(float x, float y) {
float ex = x - mPoints[8];
float ey = y - mPoints[9];
return (float)Math.sqrt(ex*ex + ey*ey);
}
private float rotation(MotionEvent event) {
float originDegree = calculateDegree(mLastPointX, mLastPointY);
float nowDegree = calculateDegree(event.getX(), event.getY());
return nowDegree - originDegree;
}
private float calculateDegree(float x, float y) {
double delta_x = x - mPoints[8];
double delta_y = y - mPoints[9];
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
public RectF getContentRect(){
return mContentRect;
}
public interface OnStickerDeleteListener {
public void onDelete(StickerView stickerView);
}
public void setOnStickerDeleteListener(OnStickerDeleteListener listener) {
mOnStickerDeleteListener = listener;
}
}
stickerview.setLock(true);
it works fine.
I have created bitmap on android canvas and I want zoom-in and zoom-out I am storing images on Arraylist . It's zooming correctly but when there is multiple bitmap all the bitmap zoom-in and zoom-out. I want to Zoom only Specific bitmap which I touch. In My code there is Functionality to drag image it work completely but when bitmap zoomed its or working.
Here is code
public class ZoomView extends View {
images touchedCircle;
private ScaleGestureDetector scaleDetector;
private float scaleFactor = 1.f;
private BitmapDrawable bitmap;
private HashSet<images> img = new HashSet<images>(CIRCLES_LIMIT);
private SparseArray<images> mCirclePointer = new SparseArray<images>(CIRCLES_LIMIT)
public ZoomView(Context context) {
this(context, null);
}
public ZoomView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(5);
mPaint.setStrokeCap(Paint.Cap.ROUND);
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.deleteforever);
bitmap = (BitmapDrawable) getResources().getDrawable(R.drawable.mesej);
scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (images img1 : img) {
canvas.save();
canvas.scale(scaleFactor, scaleFactor);
canvas.drawBitmap(bitmap.getBitmap(), img1.centerX, img1.centerY, null);
canvas.restore();
}
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
boolean handled = false;
scaleDetector.onTouchEvent(event);
images touchedCircle;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
xTouch = event.getX(0);
yTouch = event.getY(0);
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
touchedtext = obtainTouchedText(xTouch2, yTouch2);
mCirclePointer.put(event.getPointerId(0), touchedCircle);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_MOVE:
touchedCircle = mCirclePointer.get(pointerId);
if (!scaleDetector.isInProgress()){
if (null != touchedCircle) {
touchedCircle.centerX = xTouch - bitmap.getBitmap().getWidth() / 2;
touchedCircle.centerY = yTouch - bitmap.getBitmap().getHeight() / 2;
invalidate();
}
}
invalidate();
handled = true;
break;
case MotionEvent.ACTION_UP:
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
mCirclePointer.remove(pointerId);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_CANCEL:
zooming=false;
invalidate();
handled = true;
break;
default:
break;
}
invalidate();
return super.onTouchEvent(event) || handled;
}
private static class images {
int radius;
float centerX;
float centerY;
images(float X, float Y) {
this.centerX = X;
this.centerY = Y;
this.radius = radius;
}
private images obtainTouchedCircle(final float xTouch, final float yTouch) {
images touchedCircle = getTouchedCircle(xTouch, yTouch);
if (null == touchedCircle) {
touchedCircle = new images(xTouch, yTouch);
if (img.size() == CIRCLES_LIMIT) {
img.clear();
}
if (c.getImage() == 1) {
img.add(touchedCircle);
}
}
return touchedCircle;
}
private images getTouchedCircle(final float xTouch, final float yTouch) {
images touched = null;
for (images circle : img) {
if ((circle.centerX < xTouch) && (circle.centerY < yTouch)) {
float bitmap_width =bitmap.getBitmap().getWidth();
float bitmap_height = bitmap.getBitmap().getHeight();
float width = circle.centerX + bitmap_width;
float height = circle.centerY + bitmap_height;
if ((xTouch < circle.centerX + bitmap.getBitmap().getWidth()) && (yTouch < circle.centerY + bitmap.getBitmap().getHeight())) {
touched = circle;
break;
}
}
}
return touched;
}
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 3.0f));
invalidate();
return true;
}
}
}
Thanks for Help in Advance...
You're using a single scale factor for all images. You'd need to keep an array of scale factors, one for each image. Then in your onDraw when you scale, select the correct scale factor for that image.
This is the first time I am working with the Canvas class in Android
what I want is to draw with different colors and brushes on the canvas with using Path class. I don't want to use Bitmap, because I need to apply proper undo redo functionality on It. What I am doing is just draw on the canvas, but in onDraw function, I draw whole path using for loop, but If user select different colors and brushes to draw, here issue occurs. Then I will have to store all of them in particular list of Paint, same I am doing right now. Is there another good approach for this ?
Code I use is :
DrawView class
public class DrawView extends View implements OnTouchListener {
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Paint> paints = new ArrayList<Paint>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private Context context;
private int initialcolor = 0xff000000;
private StyleEnum styleEnum;
private Style mStyle;
private int strokeWidth = 6;
private int effect;
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
mPaint = new Paint();
// add paint strokes
addPaintStrokes(mPaint);
mCanvas = new Canvas();
mPath = new Path();
paths.add(mPath);
paints.add(mPaint);
rect = new Rect();
mRectPaint = new Paint(Paint.DITHER_FLAG);
mRectPaint.setColor(Color.WHITE);
mRectPaint.setStyle(Paint.Style.FILL);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
Here I use another ArrayList of type Paint to contain paint related items to maintain on every new created object
#Override
protected void onDraw(Canvas canvas) {
float cX = canvas.getWidth() / 2.0f;
float cY = canvas.getHeight() / (mScaleFactor * 10);
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor, cX, cY);
RectF rec = new RectF(0, 0, canvas.getWidth(), canvas.getHeight());
canvas.drawRect(rec, mRectPaint);
if( paths.size() > 0 ){
for(int i = 0 ; i < paths.size() ; i++){
canvas.drawPath(paths.get(i), paints.get(i));
}
}
rect = canvas.getClipBounds();
canvas.restore();
}
private void touch_start(float x, float y) {
mStyle.strokeStart(x, 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 >= 4 || dy >= 4) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mStyle.stroke(mCanvas, x, y);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
mPath = new Path();
paths.add(mPath);
mPaint = new Paint();
addPaintStrokes(mPaint);
paints.add(mPaint);
}
public void onClickUndo () {
if (paths.size()>0) {
undonePaths.add(paths.remove(paths.size()-1));
invalidate();
}
}
public void onClickRedo (){
if (undonePaths.size()>0) {
paths.add(undonePaths.remove(undonePaths.size()-1));
invalidate();
}
}
#Override
public boolean onTouch(View arg0, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
if (isDrawingEnabled) {
// Drawing Enable
float x = event.getX();
float y = event.getY();
x = x / mScaleFactor + rect.left;
y = y / mScaleFactor + rect.top;
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;
}
} else{
// Dragging Enable
final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
float x = event.getX();
float y = event.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = event.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
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
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;
}
}
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
if(!isDrawingEnabled){
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 5.0f));
invalidate();
}
return true;
}
}
private void addPaintStrokes(Paint p){
p.setAntiAlias(true);
p.setDither(true);
p.setColor(initialcolor);
setBrushStyle(effect);
p.setStyle(Paint.Style.STROKE);
p.setStrokeJoin(Paint.Join.ROUND);
p.setStrokeCap(Paint.Cap.ROUND);
p.setStrokeWidth(getStrokeWidth());
}
public void setBrushStyle(int mBrushStyle) {
Log.e("mBrushStyle : ", "" + mBrushStyle);
this.effect = mBrushStyle;
switch (mBrushStyle) {
case 0: {
mPaint.setMaskFilter(null);
break;
}
case 1: {
MaskFilter mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 },
0.4f, 6, 3.5f);
mPaint.setMaskFilter(mEmboss);
break;
}
case 2: {
int brushSize = getStrokeWidth();
if (brushSize > 0) {
MaskFilter mBlur = new BlurMaskFilter(brushSize,
BlurMaskFilter.Blur.NORMAL);
mPaint.setMaskFilter(mBlur);
} else {
MaskFilter mBlur = new BlurMaskFilter(1,
BlurMaskFilter.Blur.NORMAL);
mPaint.setMaskFilter(mBlur);
}
break;
}
}
}
Try moving the Path to undonePaths for saving the path while removing path.
undo logic:
1. move the current path to undonePaths(temp arraylist)
2. remove the current path for the paths arraylist.
redo logic:
1. add current undonePaths to paths arraylist
2. remove the moved path from undonePaths
this works gd for me,try the below code snippet.
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
}
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
}
}
Enjoy coding :) :) :)
The problem is that with every path added, you are drawing cumulatively more to the screen with each call to onDraw.
If you can limit the number of undos allowed, then you can do it with a Bitmap. Create your bitmap like this:
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, h, oldWidth, oldHeight);
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
In your touch_up method, once your paths ArrayList reaches a certain size (the number of levels of undo you will support), take its first Path and draw it to mCanvas instead (right in this method so it is done only once) and remove it from the ArrayList (and its associated Paint from the other ArrayList). In your onDraw method, draw your Bitmap first.
If you must have unlimited undo, I think you could still do this with a Bitmap. Just keep the same array lists you have now, but only draw to mBitmap in your touch methods. Draw only the mBitmap in your onDraw method. When you need to undo, you can clear mBitmap, delete your last path, and redraw all the remaining paths to mBitmap one time.
I want to erase my bitmap but I don't want to erase background image. When I try to erase is white and it draw very hard in frames.
This is from CanvasView
erasePaint.setColor(Color.WHITE);
//erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
erasePaint.setAntiAlias(true);
erasePaint.setStyle(Paint.Style.STROKE);
erasePaint.setStrokeJoin(Paint.Join.ROUND);
erasePaint.setStrokeWidth(12);
//....
protected void onDraw(Canvas canvas) {
paint.setPathEffect(null);
if(bitmap!=null){
for(MyEraser e:eraserList){
canvas.drawPath(e.p,erasePaint);
invalidate();
}
final OnTouchListener eraseListener = new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//FirstActivity.ll.setVisibility(LinearLayout.GONE);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
myEraser = new MyEraser();
lastTouchX = event.getX();
lastTouchY = event.getY();
myEraser.mouseDown(event.getX(), event.getY());
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
resetDirtyRect(event.getX(),event.getY());
int historySize = event.getHistorySize();
for(int i=0;i<historySize;i++){
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY);
myEraser.mouseUp(historicalX, historicalY);
}
myEraser.mouseUp(event.getX(), event.getY());
eraserList.add(myEraser);
break;
default:
Log.d("mock it up", "Unknown touch event " + event.toString());
return false;
}
invalidate(
(int) (dirtyRect.left - HALF_STROKE_WIDTH),
(int) (dirtyRect.top - HALF_STROKE_WIDTH),
(int) (dirtyRect.right + HALF_STROKE_WIDTH),
(int) (dirtyRect.bottom + HALF_STROKE_WIDTH));
lastTouchX = event.getX();
lastTouchY = event.getY();
return true;
}
};
}
}
This is MyEraser
public class MyEraser {
Paint paint = new Paint();
Path p = new Path();
public MyEraser(){
paint.setColor(Color.WHITE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(5);
}
public void mouseDown(float x, float y) {
//path.addCircle(x,y,5,Path.Direction.CW);
p.moveTo( x, y );
// path.lineTo(x, y);
}
public void mouseMove(Path path, float x, float y) {
// path.addCircle(x,y,5,Path.Direction.CW);
}
public void mouseUp(float x, float y){
//path.addCircle(x,y,5,Path.Direction.CW);
p.lineTo(x, y);
}
public void draw(Canvas c,Path path){
//paint.setColor(Color.WHITE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(7);
c.drawPath(p, paint);
}
}
EDIT: Started over for better explanation :)
I am not familiar with Android, in fact I have never used it. Use this code as an example of how to organize your code, rather than a functional code.
class MyView extends View {
private Eraser myEraser;
private Bitmap myBackgroundImage;
private Canvas myForegroundCanvas;
public MyView(Context context, Attributes, attrs) {
myEraser = new Eraser()
myBackgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.your_background_name);
Bitmap image = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); // the width and height of the view
myForegroundCanvas = new Canvas(image);
}
public boolean onTouchEvent(MotionEvent event) {
// update your eraser path
return true;
}
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(myBackgroundImage, 0, 0, null);
//for (Eraser item : eraserList) {
// if you have multiple eraser, add them to a list
myEraser.draw(myForegroundCanvas);
//}
canvas.drawCanvas(myForegroundCanvas, 0, 0, null);
}
}
The main idea is to keep the separation between your background and foreground images. That way, you could easily change the background and it would be updated. You could also have a reset on the foreground to erase everything, etc.
I hope this helps you.