I'm having a very similar issue described here, except instead of using ScaleAnimation, I'm allowing pinch zoom/pan in my RelativeLayout.
The zoom/panning works perfectly, but regardless of how my view is panned/zoomed, the clickable area does not change along with the visual representation. Here's what my dispatchTouchEvent looks like:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mScaleGestureDetector != null && mGestureDetector != null) {
mScaleGestureDetector.onTouchEvent(ev);
mGestureDetector.onTouchEvent(ev);
}
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleGestureDetector.isInProgress() && mScaleFactor > 1f) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
float newPosX = mPosX + dx;
float newPosY = mPosY + dy;
if (isCoordinateInBound(newPosX, mScreenSize.x))
mPosX = newPosX;
if (isCoordinateInBound(newPosY, mScreenSize.y))
mPosY = newPosY;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return super.dispatchTouchEvent(ev);
}
and my dispatchDraw:
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
How do you modify the clickable area accordingly to modified scale/transformation of canvas?
Since you are already onverriding dispatchTouchEvent, you can try this:
manually evaluate each MotionEvent by considering the current zoom/pan transformation; you can create a new MotionEvent (let's call it FakeMotionEvent) by applying the reverse zoom/pan transformation to the original MotionEvent m.
Check if the FakeMotionEvent intercepts a specific View v; this means the user is touching in a position which represents the user-visibile position of v.
If FakeMotionEvent intercepts v, consume the current MotionEvent and invoke v.dispatchTouchEvent(m);
TIP: You can use the method below to evaluate if a MotionEvent intercepts a View with a certain degree of tolerance:
private boolean intercept(MotionEvent ev, View view, float boundingBoxTolerance){
if (boundingBoxTolerance < 1.0f) {
boundingBoxTolerance = 1.0f;
}
try {
if (ev != null && view != null) {
int coords[] = new int[2];
view.getLocationOnScreen(coords);
if (ev.getRawX() >= ((float)coords[0]) / boundingBoxTolerance && ev.getRawX() <= coords[0] + ((float) view.getWidth()) * boundingBoxTolerance) {
if(ev.getRawY() >= ((float)coords[1]) / boundingBoxTolerance && ev.getRawY() <= coords[1] + ((float) view.getHeight()) * boundingBoxTolerance)
return true;
}
}
}
catch (Exception e) {}
return false;
}
I guess you are using View Animations, i will prefer to use Property animations instead
excerpt from Android documentation (see highlighted)
The view animation system provides the capability to only animate View
objects, so if you wanted to animate non-View objects, you have to
implement your own code to do so. The view animation system is also
constrained in the fact that it only exposes a few aspects of a View
object to animate, such as the scaling and rotation of a View but not
the background color, for instance.
Another disadvantage of the view animation system is that it only
modified where the View was drawn, and not the actual View itself. For
instance, if you animated a button to move across the screen, the
button draws correctly, but the actual location where you can click
the button does not change, so you have to implement your own logic to
handle this.
With the property animation system, these constraints are completely
removed, and you can animate any property of any object (Views and
non-Views) and the object itself is actually modified. The property
animation system is also more robust in the way it carries out
animation. At a high level, you assign animators to the properties
that you want to animate, such as color, position, or size and can
define aspects of the animation such as interpolation and
synchronization of multiple animators.
The view animation system, however, takes less time to setup and
requires less code to write. If view animation accomplishes everything
that you need to do, or if your existing code already works the way
you want, there is no need to use the property animation system. It
also might make sense to use both animation systems for different
situations if the use case arises.
Related
final RelativeLayout rl = (RelativeLayout) findViewById(R.id.rel);
final ImageView circle = (ImageView) findViewById(R.id.circle);
final ViewGroup.MarginLayoutParams screen = (ViewGroup.MarginLayoutParams) rl.getLayoutParams();
and..
View v;
v = arrayofImages[i];
v.setOnTouchListener(new View.OnTouchListener() {
PointF DownPT = new PointF();
PointF StartPT = new PointF();
#Override
public boolean onTouch(View v, MotionEvent event) {
int eid = event.getAction();
switch (eid) {
case MotionEvent.ACTION_MOVE:
PointF mv = new PointF(event.getX() - DownPT.x, event.getY() - DownPT.y);
v.setX((StartPT.x + mv.x));
v.setY( (StartPT.y + mv.y));
StartPT = new PointF(v.getX(), v.getY());
if (circle.getRight() > v.getX() && circle.getTop() <= v.getY()
&& circle.getBottom() > v.getY() && circle.getLeft() <= v.getX()) {
v.setVisibility(View.INVISIBLE);
}
if (v.getX() > screen.bottomMargin) {
v.setX(screen.bottomMargin);
}
break;
case MotionEvent.ACTION_DOWN:
DownPT.x = event.getX();
DownPT.y = event.getY();
StartPT = new PointF(v.getX(), v.getY());
break;
case MotionEvent.ACTION_UP:
// Nothing have to do
break;
default:
break;
}
return true;
}
});
So basically I'm trying to not allow an imageView to go ofscreen. if the view v is going too far left position it on the left side.
if (v.getX() > screen.leftMargin) {
v.setX(screen.leftMargin);
}
This should do to trick and it does but only for the left margin. If i try something like this
if (v.getX() > screen.rightMargin) {
v.setX(screen.rightMargin);
}
It acts like it's the leftMargin (it moves my image when i touch it on the left side.)
I don't know what i've done wrong...Please help..If you need more code i will paste but this is all that matters i think.
Well, you are not supposed to be using bottomMargin or topMargin fields. These are the Margins that you set for the RelativeLayout. If you want to check if the View doesn't go OUTSIDE the RelativeLayout, you need to be checking with width and height.
But if all you are going to do is to keep the View inside the RelativeLayout, you need to use View.getLeft() and View.getRight() instead of getX(). This will give the position of the View relative to the parent. You can use this in your logic, rather than using getX() (which returns the visual x position of this view, in pixels.)
It is possible to retrieve the location of a view by invoking the
methods getLeft() and getTop(). The former returns the left, or X,
coordinate of the rectangle representing the view. The latter returns
the top, or Y, coordinate of the rectangle representing the view.
These methods both return the location of the view relative to its
parent. For instance, when getLeft() returns 20, that means the view
is located 20 pixels to the right of the left edge of its direct
parent.
This is my first approach to androids opengl and I have stuck in moving my rendered object with finger.
Actually, everything works but when I try to move object really fast it loses its coordinates.
My object is hexagonal grid built by calculating coordinates for 6 vertexes. First of all I handle on touch event and check if map is grabbed:
switch(action) {
case MotionEvent.ACTION_DOWN:
mapTouched = hexMapRenderer.isMapTouched(ev);
int pointerIndex = MotionEventCompat.getActionIndex(ev);
downX = MotionEventCompat.getX(ev, pointerIndex);
downY = MotionEventCompat.getY(ev, pointerIndex);
moveX = (int)downX;
moveY = (int)downY;
break;
case MotionEvent.ACTION_MOVE:
if(!hexMapRenderer.isScaling()) {
if(mapTouched) {
hexMapRenderer.setMapMoving(true);
hexMapRenderer.moveMap((int) (moveX - ev.getX()), (int) (moveY - ev.getY()));
moveX = (int) ev.getX();
moveY = (int) ev.getY();
}
}
break;
Inside my method moveMap I calculate position of my hexagons:
public void moveMap(int shiftX, int shiftY) {
for(int i = 0; i < hexMap.length; i++) {
for (int j = 0; j < hexMap[i].length; j++) {
if(hexMap[i][j] != null && hexMap[i][j].isToDraw()) {
hexMap[i][j].setCenterPoint(new Point(hexMap[i][j].getCenterPoint().x - shiftX, hexMap[i][j].getCenterPoint().y - shiftY));
}
}
}
this.shiftX = shiftX;
this.shiftY = shiftY;
}
And inside onDrawFrame method I move map like this:
if(mapMoving) {
gl.glTranslatef(-shiftX, -shiftY, 0f);
}
As I mentioned everything works great when I move map slow and gently but when things are getting too fast it looks like that position of rendered object doesn't equals calculated position from moveMap method.
Do you have any solutions how to achieve smooth moving of object in opengl? I've tried recalculate position and redraw whole object but when map starts to move it flickers.
Thanks in advance.
this problem is driving me up the wall. I'm sure I'm doing something wrong but I can't figure it out.
I have a static OnTouchListener which I attach to the TextView of a number of identical compound view objects contained within a linearlayout. This listener is in a helper class.
public static View.OnTouchListener getValueTouchListener() {
return new View.OnTouchListener() {
private int lastY;
private int lastX;
#Override
public boolean onTouch(View v, MotionEvent event) {
TextView view = (TextView)v;
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
view.setBackgroundResource(R.drawable.cog_pressed);
lastY = (int)event.getY();
lastX = (int)event.getX();
return true;
case MotionEvent.ACTION_MOVE:
//calc size of movement
int deltaY = lastY - (int)event.getY();
int deltaX = lastX - (int)event.getX();
// pass gesture up to parent if it's a big x movement - assumes user wishes to scroll parent
if (Math.abs(deltaX) > TOUCH_SLOP *2) {
return false;
}
lastX = (int)event.getX();
//process movement if larger than a touch slop
if (StrictMath.abs(deltaY) > TOUCH_SLOP) {
// reset last touch position
lastY = (int)event.getY();
//get direction of movement
int dir = (deltaY < 0)? -1 : 1;
//change index if within min and max limits
int min = (view.getTag() == Chainring.KEY)? Chainring.MIN_COG : Sprocket.MIN_COG;
int max = (view.getTag() == Chainring.KEY)? Chainring.MAX_COG : Sprocket.MAX_COG;
int value = Integer.valueOf(view.getText().toString());
if ((dir == -1 && value > min) || (dir == 1 && value < max)) {
value = value + dir;
view.setText(String.valueOf(value));
view.playSoundEffect(SoundEffectConstants.CLICK);
}
}
return true;
case MotionEvent.ACTION_UP:
view.setBackgroundResource(R.drawable.cog_unpressed);
return true;
case MotionEvent.ACTION_CANCEL:
view.setBackgroundResource(R.drawable.cog_unpressed);
return false;
default:
return false;
}
}
};
}
I hook up this listener to my TextView in a class that extends linear layout
mValueTextView.setOnTouchListener(CogPickerHelper.getValueTouchListener());
My problem is the method doesn't seem to execute the return command in the ACTION_DOWN, ACTION_UP and ACTION-MOVE cases. For example, with an ACTION_DOWN, the code executes as far as the return command but then steps directly to the default case and returns false - even though Logcat still reports the action as being ACTION-DOWN.
If I try to set a breakpoint on the return commands, IntelliJ says: "No executable code found at line X in class com.amb.GearBuddyV2.views.CogPickerHelper$2"
Does anyone know what I'm doing wrong here?
Your code seems to be working fine, the OnTouch return value is being returned correctly.
And yes, adding a break at a return statement will lead the debugger to complain about missing executable code.
I had everything working with this test app I made that displays a 3D object using the standard vertex, color, and point buffer.
I can..
rotate the object with touch events
render object with openGL ES 1.0 using GL10
everything works great
Now I want to be able to zoom in and out using two fingers for a "pinch" motion. I found some good tutorials and sample code, but it is mostly for ImageViews. I am still semi-new to Android and I do not fully understand how to make this zoom function work for GLSurfaceView.
Below is the code I have so far for the Activity class. The first touch handler is for the zoom event I found through a Tutorial designed for ImageView(I used this because I thought it would be an easy conversion). The second handler is for the rotation event, which works just fine.
Thank you ahead of time and I hope this post will help others with the same issue.
I will be standing by for any edits or additions needed.
// Activity for rendering 3D object openGL ES 1.0
public class GL_ExampleActivity extends Activity
{
private GLSurfaceView surface;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
surface = new GL_ExampleSurfaceView(this);
setContentView(surface);
}
#Override
protected void onPause()
{
// TODO Auto-generated method stub
super.onPause();
surface.onPause();
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
surface.onResume();
}
}
class GL_ExampleSurfaceView extends GLSurfaceView
{
private static final String TAG = "Touch";
Matrix matrix_new = new Matrix();
Matrix last_matrix = new Matrix();
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
private final float SCALE_FACTOR = 180.0f / 320;
private GLRenderer renderer;
private float previous_x;
private float previous_y;
public GL_ExampleSurfaceView(Context context)
{
super(context);
renderer = new GLRenderer();
setRenderer(renderer);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
#Override
public boolean onTouchEvent(MotionEvent e)
{
// handler for drag and zoom events
switch (e.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
last_matrix.set(matrix_new);
start.set(e.getX(), e.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
//requestRender();
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = finger_distance(e);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f)
{
last_matrix.set(matrix_new);
finger_distance_midpoint(mid, e);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
//requestRender();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
//requestRender();
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG)
{
matrix_new.set(last_matrix);
matrix_new.postTranslate(e.getX() - start.x, e.getY() - start.y);
}
else if (mode == ZOOM)
{
float newDist = finger_distance(e);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f)
{
matrix_new.set(last_matrix);
float scale = newDist / oldDist;
matrix_new.postScale(scale, scale, mid.x, mid.y);
}
}
//requestRender();
break;
}
//view.setImageMatrix(matrix_new);
// handler for rotation event, y and x axis
float x = e.getX();
float y = e.getY();
switch (e.getAction())
{
case MotionEvent.ACTION_MOVE:
float dx = x - previous_x;
float dy = y - previous_y;
if (y > getHeight() / 2)
{
dx = dx * -1 ;
}
if (x < getWidth() / 2)
{
dy = dy * -1 ;
}
renderer.angle_x += dx * SCALE_FACTOR;
renderer.angle_y += dy * SCALE_FACTOR;
requestRender();
}
previous_x = x;
previous_y = y;
return true;
}
private float finger_distance(MotionEvent e)
{
float x = e.getX(0) - e.getX(1);
float y = e.getY(0) - e.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void finger_distance_midpoint(PointF point, MotionEvent e)
{
float x = e.getX(0) + e.getX(1);
float y = e.getY(0) + e.getY(1);
point.set(x / 2, y / 2);
}
}
Thanks again...
There are two obvious ways to approach this. If you just want to increase the size of an individual object, you can use glScalef when drawing the object. Using scalef is easy and relatively straightforward but it isn't exactly what you're asking for.
Another solution is to apply a scaling factor to your projection matrix which will produce the desired effect. To accomplish this, just multiply the left, right, top, and bottom distances of your projection matrix by a scale like so before drawing.
gl.glFrustumf(width/2 * zoom, width/2 * zoom, height/2 * zoom, height/2 * zoom, 1, -1);
(Note, if you're using an orthogonal matrix you will be using glOrthof instead)
If you're interested, you can find more information about the projection matrix here
here is my code now:
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getAction();
switch(action)
{
case MotionEvent.ACTION_DOWN:
// Do click work here ...
Y -= 10;
Yopen = 1;
break;
case MotionEvent.ACTION_UP:
// Do release work here ...
Yopen = 0;
break;
}
return super.onTouchEvent(event);
}
But I want to make only three different areas to perform different code.
Can some one help me, some good tutorial on the internet.
Add this code to your onTouchEvent to figure out where the user touched and respond appropriately:
//Grab the current touch coordinates
float x = event.getX();
float y = event.getY();
//If you only want something to happen then the user touches down...
if (event.getAction() != MotionEvent.ACTION_UP) return true;
//If the user pressed in the following area, run it's associated method
if (isXYInRect(x, y, new Rect(x1, y1, x2, y2)))
{
//Do whatever you want for your defined area
}
//or, if the user pressed in the following area, run it's associated method
else if (isXYInRect(x, y, new Rect(x1, y1, x2, y2)))
{
//Do whatever you want for your defined area
}
Here's the isXYinRect method:
//A helper method to determine if a coordinate is within a rectangle
private boolean isXYInRect(float x, float y, Rect rect)
{
//If it is within the bounds...
if (x > rect.left &&
x < rect.right &&
y > rect.top &&
y < rect.bottom)
{
//Then it's a hit
return true;
}
//Otherwise, it's a miss
return false;
}