So I am using onDraw in a custom View class to draw shapes on a RelativeLayout + TableLayout all that works fine, and I have another class that I use to draw lines from Point A to Point B etc. example below:
My Goal:
If I drag my finger from Point A (objectA) to Point B(objectB), how can I delete those 2 View Objects from the canvas? I added a method:
objectA.delete();
objectB.delete();
that should delete both A and B when I drag my finger through MotionEvent, but it only deletes one and not the other, so i am thinking it's not retroactive?
Codes below:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
/// get the child that corresponds to the first touch
DotView objectA = getChildForTouch((TableLayout) v, x, y);
return true;
case MotionEvent.ACTION_MOVE:
///used the x - y to get the object for every other shape the user`S finger passes over.
DotView objectB = getChildForTouch((TableLayout) v, x, y);
/// just update positions
line.setCoords(mStartX, mStartY, (int) x, (int) y);
objectA.delete(); ///Delete first shape
objectB.delete(); ///Delete second shape
break;
case MotionEvent.ACTION_UP:
///Gets the last shape where the user released their fingers
endView = getChildForTouch((TableLayout) v, x, y);
break;
Delete method inside the: DotView extends View class:
private static class DotView extends View {
private static final int DEFAULT_SIZE = 100;
private Paint mPaint = new Paint();
private Rect mBorderRect = new Rect();
private Paint mCirclePaint = new Paint();
private int mRadius = DEFAULT_SIZE / 4;
public DotView(Context context) {
super(context);
mPaint.setStrokeWidth(2.0f);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
mCirclePaint.setColor(Color.CYAN);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.parseColor("#0099cc"));
mBorderRect.left = 0;
mBorderRect.top = 0;
mBorderRect.right = getMeasuredWidth();
mBorderRect.bottom = getMeasuredHeight();
canvas.drawRect(mBorderRect, mPaint);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,
mRadius, mCirclePaint);
}
public void delete(){
mPaint.setColor(Color.TRANSPARENT);
}
}
Just something simple to fake delete the circles
Thanks in advance guys.. more codes can be provided if need be.
Edit: If I can accomplish this in any other way please feel free to share. (Draw circles on a Grid and delete the ones I used my finger to draw my line over)
First of all try changing your delete() method to this:
public void delete(){
mPaint.setColor(Color.TRANSPARENT);
invalidate();
}
You need to let your View know that you want it to redraw itself (that's why the invalidate() call).
Related
class Draw extends View {
private Paint mPaint;
public Button mCircles;
DrawFragment mDrawFragment;
private Circle mCurrentCircle;
private List<Circle> mCircle= new ArrayList<>();
public Draw(Context context){
this(context,null);
}
public Draw(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint=new Paint();
mPaint.setColor(Color.RED);
mPaint.setAlpha(80);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Circle circle : mCircle) {
float x1 = circle.getCenter().x;
float x2 = circle.getFinal().x;
float y1 = circle.getCenter().y;
float y2 = circle.getFinal().y;
double radius = Math.sqrt(Math.pow(dpx1 - dpx2, 2) + Math.pow(dpy1 - dpy2, 2));
float rad = (float) radius;
canvas.drawCircle(dpx1, dpy1, rad, mPaint);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mCurrentCircle = new Circle(current);
mCircle.add(mCurrentCircle);
break;
case MotionEvent.ACTION_MOVE:
if (mCurrentCircle != null) {
mCurrentCircle.setFinal(current);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
mCurrentCircle = null;
invalidate(); break;
}
}
Log.i(TAG, "onTouchEvent: at x="+current.x + ", y =" + current.y);
return true;
}
1.I'm saving these coordinates of circles drawn on canvas and this is to mark damages on a car.
but when i use these values to display on a bigger device , Circle positions are not intact .. what can be done?
All the coordinates are referenced to a device with a screen determined by pixels and pixels per inches.
If you wish to use the same information on a different device, you must set a sort of anchor point which works for you as a reference.
For example, if those circles will be drawn over a background image of a car, you should be able to reference theme to the position of that picture.
With that information, on any other device, you should be able to redraw those circles on any device based on the fact the coordinates are not referenced to the device but are reference to the car image.
I hope this help you to re think the problem.
I have a code in my custom view, where if I touch, a circle is drawn. When I move the circle follow along. Code as below
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (circleBounds != null) {
canvas.drawArc(circleBounds, 360, 360, false, mPaint);
}
}
// when ACTION_DOWN start touch according to the x,y values
private void startTouch(float x, float y) {
mPath.moveTo(x, y);
mX = x;
mY = y;
circleBounds.set(mX - mRadius, mY - mRadius, mX + mRadius, mY + mRadius);
invalidate();
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(x, y);
break;
case MotionEvent.ACTION_MOVE:
startTouch(x, y);
break;
}
return true;
}
When I move slowly, the movement is smooth. But when I move fast, one could feel the flickering. I guess invalidate() happens many time, causing the flickering. Is there a way to make it smoother (reduce the flickering on the drawn circle)?
Invalidate is not your issue, the system has double buffering, and you should not see a flicker.
From your code, I can think of 2 way to improve:
in onStartTouch you call mPath.moveTo(). It is not clear why you need mPath, but this path will keep growing and growing and will need more memory
canvas.drawArc is rendered by by CPU (it creates a bitmap and draws the arc into it on every call to onDraw). It would be faster to render yourself the circle once into a bitmap, and draw that bitmap in onDraw. If you keep using the same bitmap, it will be loaded once to the GPU and drawing will be much faster
I know this question is old, but in case someone else has same problem, the solution is to use postInvalidate() instead of the invalidate() in the onTouchEvent method, which will make it much more fluid.
As we receive many events in onTouchEvent, it's more effective to put invalidate call into events queue for future processing instead of doing it immediately.
public class MyView extend FrameLayout implement OnTouchListener{
public MyView(params){
init();
}
private void init(){
ImageView imageView = new ImageView();
imageView.setImageResource(R.mipmap.logo);
addView(imageview)
imageView.setOnTouchListener(this);
}
private boolean onTouch(params){
//some logic to control
//use imageView.setLayoutParams() instead of invalidate();
}
}
I want to let the user to draw lines on the image and user can erase tline they draw. So far I can draw lines on the custom ImageView created as below. However, I tried a lot of method and all of them draw black line when I selected erase mode. I want the part after earse still is the Image I set and implement the erase just like drawing lines. How can I do this?
public class PaintView : ImageView
{
private Paint _Paint = new Paint();
private Paint ErasePaint= new Paint();
public Bitmap _Bmp;
private Canvas _Canvas = null;
private PointF _StartPt = new PointF();
private PointF _EndPt = new PointF();
public int status=1; //1 draw, 0 erase
public PaintView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
public void Initialize()
{
_Paint = new Paint ();
_Paint.Color = new Color (255, 255, 255);
_Paint.StrokeWidth = 5;
_Paint.StrokeCap = Paint.Cap.Round;
int alpha = 3;
_Bmp = Bitmap.CreateScaledBitmap(_Bmp,_Bmp.Width*alpha, _Bmp.Height*alpha, false);
_Canvas = new Canvas(_Bmp);
this.SetImageBitmap(_Bmp);
}
override public bool OnTouchEvent(MotionEvent e)
{
if (status == 1) {
switch (e.Action)
{
case MotionEventActions.Down:
_StartPt.Set(e.GetX() - 1, e.GetY() - 1);// for just a tapping
DrawLine(e);
break;
case MotionEventActions.Move:
DrawLine(e);
break;
case MotionEventActions.Up:
DrawLine(e);
break;
}
}
if (status == 0) {
switch (e.Action)
{
case MotionEventActions.Down:
_StartPt.Set(e.GetX() - 1, e.GetY() - 1);// for just a tapping
eraseCanvas(e);
break;
case MotionEventActions.Move:
eraseCanvas(e);
break;
case MotionEventActions.Up:
eraseCanvas(e);
break;
}
}
return true;
}
private void DrawLine(MotionEvent e)
{
_EndPt.Set(e.GetX(), e.GetY());
_Canvas.DrawLine(_StartPt.X, _StartPt.Y, _EndPt.X, _EndPt.Y, _Paint);
_StartPt.Set(_EndPt);
Invalidate();
}
private void eraseCanvas(MotionEvent e)
{
_EndPt.Set(e.GetX(), e.GetY());
ErasePaint.Color = Color.Transparent;
ErasePaint.StrokeWidth = 5;
ErasePaint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.Clear));
_Canvas.DrawLine(_StartPt.X, _StartPt.Y, _EndPt.X, _EndPt.Y, ErasePaint);
_StartPt.Set(_EndPt);
Invalidate();
}
}
Well then, the way I do it in my app is to set the bitmap I draw on as a background to the canvas View on initialization - this should make the line go transparent after the drawing operation is done. I am not really familiar with Xamarin but in Android Java I just go something like:
BitmapDrawable bd = new BitmapDrawable(bmp);
canvas.setBitmapDrawable(bd); //or this.setBitmapDrawable(bd);
after instantiating the Canvas. It's because the xfer mode clear scrapes off everything showing View beneath the Canvas, so the line stays black. I believe you should adapt it to your code fairly easy.
If it works as I described (the line is black during drawing, and then poof - goes transparent), then to make it transparent right away, the way to go for me was to turn off hardware acceleration when drawing with Mode.Clear, again, Android Java:
boolean bool = "rubber".equals(tool);
if (bool) {
this.setLayerType(LAYER_TYPE_SOFTWARE, canvasPaint); //or canvas.setLayer...
}
//drawing stuff
if (bool) {
this.setLayerType(LAYER_TYPE_HARDWARE, canvasPaint);
}
Good luck, let me know how did it go or if you need some clarification or something. :)
I have drawn a image on ONTOUCH with (X,Y)cordinates while moving the image it's cordinates should move along with the image when it reaches it end point the image should be drawn there or else the image should go back to it's starting position.
For eg:(in our mobile if kepad lock we will drag it to open if it reaches it end point the lock will open or else the image will reaches it's starting position).
Reffered link:http://www.vogella.com/tutorials/AndroidTouch/article.html
Here is my code:
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mLocked) {
return false;
}
final int action = event.getAction();
float x = event.getX();
float y = event.getY();
if (mOnDrawerScrollListener != null) {
mOnDrawerScrollListener.onScrollStarted();
}
final int pt = getSide();
mTouchDelta = (int)(y - pt);
prepareTracking(pt);
mVelocityTracker.addMovement(event);
final Rect frame = mFrame;
final View handle = mHandle;
If anyone have idea about this please help me friends.
If you want to get(x, y) cordinates of view (ImageView), just use view.getX(), view.getY(). But when you get it in onCreate(), the results will be (0, 0), because it is not created yet. You should use view.getX(), view.getY() in onGlobalLayoutListener(). For e.g:
rlRoot.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
rlRoot.getViewTreeObserver().removeGlobalOnLayoutListener(this);
Log.d("X", view.getX()+"");
Log.d("Y", view.getY()+"");
}
});
With rlRoot is the layout includes your view and view may be ImageView or any View.
Try it, hope it help.
I would like to remove the body when the item is clicked. As you see i have it set up as a wallfixture.
When the item is clicked the sprite is removed, now i just would like to remove the body also..
public void addSprites(Scene scene, int x,int y,int width,int height,String type,Body body){
Sprite sprite = null;
if(type.equals(TAG_ENTITY_ATTRIBUTE_TYPE_VALUE_WOOD)) {
sprite = new Sprite(x, y, width, height, this.wood, this.getVertexBufferObjectManager()){
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
mScene.detachChild(this);
mPhysicsWorld.destroyBody(woodBody);
return true;
}
};
final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0.5f, 0.5f);
woodBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, sprite, BodyType.StaticBody, wallFixtureDef);
mScene.registerTouchArea(sprite);
Log.e("TYPE", "Wood");
}
scene.attachChild(sprite);
}
What i want to do is when the item is clicked i would like to remove the sprite & the body it is attached to. The code i have works but the only problem is it removes ALL of the bodies, i use the method to attach items to a level, so its a total of 3 items, and when one is clicked the body is removed from ALL of the sprites, when it should just be the one clicked.
Anyone know how to go about doing this?
I am not sure but probably that happens because you use the same variable for all bodies
woodBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, sprite, BodyType.StaticBody, wallFixtureDef);
To do good connection between body and sprite I suggest you to extend Sprite and declare body variable inside your class. For example I do this in that way
public class Ball extends Sprite{
final FixtureDef ballFixtureDef = PhysicsFactory.createFixtureDef(1.0f, 0.0f, 0.0f, false, Main.CATEGORYBIT_BALL, Main.MASKBITS_BALL, (short)0);
Body body;
float velocityX, velocityY;
int type;
public Ball(float pX, float pY, TextureRegion pTextureRegion, PhysicsWorld pWorld, float velocityX, float velocityY, int type)
{
super(pX, pY, pTextureRegion);
this.type = type;
this.velocityX = velocityX;
this.velocityY = velocityY;
body = PhysicsFactory.createCircleBody(pWorld, this, BodyType.DynamicBody, ballFixtureDef);
body.setUserData(Ball.this);
pWorld.registerPhysicsConnector(new PhysicsConnector(this, body, true, true));
}
}
and method for destroy sprite with body
private void destroyBall(final Ball ball)
{
this.runOnUpdateThread(new Runnable(){
#Override
public void run() {
final Body body = ball.body;
mPhysicsWorld.unregisterPhysicsConnector(mPhysicsWorld.getPhysicsConnectorManager().findPhysicsConnectorByShape(ball));
mPhysicsWorld.destroyBody(body);
mScene.detachChild(ball);
ballsList.remove(ball);
}});
}
and you just create object in that way
Ball b = new Ball(float pX, float pY, TextureRegion pTextureRegion, PhysicsWorld pWorld, float velocityX, float velocityY, int type)
{
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY)
{
destroyBall(this);
}
}
I'm not exactly sure, but I think you need to flag items or objects that need to be removed first. Then when the game updates it should go through all of the objects and check which ones are flagged and then remove them. I'm new to this too but here goes nothing:
So a good way to do this in AndEngine is to first make an IUpdateHandler like so:
/**
* This wil get called every frame and is placed inside of your
* main game activity or where ever you want.
**/
public IUpdateHandler getCollisionUpdateHandler(){
return new IUpdateHandler(){
#Override
public void onUpdate(float pSecondsElapsed) {
// loop through all of your sprites
for(int i=0; i<GameActivity.this.spriteList.length; i++) {
//if sprite is flagged for deletion, begin removal
if( GameActivity.this.spriteList.get(i).isDelete()){
// get the next sprite to delete
final mSprite Object = GameActivity.this.spriteList.get(i);
// can remove it from the list now
Game.this.spriteList.remove(i);
final Scene scene = GameActivity.this.mEngine.getScene();
final Body body = GameActivity.this.mPhysicsWorld.getPhysicsConnectorManager().findBodyByShape(Object);
mPhysicsWorld.destroyBody(body);
//scene.getLayer(0).removeEntity(Object);
//scene.getLayer(1).removeEntity(Object);
scene.detachChild( Object );
}
}
}
}
}
Then simply register that update handler. Like so:
mScene.registerUpdateHandler( this.getCollisionUpdateHandler() );
So now whenever you want to delete a sprite (when it's pressed) then you simply change your method to:
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
MainActivity.spriteList.add( this );
this.delete = true;
return true;
}
Hope that helps. Here's where I got it from: (I simply tried to make it fit your problem :) )
www.andengine.org/forums/tutorials/box2d-collision-and-removal-t523.html