I need to draw the rectangle from the center of the canvas based on ACTION_MOVE.
I tried this way
public class CustomRectangle extends View {
private Bitmap bitTopLeft;
private Bitmap bitTopRight;
private Bitmap bitBottomLeft;
private Bitmap bitBottomRight;
private Paint rectAnglePaint;
private Context context;
private Paint bitmapPaint;
private Rect rect;
private int maxX;
private int maxY;
private int centerX;
private int centerY;
private Paint canvasPaint;
public CustomRectangle(Context context) {
super(context);
this.context = context;
Init();
}
private void Init() {
bitTopLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitTopRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
bitmapPaint = new Paint();
bitmapPaint.setColor(Color.BLACK);
rect = new Rect();
Display display = ((Activity) context).getWindowManager()
.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
maxX = size.x;
maxY = size.y;
centerX = maxX / 2;
centerY = maxY / 2;
rect.left = centerX / 2 + bitBottomRight.getWidth() / 2;
rect.top = centerY / 2 + bitBottomRight.getWidth() / 2;
rect.right = centerX + centerX / 2 + bitBottomRight.getWidth() / 2;
rect.bottom = centerY + centerY / 2 + bitBottomRight.getWidth() / 2;
canvasPaint = new Paint();
canvasPaint.setColor(Color.TRANSPARENT);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPaint(canvasPaint);
// Path p = new Path();
canvas.drawBitmap(bitTopLeft,
rect.left - bitBottomRight.getWidth() / 2, rect.top
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitTopRight, rect.right - bitBottomRight.getWidth()
/ 2, rect.top - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomLeft, rect.left - bitBottomRight.getWidth()
/ 2, rect.bottom - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomRight,
rect.right - bitBottomRight.getWidth() / 2, rect.bottom
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawRect(rect, rectAnglePaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
invalidate();
break;
case MotionEvent.ACTION_MOVE:
int currentx = (int) event.getX();
int currenty = (int) event.getY();
Calculatingpoint(currentx, currenty);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
private void Calculatingpoint(int x, int y) {
if (x < maxX && y < maxY) {
int dx = 0;
int dy = 0;
dx = rect.right - x;
dy = rect.bottom - y;
// this is working perfect when i touched right bottom.
rect.inset(dx, dy);
invalidate();
}
}
}
But when i touch other corners the rectangle is gone.
I need to draw the rectangle from center point. i need to increase or decrease the width and height based on the touch.
Please advice, how to achieve this. Please give me samples.
I find the Direction from the user touching position, Here is my updated code.
public class CustomRectangle extends View {
private Bitmap bitTopLeft;
private Bitmap bitTopRight;
private Bitmap bitBottomLeft;
private Bitmap bitBottomRight;
private Paint rectAnglePaint;
private Context context;
private Paint bitmapPaint;
private Rect rect;
private int maxX;
private int maxY;
private int centerX;
private int centerY;
private Paint canvasPaint;
private String direction;
public CustomRectangle(Context context) {
super(context);
this.context = context;
Init();
}
private void Init() {
bitTopLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitTopRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
bitmapPaint = new Paint();
bitmapPaint.setColor(Color.BLACK);
rect = new Rect();
Display display = ((Activity) context).getWindowManager()
.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
maxX = size.x;
maxY = size.y;
centerX = maxX / 2;
centerY = maxY / 2;
rect.left = centerX / 2 + bitBottomRight.getWidth() / 2;
rect.top = centerY / 2 + bitBottomRight.getWidth() / 2;
rect.right = centerX + centerX / 2 + bitBottomRight.getWidth() / 2;
rect.bottom = centerY + centerY / 2 + bitBottomRight.getWidth() / 2;
canvasPaint = new Paint();
canvasPaint.setColor(Color.TRANSPARENT);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPaint(canvasPaint);
// Path p = new Path();
canvas.drawBitmap(bitTopLeft,
rect.left - bitBottomRight.getWidth() / 2, rect.top
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitTopRight, rect.right - bitBottomRight.getWidth()
/ 2, rect.top - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomLeft, rect.left - bitBottomRight.getWidth()
/ 2, rect.bottom - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomRight,
rect.right - bitBottomRight.getWidth() / 2, rect.bottom
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawRect(rect, rectAnglePaint);
}
float x1, x2, y1, y2, dx, dy;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x1 = event.getRawX();
y1 = event.getRawY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
x2 = event.getRawX();
y2 = event.getRawY();
int currentx = (int) event.getX();
int currenty = (int) event.getY();
Calculatingpoint(currentx, currenty);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
private void Calculatingpoint(int x, int y) {
if (x2 < maxX && y2 < maxY) {
int dx = 0;
int dy = 0;
// dx = rect.right - (int)x2 ;
// dy = (int)y2 - rect.top ;
//
// Log.e("dx", ""+dx);
// Log.e("dy", ""+dy);
boolean isLeft = false;
boolean isTop = false;
if (x2 > maxX / 2) {
direction = "right";
isLeft = false;
} else {
direction = "left";
isLeft = true;
}
if (y2 > maxY / 2) {
direction = "bottom";
isTop = false;
} else {
direction = "top";
isTop = true;
}
if (!isLeft && !isTop) {
dx = rect.right - (int) x2;
dy = rect.bottom - (int) y2;
}
if (isLeft && !isTop) {
dx = (int) x2 - rect.left;
dy = rect.bottom - (int) y2;
}
if (isLeft && isTop) {
dx = (int) x2 - rect.left;
dy = (int) y2 - rect.top;
}
if (!isLeft && isTop) {
dx = rect.right - (int) x2;
dy = (int) y2 - rect.top;
}
// this will perfect
rect.inset(dx, dy);
invalidate();
}
}
}
Related
I am creating custom view, which is like screen below. I can draw views but I am unable to create separate click events for each view. How to set separate click events for each arc view?. Thanks in advance.
Here is the code:
ArcView.java
public class ItemView extends View {
Utils utils;
int left, right, top, bottom;
private int color;
private int start;
private int sweep;
private boolean inner;
private RectF oval;
public float center_x, center_y;
int divOn;
public float radius;
public ItemView(Context context, int color, int start, int sweep) {
super(context);
this.color = color;
this.start = start;
this.sweep = sweep;
utils = Utils.getInstance(getContext());
}
public ItemView(Context context, int color, int start, int sweep, boolean inner, int divOn) {
super(context);
this.color = color;
this.start = start;
this.sweep = sweep;
this.inner = inner;
this.divOn = divOn;
utils = Utils.getInstance(getContext());
}
public RectF getOval() {
return oval;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//There is another flag for inner white circle
if (inner) {
float width = (float) getWidth();
float height = utils.getScreenHeight() - utils.getActionBarSize();
radius = (width - utils.dpTopixel(50)) / divOn;
Path path = new Path();
path.addCircle((width - utils.dpTopixel(50)) / divOn,
(height - utils.dpTopixel(50)) / divOn, radius,
Path.Direction.CW);
Paint paint = new Paint();
paint.setColor(ContextCompat.getColor(getContext(), color));
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
final RectF oval = new RectF();
paint.setStyle(Paint.Style.FILL);
left = -(int) radius;
right = (int) radius;
top = (getHeight() / 2) - (int) radius;
bottom = (getHeight() / 2) + (int) radius;
oval.set(left,
top,
right,
bottom);
canvas.drawArc(oval, start, sweep, true, paint);
} else {
float width = (float) getWidth();
float height = utils.getScreenHeight() - utils.getActionBarSize();
float radius;
radius = (width - utils.dpTopixel(50)) / divOn;
Path path = new Path();
path.addCircle((width - utils.dpTopixel(50)) / 1,
(width - utils.dpTopixel(50)) / 1, radius,
Path.Direction.CW);
Paint paint = new Paint();
paint.setColor(ContextCompat.getColor(getContext(), color));
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
oval = new RectF();
paint.setStyle(Paint.Style.FILL);
TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
textPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
textPaint.setTextAlign(Paint.Align.CENTER);
/*Paint.FontMetrics metrics = paint.getFontMetrics();
float textheight = Math.abs(metrics.top - metrics.bottom);
float x = getWidth() / 2;
float y = (getHeight() / 2) + (height / 2);
canvas.drawText("My Text", x, y, paint);*/
left = -(int) radius;
right = (int) radius;
top = (getHeight() / 2) - (int) radius;
bottom = (getHeight() / 2) + (int) radius;
center_x = width / 2;
center_y = utils.getOffset();
int xPos = (getWidth() / 2);
int yPos = (int) ((getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2)) ;
//((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
oval.set(left,
top,
right,
bottom);
canvas.drawArc(oval, start, sweep, true, paint);
}
}
}
utils class to detect height and width and convert to px
i want equation to check i click which circle
Create a slice selected listener, override the onTouchEvent in your custom class, calculate which slice the touch event (with the action ACTION_UP) is on using the x,y values. You can then call the function from your slice selected listener and pass details about the selected slice.
I am trying to create a 10x10 grid in android studio that will display in the center of the screen, i am trying to use a for loop to do this but cant get it right. Does anyone know how to do this, ive been looking on google and there are no examples or guides on how to do it. my code is below.
public class Draw extends View {
Paint red = new Paint();
Paint green = new Paint();
float startX;
float stopX;
float startY;
float stopY;
int rectSide = 1000;
public Draw(Context context) {
super(context);
red.setColor(Color.RED);
green.setColor(Color.GREEN);
red.setStrokeWidth(8);
green.setStrokeWidth(8);
}
#Override
public void onDraw(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
for (int i = 2; i < 10; i++) {
startX = width / 2 - rectSide / 2;
startY = i*100;
stopX = width / 2 + rectSide / 2;
stopY = i*100;
canvas.drawLine(startX, startY, stopX, stopY, red);
}
for (int i = 2; i < 10; i++) {
startX = i*100;
startY = height / 2 - rectSide / 2;
stopX = i*100;
stopY = height / 2 + rectSide / 2;
canvas.drawLine(startX, startY, stopX, stopY, red);
}
}
}
Okay so i have ran into a small dilemma.
I have a Generic Pool that allows me to recycle and reuse my sprites. The issue is that i have 6 textures i would like to load into the pool to make sure each of them have a chance to be pulled out of the pool.
What is happening now is the same sprite is being recycled and used over and over. Here is my pool code.
public class ObjectPool extends GenericPool<Sprite> {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private TextureRegion texture1;
private TextureRegion texture2;
private TextureRegion texture3;
private TextureRegion texture4;
private TextureRegion texture5;
private TextureRegion texture6;
private final Scene Gamescene;
private final VertexBufferObjectManager spriteVertexBuffer;
private Sprite fruit;
private final float CAMERA_WIDTH;
private final float CAMERA_HEIGHT;
public static boolean sprite_touched;
private final Camera camera;
// Variables
private final float minDuration = 3.1f;
private final float maxDuration = 5.1f;
private final TextureRegion blueParticleRegion;
private final Sound PersonTouchedSound;
private PointParticleEmitter particleEmitter;
private SpriteParticleSystem particleSystem;
private Random random = new Random();
// ===========================================================
// Constructors
// ===========================================================
public ObjectPool(final TextureRegion Region1,final TextureRegion Region2,final TextureRegion Region3,final TextureRegion Region4,final TextureRegion Region5, final TextureRegion Region6,
VertexBufferObjectManager spriteBufferObject, Camera camera,
Sound sound, Scene scene, TextureRegion particle) {
this.texture1 = Region1;
this.texture2 = Region2;
this.texture3 = Region3;
this.texture4 = Region4;
this.texture5 = Region5;
this.texture6 = Region6;
this.CAMERA_HEIGHT = camera.getHeight();
this.CAMERA_WIDTH = camera.getWidth();
this.camera = camera;
this.PersonTouchedSound = sound;
this.blueParticleRegion = particle;
Gamescene = scene;
initTrail(-70, -70);
this.spriteVertexBuffer = spriteBufferObject;
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
#Override
protected Sprite onAllocatePoolItem() {
int sprite = random.nextInt(1)+6;
switch(sprite){
case 1:
fruit = new Sprite(210, -10, 60, 100, this.texture1,
this.spriteVertexBuffer) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
// Detects if player is outside of bounds
final float width = this.getWidth();
final float height = this.getHeight();
float x = pSceneTouchEvent.getX() - width / 2;
float y = pSceneTouchEvent.getY() - height / 2;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x > (CAMERA_WIDTH - width))
x = CAMERA_WIDTH - width;
if (y > (CAMERA_HEIGHT - height))
y = (CAMERA_HEIGHT - height);
moveTrail(x, this.getY());
if (pSceneTouchEvent.isActionDown()) {
this.setScale(2f);
sprite_touched = true;
PersonTouchedSound.play();
}
if (pSceneTouchEvent.isActionUp()) {
this.setScale(1f);
particleEmitter.setCenter(-70, -70);
sprite_touched = false;
}
this.setPosition(x, this.getY());
return true;
}
};
break;
case 2:
fruit = new Sprite(210, -10, 60, 100, this.texture2,
this.spriteVertexBuffer) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
// Detects if player is outside of bounds
final float width = this.getWidth();
final float height = this.getHeight();
float x = pSceneTouchEvent.getX() - width / 2;
float y = pSceneTouchEvent.getY() - height / 2;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x > (CAMERA_WIDTH - width))
x = CAMERA_WIDTH - width;
if (y > (CAMERA_HEIGHT - height))
y = (CAMERA_HEIGHT - height);
moveTrail(x, this.getY());
if (pSceneTouchEvent.isActionDown()) {
this.setScale(2f);
sprite_touched = true;
PersonTouchedSound.play();
}
if (pSceneTouchEvent.isActionUp()) {
this.setScale(1f);
particleEmitter.setCenter(-70, -70);
sprite_touched = false;
}
this.setPosition(x, this.getY());
return true;
}
};
break;
case 3:
fruit = new Sprite(210, -10, 60, 100, this.texture3,
this.spriteVertexBuffer) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
// Detects if player is outside of bounds
final float width = this.getWidth();
final float height = this.getHeight();
float x = pSceneTouchEvent.getX() - width / 2;
float y = pSceneTouchEvent.getY() - height / 2;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x > (CAMERA_WIDTH - width))
x = CAMERA_WIDTH - width;
if (y > (CAMERA_HEIGHT - height))
y = (CAMERA_HEIGHT - height);
moveTrail(x, this.getY());
if (pSceneTouchEvent.isActionDown()) {
this.setScale(2f);
sprite_touched = true;
PersonTouchedSound.play();
}
if (pSceneTouchEvent.isActionUp()) {
this.setScale(1f);
particleEmitter.setCenter(-70, -70);
sprite_touched = false;
}
this.setPosition(x, this.getY());
return true;
}
};
break;
case 4:
fruit = new Sprite(210, -10, 60, 100, this.texture4,
this.spriteVertexBuffer) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
// Detects if player is outside of bounds
final float width = this.getWidth();
final float height = this.getHeight();
float x = pSceneTouchEvent.getX() - width / 2;
float y = pSceneTouchEvent.getY() - height / 2;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x > (CAMERA_WIDTH - width))
x = CAMERA_WIDTH - width;
if (y > (CAMERA_HEIGHT - height))
y = (CAMERA_HEIGHT - height);
moveTrail(x, this.getY());
if (pSceneTouchEvent.isActionDown()) {
this.setScale(2f);
sprite_touched = true;
PersonTouchedSound.play();
}
if (pSceneTouchEvent.isActionUp()) {
this.setScale(1f);
particleEmitter.setCenter(-70, -70);
sprite_touched = false;
}
this.setPosition(x, this.getY());
return true;
}
};
break;
case 5:
fruit = new Sprite(210, -10, 60, 100, this.texture5,
this.spriteVertexBuffer) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
// Detects if player is outside of bounds
final float width = this.getWidth();
final float height = this.getHeight();
float x = pSceneTouchEvent.getX() - width / 2;
float y = pSceneTouchEvent.getY() - height / 2;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x > (CAMERA_WIDTH - width))
x = CAMERA_WIDTH - width;
if (y > (CAMERA_HEIGHT - height))
y = (CAMERA_HEIGHT - height);
moveTrail(x, this.getY());
if (pSceneTouchEvent.isActionDown()) {
this.setScale(2f);
sprite_touched = true;
PersonTouchedSound.play();
}
if (pSceneTouchEvent.isActionUp()) {
this.setScale(1f);
particleEmitter.setCenter(-70, -70);
sprite_touched = false;
}
this.setPosition(x, this.getY());
return true;
}
};
break;
case 6:
fruit = new Sprite(210, -10, 60, 100, this.texture6,
this.spriteVertexBuffer) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
// Detects if player is outside of bounds
final float width = this.getWidth();
final float height = this.getHeight();
float x = pSceneTouchEvent.getX() - width / 2;
float y = pSceneTouchEvent.getY() - height / 2;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x > (CAMERA_WIDTH - width))
x = CAMERA_WIDTH - width;
if (y > (CAMERA_HEIGHT - height))
y = (CAMERA_HEIGHT - height);
moveTrail(x, this.getY());
if (pSceneTouchEvent.isActionDown()) {
this.setScale(2f);
sprite_touched = true;
PersonTouchedSound.play();
}
if (pSceneTouchEvent.isActionUp()) {
this.setScale(1f);
particleEmitter.setCenter(-70, -70);
sprite_touched = false;
}
this.setPosition(x, this.getY());
return true;
}
};
break;
}
return fruit;
}
#Override
protected void onHandleObtainItem(final Sprite pItem) {
pItem.reset();
}
#Override
protected void onHandleRecycleItem(final Sprite pItem) {
pItem.setVisible(false);
pItem.setPosition(210, -5);
pItem.setIgnoreUpdate(true);
particleEmitter.setCenter(-70, -70);
pItem.clearEntityModifiers();
}
private void initTrail(float x, float y) {
this.particleEmitter = new PointParticleEmitter(x, y);
this.particleSystem = new SpriteParticleSystem(particleEmitter, 100, 100,
360, this.blueParticleRegion, spriteVertexBuffer);
particleSystem.addParticleInitializer(new AlphaParticleInitializer<Sprite>(
1));
particleSystem
.addParticleInitializer(new BlendFunctionParticleInitializer<Sprite>(
GLES20.GL_BLEND_COLOR, GLES20.GL_ONE));
particleSystem
.addParticleInitializer(new VelocityParticleInitializer<Sprite>(0));
particleSystem
.addParticleInitializer(new ExpireParticleInitializer<Sprite>(.6f));
particleSystem.addParticleModifier(new ScaleParticleModifier<Sprite>(0, 1,
1, 0));
Gamescene.attachChild(particleSystem);
}
public void moveTrail(float x, float y) {
particleEmitter.setCenter(x, y);
}
private void fillPool(){
}
}
You must create 6 pools instead of one or use TiledTextureRegion and switch to required tile on obtaining item.
I'm writing a application that there are a upper-imageview and a scollview under the imageview.
The imageview is used by a class provided by my friend, it supports pinch zoom, but after i zood in the image then i touch the scollview below, the image will resize to fit the imageview's size.
It's a big trouble for me to build my app because i want to scroll the information after zooming.
Then i try to change the scrollview to be a imageview as well, i find that there is not resizimg between two views.
I wonder if it is possible to disable the resizing function for the imageview to implement my idea.
The class below is used for the imageview.
Thank you for solutions.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
Matrix matrix = new 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;
float redundantXSpace, redundantYSpace;
float width, height;
static final int CLICK = 3;
float saveScale = 1f;
float right, bottom, origWidth, origHeight, bmWidth, bmHeight;
int windowId;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context,int id) {
super(context);
super.setClickable(true);
this.context = context;
this.windowId=id;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
setBackgroundColor(Color.BLACK);
matrix.setTranslate(1f, 1f);
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(event.getX(), event.getY());
start.set(last);
mode = DRAG;
EasyPageActivity.resetView();
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float scaleWidth = Math.round(origWidth * saveScale);
float scaleHeight = Math.round(origHeight * saveScale);
if (scaleWidth < width) {
deltaX = 0;
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
} else if (scaleHeight < height) {
deltaY = 0;
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
} else {
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
}
matrix.postTranslate(deltaX, deltaY);
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();
if(windowId==1){
EasyPageActivity.defineId(1);
}
if(windowId==2){
EasyPageActivity.defineId(2);
}
if(windowId==3){
EasyPageActivity.defineId(3);
}
if(windowId==4){
EasyPageActivity.defineId(4);
}
if(windowId==5){
EasyPageActivity.defineId(5);
}
if(windowId==6){
EasyPageActivity.defineId(6);
}
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
bmWidth = bm.getWidth();
bmHeight = bm.getHeight();
}
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 = (float)Math.min(Math.max(.301f, detector.getScaleFactor()), 4);
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
if (origWidth * saveScale <= width || origHeight * saveScale <= height) {
matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
if (mScaleFactor < 1) {
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (Math.round(origWidth * saveScale) < width) {
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
} else {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
}
}
}
} else {
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
}
}
return true;
}
}
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
//Fit to screen.
float scale;
float scaleX = (float)width / (float)bmWidth;
float scaleY = (float)height / (float)bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
setImageMatrix(matrix);
saveScale = 1f;
// Center the image
redundantYSpace = (float)height - (scale * (float)bmHeight) ;
redundantXSpace = (float)width - (scale * (float)bmWidth);
redundantYSpace /= (float)2;
redundantXSpace /= (float)2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = width - 2 * redundantXSpace;
origHeight = height - 2 * redundantYSpace;
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
setImageMatrix(matrix);
}
}
Did you find any solution?
If not, then you must understand that your image re-sizes itself to fit the screen in the onMeasure method. So, to detect the problem you must debug and see why onMeasure is called after you change the focus to the ScrollView. Try to put some logs and see the flow of actions.
I'm fairly new to Android and have been toying around with Canvas. I'm attempting to draw an arrow but I'm only having luck with drawing the shaft, none of the arrowhead is working.
I have searched a bit and found a Java example, but Android doesn't have GeneralPath or AffineTransform.
Right now my code looks like the following (the arrowhead looks nothing like an arrowhead):
public class DrawableView extends View {
Context mContext;
private int centerX;
private int centerY;
private int radius;
private double arrLength;
private double arrHeading;
private int margin = 10;
public DrawableView(Context context) {
super(context);
mContext = context;
}
#Override
protected void onDraw(Canvas canvas) {
//Paint Background
Paint background = new Paint();
background.setColor(getResources().getColor(R.color.background);
canvas.drawRect(0, 0, getWidth(), getHeight(), background);
//Set vars for Arrow Paint
Paint paint = new Paint();
paint.setColor(getResources().getColor(R.color.arrowColor);
centerX = getWidth() / 2;
centerY = getHeight() / 2;
arrLength = radius - 10;
if(centerX < centerY)
radius = centerX - margin;
else
radius = centerY - margin;
//Draw Shaft
int[] xy = findArrowPos(arrLength, arrHeading);
canvas.drawLine(centerX, centerY, xy[0], xy[1], paint);
//Draw ArrowHead
//This is where I'm confused
}
private int[] findArrowPos(double length, double angle) {
int[] points = new int[2];
double theta = Math.toRadians(angle);
points[0] = centerX + (int) (length * Math.cos(theta));
points[1] = centerY + (int) (length * Math.sin(theta));
return points;
}
}
I have taken a look at the following threads for guidance:
* http://www.java-forums.org/awt-swing/6241-how-u-rotate-arrow-mark-line-moves-accordingly.html
* How to draw a directed arrow line in Java?
How about using "Path myPath = new Path();" where you would give the x and y positions to create a triangle using lines and filling it. You can read about it, here is an example I took from somewhere.
// create and draw triangles
// use a Path object to store the 3 line segments
// use .offset to draw in many locations
// note: this triangle is not centered at 0,0
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.RED);
Path path = new Path();
path.moveTo(0, -10);
path.lineTo(5, 0);
path.lineTo(-5, 0);
path.close();
path.offset(10, 40);
canvas.drawPath(path, paint);
path.offset(50, 100);
canvas.drawPath(path, paint);
// offset is cumlative
// next draw displaces 50,100 from previous
path.offset(50, 100);
canvas.drawPath(path, paint);
My Arrow Drawing code, maybe it can be of some use for somebody:
/**
* Draw an arrow
* change internal radius and angle to change appearance
* - angle : angle in degrees of the arrows legs
* - radius : length of the arrows legs
* #author Steven Roelants 2017
*
* #param paint
* #param canvas
* #param from_x
* #param from_y
* #param to_x
* #param to_y
*/
private void drawArrow(Paint paint, Canvas canvas, float from_x, float from_y, float to_x, float to_y)
{
float angle,anglerad, radius, lineangle;
//values to change for other appearance *CHANGE THESE FOR OTHER SIZE ARROWHEADS*
radius=10;
angle=15;
//some angle calculations
anglerad= (float) (PI*angle/180.0f);
lineangle= (float) (atan2(to_y-from_y,to_x-from_x));
//tha line
canvas.drawLine(from_x,from_y,to_x,to_y,paint);
//tha triangle
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(to_x, to_y);
path.lineTo((float)(to_x-radius*cos(lineangle - (anglerad / 2.0))),
(float)(to_y-radius*sin(lineangle - (anglerad / 2.0))));
path.lineTo((float)(to_x-radius*cos(lineangle + (anglerad / 2.0))),
(float)(to_y-radius*sin(lineangle + (anglerad / 2.0))));
path.close();
canvas.drawPath(path, paint);
}
I try this code it has been working perfectly:
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
startPoint = new PointF(event.getX(), event.getY());
endPoint = new PointF();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - mX);
System.out.println("action move");
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
// currentDrawingPath.path.quadTo(mX,mY,(x + mX)/2, (y + mY)/2);
}
mX = x;
mY = y;
endPoint.x = event.getX();
endPoint.y = event.getY();
isDrawing = true;
invalidate();
break;
case MotionEvent.ACTION_UP:
mPath.lineTo(mX, mY);
float deltaX = endPoint.x-startPoint.x;
float deltaY = endPoint.y-startPoint.y;
float frac = (float) 0.1;
float point_x_1 = startPoint.x + (float) ((1 - frac) * deltaX + frac * deltaY);
float point_y_1 = startPoint.y + (float) ((1 - frac) * deltaY - frac * deltaX);
float point_x_2 = endPoint.x;
float point_y_2 = endPoint.y;
float point_x_3 = startPoint.x + (float) ((1 - frac) * deltaX - frac * deltaY);
float point_y_3 = startPoint.y + (float) ((1 - frac) * deltaY + frac * deltaX);
mPath.moveTo(point_x_1, point_y_1);
mPath.lineTo(point_x_2, point_y_2);
mPath.lineTo(point_x_3, point_y_3);
mPath.lineTo(point_x_1, point_y_1);
mPath.lineTo(point_x_1, point_y_1);
mCanvas.drawPath(mPath, ppaint);
endPoint.x = event.getX();
endPoint.y = event.getY();
isDrawing = false;
invalidate();
break;
default:
break;
}
I've been having the same problem, I need an arrow to point in a certain direction. After playing around with drawing algorithms I decided the simplest method is to use a bitmap & simply use a Matrix to rotate it, e.g.
ImageView image = (ImageView) findViewById(R.id.bitmap_image);
Bitmap bMap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
Matrix mat = new Matrix();
mat.postRotate(90);
Bitmap bMapRotate = Bitmap.createBitmap(bMap, 0, 0, bMap.getWidth(), bMap.getHeight(), mat, true);
image.setImageBitmap(bMapRotate);
then your bitmap can be any fancy looking arrow you like.
If you are looking for the solution to draw thousands of arrows under a second, with fixed length head lines, try this function (draws only arrow heads):
private void fillArrow(Paint paint, Canvas canvas, float x0, float y0, float x1, float y1) {
paint.setStyle(Paint.Style.STROKE);
int arrowHeadLenght = 10;
int arrowHeadAngle = 45;
float[] linePts = new float[] {x1 - arrowHeadLenght, y1, x1, y1};
float[] linePts2 = new float[] {x1, y1, x1, y1 + arrowHeadLenght};
Matrix rotateMat = new Matrix();
//get the center of the line
float centerX = x1;
float centerY = y1;
//set the angle
double angle = Math.atan2(y1 - y0, x1 - x0) * 180 / Math.PI + arrowHeadAngle;
//rotate the matrix around the center
rotateMat.setRotate((float) angle, centerX, centerY);
rotateMat.mapPoints(linePts);
rotateMat.mapPoints(linePts2);
canvas.drawLine(linePts [0], linePts [1], linePts [2], linePts [3], paint);
canvas.drawLine(linePts2 [0], linePts2 [1], linePts2 [2], linePts2 [3], paint);
}
Based on https://gamedev.stackexchange.com/questions/44456/drawing-lines-on-android-with-matrix
Here is code working perfect for me draw arrow head while drawing line on canvas
package com.example.canvasexample;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
public class DrawerViewArrow extends View {
private ArrayList<Path> drawingLinePath;
private ArrayList<Path> drawingArrowPath;
private ArrayList<Paint> drawingLinePaint;
private int pathIndex = 0;
private float startX = -1, startY = -1;
private float mX = -1, mY = -1;
public int arrowLength = 80;
public int arrowWidth = 45;
public int strokeWidth = 10;
public DrawerViewArrow(Context context) {
super(context);
initPath();
}
public DrawerViewArrow(Context context, #NonNull AttributeSet attrs) {
super(context, attrs);
initPath();
}
public DrawerViewArrow(Context context, #NonNull AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPath();
}
private Paint initPaint() {
Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(strokeWidth);
return mPaint;
}
private void initPath() {
drawingLinePath = new ArrayList<>();
drawingArrowPath = new ArrayList<>();
drawingLinePath.add(new Path());
drawingArrowPath.add(new Path());
drawingLinePaint = new ArrayList<>();
drawingLinePaint.add(initPaint());
pathIndex++;
}
private Path createPath(MotionEvent event) {
Path path = new Path();
path.moveTo(event.getX(), event.getY());
return path;
}
private void updateIndex(MotionEvent event) {
if (pathIndex == drawingLinePath.size()) {
drawingLinePath.add(createPath(event));
drawingArrowPath.add(createPath(event));
drawingLinePaint.add(initPaint());
pathIndex++;
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (startX > -1 && mX > -1) {
canvas.drawLine(startX, startY, mX, mY, initPaint());
drawArrow(canvas);
}
for (int index = 0; index < pathIndex; index++) {
Path path = drawingLinePath.get(index);
Path arrow_path = drawingArrowPath.get(index);
Paint paint = drawingLinePaint.get(index);
canvas.drawPath(path, paint);
canvas.drawPath(arrow_path, paint);
}
}
private void drawArrow(Canvas canvas) {
double angle = calculateAngle(startX, startY, mX, mY);
float final_angle = (float) (180 - angle);
Path arrow_path = new Path();
Matrix arrow_matrix = new Matrix();
arrow_matrix.postRotate(final_angle, mX, mY);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX - arrowWidth, mY + arrowLength);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX + arrowWidth, mY + arrowLength);
arrow_path.lineTo(mX - (arrowWidth), mY + arrowLength);
arrow_path.transform(arrow_matrix);
canvas.drawPath(arrow_path, initPaint());
}
private void saveArrow() {
if (mX == -1 || mY == -1) {
return;
}
double angle = calculateAngle(startX, startY, mX, mY);
float final_angle = (float) (180 - angle);
Path arrow_path = drawingArrowPath.get(pathIndex - 1);
Matrix arrow_matrix = new Matrix();
arrow_matrix.postRotate(final_angle, mX, mY);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX - arrowWidth, mY + arrowLength);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX + arrowWidth, mY + arrowLength);
arrow_path.lineTo(mX - (arrowWidth), mY + arrowLength);
arrow_path.transform(arrow_matrix);
}
public double calculateAngle(double x1, double y1, double x2, double y2) {
double angle = Math.toDegrees(Math.atan2(x2 - x1, y2 - y1));
angle = angle + Math.ceil(-angle / 360) * 360; //Keep angle between 0 and 360
return angle;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case ACTION_UP:
actionUp(event);
break;
case ACTION_MOVE:
actionMove(event);
break;
case ACTION_DOWN:
actionDown(event);
break;
}
invalidate();
return true;
}
private void actionDown(MotionEvent event) {
updateIndex(event);
startX = event.getX();
startY = event.getY();
}
private void actionMove(MotionEvent event) {
mX = event.getX();
mY = event.getY();
}
private void actionUp(MotionEvent event) {
drawingLinePath.get(pathIndex - 1).lineTo(event.getX(), event.getY());
saveArrow();
startX = -1;
startY = -1;
mX = -1;
mY = -1;
}
}
Use a Path as below and adjust the co-ordinates accordingly:
// Construct a wedge-shaped path
Path mPath = new Path();
mPath.moveTo(0, -50);
mPath.lineTo(-20, 60);
mPath.lineTo(0, 50);
mPath.lineTo(20, 60);
mPath.close();
Copypast from this answer https://stackoverflow.com/a/29383352/9975029
private void fillArrow(Canvas canvas, float x0, float y0, float x1, float y1) {
paint.setStyle(Paint.Style.FILL);
float deltaX = x1 - x0;
float deltaY = y1 - y0;
double distance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
float frac = (float) (1 / (distance / 30));
float point_x_1 = x0 + (float) ((1 - frac) * deltaX + frac * deltaY);
float point_y_1 = y0 + (float) ((1 - frac) * deltaY - frac * deltaX);
float point_x_2 = x1;
float point_y_2 = y1;
float point_x_3 = x0 + (float) ((1 - frac) * deltaX - frac * deltaY);
float point_y_3 = y0 + (float) ((1 - frac) * deltaY + frac * deltaX);
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(point_x_1, point_y_1);
path.lineTo(point_x_2, point_y_2);
path.lineTo(point_x_3, point_y_3);
path.lineTo(point_x_1, point_y_1);
path.lineTo(point_x_1, point_y_1);
path.close();
canvas.drawPath(path, paint);
}
Here's my arrow drawing code without using trig functions explicitly (although the underlying math obviously uses trig)
The math makes an arrow head like half of a square (cut diagonally) where variable L is the length of the diagonal. Also, the arrow ends at point p2 which means that for small difference between p2 and p1, the arrow head will be drawn 'before' p2 for sufficient L. Also if p1 and p2 are the same, the arrow will not be drawn because the math would cause division by zero.
I suggest you use Paint.Style.FILL_AND_STROKE to draw this arrow.
I'm open for any questions.
void drawArrow(Canvas canvas, Point p1, Point p2, float L) {
float fsin, fcos;
double d;
if(p1.equals(p2))
return;
d = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
fsin = (p2.y - p1.y)/(float)d;
fcos = (p2.x - p1.x)/(float)d;
PointF p3 = new PointF(p2.x - L/2*(fsin + fcos), p2.y + L/2*(fcos - fsin));
PointF p4 = new PointF(p2.x + L/2*(fsin - fcos), p2.y - L/2*(fsin + fcos));
canvas.drawLine(p1.x, p1.y, p2.x, p2.y, arrowPaint);
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(p2.x, p2.y);
path.lineTo(p3.x, p3.y);
path.lineTo(p4.x, p4.y);
path.close();
canvas.drawPath(path, arrowPaint);
}