I've an Activity with only 1 image with Pan/Zoom/Rotation system added.
I need to implement a double tap on image to add another function to my code, but I can't make it..
I've try to add to my image the setOnClickListener with onClick method but nothing happens although without any code error.
public class LastActivity extends AppCompatActivity
{
ImageView my_View;
float scalediff;
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
private float oldDist = 1f;
private float d = 0f;
private float newRot = 0f;
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_last);
my_View = findViewById(R.id.my_View);
final Bundle mBundle = getIntent().getExtras();
if (mBundle != null)
{
my_View.setImageResource(mBundle.getInt("fullImg"));
}
init();
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int sWidth = size.x;
int sHeight = size.y;
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(800, 800);
my_View.setLayoutParams(layoutParams);
my_View.setOnTouchListener(new View.OnTouchListener() {
RelativeLayout.LayoutParams parms;
int startwidth;
int startheight;
float dx = 0, dy = 0, x = 0, y = 0;
float angle = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
final ImageView view = (ImageView) v;
((BitmapDrawable) view.getDrawable()).setAntiAlias(true);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
parms = (RelativeLayout.LayoutParams) view.getLayoutParams();
startwidth = parms.width;
startheight = parms.height;
dx = event.getRawX() - parms.leftMargin;
dy = event.getRawY() - parms.topMargin;
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
mode = ZOOM;
}
d = rotation(event);
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
x = event.getRawX();
y = event.getRawY();
parms.leftMargin = (int) (x - dx);
parms.topMargin = (int) (y - dy);
parms.rightMargin = 0;
parms.bottomMargin = 0;
parms.rightMargin = parms.leftMargin + (5 * parms.width);
parms.bottomMargin = parms.topMargin + (10 * parms.height);
view.setLayoutParams(parms);
} else if (mode == ZOOM) {
if (event.getPointerCount() == 2) {
newRot = rotation(event);
angle = newRot - d;
x = event.getRawX();
y = event.getRawY();
float newDist = spacing(event);
if (newDist > 10f) {
float scale = newDist / oldDist * view.getScaleX();
if (scale > 0.6) {
scalediff = scale;
view.setScaleX(scale);
view.setScaleY(scale);
}
}
view.animate().rotationBy(angle).setDuration(0).setInterpolator(new LinearInterpolator()).start();
x = event.getRawX();
y = event.getRawY();
parms.leftMargin = (int) ((x - dx) + scalediff);
parms.topMargin = (int) ((y - dy) + scalediff);
parms.rightMargin = 0;
parms.bottomMargin = 0;
parms.rightMargin = parms.leftMargin + (5 * parms.width);
parms.bottomMargin = parms.topMargin + (10 * parms.height);
view.setLayoutParams(parms);
}
}
break;
}
return true;
}
});
}
private void init()
{
my_View = findViewById(R.id.my_View);
}
private float spacing(MotionEvent event)
{
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
private float rotation(MotionEvent event)
{
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
}
How can implements the double tap to the image without doing damage to the original code?
this is the onClick method
int c = 0;
my_View.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
c++;
Handler handler = new Handler();
Runnable run = new Runnable()
{
#Override
public void run()
{
c = 0;
}
};
handler.postDelayed(run,350);
if (c == 2)
{
// event code
}
}
});
you can implement a gesture detector adapting this answer:
GestureDetector gestureDetector = new GestureDetector(context, new GestureListener());
}
// skipping measure calculation and drawing
// delegate the event to the gesture detector
#Override
public boolean onTouchEvent(MotionEvent e) {
return gestureDetector.onTouchEvent(e);
}
//since you already have the previous bookean for your original code to work, try changing "return true" (of your on touch event) to "return gestureDetector.onTouchEvent(e)"
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onDown(MotionEvent e) {
return true;
}
// event when double tap occurs
#Override
public boolean onDoubleTap(MotionEvent e) {
float x = e.getX();
float y = e.getY();
//double tapped
return true;
}
}
If you have problems with the ggeasture detector getting the event and your original touch listener not reacting, just get all the other events in the geaturelistener (copying your code to the apprpiate event (which tou will need to add to onDown and onDoubleClicked, to do this you probably need to change the "extends simple geasture listener" to a extends gesture listener)
The better and working solution for me is adding the birdman answer of this post into my code....
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'm trying to draw a circle on the bitmap exactly where the user is double-tapping. For some reason, when the bitmap is double tapped, the circle is generated in the top left corner as opposed to where the user touches...Am I using getX and getY correctly?
public class ImageDisplayActivity extends FragmentActivity {
public static final String KEY_PATH = "img.jpg";
private ZoomInZoomOut touch;
private Bitmap bitmap;
Context context;
GestureDetector gestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_display);
Intent intent = getIntent();
String path = getIntent().getStringExtra(ImageDisplayActivity.KEY_PATH);
try {
java.io.FileInputStream in = this.openFileInput(path);
bitmap = BitmapFactory.decodeStream(in);
bitmap = bitmap.copy(bitmap.getConfig(), true);
touch = (ZoomInZoomOut)findViewById(R.id.IMAGEID);
touch = arrangeImageView(touch);
touch.setImageBitmap(bitmap);
in.close();
touch.setOnTouchListener(new View.OnTouchListener() {
GestureDetector gestureDetector = new
GestureDetector(new MyGestureDetector(context));
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {
public Context context;
public MyGestureDetector(Context con)
{
this.context=con;
}
#Override
public boolean onDoubleTap(MotionEvent event) {
Bitmap bmOverlay = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(),
bitmap.getConfig());
Canvas canvas = new Canvas(bmOverlay);
Paint p = new Paint();
p.setAntiAlias(true);
p.setColor(Color.BLUE);
p.setStrokeWidth(2);
p.setStyle(Paint.Style.STROKE);
canvas.drawBitmap(bitmap,new Matrix(),null);
canvas.drawCircle((int)event.getX(),(int)event.getY(),
100, p);
touch.setImageBitmap(bmOverlay);
return true;
}
}
}
Here is the ZoomInZoomOut Class...
public class ZoomInZoomOut extends ImageView {
private static final String TAG = "ZoomableImageView";
private Bitmap imgBitmap = null;
private int containerWidth;
private int containerHeight;
Paint background;
//Matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
PointF start = new PointF();
float currentScale;
float curX;
float curY;
//We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
//For animating stuff
float targetX;
float targetY;
float targetScale;
float targetScaleX;
float targetScaleY;
float scaleChange;
float targetRatio;
float transitionalRatio;
float easing = 0.2f;
boolean isAnimating = false;
float scaleDampingFactor = 0.5f;
//For pinch and zoom
float oldDist = 1f;
PointF mid = new PointF();
private Handler mHandler = new Handler();
float minScale;
float maxScale = 8.0f;
float wpRadius = 25.0f;
float wpInnerRadius = 20.0f;
float screenDensity;
Context context;
private GestureDetector gestureDetector;
public static final int DEFAULT_SCALE_FIT_INSIDE = 0;
public static final int DEFAULT_SCALE_ORIGINAL = 1;
private int defaultScale;
public int getDefaultScale() {
return defaultScale;
}
public void setDefaultScale(int defaultScale) {
this.defaultScale = defaultScale;
}
public ZoomInZoomOut(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
screenDensity = context.getResources().getDisplayMetrics().density;
initPaints();
gestureDetector = new GestureDetector(new MyGestureDetector());
this.context=context;
}
public ZoomInZoomOut(Context context, AttributeSet attrs) {
super(context, attrs);
screenDensity = context.getResources().getDisplayMetrics().density;
initPaints();
gestureDetector = new GestureDetector(new MyGestureDetector());
defaultScale = ZoomInZoomOut.DEFAULT_SCALE_FIT_INSIDE;
}
private void initPaints() {
background = new Paint();
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
//Reset the width and height. Will draw bitmap and change
containerWidth = width;
containerHeight = height;
if(imgBitmap != null) {
int imgHeight = imgBitmap.getHeight();
int imgWidth = imgBitmap.getWidth();
float scale;
int initX = 0;
int initY = 0;
if(defaultScale == ZoomInZoomOut.DEFAULT_SCALE_FIT_INSIDE) {
if(imgWidth > containerWidth) {
scale = (float)containerWidth / imgWidth;
float newHeight = imgHeight * scale;
initY = (containerHeight - (int)newHeight)/2;
matrix.setScale(scale, scale);
matrix.postTranslate(0, initY);
}
else {
scale = (float)containerHeight / imgHeight;
float newWidth = imgWidth * scale;
initX = (containerWidth - (int)newWidth)/2;
matrix.setScale(scale, scale);
matrix.postTranslate(initX, 0);
}
curX = initX;
curY = initY;
currentScale = scale;
minScale = scale;
}
else {
if(imgWidth > containerWidth) {
initY = (containerHeight - (int)imgHeight)/2;
matrix.postTranslate(0, initY);
}
else {
initX = (containerWidth - (int)imgWidth)/2;
matrix.postTranslate(initX, 0);
}
curX = initX;
curY = initY;
currentScale = 1.0f;
minScale = 1.0f;
}
invalidate();
}
}
#Override
protected void onDraw(Canvas canvas) {
if(imgBitmap != null && canvas != null)
{
canvas.drawBitmap(imgBitmap, matrix, background);
}
}
//Checks and sets the target image x and y co-ordinates if out of bounds
private void checkImageConstraints() {
if(imgBitmap == null) {
return;
}
float[] mvals = new float[9];
matrix.getValues(mvals);
currentScale = mvals[0];
if(currentScale < minScale) {
float deltaScale = minScale / currentScale;
float px = containerWidth/2;
float py = containerHeight/2;
matrix.postScale(deltaScale, deltaScale, px, py);
invalidate();
}
matrix.getValues(mvals);
currentScale = mvals[0];
curX = mvals[2];
curY = mvals[5];
int rangeLimitX = containerWidth - (int)(imgBitmap.getWidth() * currentScale);
int rangeLimitY = containerHeight - (int)(imgBitmap.getHeight() * currentScale);
boolean toMoveX = false;
boolean toMoveY = false;
if(rangeLimitX < 0) {
if(curX > 0) {
targetX = 0;
toMoveX = true;
}
else if(curX < rangeLimitX) {
targetX = rangeLimitX;
toMoveX = true;
}
}
else {
targetX = rangeLimitX / 2;
toMoveX = true;
}
if(rangeLimitY < 0) {
if(curY > 0) {
targetY = 0;
toMoveY = true;
}
else if(curY < rangeLimitY) {
targetY = rangeLimitY;
toMoveY = true;
}
}
else {
targetY = rangeLimitY / 2;
toMoveY = true;
}
if(toMoveX == true || toMoveY == true) {
if(toMoveY == false) {
targetY = curY;
}
if(toMoveX == false) {
targetX = curX;
}
//Disable touch event actions
isAnimating = true;
//Initialize timer
mHandler.removeCallbacks(mUpdateImagePositionTask);
mHandler.postDelayed(mUpdateImagePositionTask, 100);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float oldX = 0, newX = 0, sens = 5;
if(gestureDetector.onTouchEvent(event)) {
return true;
}
if(isAnimating == true) {
return true;
}
//Handle touch events here
float[] mvals = new float[9];
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if(isAnimating == false) {
savedMatrix.set(matrix);
oldX = event.getX();
start.set(event.getX(), event.getY());
mode = DRAG;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if(oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
newX = event.getX();
if (Math.abs(oldX - newX) < sens) {
Toast.makeText(context, "Hello", Toast.LENGTH_LONG).show();
return true;
}
oldX = 0;
newX = 0;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
matrix.getValues(mvals);
curX = mvals[2];
curY = mvals[5];
currentScale = mvals[0];
if(isAnimating == false) {
checkImageConstraints();
}
break;
case MotionEvent.ACTION_MOVE:
if(mode == DRAG && isAnimating == false) {
matrix.set(savedMatrix);
float diffX = event.getX() - start.x;
float diffY = event.getY() - start.y;
matrix.postTranslate(diffX, diffY);
matrix.getValues(mvals);
curX = mvals[2];
curY = mvals[5];
currentScale = mvals[0];
}
else if(mode == ZOOM && isAnimating == false) {
float newDist = spacing(event);
if(newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.getValues(mvals);
currentScale = mvals[0];
if(currentScale * scale <= minScale) {
matrix.postScale(minScale/currentScale, minScale/currentScale, mid.x, mid.y);
}
else if(currentScale * scale >= maxScale) {
matrix.postScale(maxScale/currentScale, maxScale/currentScale, mid.x, mid.y);
}
else {
matrix.postScale(scale, scale, mid.x, mid.y);
}
matrix.getValues(mvals);
curX = mvals[2];
curY = mvals[5];
currentScale = mvals[0];
}
}
break;
}
//Calculate the transformations and then invalidate
invalidate();
return true;
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x/2, y/2);
}
public void setImageBitmap(Bitmap b) {
if(b != null) {
imgBitmap = b;
containerWidth = getWidth();
containerHeight = getHeight();
int imgHeight = imgBitmap.getHeight();
int imgWidth = imgBitmap.getWidth();
float scale;
int initX = 0;
int initY = 0;
matrix.reset();
if(defaultScale == ZoomInZoomOut.DEFAULT_SCALE_FIT_INSIDE) {
if(imgWidth > containerWidth) {
scale = (float)containerWidth / imgWidth;
float newHeight = imgHeight * scale;
initY = (containerHeight - (int)newHeight)/2;
matrix.setScale(scale, scale);
matrix.postTranslate(0, initY);
}
else {
scale = (float)containerHeight / imgHeight;
float newWidth = imgWidth * scale;
initX = (containerWidth - (int)newWidth)/2;
matrix.setScale(scale, scale);
matrix.postTranslate(initX, 0);
}
curX = initX;
curY = initY;
currentScale = scale;
minScale = scale;
}
else {
if(imgWidth > containerWidth) {
initX = 0;
if(imgHeight > containerHeight) {
initY = 0;
}
else {
initY = (containerHeight - (int)imgHeight)/2;
}
matrix.postTranslate(0, initY);
}
else {
initX = (containerWidth - (int)imgWidth)/2;
if(imgHeight > containerHeight) {
initY = 0;
}
else {
initY = (containerHeight - (int)imgHeight)/2;
}
matrix.postTranslate(initX, 0);
}
curX = initX;
curY = initY;
currentScale = 1.0f;
minScale = 1.0f;
}
invalidate();
}
else {
Log.d(TAG, "bitmap is null");
}
}
public Bitmap getPhotoBitmap() {
return imgBitmap;
}
private Runnable mUpdateImagePositionTask = new Runnable() {
public void run() {
float[] mvals;
if(Math.abs(targetX - curX) < 5 && Math.abs(targetY - curY) < 5) {
isAnimating = false;
mHandler.removeCallbacks(mUpdateImagePositionTask);
mvals = new float[9];
matrix.getValues(mvals);
currentScale = mvals[0];
curX = mvals[2];
curY = mvals[5];
//Set the image parameters and invalidate display
float diffX = (targetX - curX);
float diffY = (targetY - curY);
matrix.postTranslate(diffX, diffY);
}
else {
isAnimating = true;
mvals = new float[9];
matrix.getValues(mvals);
currentScale = mvals[0];
curX = mvals[2];
curY = mvals[5];
//Set the image parameters and invalidate display
float diffX = (targetX - curX) * 0.3f;
float diffY = (targetY - curY) * 0.3f;
matrix.postTranslate(diffX, diffY);
mHandler.postDelayed(this, 25);
}
invalidate();
}
};
private Runnable mUpdateImageScale = new Runnable() {
public void run() {
float transitionalRatio = targetScale / currentScale;
float dx;
if(Math.abs(transitionalRatio - 1) > 0.05) {
isAnimating = true;
if(targetScale > currentScale) {
dx = transitionalRatio - 1;
scaleChange = 1 + dx * 0.2f;
currentScale *= scaleChange;
if(currentScale > targetScale) {
currentScale = currentScale / scaleChange;
scaleChange = 1;
}
}
else {
dx = 1 - transitionalRatio;
scaleChange = 1 - dx * 0.5f;
currentScale *= scaleChange;
if(currentScale < targetScale) {
currentScale = currentScale / scaleChange;
scaleChange = 1;
}
}
if(scaleChange != 1) {
matrix.postScale(scaleChange, scaleChange, targetScaleX, targetScaleY);
mHandler.postDelayed(mUpdateImageScale, 15);
invalidate();
}
else {
isAnimating = false;
scaleChange = 1;
matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
currentScale = targetScale;
mHandler.removeCallbacks(mUpdateImageScale);
invalidate();
checkImageConstraints();
}
}
else {
isAnimating = false;
scaleChange = 1;
matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
currentScale = targetScale;
mHandler.removeCallbacks(mUpdateImageScale);
invalidate();
checkImageConstraints();
}
}
};
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
}
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return super.onFling(e1, e2, velocityX, velocityY);
}
#Override
public boolean onDown(MotionEvent e) {
return false;
}
}
}
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 have class, which draw many crosses in vertical way. I want learn how do this, so I try it on this simple example:
public class Draw extends View {
Paint paint = new Paint();
public Draw(Context context) {
super(context);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas) {
int x = 1;
for(int i = 0; i < 100; i++){
if (i%2 == 0) x = 2;
else x=1;
canvas.drawLine(0, 0 + x*i*20, 20, 20 + x*i*20, paint);
canvas.drawLine(20, 0 + x*i*20, 0, 20 + x*i*20, paint);
}
}
}
and I want scroll it.
I implemented onTouchEvent with MotionEvent.ACTION_MOVE:
public class DrawActivity extends Activity {
private Draw dv;
private float xDistance, yDistance, lastX, lastY;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new Draw(this);
setContentView(dv);
}
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case (MotionEvent.ACTION_MOVE):
final float curX = event.getX();
final float curY = event.getY();
xDistance += Math.abs(curX - lastX);
yDistance += Math.abs(curY - lastY);
lastX = curX;
lastY = curY;
if (xDistance > yDistance)
return false;
default:
return super.onTouchEvent(event);
}
}
}
but it not working. How Can I scroll a canvas?
Since you are extending View you should update it's scrollX value (or store itself) and translate the content of the drawing by its value.