I have an Activity with a ViewPager with 3 dynamically created Fragments. Each page has numerous Buttons and ImageButtons that activate on their own onTouch..MotionEvent.ACTION_DOWN.
The problem I'm having is that these buttons activate accidentally when am trying to swipe the ViewPager.
Can anyone recommend a way to keep this from happening?
Thanks
Josh
Instead of using onTouch..MotionEvent.ACTION_DOWN.
on the ImageButton I will make them implement;
.OnItemClickListener() /This ensures that only when clicked they will call the desired functionality, I suggest you to check it out this way and let me know.
Hope it helps, more info at; The Documentation for OnItemClickListener
I think that the flow you are following is not really good, the button also has the business code to handle the ACTION_DOWN touch event and the parent (ViewPager) always need handle touch event to detect the scroll events, so button will handle the ACTION_DOWN touch event first and then pass the touch to ViewPager.
You can move the business code into the OnClickListener of Button or another way you should let the button intercept the touch event and don't pass it to ViewPager (Parent View).
This can be easy to handle by customize the Viewpager class and add a variable in the custom ViewPager like this :
private boolean mTouchEnable = true;
#Override
public boolean onTouchEvent(MotionEvent arg0) {
if (!mTouchEnable) {
return false;
}
return super.onTouchEvent(arg0);
}
The button will need a reference to the viewPager and set the mTouchEnable = false in ACTION_DOWN and true in ACTION_UP
I did a viewpager of buttons and I was able to replicate the issue.
I solved to this way:
My buttons have a listener OnClickListener
optionButton.setOnClickListener(this);
public void onClick(View v) {
// your code here
}
Then I add this button to my customPagerAdapter
In your customViewPager you have to override the onInterceptTouchEvent method like this:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
return true;
}
else{
return false;
}
}
This code will intercept the MotionEvent.ACTION_MOVE in child views (return true) and won't intercept MotionEvent.ACTION_DOWN neither onClick event in the child views.
Related
I have a recyclerview with items inside, I would like to distinguish between swipe (I am moving the element horizontaly making some action and setting it back to its original place) singleTouch and longClick, what is the best Practice of achieving it?
I seen a lot of implementations here, but none that work\super messy(and also dont work properly), if i implement custom gestureDetector and switchcase inside it works but it takes the phone about a second to react, if i implement onTouch in only catches onTouch, if I dont the onClick and onLong work but not the swipe
currently only the swipe works:
item.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
logd("TAG","2");
return false;
}
});
}
item.setOnClickListener(v -> {
logd("TAG","1");
});
item.setOnLongClickListener(v -> {
logd("TAG","2");
});
any advice would be appreciated,
Thanks
Let's say:
Activity1 contains some items which will be dragged
Activity2 contains some "boxes" which will be filled with Activity1's item. The boxes already have drag listener.
I want to drag item from Activity1 into Activity2
I tried these method:
I implement onLongClickListener() on Activity1 items and use startDrag() there. Then I open Activity2. I could make the drag shadow appear but the boxes cannot receive the item. In fact, they doesn't respond to any DragEvent.
I implement onLongClickListener() on Activity1 items, but only to pass the data into Activity2. Then I use startDrag() when Activity2 start(specifically in onResume()). Here, the shadow not appear and the boxes doesn't respond to any DragEvent.
Is there any way to make this possible?
its possible using two fragment of view pager instead of 2 activity and you can set logic of drag and drop in onTouch() or dispatchTouch().
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
using dispatchTouch() in activity of fragment or
viewPager.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
using touch event of view pager you can set drag and drop logic within inside.
I hope this idea can help you.
In my layout I have a structure like that:
--RelativeLayout
|
--FrameLayout
|
--Button, EditText...
I want to handle touch events in the RelativeLayout and in the FrameLayout, so I set the onTouchListener in these two view groups. But only the touch in the RelativeLayout is captured.
To try solve this, I wrote my own CustomRelativeLayout, and override the onInterceptTouchEvent, now the click in the child ViewGroup (FrameLayout) is captured, but the click in the buttons and other views doesn't make any effect.
In my own custom layout, I have this:
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
You need to override the onInterceptTouchEvent() for each child, otherwise it will remain an onTouchEvent for the parent.
Intercept Touch Events in a ViewGroup
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
*/
...
// In general, we don't want to intercept touch events. They should be
// handled by the child view.
return false;
}
You need to return false to have the child handle it, otherwise you are returning it to the parent.
Your custom solution will capture touch events from anywhere in your relative layout since the overridden method is set to always throw true.
For your requirement I guess its better to use
the onClick method rather than using onTouch.
OnTouch method invokes different threads on every TouchEvent and I guess that is the cause of your problem
Rather than handling these events its better to try onClick method.
I was able to solve this problem with the following code:
Step 1: declare the EditText above the onCreate () method
public EditText etMyEdit;
Step 2: in the onResume () method the configuration ends:
etMyEdit = (EditText) findViewById (R.id.editText);
etMyEdit.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_UP:
v.getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
return false;
}
});
Hope it helps someone!
The question explains everything I want.
HorizontalScrollView seems to be the only widget for which setOnClickListener() does'nt trigger.
NOTE: I can't use onTouch event because on every touch it is triggered 4-5 times.
I also can't use onTouch on its parent view because parent view has many buttons with different functionality.
The following description is not important:
But still, these are the existing links I've searched(none helped):
horizontalscrollview onclicklistener doesn't get called
How to implement on click listener for horizontalscrollview
Click event of HorizontalScrollView
Android click event of items in HorizontalScrollView not respond after scroll
These are the links which I posted(none got answered completely):
Insert views in HorizontalScrollView programatically
One of the ImageButton not clicking & make HorizontalScrollView clickable
I asked multiple questions in these links, 1 of which was "onClickListener for HorizontalScrollView". That part of my question was never answered.
Hence an all exclusive question.
I came up with two ways of doing it.
The simpler (but less ideal) one:
HorizontalScrollView scrollView = (HorizontalScrollView) findViewById(R.id.scrollView);
scrollView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// Do stuff
}
return false;
}
});
This will get called only once when you click, drag or whatever you do with your finger. However, it will also react to all kinds of gestures, so if you want to detect only tap/click events, you need to further investigate the "event" object and filter out events you don't need. This may be more work than you would like, so you should better use a GestureDetector to do it for you.
This leads to method 2:
HorizontalScrollView scrollView = (HorizontalScrollView) findViewById(R.id.scrollView);
final GestureDetector detector = new GestureDetector(this, new OnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
// Do stuff.
return false;
}
// Note that there are more methods which will appear here
// (which you probably don't need).
});
scrollView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
detector.onTouchEvent(event);
return false;
}
});
Well it happened to me now and I came here for solution, but in my case it was just easier to listen to onClick to its child view instead...
I have a Linear Layout that has a Button and a TextView on it. I have written a OnTouchEvent for the activity. The code works fine if I touch on the screen, but if I touch the button the code does not work. What is the possible solution for this?
public boolean onTouchEvent(MotionEvent event) {
int eventaction=event.getAction();
switch(eventaction)
{
case MotionEvent.ACTION_MOVE:
reg.setText("hey");
break;
}
return true;
}
The problem is the order of operations for how Android handles touch events. Each touch event follows the pattern of (simplified example):
Activity.dispatchTouchEvent()
ViewGroup.dispatchTouchEvent()
View.dispatchTouchEvent()
View.onTouchEvent()
ViewGroup.onTouchEvent()
Activity.onTouchEvent()
But events only follow the chain until they are consumed (meaning somebody returns true from onTouchEvent() or a listener). In the case where you just touch somewhere on the screen, nobody is interested in the event, so it flows all the way down to your code. However, in the case of a button (or other clickable View) it consumes the touch event because it is interested in it, so the flow stops at Line 4.
If you want to monitor all touches that go into your Activity, you need to override dispatchTouchEvent() since that what always gets called first, onTouchEvent() for an Activity gets called last, and only if nobody else captured the event. Be careful to not consume events here, though, or the child views will never get them and your buttons won't be clickable.
public boolean dispatchTouchEvent(MotionEvent event) {
int eventaction=event.getAction();
switch(eventaction) {
case MotionEvent.ACTION_MOVE:
reg.setText("hey");
break;
default:
break;
}
return super.dispatchTouchEvent(event);
}
Another option would be to put your touch handling code into a custom ViewGroup (like LinearLayout) and use its onInterceptTouchEvent() method to allow the parent view to steal away and handle touch events when necessary. Be careful though, as this interaction is one that cannot be undone until a new touch event begins (once you steal one event, you steal them all).
HTH
Let me add one more comment to this excellent post by #Devunwired.
If you've also set an onTouchListener on your View, then its onTouch() method will be called AFTER the dispatch methods, but BEFORE any onTouchEvent() method, i.e. in between no.3 and no.4 on #Devunwired's answer.
Try to set the descendantFocusability attribute of your layout to blocksDescendants
Activity::onTouchEvent will be called only when non of the views in the Activity WIndow consumes/handles the event. If you touch the Button, the Button will consume the events, so the Activity won't be able to handle it.
Check out following articles for more about Android Touch Event handling pipeline.
http://pierrchen.blogspot.jp/2014/03/pipeline-of-android-touch-event-handling.html
you can also try onUserInteraction():
#Override
public void onUserInteraction(){
//your code here
super.onUserInteraction();
}
works well for me!
RecyclerView list_view = findViewById(R.id.list_view);
list_view.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener(){
#Override
public boolean onInterceptTouchEvent(#NonNull RecyclerView rv, #NonNull MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
Log.i("Hello", "World");
return false;
}
});
use public boolean dispatchTouchEvent(MotionEvent event) instead on onTouchEvent()