I am developing an android application where I am creating dynamic Images arrow on relative layout. The images are created on a x,y coordinated of the click area of relative layout. Below is the code the I am using for it.
presciptionScreenArrowImg.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (canSelectMedianStatus == 2) {
if (event == simulationEvent)
return false;
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
Log.e("onTouchListener", "User touch at X:" + x + " Y:" + y);
pointerArrow = new ImageView(getApplication());
pointerArrow.setImageResource(R.drawable.pointer);
pointerArrow.setId(imageArrayTag);
imageArrayTag++;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(40, 40);
params.topMargin = y;
params.leftMargin = x;
pointerArrow.setLayoutParams(params);
presciptionScreenArrowImg.addView(pointerArrow);
long length = 0;
if (action == MotionEvent.ACTION_DOWN) {
// click(v, x, y);
}
}
return false;
}
});
Now, I need is there on button click the last Image drawn should remove first. Basically I need an undo functionality to remove Images as LIFO structure.
let consider presciptionScreenArrowImg is your main layout which contains your all views so for removing
int index=presciptionScreenArrowImg.getChildCount();
if(index>0)
presciptionScreenArrowImg.removeViewAt(index-1);
from above get count of child and remove last view if you have any problem let me know
Store the views in a Queue, and when you add a new view check if the queue is full. If it is, pop a view from the queue, and call presciptionScreenArrowImg.remove(poppedView);
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'm trying to make an Android puzzle game out of a picture.
What i've done 'till now is:
-i've cut the picture in how many parts i get from the EditText field;
-i've put the cut pictures in a gridview and got a puzzle table;
-i've also used the Collection.suffle(list) to suffle the images in the gridview;
Now, i am trying to implement the sliding effect on two images from gridview. So what i want to achieve is a sliding effect similar to CandyCrush app or this one http://www.youtube.com/watch?v=qUoP87fNB8w . I mean, i want to hold an image and drag it top so it should slide top. If i click the picture and slide bottom, it should change with the picture below it and so on...
Here is my code:
Reset = (Button) findViewById(R.id.BReset);
SplitNo = (EditText) findViewById(R.id.edSplitBy);
SplitBy = Integer.parseInt(SplitNo.getText().toString());
ImgNo = SplitBy * SplitBy;
puzzle = (GridView) findViewById(R.id.puzzle);
rootImg = (ImageView) findViewById(R.id.source_image);
splitImage(rootImg);
formPuzzle(smallimages);
Reset.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Reset.setText("Reset");
SplitNo.getEditableText().toString();
SplitBy = Integer.parseInt(SplitNo.getText().toString());
ImgNo = SplitBy * SplitBy;
Collections.shuffle(smallimages);
formPuzzle(smallimages);
}
});
And here is how i form the puzzle:
private void formPuzzle(ArrayList<PuzzleItem> smallimages) {
SMI = new SmallImageAdapter(this, smallimages);
puzzle.setAdapter(SMI);
puzzle.setNumColumns(SplitBy);
puzzle.setHorizontalSpacing(0);
puzzle.setVerticalSpacing(0); }
PuzzleItem.class:
public class PuzzleItem {
Bitmap puzzlepartImage;
int correctPosition;
public PuzzleItem(Bitmap d, int index) {
puzzlepartImage = d;
correctPosition = index;
}
I used this class just to remember the position of the pieces so i can check if the puzzle is finished...
So i'm only interested in how to implement the sliding motion by draging an item from gridview...
Thanks :)
EDIT: Should i use an OnTouchListener ?
Ok, I've managed to find the solution by myself :
private void formPuzzle(final ArrayList<PuzzleItem> smallimages) {
SMI = new SmallImageAdapter(this, smallimages);
puzzle.setAdapter(SMI);
puzzle.setNumColumns(SplitBy);
puzzle.setHorizontalSpacing(0);
puzzle.setVerticalSpacing(0);
puzzle.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
startX = event.getX();
startY = event.getY();
smallimage_Position = puzzle.pointToPosition((int) startX,
(int) startY);
return true;
}
case MotionEvent.ACTION_MOVE: {
endX = event.getX();
endY = event.getY();
Log.d("X", "..." +endX);
Log.d("Y", "..." +endY);
if(endX > startX + smallimage_Width/2 && (endY < startY - smallimage_Height/2 || endY < startY + smallimage_Height/2) ){
PuzzleItem p1 = SMI.smallimages.get(smallimage_Position);
PuzzleItem p2 = SMI.smallimages.get(smallimage_Position +1);
SMI.smallimages.set(smallimage_Position, p2);
SMI.smallimages.set(smallimage_Position +1, p1);
SMI.notifyDataSetChanged();
startX = endX*200;
startY = endY*200;
}
return true;
}
}
return true;
}
});
The slide is given by the MotionEvent.Action_MOVE and by calculating the pixels you can find out in what direction is the finger moving ... For example .. the if from my Action_Move case is recognising a swipe to the right ...
I hope this will help some of you guys ...
Another way to go about achieving slides in a slider puzzle game is to use a game engine. I recommend Cocos2D for Android, and theres a detailed tutorial on that here ...
http://denvycom.com/blog/step-by-step-guide-on-how-to-build-your-first-slider-puzzle-game-in-cocos2d-for-android-part-1/
I'm trying to build a drag and drop function using android 2.1 (sdk 2.1). I searched the web and could only find using android 4.0+.
I have an ImageView (wrapped in a class with some attributes and properties) created and set dynamically using this code.
public void setTileImage(Drawable tileImage) {
if (tileImage == null) {
Log.d("Tiles", "Null");
}
this.tileImage = new ImageView(this.context);
this.tileImage.setImageDrawable(tileImage);
this.tileImage.setAdjustViewBounds(true);
this.tileImage.setScaleType(ImageView.ScaleType.FIT_START);
this.createImageMargins();
}
public void createImageMargins() {
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(this.imageSizeX, this.imageSizeY);
this.tileImage.setLayoutParams(layoutParams);
MarginLayoutParams marginParams = new MarginLayoutParams(this.tileImage.getLayoutParams());
marginParams.setMargins(this.initialPositionX, this.initialPositionY, this.initialPositionX + imageSizeX, this.initialPositionY + this.imageSizeY);
RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(marginParams);
this.tileImage.setLayoutParams(layoutParams2);
this.tileImage.requestLayout();
}
I add this ImageView to a RelativeLayout.
RelativeLayout layout = (RelativeLayout) findViewById(R.id.playscreen);
Tiles t1 = this.createTitle();
layout.addView(t1.getTileImage());
And for each ImageView (which are several) created I add the OnTouchListener(unique for all of them)
private static TouchListener listener = new TouchListener();;
t1.createDragHandler(listener);
public void createDragHandler(TouchListener listener){
this.tileImage.setOnTouchListener(listener);
}
And this the class that implements the OnTouchListener methods. This class change the position (updating the margins - method after the class) of the ImageView with the position of the event that it received.
public class TouchListener implements OnTouchListener{
public boolean onTouch(View v, MotionEvent motionEvent) {
Tiles currentTile = Tiles.searchTileByView(Play.getAvailableTiles(), (ImageView)v);
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
return true;
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
Tiles t = Play.checkCollision(currentTile, currentTile.getPositionX(), currentTile.getPositionY());
if(t == null){
if(currentTile.getPositionX() == currentTile.getInitialPositionX() && currentTile.getPositionY() == currentTile.getInitialPositionY()){
Log.d("Log","This was a click");
}else{
currentTile.setPositionX(currentTile.getInitialPositionX());
currentTile.setPositionY(currentTile.getInitialPositionY());
currentTile.createImageMargins();
}
}else{
if(t.getId() == currentTile.getId()){
Play.updateScore(t.getPoints()*t.getSpecialPoints()+currentTile.getPoints()*currentTile.getSpecialPoints());
t.setActive(false);
currentTile.setActive(false);
t.getTileImage().setVisibility(View.INVISIBLE);
currentTile.getTileImage().setVisibility(View.INVISIBLE);
Log.d("Log","Tile Removed");
}
}
return true;
}else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
Log.d("Log:" + this.toString() + ":" + v,"Moved -> X: " + motionEvent.getX() + " Y: " + motionEvent.getY());
currentTile.setPositionX((int) motionEvent.getX());
currentTile.setPositionY((int) motionEvent.getY());
currentTile.updateImageMargins();
return true;
}else{
return false;
}
}
public void updateImageMargins(){
MarginLayoutParams marginParams = new MarginLayoutParams(this.tileImage.getLayoutParams());
marginParams.setMargins(this.positionX, this.positionY, this.positionX + imageSizeX, this.positionY + this.imageSizeY);
RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(marginParams);
this.tileImage.setLayoutParams(layoutParams2);
}
I imagine that the problem is that I'm receiving two events in the OnTouchListener.onTouch method. The first is using the coordinates of the RelativeLayout (so it starts in the top left corner of the screen) and other with the coordinates of the ImageView (it start on the top left corner of the ImageView) so when I click and move I receive to events with two different coordinates of the event, like this:
TouchListener#44f3bf80:android.widget.ImageView#44f1b4c8 Moved -> X: 59.0 Y: 24.000015
TouchListener#44f3bf80:android.widget.ImageView#44f1b4c8 Moved -> X: 100.0 Y: 148.00002
In other words, the first line received the coordinates from the position of my click in the imageview (if i click nearer to the any of the borders this value will change) and the second is the position of my click in the relativelayout. So the image start to blink from one point to the other while i move the cursor. Both values change proportionally when I move the cursor towards any direction.
Try using motionEvent.getRawX() and motionEvent.getRawY() instead of getX() and getY(). You may need to make additional adjustments to align it properly within your View.
I am overriding onInterceptTouch(MotionEvent) for the purpose of allowing horizontal scrolling. What I am finding however is that I cannot detect when the user has touched the embedded v. The x,y on the view are like 2000, 2400 while the MotionEvent.getX(),getY() are like 400,500
View v = findViewById(R.id.myView);
Rect r = new Rect();
v.getHitRect(r);
int[] loc = new int[2];
v.gtLocationOnScreen(loc);
int x = loc[0];
int y = loc[1];
// Result x,y are huge numbers 2400, etc
// event.getX() is 30, event.getY() == 500 nothing close to 2400.
if (r.contains((int)event.getX(), (int)event.getY())
{
return false; // this is never true even when I click right on View v.
}
I know this is an old question and is mostly answered in the linked post, but I just came across this problem so I thought I'd fill in my solution.
private boolean isTouchInView(View view, MotionEvent event) {
Rect hitBox = new Rect();
view.getGlobalVisibleRect(hitBox);
return hitBox.contains((int) event.getRawX(), (int) event.getRawY());
}
Try using getRawX() and getRawY(). These will give you the absolute positions you need.
See:
How do I know if a MotionEvent is relative or absolute?
You also have to adjust the location of your destination view to account for any displacement by other views like so:
int[] screenLocation = new int[2];
view.getLocationOnScreen(screenLocation);
hitRect.offset(screenLocation[0] - view.getLeft(), screenLocation[1] - view.getTop());
//Then check if source view is contained in target view
x=event.getRawX();
y=event.getRawY();
if (hitRect.contains(x, y)) {
//do your stuff
}