I've created a Relative layout that will change a button position when I touch it. Using OnTouchListener. But I want when I release the button (ACTION_UP), it go to it's own position before this operation. Can anyone help me out please?
My code : (inside on Create)
...
this.relative_layout.setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View view, MotionEvent event)
{
// TODO: Implement this method
if(event.getAction() == event.ACTION_MOVE)
{
float x = event.getX();
float y = event.getY();
my_button.setX(x);
my_button.setY(y);
} else if(event.getAction() == event.ACTION_UP)
/* here's the issue that I'm
* asking for help about
* What to do to return the button position back
* Like it was? */
return true;
}
});
According to the doc, setX does:
Sets the visual x position of this view, in pixels. This is
equivalent to setting the translationX property to be the difference
between the x value passed in and the current left property.
So to reverse that, use setTranslationX(0);
Same thing for the Y- setTranslationY(0);
Related
while working on the listener class and implementing handling of multiple touch gesture I came across a possible bug.
The code implementation
public class MyListener extends ClickListener {
private List<Pointer> pointers = new ArrayList<Pointer>();
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointerIndex, int button) {
System.out.println("Listener: touch down" + pointerIndex);
pointers.add(new ListenerPointer(x, y, button));
event.handle();
return super.touchDown(event, x, y, pointerIndex, button);
}
#Override
public void touchDragged(InputEvent event, float x, float y, int pointerIndex) {
System.out.println("Listener: dragged " + pointerIndex);
// Update the current point the user is dragging.
for (ListenerPointer pointer : pointers) {
if (pointer.getPointerIndex() == pointerIndex) {
pointer.update(x, y);
event.handle();
}
}
}
}
While touching down on the screen with a new finger, and still holding the old finger on the screen the indexPointer increases. Resulting in the following log:
Listener: touch down0
Listener: touch down1
If I then move both fingers across the screen, it will only fire the touchDragged event with the pointerIndex always zero. Even though the touchDown gesture said it has a pointerIndex of 1. The log of the touchDragged is always:
Listener: dragged 0
Listener: dragged 0
I think it might be a bug in the code of LibGDX, since such a simple piece of code could not really go wrong.
I thought that I had carefully read the documentation of touchDragged, but what a fool I am. It states the following:
Called when a mouse button or a finger touch is moved anywhere, but only if touchDown previously returned true for the mouse button or touch.
I guess that super.touchDown(event, x, y, pointerIndex, button) only return true if the pointerIndex is 0. Explaining the reason why the touchDragged events did not fire for pointerIndex > 0.
The simple fix is to make touchDown always return true
I am making a bridge building game for Android. As you know there are two coordinates for drawing a line.
Firstly, When we push "put" button and select a dot then it's the first coordinate of the line, secondly the coordinates where we keep touching on screen is always displayed as the second coordinate, lastly, where we release our finger is decided as the second coordinate of the line. And there will be more than one lines.
I'd be glad if anyone explain these to me.
You will have to override the onTouchEvent function of the respective activity:
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
float x = event.getX();
float y = event.getY();
if (action == MotionEvent.ACTION_DOWN) {
// save the coordinates somewhere
} else if (action == MotionEvent.ACTION_UP) {
// save the coordinates as well
} else if (action == MotionEvent.ACTION_MOVE) {
// display the coordinates
}
}
Then you simply have to use the stored coordinates to draw a line between the coordinates, e.g. within a canvas that is located on your activity.
You will find a sophisticated example here: http://www.vogella.com/tutorials/AndroidTouch/article.html
I have implemented a ZoomViewGroup, which is capable of scrolling infinitely in all directions and also zooming infinitely while still delivering all touch events correctly offset to its child Views.
But when it comes to multi-touch, only the first pointer is offset correctly, and all others are pointing to a wrong location, and thats because there is the scaling factor I have to take care of. (As pointer 0 has a different offset from its original location than pointer 1 or 2, when scaling_factor != 1.0f)
I am saving my transformation in a Matrix, so it's easy to calculate the coordinates from screen to workspace and back using matrix.mapPoints(..) and the matrix inverse.
When I draw the child views, I can just apply the transformation matrix like this:
protected void dispatchDraw(Canvas canvas)
{
canvas.save();
canvas.concat(transformation_matrix);
super.dispatchDraw(canvas);
canvas.restore();
}
Same with touch events:
float[] touch_array = new float[2];
public boolean dispatchTouchEvent(MotionEvent event)
{
touch_array[0] = event.getX();
touch_array[1] = event.getY();
transformation_matrix.mapPoints(touch_array);
event.setLocation(touch_array[0], touch_array[1]);
return super.dispatchTouchEvent(event);
}
But MotionEvent.setLocation(float x, float y) does actually offset all pointers by the same amount. If we zoomed by 2.0f the offset is different for each pointer, so I have to be able to do something like MotionEvent.setLoction(int pointer_index, float x, float y) for each one individually. Is there anything I can do to achieve this?
EDIT: The only solution I know (and searched for) so far is to create a new MotionEvent with the new coordinates.
--
You can get all pointers (fingers touching) positions trough the methods getX(int pointerId)/getY(int).
You can get all on screen pointers trough the getPointerCount()
So to parse multi-touch you must do something like:
public boolean dispatchTouchEvent(MotionEvent event)
{
for(int i = 0; i < event.getPointerCount(); i++){
touch_array[0] = event.getX(i);
touch_array[1] = event.getY(i);
transformation_matrix.mapPoints(touch_array);
MotionEvent copy = MotionEvent.obtainNoHistory(event);
copy.setLocation(touch_array[0], touch_array[1]);
boolean handled = super.dispatchTouchEvent(copy);
copy.recycle();
if(handled) return true;
}
return false;
}
Also note that I created a copy of the MotionEvent object, so childrem can modify it without breaking the position trough the for.
Sorry, just now I noticed your last statement, you must copy the event to a new MotionEvent, so the childrem can parse itself and do its works, copying also will make your code safe of modifications, it's a bad idea to change the MotionEvent directly since it can break a for loop in any dispatch method.
I am trying to draw an image that is larger than the screen, and let the user scroll around it. The image is some graphs that are calculated by my code from some data. (This is in Android using Java).
What I want is a View (actually a SurfaceView) that can be scrolled by the user. I have tried putting a SurfaceView inside a ScrollView but the whole image drawn by the SurfaceView is very large and so (a) there are performance issues as my app is drawing the whole SurfaceView when it should only need to do the part that is in the viewport and (b) my app crashes with "dimensions too large... out of memory" errors.
It would be best for me if the View could implement scrolling (vertical only) and then pass the y-value to my code so it could draw the visible part of my image in the viewport.
Alternatively I could capture the 'mouse' events and calculate the y-value myself.
Does anyone have any code so that I can do this?
Update: I should clarify, the image is drawn by my program code using Canvas.drawLine() and the like. I can calculate which parts of the image fit within my viewport, provided I have the state of the scroll as a y-value, and I can calculate the absolute y-coordinate of each point by subtracting scroll y-value. What I need is something that works to find when the user scrolls the image and what the resulting y-value is. A scrollbar would also be nice.
Update: I am using SurfaceView so that I can update the image from another Thread and so improve user interface performance.
if you really have just a draw content... you don't need a scrollable client then...
have a model (x,y,lines, etc)
have a pan & zoom
draw a scaled/zoomed instance of the model
just draw the content (sorry - no scroller in this solution)
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean isProcessed = scaleGestureDetector.onTouchEvent(event);
if (isProcessed) {
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY());
tap( event.getX(), event.getY() );
mode = DRAG_OR_TAP;
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG_OR_TAP) {
doPan(event.getX() - start.x, event.getY() - start.y);
start.set(event.getX(), event.getY());
}
break;
}
}
myView.scale(currentPan, currentScaleFactor);
invalidate();
return true;
}
private void doPan(float panX, float panY) {
currentPan.x = currentPan.x + panX;
currentPan.y = currentPan.y + panY;
}
private void tap(float x, float y) {
...
}
and last not least use the scaleListener
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
float value = detector.getScaleFactor();
currentScaleFactor = currentScaleFactor * value;
// don't let the object get too small or too large.
boolean doesntMatch = false;
if (currentScaleFactor < 1f || currentScaleFactor > 20f){
currentScaleFactor = Math.max(1f, Math.min(currentScaleFactor, 20f));
doesntMatch = true;
}
if(!doesntMatch){
//scale the viewport as well
currentPan.x = currentPan.x*value;
currentPan.y = currentPan.y*value;
}
return true;
}
}
you can simply use a WebView to display an image
provides pan
provides zoom
no library required
has a scrollbar
Android: Easiest way to make a WebView display a Bitmap?
i'm not sure if i really hit your question...
Does anyone know if there is a simple way to make images snap to grid when dragging a bitmap?
At the moment I can touch a bitmap and move it smoothly around the screen. I want to be able to make it snap to an invisible grid whilst you are dragging.
It's what I do in an application I'm just finishing at the moment. When the user is dragging something on the screen, I display a visible snap grid, and the object is snapped to that grid when the dragging has finished. To show a grid, my approach is to use a separate custom View that I named GridOverLayView. It is overlaid over the entire screen area and it very simply draws a snap grid in its onDraw() method. It is made visible only when something is being dragged.
Now, concerning the actual Activity in which dragging and dropping is handled, one particular constant I've defined is:
static final int SNAP_GRID_INTERVAL = 20;
When the object is being dragged around, i.e. when processing event.getAction()==MotionEvent.ACTION_MOVE events within my OnTouchListener, I perform snapping of the object's location to grid using the following:
RelativeLayout.LayoutParams par = (RelativeLayout.LayoutParams) mThingBeingDragged.getLayoutParams();
par.topMargin = Math.round((event.getRawY() - draggedInitialY) / SNAP_GRID_INTERVAL ) * SNAP_GRID_INTERVAL;
par.leftMargin = Math.round((event.getRawX() - draggedInitialX) / SNAP_GRID_INTERVAL ) * SNAP_GRID_INTERVAL;
mThingBeingDragged.setLayoutParams(par);
...where draggedInitialY and draggedInitialX store the initial touch position recorded during the initial MotionEvent.ACTION_DOWN.
A further nice touch is to allow the object being dragged to be moved around without snapping, but have it snap to the grid only in the .ACTION_UP when the user lifts their finger. In practice, this feels much nicer to use.
private PointF touchDown;
private int gridCellSize = 10;
private OnTouchListener touchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
touchDown = new PointF(event.getRawX(), event.getRawY());
break;
}
case MotionEvent.ACTION_MOVE:
{
RelativeLayout.LayoutParams par = (RelativeLayout.LayoutParams) v.getLayoutParams();
float yDeff = ((event.getRawY() - touchDown.y) / gridCellSize ) * gridCellSize;
float xDeff = ((event.getRawX() - touchDown.x) / gridCellSize ) * gridCellSize;
if(Math.abs(xDeff) >= gridCellSize)
{
par.leftMargin += (int)(xDeff / gridCellSize) * gridCellSize;
touchDown.x = event.getRawX() - (xDeff % gridCellSize);
}
if(Math.abs(yDeff) >= gridCellSize)
{
par.topMargin += (int)(yDeff / gridCellSize) * gridCellSize;
touchDown.y = event.getRawY() - (yDeff % gridCellSize);
}
v.setLayoutParams(par);
break;
}
default :
{
break;
}
}
return true;
}
};