I have a view in a linearlayout. When the view is longpressed the view will be removed from this linearlayout and placed, on the same position of the screen, to a relative layout. On this way a can move the view over the screen with my finger.
it almost works:
i got the longpress event working (remove the view and place the view to the relativelayout). after that i add an ontoucheventlistener so my view stays with my finger, but only for a second. the last time the touchevent is fired i got "MotionEvent.ACTION_CANCEL". When i remove my finger and place my finger again to the view i can go feature with my movement, then it will keep until i remove my finger.
I think that my problem is that the view it removed for a short moment, at that time i get a "MotionEvent.ACTION_CANCEL", however, there are still some unhandled events, they will be fired first. Thats why i got for about 1 second still ontouchevents. (this is just a thought).
someone a idee how i can keep the ontouchevent, or let the ontouchevent fired without replacing my finger?
Edited
My thought is not correct. When i do a longpress the view stays with my finger, however i lost the view as soon as i move about 50 to 100 pixels to any direction.
Edited 2
the longpress code of the view inside the linearlayout
view.setOnLongClickListener(new OnLongClickListener() {
public boolean onLongClick(View v) {
_linearLayout.removeView(v);
moveView(v);
return true;
}
});
moveView will be called by the longpress
private void moveView(View v) {
_relativeLayout.addView(v);
v.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_MOVE:
int x = (int) event.getRawX();
int y = (int) event.getRawY();
v.layout(x, y, x + v.getWidth(), y + v.getHeight());
break;
case MotionEvent.ACTION_UP:
_relativeLayout.removeView(v);
v = null;
break;
case MotionEvent.ACTION_CANCEL:
//it comes here when i move my finger more then 100 pixels
break;
}
return true;
}
});
}
of corse, this is the relevant part of the code and not the original code
Have a look at the Experience - Android Drag and Drop List post. There is also a source code that does something very similar to what are you trying to get. Eric, the author of that post, uses a separate temporary ImageView to hold a dragged view image, taken with getDrawingCache(). He also hides the original list item by setVisibility(View.INVISIBLE).
In the DragNDrop project (link above) you can replace drag(0,y) calls by drag(x,y) in DragNDrop.java to see a dragging in all directions.
Just throwing some possibilities in because I can't see the code but MotionEvent.ACTION_CANCEL could be being called as the parent layout is switched and the view is getting redrawn.
You could try overriding the onTouchEvent and at the point the ACTION_CANCEL event is sent try get hold of an equivalent view in the new layout and operate on that but I still think the better idea is to redesign so it all takes place in the same layout.
Related
Disclaimer: I have already tried following similar threads (namely How can I use OnClickListener in parent View and onTouchEvent in Child View? and How can i get both OnClick and OnTouch Listeners), yet my code still fails to work.
I have a (parent) activity which contains a (child) view. Activity has all the network code, while view contains a bitmap displaying game state. View executes the game moves and draws them, while activity sends and receives the moves.
As such, I need to listen for player's actions on the bitmap, handle them there and immediately after it is handled in the view, I need to send the made move to the server. So the best way I've found to handle all that so far is by using onClickListener along with onTouchListener. But I've spent 4 hours and can't get this to work, so any help would be appreciated.
My parent onClick:
gameView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
//gameView.performClick();
sendMove(theGame.getLastMove());
}
});
My child view:
#Override
public boolean onTouchEvent(MotionEvent event)
{
if(canIMove) {
float x = event.getX();
float y = event.getY();
int recWidth = this.getWidth() / 7;
int currentWidth = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x > currentWidth && x < currentWidth + recWidth)
{
//Bitmap touched
theGame.Move();
ReloadGameBoard();
}
return true;
}
}
return false;
}
It matters not whether I try to performClick() in onClick or in onTouch, it doesn't also matter whether I return true or false in onTouch, it doesn't matter whether I put it in ACTION_DOWN nor ACTION_UP, the result is always the same: child View onTouch gets executed and parent onClick does not.
And please, if you decide to downvote this, at least tell me why.
I have some views which I like to drag around. The views are within
a LinearLayout, which itself is within a scrollview.
I want to get the position of the current finger (touch), to
make a smooth scroll on my scrollview, depending of the
height of the current drag.
I start my draggin after a long click with the
View build-in listener startDrag
view.startDrag(null, shadowBuilder, view, 0);
I am also able to get the relativ position of the drag
on the view that is currently hovered by
view.setOnDragListener(new OnDragListener() {
#Override
public boolean onDrag(View v, DragEvent event) {
//get event positions
}
});
But this only works for the view where the drag shadow is currently on
and DragEvent only provides relative positions, not raw positions.
What I need is the position of the finger, while the drag happens. Unfortunately all onTouchEvents get consumed while dragging.
Does anybody know how I get this to work?
Ps: One way that works (kind of) which I currently use, is to calculate the
position of the touch via the dragEvent.relativePosition in combination
with the view position. But isn't there a better way to go?
OK, since there seems not to be an easier answer, I will provide my own relativly easy solution for further readers, with no need of gesture detection.
At first you need the view that receives the drag event and at second the
drag event ifself (or at least the x and y coordinates).
With fetching of the view position and some simple addition you get the
raw touch position.
This method is tested with the show pointer position developer options
to provide the correct data.
The method for the calculation looks like this:
/**
* #param item the view that received the drag event
* #param event the event from {#link android.view.View.OnDragListener#onDrag(View, DragEvent)}
* #return the coordinates of the touch on x and y axis relative to the screen
*/
public static Point getTouchPositionFromDragEvent(View item, DragEvent event) {
Rect rItem = new Rect();
item.getGlobalVisibleRect(rItem);
return new Point(rItem.left + Math.round(event.getX()), rItem.top + Math.round(event.getY()));
}
Call this method inside of your onDragListener implementation:
#Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
//etc etc. do some stuff with the drag event
break;
case DragEvent.ACTION_DRAG_LOCATION:
Point touchPosition = getTouchPositionFromDragEvent(v, event);
//do something with the position (a scroll i.e);
break;
default:
}
return true;
}
Addtional:
If you want to determinate if the touch is inside of a specific view, you can
do something like this:
public static boolean isTouchInsideOfView(View view, Point touchPosition) {
Rect rScroll = new Rect();
view.getGlobalVisibleRect(rScroll);
return isTouchInsideOfRect(touchPosition, rScroll);
}
public static boolean isTouchInsideOfRect(Point touchPosition, Rect rScroll) {
return touchPosition.x > rScroll.left && touchPosition.x < rScroll.right //within x axis / width
&& touchPosition.y > rScroll.top && touchPosition.y < rScroll.bottom; //withing y axis / height
}
It is also possible to implement a smooth scroll on a ListView based on this solution.
So that the user can drag an item out of a list, and scroll the list via dragging the item either to the top or bottom of the list view.
Cheers.
Is it possible to create a list view in android that is not fixed to a particular position and user can move the list view on a gesture some what floating listview that can be moved anywhere on the screen?I tried finding it but could not find some link.Does any one have some idea about it?
You can achieve this by setting an OnTouchListener on the list view and overriding it's onTouch() method. In the onTouch() method you will have to handle the touch events like(ACTION_DOWN, ACTION_MOVE). Here is how you can handle touch event:
#Override
public boolean onTouch (View v, MotionEvent event)
{
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY()); //saving the initial x and y position
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if(mode == DRAG) {
float scrollByX = event.getX() - start.x; //computing the scroll in X
float scrollByY = event.getY() - start.y; //computing the scroll in Y
v.scrollBy((int)scrollByX/20, 0);
}
break;
}
return false;
}
When the user places the finger down on the screen the ACTION_DOWN event is triggered and when the user drags his finger on the screen ACTION_MOVE event is triggered. Hence a drag event is a combination of ACTION_DOWN and ACTION_MOVE.
Once you have the scrollX and scrollY value the you can move the view on the screen by passing these values to the scrollBy() method of the View class.
Here in the code there are two things to notice:
first is that I have passed 0 as the value of scrollY as it will prevent the list view to be dragged and scrolled simultaneously.
second is that the return value is false as you want the android to handle the scrolling of the list items
Here you can find the complete code for this
I would move the listview using the margins from the layout.
Outline:
Override OnTouch, and based on the event.getX() and event.getY(), update the margins of the listview as follows:
if the parent is a relative layout:
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mListView.getLayoutParams();
lp.leftMargin = event.getX();
lp.topMargin = event.getY();
mListView.setLayoutParams(lp);
Add the dispatchTouchEvent to your activity so that events from the listview get passed up to onTouch:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
Hope this helps.
Although it's hard to maintain, AbsoluteLayout lets you specify a position in (x,y) coordinates. You would just declare your ListView inside a AbsoluteLayout, get a handle to it in java code where you intercept the clicks and then set new coordinates.
In the Android framework, the word "floating" is usually synonymous with new Window (such as a Dialog). It depends somewhat on what your end goal is, but if you want to create a ListView that floats over the rest of the content in your hierarchy, that you can easily hide and show, you should probably consider wrapping that view inside a PopupWindow, which has methods to allow you to easily show the window with showAsDropdown() and shotAtLocation() and also move it around while visible with update().
This is how the newer versions of the SDK create pop-up lists for Spinner and Menu
I got a custom ListView which I fill with an Adaptar. During my application the items within the list will change according to their status so I'm updating my ImageViews like so:
mStatusIcon = (ImageView) findViewById(R.id.imgStatusIcon);
mStatusIcon.setImageResource(R.drawable.icon_cancel);
So far so good. The problem is that I want some kind of focus/hover state on a certain part of my layout. I've set up an OnTouchListener() on my View mHitfield in my layout xml.
I can catch all the relevant actions: ACTION_MOVE, ACTION_DOWN, ACTION_UP and ACTION_CANCEL.
The problem is that when I change my ImageView mStatusIcon the next action I catch is always ACTION_CANCEL.
View mHitfield = (View) findViewById(R.id.outerShape);
mHitfield.setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
int currentAction = event.getAction();
switch(currentAction)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
// if I comment out these lines I keep receiving all actions
// if I don't, I only receive ACTION_DOWN followed by ACTION_CANCEL
mStatusIcon.setImageResource(R.drawable.icon_download_normal);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// if I comment out these lines I keep receiving all actions
// if I don't, I only receive ACTION_DOWN followed by ACTION_CANCEL
mStatusIcon.setImageResource(R.drawable.icon_download_hover);
break;
}
return false;
}
});
Can somebody explain to me why this is happening and if there is a way to work arround this?
I'm still not really sure why it happened but it was seems the TouchEvent was canceled because the layout was updated within the custom View created. Once I didn't use a custom View but created a XML Layout instead the problem ceased to exist.
I have a simple application with two views, one is a TableView and the other is ListView. I use GestureDetector to detect the swipes across the screen similarly to how it is done here. Everything works OK, if the list view is populated with just a few items, however when the ListView fills up the whole screen the gesture detection stops working. Doing the swipe across the screen simply shows highlights one of the list items.
I think this is happening because ListView somehow steals the touch events from the GestureListener. Is there a way to prevent this?
I found that GestureDetector works fine in ListItems if you traverse a fairly accurate horizontal path. However if you stray slightly, the list scrolls and the gesture does not complete. What is going on is as follows:
The GestureDetector starts off happily taking MotionEvents from the onTouch you install in the ListItems via setOnTouchListener().
The ListView is meanwhile listening in on events sent to its child views via onInterceptTouchEvent()
The ListView detects that you have started to scroll and returns true from onInterceptTouchEvent().
From then on the MotionEvents are sent to the ListView and NOT to the original target... the ListView starts receiving MotionEvents in its onTouch handler. This continues until the final ACTION_UP. (Note that the MotionEvents from ACTION_DOWN through all the ACTION_MOVEs to ACTION_UP are considered a single gesture and everything starts again after the final ACTION_UP in the sequence)
The original target (ListItem) gets an ACTION_CANCEL MotionEvent and the GestureDetector in your ListItem bails out. This can be seen to happen if you paste the code for GestureDetector into your app and step through it.
I needed my app to behave as if the horizontal swipe continued even if the touch strays from horizontal slightly.
SOLUTION:
This involves ViewGroup.requestDisallowInterceptTouchEvent (boolean disallowIntercept) which stops the parent being able to peek at the motion events. The method involves implementing onTouchListener to detect a slight swipe (10 pixels or so) then stopping the parent intercepting motion events. The parent will then not scroll and the gesture detector continues to completion.
Here's the code:
private boolean mFlingInProgress = false;
private float mStartX = 0;
private final int FLING_TRIGGER_DISTANCE = 10;
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
float currentX = event.getRawX();
switch (action) {
case MotionEvent.ACTION_DOWN:
mStartX = currentX;
break;
case MotionEvent.ACTION_MOVE:
if (false == mFlingInProgress) {
if (Math.abs(currentX - mStartX) > FLING_TRIGGER_DISTANCE) {
// stop the parent intercepting motion events
mLayout.getParent().requestDisallowInterceptTouchEvent(true);
mFlingInProgress = true;
}
}
break;
case MotionEvent.ACTION_UP:
mFlingInProgress = false;
break;
}
return mGestureDetector.onTouchEvent(event);
}
You could create a custom listview and then implement the gesture detector inside of this i.e. on each row of the list. Could be worth a try.