I'm having a problem with my pointer for my tablet. I'm designing an art app and I'm trying to figure out how to mirror my movement (symmetry). Currently, if you touch the screen with your finger or stylus, an image (programmatically generated circle) is drawn and updates itself to wherever you move your finger/stylus. This works perfectly. But now I want to add two new features: Horizontal and vertical symmetry. So if you choose horizontal symmetry, another pointer will appear at the opposite X position, but along the same Y axis. So if you touch at 300x, 250y, and another pointer will show at -300x, 250y on the screen along with the original pointer. Now when I tried to apply and test it, I still only get the original pointer. Here is what I have so far:
rLO.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Hardware accelerated at runtime
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
cursor.x = (int)event.getX() - (cursor.radius / 2);
cursor.y = (int)event.getY() - (cursor.radius / 2);
cursor.onDraw(cursor.e);
rLO.addView(cursor);
if (hSym > 0) {
vSym = 0;
cursorH.x = -cursor.x;
cursorH.y = cursor.y;
cursorH.onDraw(cursorH.e);
rLO.addView(cursorH);
}
if (vSym > 0) {
hSym = 0;
cursorV.x = cursor.x;
cursorV.y = -cursor.y;
cursorV.onDraw(cursorV.e);
rLO.addView(cursorV);
}
break;
case MotionEvent.ACTION_MOVE:
// Hardware accelerated at runtime
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
//update pointer to new position of stylus/finger
cursor.x = (int)event.getRawX() - (cursor.radius / 2);
cursor.y = (int)event.getRawY() - (cursor.radius / 2);
rLO.removeView(cursor);
rLO.addView(cursor);
if (hSym > 0) {
cursorH.x = -cursor.x;
cursorH.y = cursor.y;
rLO.removeView(cursorH);
rLO.addView(cursorH);
}
if (vSym > 0) {
cursorV.x = cursor.x;
cursorV.y = -cursor.y;
rLO.removeView(cursorV);
rLO.addView(cursorV);
}
break;
case MotionEvent.ACTION_UP:
if (vSym > 0 || hSym > 0) {
rLO.removeView(cursorH);
rLO.removeView(cursorV);
}
rLO.removeView(cursor);
break;
} // end switch
NOTE:
I used the word "cursor" in the code for "pointer." The pointer (cursor) is programmatically drawn from a different class which creates a circle.
It's probably working, but the problem is that your screen's top left is (0,0). Any negative position is off the screen. Instead divide the screen width or height in half mirror across that.
Related
Hello every body I'm working on an android game that allows user to drag some views and place them above another.
I'm kinda newbie and so far i was able to achieve the drag and drop operation, but now the user is allowed to let the view any where in the screen.
All i want to do is to reset the view to its original location if it wasn't placed above any of the views that acts as a drop zone. please help.
This is pretty late! however I successfully accomplished it many days ago and now I have the time to share my solution in case any one is interested... I'll explain how I managed to do it on the following simplified example as my original code is pretty large to attach
as indicated in my illustrative example above there are:
2 Image Views (let1, let2) with two Relative Layouts (let1Container, let2Container)
Additionally there are two blank Image Views (slot1, slot2) that are intended to act as drop zones, along with their containers (slot1Container, slot2Container) of type (Relative Layout)
All placed in RelativeLayout filling the screen and named (dragAreaRelativeLayout)
first we set the ontouchListener for both letter views:
let1_ImageView.setOnTouchListener(this);
let2_ImageView.setOnTouchListener(this);
and then we make our activity implements View.OnTouchListener and provide an implementation of the method like the following :
#Override
public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
// detach the child from its parent
parent.removeView(view);
}
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(view.getLayoutParams());
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
layoutParams.leftMargin = X - 25;
layoutParams.topMargin = Y - 25;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
viewRootLayout.addView(view);
break;
case MotionEvent.ACTION_UP:
adjustLocation(view, X, Y);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
viewRootLayout = (ViewGroup) findViewById(R.id.dragAreaRelativeLayout);
layoutParams.leftMargin = X - 25;
layoutParams.topMargin = Y - 25;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
viewRootLayout.addView(view);
break;
}
viewRootLayout.invalidate();
return true;
}
the previous code allows the user to drag the letters to any where in the screen and calls a method I named as adjustLocation(view, X, Y); once the dragged image is released at MotionEvent.ACTION_UP this method will check if the dragged image is placed in one of the slots and in case it is not correctly placed it will put it back to its original location and here is how it works...
void adjustLocation(View view, int X, int Y) {
RelativeLayout.LayoutParams slot1_LayoutParams = (RelativeLayout.LayoutParams) slot1.getLayoutParams();
RelativeLayout.LayoutParams slot2_LayoutParams = (RelativeLayout.LayoutParams) slot2.getLayoutParams();
int[] slot1_viewLocation = new int[2];
int[] slot2_viewLocation = new int[2];
slot1.getLocationInWindow(slot1_viewLocation);
slot2.getLocationInWindow(slot1_viewLocation);
//detect drop zone boundaries and check if the view is dropped at relative location
if (Y >= slot1_viewLocation[1] && (Y < (slot1_viewLocation[1] + slot1.getHeight()))) {
//first we check if it is placed over SLOT 1
if ((X >= slot1_viewLocation[0]) && (X < (slot1_viewLocation[0] + slot1.getWidth()))) {
view.setLayoutParams(slot1_LayoutParams);
viewRootLayout = (ViewGroup) findViewById(R.id.slot1Container);
viewRootLayout.addView(view);
}
} else if (Y >= slot2_viewLocation[1] && (Y < (slot2_viewLocation[1] + slot2.getHeight()))) {
//then we check if it is placed over SLOT 2
if ((X >= slot2_viewLocation[0]) && (X < (slot2_viewLocation[0] + slot2.getWidth()))) {
view.setLayoutParams(slot2_LayoutParams);
viewRootLayout = (ViewGroup) findViewById(R.id.let2SlotRelativeLayout);
viewRootLayout.addView(view);
}
} else {
// if the dragged image wasn't dropped neither in slot 1 or slot 2 (drop zaone 1,2)
// we send it back to its location
resetViewLocation(view);
}
}
now let us send the miss placed image back to where it came from, to do so I managed to keep track of veiws original LayoutParams at onCreate()
let1_LayoutParams = (RelativeLayout.LayoutParams) let1.getLayoutParams();
let2_LayoutParams = (RelativeLayout.LayoutParams) let2.getLayoutParams();
and finally here is how we reset its location to the original location :
void resetViewLocation(View view) {
ViewGroup viewOriginalLayout = null;
RelativeLayout.LayoutParams viewOriginalParams = null;
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
// detach the child from current parent
parent.removeView(view);
}
int viewId = view.getId();
if (viewId == let1.getId()) {
viewOriginalParams = let1_LayoutParams;
viewOriginalLayout = (ViewGroup) findViewById(R.id.let1Container);
} else if (viewId == let2.getId()) {
viewOriginalParams = let2_LayoutParams;
viewOriginalLayout = (ViewGroup) findViewById(R.id.let2Container);
}
view.setLayoutParams(viewOriginalParams);
viewOriginalLayout.addView(view);
viewOriginalLayout.invalidate();
}
Please be noted that all snippets included are not tested I just wrote them at the moment I was posting this answer. However the technique is working like charm to me and this code is supposed to work as well, I'll just leave you to try and I'll gladly offer any help if needed.
P.S. I don't know if this is really the best way to do it but I'm done waiting and wanted to move on, so any ideas to enhance the code or the technique will be more than appreciated.
I have a drag and drop list implementation in Xamarin. The problem is that I want to be able to drag and drop element only when the drag and drop button is touched not the whole element.
I was trying to detect for the ImageView being pressed but I am unable to get the correct hit ImageView.
public bool OnDown (MotionEvent e) {
int x = (int) e.GetX();
int y = (int) e.GetY();
int itemnum = PointToPosition(x, y);
if (itemnum == AdapterView.InvalidPosition) {
return false;
}
if (m_dragging != null) {
m_dragging.Stop();
m_dragging = null;
}
View item = GetChildAt(itemnum - FirstVisiblePosition);
item.Pressed = false;
var dragImage = item.FindViewById<ImageView> (Resource.Id.drag_image);
Rect bounds = new Rect();
dragImage.GetDrawingRect (bounds);
if (!bounds.Contains (x, y)) {
return false;
}
The following code works only for the first element in the list and does not apply to any other. I am suspecting that the detection of hit is incorrect.
Solved followingly. I realized that I actually do not need to care about the Y axis and it's enough to take just X axis in account.
// Detect if the user is actually pressing the drag and drop image or not.
var dragImage = item.FindViewById<ImageView> (Resource.Id.drag_image);
if (dragImage != null) {
var dragImageHitRect = new Rect ();
dragImage.GetDrawingRect (dragImageHitRect);
// In this case we do not care about the Y axis. Just compare the x axis.
if (!dragImageHitRect.Contains (x, (int)dragImage.GetY ())) {
return false;
}
}
I have a RelativeLayout (params = MatchParent,MatchParent) as my content view.
The RelativeLayout contains a ScrollView (params=Wrap_Content, Wrap_Content).
The ScrollView then contains a LinearLayout ( no params, set vertical orientation).
To the LinearLayout I'm adding "myrows".
Each myrow is made up of another LinearLayout (no params, horizontal orientation) which contains a SpannableString which contains equally spaced/padded numbers.
The result is a vertically scrollable table, and looks ok. The problem is that if I run it on a smaller device, the SpannableStrings extend out of the screen.
On the smaller screen I want the text to be large enough to read, and so I need to be able to scroll horizontally as well to see the part of the SpannableString which has extended beyond screen. I have tried a couple of solutions already. One was to put the vertical ScrollView inside a horizontal ScrollView and capture the xy etc...but people mentioned this is dangerous. I don't want the strings to wrap. I can't use a dropbox etc...to show the beyondcreen values. It has to be a flat table with text large enough to read, I can't eliminate any of the string length.
I can make it instead a horizontal ScrollView and put a page up/down button but it won't work as smooth as it does now. Could I put a ListView inside a horizontal ScrollView and be able to scroll in both directions?
Any other ideas for this? Is there a scroll bar I can put at the bottom that scrolls the screen horizontally where it only captures your finger touch on the bar itself?
Thanks!
I finally found a solution which works great. Create a custom gridView with the following overrides. I also created a scrolling header which follows the scroll of the gridView.
#Override
public boolean onInterceptTouchEvent (MotionEvent ev) {
getMaxLeftScroll();
myLastX = ev.getX();
myLastY = ev.getY();
return super.onInterceptTouchEvent(ev);
}
#Override
public boolean onTouchEvent(final MotionEvent ev){
final int action = ev.getAction();
final float x;
final float y;
switch (action) {
case MotionEvent.ACTION_MOVE:
// This handles Scrolling only.
x = ev.getX();
y = ev.getY();
final int deltaX = (int) (myLastX - x);
final int deltaY = (int) (myLastY - y);
if(Math.abs(deltaX) > Math.abs(deltaY)) {
if( (iCurrentLeftScroll+deltaX) > 0 &&
iCurrentLeftScroll+deltaX < iMaxLeftScroll) {
scrollBy(deltaX, 0);
//Listener and call the method here to scroll the header
mScrollChangeListener.onGridViewScrolled(deltaX);
iCurrentLeftScroll+=deltaX;
} //Scrolled by some value
// Close to right edge within 10? Smooth Scroll remaining small distance if not already there
else if(deltaX > 0 && (iCurrentLeftScroll<iMaxLeftScroll)) {
if ((iMaxLeftScroll - iCurrentLeftScroll) < 10) {
scrollBy(iMaxLeftScroll-iCurrentLeftScroll, 0);
mScrollChangeListener.onGridViewScrolled(iMaxLeftScroll-iCurrentLeftScroll);
iCurrentLeftScroll = iMaxLeftScroll;
}
}
// Close to left edge within 30? Smooth Scroll remaining small distance if not already there
else if(deltaX < 0 && (iCurrentLeftScroll>0)) {
if ((iCurrentLeftScroll) < 10) {
scrollBy(iCurrentLeftScroll*-1, 0);
mScrollChangeListener.onGridViewScrolled(iCurrentLeftScroll*-1);
iCurrentLeftScroll = 0;
}
}
//left and right are subjective
if(iCurrentLeftScroll == iMaxLeftScroll)
iScrollStatus = SCROLLED_FULL_LEFT;
if(iCurrentLeftScroll == 0)
iScrollStatus = SCROLLED_FULL_RIGHT;
if( (iCurrentLeftScroll > 0) && (iCurrentLeftScroll < iMaxLeftScroll) )
iScrollStatus = 0;
Log.d(TAG," Scroll Status " + String.valueOf(iScrollStatus) );
Log.d(TAG," MaxLeftScroll " + String.valueOf(iMaxLeftScroll) );
Log.d(TAG," Current Left Scroll " + String.valueOf(iCurrentLeftScroll) );
}
myLastX = x;
myLastY = y;
break;
}
return super.onTouchEvent(ev);
}
After getting the calculator application to work I decided to try to create pong. There is a box in the center and two paddles on both ends. The phone is horizontal. I have the box bouncing off the walls and the paddle moves with me moving my finger down. My problem is i want to make it two player and i want to have multiple finger input for the game. I want one finger to move paddle 1 and the other to move paddle 2. So far this is my input code
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
float p1y = ev.getY();
if(ev.getX()<300)
{
player1y = p1y;
}
if(ev.getX()>300)
{
player2y = p1y;
}
//player1y = p1y;
invalidate();
break;
}
}
return true;
}
it resides in my surfaceview class. How can i modify the input method or completely get rid of it and change it to accomplish my goal? Also sorry about my variables. Eclipse crashes a lot on me and my laptops touch panel tends to move my cursor so shorter variables seemed viable. p1y is the y of the touch. and player1y and player2y is the y positions of the player1 and player2 paddle.
A MotionEvent can hold multiple pointers. Use getPointerCount() to see how many pointers are touching the screen in the current event. There are alternate versions of getX and getY that take an index from 0-getPointerCount() - 1.
In a more complex app you would want to track fingers by pointer ID, but for something this simple where you are using a cutoff point on the screen you could do something like this in your ACTION_MOVE case:
int pointerCount = ev.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
float x = ev.getX(i);
float y = ev.getY(i);
if (x < 300) {
player1y = y;
} else if (x > 300) {
player2y = y;
}
}
This post from the Android Developers Blog might help if you'd like more information: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
Can anyone help please.
Im writing a small Android game where the player is able to select a "barrier" and drag it accross the screen with their finger. I have the barriers drawn on the screen and I am able to drag it accross the screen.
My problem however is when I add more than 1 barrier, eg 3 barriers, and drag a barrier accross the screen, they all drag and they all drag to the same position. That is to say they all lie on top of each other, making it look as though there is only 1 barrier.
Here is my code, can anyone please tell me where I am going wrong/explain where I am going wrong.
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback, SensorEventListener {
// Initialising the Barrier
private Barrier barrier[] = new Barrier[3];
// The Main Game Panel
public MainGamePanel(Context context) {
super(context);
// Adding a call-back (this) to the surfaceHolder to intercept events
getHolder().addCallback(this);
// Creating the Game items
// The starting coordinates of the Barrier
int x = 30;
int y = 270;
barrier[0] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.blue_barrier), x, y);
barrier[1] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.green_barrier), x + 15, y);
barrier[2] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.pink_barrier), x + 30, y);
// Create the Game Loop Thread
thread = new MainThread(getHolder(), this);
// Make the GamePanel focusable so it can handle events
setFocusable(true);
}
// Handles the touch events
public boolean onTouchEvent(MotionEvent event)
{
int eventAction = event.getAction();
int x = (int)event.getX();
int y = (int)event.getY();
switch (eventAction)
{
// Touch down so check if finger is on Barrier
case MotionEvent.ACTION_DOWN:
if (x > barrier[0].getX() && x < barrier[0].getX() + 8
&& y > barrier[0].getX() && y < barrier[0].getY() + 8)
{
barrier[0].isTouched();
}
else if (x > barrier[1].getX() && x < barrier[1].getX() + 8
&& y > barrier[1].getX() && y < barrier[1].getY() + 8)
{
barrier[1].isTouched();
}
else if (x > barrier[2].getX() && x < barrier[2].getX() + 8
&& y > barrier[2].getX() && y < barrier[2].getY() + 8)
{
barrier[2].isTouched();
}
break;
// Touch-drag with the Barrier
case MotionEvent.ACTION_MOVE:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i] == barrier[0])
{
barrier[0].setX(x);
barrier[0].setY(y);
} // end if
else if (barrier[i] == barrier[1])
{
barrier[1].setX(x);
barrier[1].setY(y);
}
else if (barrier[i] == barrier[2])
{
barrier[2].setX(x);
barrier[2].setY(y);
} // end else if
} // end for
break;
case MotionEvent.ACTION_UP:
// Finger no longer on Barrier - Do Nothing
break;
}
return true;
}
// Render - Draws the Game Item Bitmaps to the screen
public void render(Canvas canvas)
{
// Set the background to white
canvas.drawColor(Color.WHITE);
barrier[0].draw(canvas);
barrier[1].draw(canvas);
barrier[2].draw(canvas);
}
// Update
// This is the Game's update method
// It iterates through all the Objects and calls their update() methods (if they have one)
public void update()
{
} // end update
In your barrier move code, you aren't checking a particular barrier is touched, so you're moving all of them to the same coordinates. The following loop does exactly the same thing as your current code:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
barrier[i].setX(x);
barrier[i].setY(y);
} //end for
To fix this, you need to check if the current barrier in the loop is the one that was touched, and can change that whole loop to something like:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i].isTouched())
{
barrier[i].setX(x);
barrier[i].setY(y);
} // end if
} // end for
You would then need to make sure that you un-set the touched property in the ACTION_UP section. If you post your Barrier class definition I can probably help more.
I'm pretty sure your problem could be solved by making the barriers three separate objects instead of in an array.
As already pointed out, your code in handling the move is wrong because it moves all the Barriers to the same X,Y when it should only move the one that is touched.
Also, you are never resetting the isTouched on the objects in the Action up. When the user lifts their finger you should set them all to isTouched == false. If you don't do that then once you touch one it will always move to the X,Y.