im new in this website...i have a force stop eeror when im opening this project up...i havnt any error but the application has a force stop problem...this project about pinch zooming on pictures...it a sample code that i use...does anyone know why? :(
this is my TouchImageView Scripts :
package aa.aaa.aaaa;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
Matrix matrix;
// 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;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
int viewWidth, viewHeight;
static final int CLICK = 3;
float saveScale = 1f;
protected float origWidth, origHeight;
int oldMeasuredWidth, oldMeasuredHeight;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
public void setMaxZoom(float x) {
maxScale = x;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
else
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
fixTrans();
return true;
}
}
void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
if (fixTransX != 0 || fixTransY != 0)
matrix.postTranslate(fixTransX, fixTransY);
}
float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
|| viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;
if (saveScale == 1) {
//Fit to screen.
float scale;
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();
Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
}
this is the XML file codes :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main" >
<aa.aaa.aaaa.TouchImageView
android:id="#+id/touchImageView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
and this is the main activity:
package aa.aaa.aaaa;
import android.app.Activity;
import android.os.Bundle;
public class main extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TouchImageView img = (TouchImageView) findViewById(R.id.touchImageView);
img.setImageResource(R.drawable.reference);
img.setMaxZoom(4f);
}
}
Related
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....
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;
}
}
}
I want to move a sprite between two points.
first point is x=0 and y=0 and the second point is a point where the user touched the screen.
To move, I want to use an equation to go through the two points which is y=ax+b.
I have attempted this in the method move() but the sprite does not move.
Please help.
Class Display:
package com.example.name;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Display extends SurfaceView {
private Bitmap bmp;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private Sprite sprite;
private long lastClick;
private float x = 0.0F;
private float y = 0.0F;
public Display(Context context) {
super(context);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.gonia);
sprite = new Sprite(this, bmp, x, y, x, y);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
sprite.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 500) {
lastClick = System.currentTimeMillis();
x = (float) sprite.ostatniaWartoscX();
y = (float) sprite.ostatniaWartoscY();
float gotox = (float) event.getX();
float gotoy = (float) event.getY();
synchronized (getHolder()) {
sprite = new Sprite(this, bmp, x, y, gotox, gotoy);
}
}
return true;
}
}
Class Sprite:
package com.example.name;
import android.graphics.Bitmap;
import android.graphics.Canvas;
public class Sprite {
private float a;
private float b;
private float x;
private float y;
private float gotox;
private float gotoy;
private int executeMove = 0;
private Display display;
private Bitmap bmp;
public Sprite(Display display, Bitmap bmp, float x, float y, float gotox,
float gotoy) {
this.display = display;
this.bmp = bmp;
this.x = x;
this.y = y;
this.gotox = gotox;
this.gotoy = gotoy;
}
void update() {
if (x < gotox) {x++;executeMove = 1;}
if (x > gotox) {x--;executeMove = 1;}
if (executeMove == 1) {move();}
executeMove = 0;
}
void move() {
float x1 = x;
float y1 = y;
float x2 = gotox;
float y2 = gotoy;
a = (y2-y1)/(x2-x1);
b = y1 - x1*a;
y = x1 * a + b;
}
public float ostatniaWartoscX() {
return x;
}
public float ostatniaWartoscY() {
return y;
}
public void onDraw(Canvas canvas) {
update();
canvas.drawBitmap(bmp, x, y, null);
}
}
Thank You!
You need Bresenham's line algorithm to move your player. You can even cut it short that you only need to calculate the next movement (not the whole line). It's a simple/easy and low-calorie-algorithm.
You must adjust it to your needs.
public static ArrayList<Point> getLine(Point start, Point target) {
ArrayList<Point> ret = new ArrayList<Point>();
int x0 = start.x;
int y0 = start.y;
int x1 = target.x;
int y1 = target.y;
int sx = 0;
int sy = 0;
int dx = Math.abs(x1-x0);
sx = x0<x1 ? 1 : -1;
int dy = -1*Math.abs(y1-y0);
sy = y0<y1 ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
for(;;){ /* loop */
ret.add( new Point(x0,y0) );
if (x0==x1 && y0==y1) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
}
return ret;
}
Here is an example of code I use to move an object between two points.
Math.atan2 calculates the theta angle (-pi to +pi) which is the trajectory the object needs to travel. Delta is the time between this this update and the last update and velocity is the desired speed of the object. These all need to be multiplied together and then added to the current position to get the new position.
#Override
protected void update(float delta) {
double theta = Math.atan2(targetPos.y - pos.y, targetPos.x - pos.x);
double valX = (delta * velocity) * Math.cos(theta);
double valY = (delta * velocity) * Math.sin(theta);
pos.x += valX;
pos.y += valY;
}
i'm developing an android drawing application, it has a relativelayout with a custom view in it for drawing with. So far it all works except when I scale the canvas the drawing happens in the wrong spot, it's probably best to show the code rather than try to explain it. Yes I have tried with using path.offset but with no success. The main issue is that when you draw on it when it's been scaled in, while it paints in the right spot, the path is too sensitive, when your finger is centered it's fine but if you go up/down or side to side the path moves faster than your finger.
package com.nick.sketchr;
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.Rect;
import android.view.MotionEvent;
import android.view.View;
public class CustomCanvas extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private Paint mPaint;
private float old_scale;
private float dmx,dmy;
private double scale;
private long timevar2;
private Rect bounds;
public CustomCanvas(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFF000000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(3);
dmx = Main_App.w/2/2;
dmy = Main_App.h/2/2;
scale = 1;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(Main_App.w/2, Main_App.h/2, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mBitmap.eraseColor(Color.WHITE);
}
#Override
protected void onDraw(Canvas canvas) {
float sc = (float) scale;
canvas.scale(sc, sc, Main_App.w/2, Main_App.h/2);
canvas.drawBitmap(mBitmap, dmx, dmy, 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);
mPath.offset(-dmx, -dmy); //this is the problem area
//also the path shows wrong when canvas is scaled... o_o
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX()/(float)scale+bounds.left;
float y = event.getY()/(float)scale+bounds.top;
if(event.getPointerCount() == 2){
timevar2 = event.getEventTime();
float newx = event.getX(0);
float newy = event.getY(0);
float oldx = event.getX(1);
float oldy = event.getY(1);
mPath.reset();
float equa = (float) (Math.pow(newx - oldx, 2) + Math.pow(newy - oldy, 2));
float cscale = (float) Math.sqrt(equa)/100;
float scaled = (cscale - old_scale);
if(scaled < -0.1){
if(scale > 0.1){
scale -= 0.03;
}
}
if(scaled > 0.1){
scale += 0.03;
}
dmx = (float) (((newx + oldx)/2)-(Main_App.w/2/2)*scale);
dmy = (float) (((newy + oldy)/2)-(Main_App.h/2/2)*scale);
old_scale = cscale;
invalidate();
return true;
}
long dt = event.getEventTime() - timevar2;
if(dt < 100){
return true;
}
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;
}
public void clear(){
mBitmap.eraseColor(Color.TRANSPARENT);
invalidate();
System.gc();
}
}
Not entirely sure but you may be scaling the Canvas incorrectly. Instead try:
canvas.scale(width_of_canvas, height_of_canvas)
in your onDraw() method.Currently you are using a scale value of 1.