Android touch event stucked in ListView - android

I have a container View that should process touches. It has Listview with overrided onTouch(), which returns false (it will return true, when container is expanded by swipe). When Listview touch occurs it passes to container View and everything is ok. But I need to add click handling for items of Listview.
When I set onClickListener for item, I see in logs it is no MotionEvent passed to container, it is stucked in ListView onTouch() (which return false and does nothing). I'm trying to set onTouchListener for item like this:
viewHolder.itemLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
return false;
case MotionEvent.ACTION_UP:
Log.d(TAG, "something like click detected");
return true;
}
return false;
}
});
But result is the same. Can someone explain why my container does not receive touch event even ListView returns false for its overrided onTouch()?

If you wish to handle the clicks of ListView items : use OnItemClickListener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//parent.getItemAtPosition(position) returns the value of item clicked.. use it to do whatever you wish to do
}
});

I had the same problem about 2 weeks ago. Returning false from listview's onTouch method did not pass event to parent container. I ended up doing this:
1. You need custom layout for your container View. Then in that view, override onInterceptTouchEvent, for example like this:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
previousX_actionDOWN = ev.getRawX();
isOnClick = true;
timePressed = System.currentTimeMillis();
return false;
case MotionEvent.ACTION_MOVE:
if (isOnClick && (Math.abs(previousX_actionDOWN - ev.getRawX()) > SCROLL_THRESHOLD)) {
return true;
} else {
return false;
}
case MotionEvent.ACTION_UP:
if (!wasDragged) {
return false;
}
else {
return true;
}
}
return false;
}
I also have complicated onTouch method in that view but it's not worth pasting. Point is that your parent view should only intercept touch event if some threshold was passed while ACTION_MOVE - well at least that's what I wanted to achieve. Then in your listview you just implement standard onItemClickListener instead of onTouch.

Related

Getting the ripple/selector effect without a clickable layout

I have two view pager, one is nested in the other. To get the correct behaviour (swiping the inner view pager without changing the outer) I had to override the inner view pagers onTouchListener and put all my onTouch/onClick logic into it (got the idea from here).
Works all fine, but since I don't have a onClickListener anymore I lost my selector effect. When I put android:clickable="true" on the layout element I get my selector effect, but the view pagers behaviour is wrong again.
Is there any way to achieve the selector effect out of the onTouchListener?
innerPager.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
Log.d(DEBUG_LOG, "Single tap.");
return true;
} else if(event.getAction() == MotionEvent.ACTION_DOWN && v instanceof ViewGroup) {
((ViewGroup) v).requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
This solved it for me. Just added the following code to my OnTouchListener and replaced the card view with my inner view pagers current item:
// Since the host view actually supports clicks, you can return false
// from your touch listener and continue to receive events.
myTextView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent e) {
// Convert to card view coordinates. Assumes the host view is
// a direct child and the card view is not scrollable.
float x = e.getX() + v.getLeft();
float y = e.getY() + v.getTop();
// Simulate motion on the card view.
myCardView.drawableHotspotChanged(x, y);
// Simulate pressed state on the card view.
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
myCardView.setPressed(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
myCardView.setPressed(false);
break;
}
// Pass all events through to the host view.
return false;
}
});

Passing touch event from one component to ListView

I have a simple Button, that has a touch listener. Once it's triggered on ACTION_DOWN action, a ListView appears on top of that button (so that the ListView is under user's finger).
What I want is to "pass" that touch event from Button to that ListView, so that when moved up/down, the list view would also scroll up/down.
Simply put:
User touches a Button
A list view appears on covering that button
WITHOUT RELEASING A FINGER, user starts moving finger to the top/bottom screen edge,
The list view scrolls.
UPDATE
I tried making a custom ListView component with onInterceptTouchEvent overridden, but I do not clearly get what should go into that method?
You should create a custom class extending ListView, then override the method onInterceptTouchEvent(MotionEvent e). That method gets called whenever a view inside the list is touched.
You don't need any custom component.
Set your button onTouchListener as:
button.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent)
{
switch (motionEvent.getAction())
{
case MotionEvent.ACTION_DOWN:
// popup the listView
break;
case MotionEvent.ACTION_MOVE:
// lstPopup is your ListView
lstPopup.dispatchTouchEvent(motionEvent);
break;
case MotionEvent.ACTION_UP:
// do other stuff
}
return false;
}
});
And set your listView onTouchListener as:
lstPopup.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_MOVE)
{
lstPopup.setSelectionFromTop(0, (int) motionEvent.getY());
}
return false;
}
});

OnItemClickListener and Custom OnTouchListener

I have a list of views on a list view that I would like to click on and have the OnItemClickListener get triggered. Though at the same time I want to be able to swipe each view and have a custom action occur. This means that I had to create our own OnTouchEvent for each view when it is made in the ArrayAdapter.
Is there a way to have both of those working together, so that I can have a custom action such as swiping an item and clicking on the item occur easily
This is very similar to how android Recent Activities are handled - you know, they show a list of all recently opened apps, they can be swiped to remove, or clicked to open. Check out their code, I think you'll get a pretty good idea: https://github.com/android/platform_frameworks_base/tree/master/packages/SystemUI/src/com/android/systemui/recent
You class can implement both View.OnTouchListener, AdapterView.OnItemClickListener
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction() == MotionEvent.ACTION_UP){
Log.d(TAG, "ontouch: UP");
**// Here you can figure if it was simple item click event.
// We return false only when user touched once on the view.
// this will be handled by onItemClick listener.**
if(lastAction == -1){
lastAction = MotionEvent.ACTION_UP;
view.clearFocus();
return true;
}
return false;
}
else if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
Log.d(TAG, "ontouch: DOWN");
return false;
}
else {
// This is all action events.
lastAction = -1;
return true;
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// We come here after onTouch event figured out that its a simple touch event and needs to be handled here.
}

Android: Long touch on Button while MotionEvent.ACTION_MOVE

Ok... in my app i update the layout on MotionEvent.ACTION_DOWN and then i check the motion event coordinates to locate my buttons. I can show a toast when finger is released on different buttons. The problem is i need a long touch on my buttons to call another action without conflicting with the MotionEvent.ACTION_UP. Implemented a long click handler but since i don't 'click' its not working. Hope you guys understand my problem.
Whats the best way to get my app working as intended?
My class implements OnTouchListener, OnGestureListener
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
// UPDATE LAYOUT
break;
case MotionEvent.ACTION_UP:
// GET BUTTON X Y
if (x and y match the button location){
// DO ACTION
}else{
// DO NOTHING
}
// CHANGE LAYOUT TO INITIAL STATE
break;
case MotionEvent.ACTION_MOVE:
break;
}
return false;
mybutton.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// DO STUFF
return true;
}
});
}
just try to return false in your onTouch(...) method and use onLongClickListener(...) as usual

Android onTouch with onClick and onLongClick

I've got a custom view which acts like a button. I want to change the background when user press it, revert the background to original when user moves the finger outside or release it and I also want to handle onClick/onLongClick events. The problem is that onTouch requires me to return true for ACTION_DOWN or it won't send me the ACTION_UP event. But if I return true the onClick listener won't work.
I thought I solved it by returning false in onTouch and registering onClick - it somehow worked, but was kinda against the docs. I've just received a message from an user telling me that he's not able to long-click on the button, so I'm wondering what's wrong here.
Part of the current code:
public boolean onTouch(View v, MotionEvent evt)
{
switch (evt.getAction())
{
case MotionEvent.ACTION_DOWN:
{
setSelection(true); // it just change the background
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_OUTSIDE:
{
setSelection(false); // it just change the background
break;
}
}
return false;
}
public void onClick(View v)
{
// some other code here
}
public boolean onLongClick(View view)
{
// just showing a Toast here
return false;
}
// somewhere else in code
setOnTouchListener(this);
setOnClickListener(this);
setOnLongClickListener(this);
How do I make them work together correctly?
Thanks in advance
onClick & onLongClick is actually dispatched from View.onTouchEvent.
if you override View.onTouchEvent or set some specific View.OnTouchListener via setOnTouchListener,
you must care for that.
so your code should be something like:
public boolean onTouch(View v, MotionEvent evt)
{
// to dispatch click / long click event,
// you must pass the event to it's default callback View.onTouchEvent
boolean defaultResult = v.onTouchEvent(evt);
switch (evt.getAction())
{
case MotionEvent.ACTION_DOWN:
{
setSelection(true); // just changing the background
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_OUTSIDE:
{
setSelection(false); // just changing the background
break;
}
default:
return defaultResult;
}
// if you reach here, you have consumed the event
return true;
}

Categories

Resources