I am trying to implement a basic virtual joystick in an Android custom View, using the following code.
this.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent e) {
switch(e.getAction()){
case(MotionEvent.ACTION_DOWN):
moving = true;
joystickVisibility = true;
joystickCoordinates[0] = (int) e.getX() - imageJoystick.getWidth() / 2;
joystickCoordinates[1] = (int) e.getY() - imageJoystick.getHeight() / 2;
joystickBaseCoordinates[0] = (int) e.getX() - imageJoystickBase.getWidth() / 2;
joystickBaseCoordinates[1] = (int) e.getY() - imageJoystickBase.getHeight() / 2;
break;
case(MotionEvent.ACTION_MOVE):
joystickCoordinates[0] = (int) e.getX() - imageJoystick.getWidth() / 2;
joystickCoordinates[1] = (int) e.getY() - imageJoystick.getHeight() / 2;
if (joystickCoordinates[0] > joystickBaseCoordinates[0] + 50)
joystickCoordinates[0] = joystickBaseCoordinates[0] + 50;
if (joystickCoordinates[0] < joystickBaseCoordinates[0] - 50)
joystickCoordinates[0] = joystickBaseCoordinates[0] - 50;
if (joystickCoordinates[1] > joystickBaseCoordinates[1] + 50)
joystickCoordinates[1] = joystickBaseCoordinates[1] + 50;
if (joystickCoordinates[1] < joystickBaseCoordinates[1] - 50)
joystickCoordinates[1] = joystickBaseCoordinates[1] - 50;
break;
case(MotionEvent.ACTION_UP):
moving = false;
joystickVisibility = false;
break;
}
move();
postInvalidate();
return true;
}
});
It is working except when the joystick is being held in one direction but not being moved.
I can't find any event that works while the screen is being touched.
Related
I do not speak English well.
We apologize if you do not understand the question.
LinearLayout _Main_Contact_Layout;
FrameLayout _Main_Frame;
ImageView _Main_Background;
private List<string> _DrawerList_Item;
private ListView _DrawerList;
float _X, _Y;
float _XX, _YY;
int offset = 0, duration = 100;
float scaleX = 1.0f, scaleY = 1.0f;
float maxZoomLimit = 2.6f, minZoomLimit = 1.0f;
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
_X = e.GetX();
_Y = e.GetY();
float _Start_X = 760, _Start_Y = 160;
float _End_X = 1150, _End_Y = 490;
WindowManagerLayoutParams _Params = new WindowManagerLayoutParams();
_Params.X = (int)e.RawX;
_Params.Y = (int)e.RawY;
LayoutInflater inflater = (LayoutInflater)this.GetSystemService(Context.LayoutInflaterService);
View _Popup = inflater.Inflate(Resource.Layout.Custom_Popup, null);
PopupWindow _Window = new PopupWindow(_Popup, WindowManagerLayoutParams.WrapContent, WindowManagerLayoutParams.WrapContent);
_Window.SetBackgroundDrawable(new BitmapDrawable());
_Window.OutsideTouchable = true;
TextView _SetupX = (TextView)_Popup.FindViewById(Resource.Id.x_text);
TextView _SetupY = (TextView)_Popup.FindViewById(Resource.Id.y_text);
_SetupX.Text = "X 좌표 : " + _X.ToString("#.##");
_SetupY.Text = "Y 좌표 : " + _Y.ToString("#.##");
DrawerLayout _lstDrawer = (DrawerLayout)v.FindViewById(Resource.Id._DrawerLayout);
if(_Start_X > _X | _Start_Y > _Y | _End_X < _X | _End_Y < _Y )
{
_Window.Focusable = true;
_Window.ShowAsDropDown(_Popup, _Params.X, _Params.Y);
_Window.ShowAtLocation(_Popup, GravityFlags.Left | GravityFlags.Top, 0, 0);
_lstDrawer.CloseDrawer((int)GravityFlags.Left);
}
else if (_Start_X < _X & _Start_Y < _Y & _End_X > _X & _End_Y > _Y & !_lstDrawer.IsDrawerOpen((int)GravityFlags.Left))
{
_lstDrawer.OpenDrawer((int)GravityFlags.Left);
_Window.ShowAsDropDown(_Popup, _Params.X, _Params.Y);
_Window.ShowAtLocation(_Popup, GravityFlags.Left | GravityFlags.Top, 0, 0);
}
else if (_lstDrawer.IsDrawerOpen((int)GravityFlags.Left))
{
_lstDrawer.CloseDrawer((int)GravityFlags.Left);
}
break;
case MotionEventActions.Move:
_XX = e.GetX() - _X;
_YY = e.GetY() - _Y;
//_Main_Background.SetX(_Main_Background.GetX() + _XX);
//_Main_Background.SetY(_Main_Background.GetY() + _YY);
_X = e.GetX();
_Y = e.GetY();
if (scaleX > 1.0f && scaleY > 1.0f)
{
_MoveToX(_XX);
_MoveToY(_YY);
Console.WriteLine("_XX : " + _XX.ToString());
Console.WriteLine("_YY : " + _YY.ToString());
}
break;
}
return true;
}
private void _MoveToX(float xX)
{
float futureX = _Main_Background.GetX() + _XX;
if (futureX > 0 | futureX < 500)
{
_Main_Background.SetX(futureX);
}
}
private void _MoveToY(float yY)
{
float futureY = _Main_Background.GetY() + _YY;
if (futureY > 0 | futureY < 500)
{
_Main_Background.SetY(futureY);
}
}
private void _Zoom_In(View v)
{
if (scaleX < maxZoomLimit && scaleY < maxZoomLimit)
{
Animation animation = new ScaleAnimation(scaleX, (scaleX + 0.2f), scaleY, (scaleY + 0.2f), _X, _Y);
scaleX += 0.2f;
scaleY += 0.2f;
animation.Interpolator = new DecelerateInterpolator();
animation.Duration = duration;
animation.StartOffset = offset;
animation.FillAfter = true;
v.StartAnimation(animation);
}
}
private void _Zoom_Out(View v)
{
if (scaleX > minZoomLimit && scaleY > minZoomLimit)
{
Animation animation = new ScaleAnimation(scaleX, (scaleX - 0.2f), scaleY, (scaleY - 0.2f), _X, _Y);
scaleY -= 0.2f;
scaleX -= 0.2f;
animation.Interpolator = new DecelerateInterpolator();
animation.Duration = duration;
animation.StartOffset = offset;
animation.FillAfter = true;
v.StartAnimation(animation);
}
}
I am worried about this for about a week.
How do I keep from leaving the screen?
Adjusting the scale? How do I touch the scale?
Zoom in and Zoom out using the Button
-> resolution
Moving images with Zoom in using Button
-> resolution
If you move it after Zoom in, start with moving the image to a strange place.
-> resolution
Layout out of range
-> resolution
Move only within layout range (Unresolved)
Modify the if condition in your _MoveTo method. The x, y is the coordinate of the top left corner of your view. Calculate the width and height of the imageview, make sure the x and y value is smaller than 0 and not smaller than the value of screenwidth minus the imageview width.
For example:
private void _MoveToX(float xX)
{
float futureX = _Main_Background.GetX() + xX;
if (futureX <=0 && futureX >=YourScreenWidth -(_Main_Background.Width * scaleX))
{
_Main_Background.SetX(futureX);
Console.WriteLine("futureY : " + (_Main_Background.Height * scaleX).ToString());
Console.WriteLine("futureX : " + futureX.ToString());
}
}
private void _MoveToY(float yY)
{
float futureY = _Main_Background.GetY() + yY;
if (futureY <= 0 && futureY >=YourScreenHeight- (_Main_Background.Height * scaleY))
{
_Main_Background.SetY(futureY);
Console.WriteLine("futureY : " + (_Main_Background.Height * scaleY).ToString());
Console.WriteLine("futureY : " + futureY.ToString());
}
}
I have been trying to create a simple breakout type game. I have a ball and a bat and want to invert the ball's direction when it collides with the bat. The bat is controlled by the user's touch and can freely move around the screen.
#Override
public boolean onTouch(View v, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
return true;
}
In my game loop:
int cx = -1;
int cy = -1;
private int xVelocity = -11;
private int yVelocity = -14;
Bitmap player, ball;
RectF boundsPlayer, boundsBall;
float boundCX, boundCY;
boundCX = (float) cx;
boundCY = (float) cy;
player = BitmapFactory.decodeResource(getResources(), R.drawable.nightplayer);
canvas.drawBitmap(player, x - (player.getWidth() / 2), y - (player.getHeight() + 100), null);
ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
boundsPlayer = new RectF((x - (player.getWidth()/2)), (y - (player.getHeight() + 150)), ((x - (player.getWidth()/2)) + player.getWidth()), ((y - (player.getHeight() + 150)) + player.getHeight()));
boundsBall = new RectF(boundCX, boundCY, boundCX + ball.getWidth(), boundCY + ball.getHeight());
if (RectF.intersects(boundsBall, boundsPlayer)) {
collision();
}
private void collision() {
yVelocity = yVelocity * -1;
score = score + 1;
//Log.d(TAG, String.valueOf(score));
yVelocity = yVelocity - 2;
if(xVelocity < 0) {
xVelocity = xVelocity - 2;
} else if (xVelocity > 0) {
xVelocity = xVelocity + 2;
}
collision = false;
}
The issue I am having is that when the ball collides with the bat it seems to collide multiple times and repeatedly inverts the direction instead of "bouncing off" of the bat.
I'm trying to make a view invisible when it's dragged to a certain Y coordinate on the screen.
This is what I got now:
private final class dragTouchListener implements View.OnTouchListener {
#Override
public boolean onTouch(View v, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
ContentFrameLayout.LayoutParams lParams = (ContentFrameLayout.LayoutParams) v.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
trash_image.setVisibility(View.INVISIBLE);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
trash_image.setVisibility(View.VISIBLE);
ContentFrameLayout.LayoutParams layoutParams = (ContentFrameLayout.LayoutParams) v.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
v.setLayoutParams(layoutParams);
if(v.getY() > trash_image.getY()){
v.setVisibility(View.INVISIBLE);
}
break;
}
contentView.invalidate();
return true;
}
}
So basically what I've tried here is simply an if statement to check if the Y position of the view has "passed" that of another ImageView placed on the upper part of the screen. This however result in becoming invisible as soon as I touch the view to be dragged, no matter where on the screen it's located. So it's far from accurate, or wrong all together.
As requested in comment, a suggestion (do note I had to change it, since this is for a client program, so I didnt test it again):
First, discover SCREEN_DENSITY
private static float SCREEN_DENSITY = getResources().getDisplayMetrics().density;
The onTouch function:
public boolean onTouch(final View v, MotionEvent event) {
case MotionEvent.ACTION_DOWN:
mainFinger = new Point(
(int) (event.getRawX() / SCREEN_DENSITY),
(int) (event.getRawY() / SCREEN_DENSITY)
);
break;
case MotionEvent.ACTION_MOVE:
movFrame(
(FrameLayout) v,
(int) (event.getRawX() / SCREEN_DENSITY),
(int) (event.getRawY() / SCREEN_DENSITY)
);
}
movFrame function:
private void movFrame(FrameLayout v, int movX, int movY) {
MyObject p = (MyObject ) v.getTag();
int[] x, y, h, w;
x = p.x;
y = p.y;
h = p.h;
w = p.w;
x = p.x + movX - mainFinger.x;
mainFinger.x = movX;
if (x + w > p.parent.w) {
x = p.parent.w - w;
}
if (x < 0) {
x = 0;
}
y = p.y + movY - mainFinger.y;
mainFinger.y = movY;
if (y + h > p.parent.h) {
y = p.parent.h - h;
}
if (y < 0) {
y = 0;
}
p.updateMyObject(x, y, h, w);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
(int) ((p.w * SCREEN_DENSITY) + 0.5),
(int) ((p.h * SCREEN_DENSITY) + 0.5)
);
params.leftMargin = (int) ((p.x * SCREEN_DENSITY) + 0.5);
params.topMargin = (int) ((p.y * SCREEN_DENSITY) + 0.5);
v.setLayoutParams(params);
}
I am able to plot bar graph using canvas and drawing rectangle in the view. But the onTouch interaction works for the whole view and hence I am not able to interact with each bar separately.I am not looking for using any library for plotting graphs. Any suggestions would be helpful. Thanks!
protected void onDraw(Canvas canvas) {
float border = 20;
float horstart = border * 2;
float height = getHeight();
float width = getWidth() - 1;
float max = getMax();
float min = getMin();
float diff = max - min;
float graphheight = height - (2 * border);
float graphwidth = width - (2 * border);
paint.setTextAlign(Align.LEFT);
int vers = verlabels.length - 1;
for (int i = 0; i < verlabels.length; i++) {
paint.setColor(Color.BLUE);
float y = ((graphheight / vers) * i) + border;
//canvas.drawLine(horstart, y, width, y, paint);
paint.setColor(Color.BLUE);
canvas.drawText(verlabels[i], 0, y, paint);
}
int hors = horlabels.length - 1;
for (int i = 0; i < horlabels.length; i++) {
paint.setColor(Color.BLUE);
float x = ((graphwidth / hors) * i) + horstart;
//canvas.drawLine(x, height - border, x, border, paint);
paint.setTextAlign(Align.CENTER);
if (i == horlabels.length - 1)
paint.setTextAlign(Align.RIGHT);
if (i == 0)
paint.setTextAlign(Align.LEFT);
paint.setColor(Color.BLUE);
canvas.drawText(horlabels[i], x, height - 4, paint);
}
paint.setTextAlign(Align.CENTER);
canvas.drawText(title, (graphwidth / 2) + horstart, border - 4, paint);
if (max != min) {
paint.setColor(Color.BLUE);
if (type == BAR) {
float datalength = values.length;
float colwidth = (graphwidth / hors);
for (int i = 0; i < values.length; i++) {
float val = values[i] - min;
float rat = val / diff;
float h = graphheight * rat;
canvas.drawRect((i * colwidth) + horstart, (border - h)
+ graphheight+curY, ((i * colwidth) + horstart)
+ (colwidth - 1), height - (border - 1), paint);
}
} else {
float datalength = values.length;
float colwidth = (width - (2 * border)) / datalength;
float halfcol = colwidth / 2;
float lasth = 0;
float h = 0;
for (int i = 0;i<values.length;i++)
canvas.drawLine(((i - 1) * colwidth) + (horstart + 1)
+ halfcol, (border - lasth) + graphheight+curY,
(i * colwidth) + (horstart + 1) + halfcol,
(border - h) + graphheight, paint);
lasth = h;
}
}
}
onTouch method for the view :
public boolean onTouch(View v, MotionEvent event)
{
boolean result=false;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
case MotionEvent.ACTION_MOVE:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
case MotionEvent.ACTION_UP:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
}
if (result) invalidate();
return result;
}
Whenever an user touches that view, it causes the onTouch method gets called. Once it's getting called, you're given two float numbers: x and y indicating where user's finger touches your view relative to the view coordination system.
As you might grasped the idea, you should get those numbers and internally in your custom view (i.e. bar chart) calculate which bar is affected. Then, you can for example apply a hover effect or do something else.
Note: For updating appearance of your view, you should store changes in data models of your chart view and then issue invalidate(). Subsequently, as a result, your onDraw is invoked and in which you can re-draw your chart. (i.e. You should each time, re-draw your whole chart again)
I am currently developing a tree shaped structure using Canvas Circles and I have almost brought about the layouts using Canvas but facing problem while implementing Drag and Drop in Canvas. The code is as follows for better understanding :
private HashSet<CircleArea> mCircles = new HashSet<CircleArea>(CIRCLES_LIMIT);
private SparseArray<CircleArea> mCirclePointer = new SparseArray<CircleArea>(CIRCLES_LIMIT);
private static class CircleArea {
int radius;
int centerX;
int centerY;
CircleArea(int centerX, int centerY, int radius) {
this.radius = radius;
this.centerX = centerX;
this.centerY = centerY;
}
public int getCenterX()
{
return centerX;
}
public int getCenterY()
{
return centerY;
}
public int getRadius()
{
return radius;
}
#Override
public String toString() {
return "Circle[" + centerX + ", " + centerY + ", " + radius + "]";
}
}
private void init(final Context ct) {
Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
System.out.println("Width is " + width + "Height is " + height);
scrWidth = width;
scrHeight = height;
}
#Override
public void onDraw(final Canvas canv) {
// setWillNotDraw(false);
for (int i = 0; i < name.length; i++) {
if (i == 0) {
for (int j = 0; j < latitude_0.length; j++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_0[j]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_0[j]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
} else if (i == 1) {
for (int k = 0; k < latitude_1.length; k++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_1[k]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_1[k]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
} else if (i == 2) {
for (int l = 0; l < latitude_2.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_2[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_2[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
} else if (i == 3) {
for (int l = 0; l < latitude_3.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_3[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_3[l]));
mCircles.add(new CircleArea(x, y, RADIUS_LIMIT));
}
} else if (i == 4) {
for (int l = 0; l < latitude_4.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_4[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_4[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
}else if (i == 5) {
for (int l = 0; l < latitude_5.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_5[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_5[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
}else if (i == 6) {
for (int l = 0; l < latitude_6.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_6[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_6[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
}
}
for (CircleArea circle : mCircles) {
canv.drawCircle(circle.getCenterX(),circle.getCenterY(),RADIUS_LIMIT, mCirclePaint);
}
// invalidate();;
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
boolean handled = false;
CircleArea touchedCircle;
int xTouch;
int yTouch;
int pointerId;
int actionIndex = event.getActionIndex();
// get touch event coordinates and make transparent circle from it
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
xTouch = (int) event.getX(0);
yTouch = (int) event.getY(0);
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
mCirclePointer.put(event.getPointerId(0), touchedCircle);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.w(TAG, "Pointer down");
// It secondary pointers, so obtain their ids and check circles
pointerId = event.getPointerId(actionIndex);
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
// check if we've touched inside some circle
for(CircleArea circle: mCircles)
{
touchedCircle = obtainTouchedCircle(circle.getCenterX(), circle.getCenterY());
mCirclePointer.put(pointerId, touchedCircle);
touchedCircle.centerX = circle.getCenterX();
touchedCircle.centerY = circle.getCenterY();
}
invalidate();
handled = true;
break;
case MotionEvent.ACTION_MOVE:
final int pointerCount = event.getPointerCount();
Log.w(TAG, "Move");
for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) {
// Some pointer has moved, search it by pointer id
pointerId = event.getPointerId(actionIndex);
for(CircleArea circle: mCircles)
{
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
float dx = xTouch - circle.getCenterX();
float dy = yTouch - circle.getCenterY();
float r = FloatMath.sqrt((dx * dx) + (dy * dy));
touchedCircle = mCirclePointer.get(pointerId);
if (null != touchedCircle) {
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
}
}
}
invalidate();
handled = true;
break;
case MotionEvent.ACTION_UP:
//clearCirclePointer();
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
// not general pointer was up
pointerId = event.getPointerId(actionIndex);
mCirclePointer.remove(pointerId);
// invalidate();
handled = true;
break;
case MotionEvent.ACTION_CANCEL:
handled = true;
break;
default:
// do nothing
break;
}
return super.onTouchEvent(event) || handled;
}
private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch);
if (null == touchedCircle) {
touchedCircle = new CircleArea(xTouch, yTouch, RADIUS_LIMIT);
}
return touchedCircle;
}
private CircleArea getTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touched = null;
for (CircleArea circle : mCircles) {
if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) {
touched = circle;
break;
}
}
return touched;
}
I am well aware of the shabby code that I've written but highly helpless to generate a mathematical calculation on how to place the dragged circle to the nearest available canvas circle. Below depicted is a screenshot which helps in more understanding.
Points to be noted :
The function obtainTouchedCircle and getTouchedCircle are vital.
The MotionEvent.ACTION_MOVE helps me in moving a circle but what it does is that it generates a new circle with the same dimensions every time which is totally fine but when a user drags and drops in somewhere, it should get placed in the nearest available circle.
Consider the below image where the yellow circles need to be moved around and placed in the blue circles according to the user's wish. That is my objective.
If someone can help me with the calculation on how I should figure out the nearest available circle while dragging would be of great help !!!! Thanks in advance.