I am developing an android application which has a scrollview. I have overriden my view from ScrollView and have implemented the onScrollChanged method. As the scrollview scrolls and reaches the bottom, it loads more products and add them to scrollvew. I am having a strange issue. First time when products loads and I try to drag up the scrollview, onScrollChanged never happens. Then I googled and found something:
scrollView.setOnTouchListener(new ListView.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
// Handle ListView touch events.
v.onTouchEvent(event);
return true;
}
});
After adding this, when I try to drag the scrollview from empty area of scrollview, it starts to call onScrollChanged and afterwards I start dragging from anywhere else, onScrollChanged is being called. My question is, why it is behaving like that? It should call onScrollChanged from whatever the touch event starts. I hope, I explained my point well. Any solution?
Why don't you use endless adapter with a view like ListView or GridView to achieve the desired behavior instead
Related
I am developing an app which it has a vertical ViewPager within ScrollView. The thing is ViewPager only recognize touch events when I scroll horizontally, otherwise ScrollView will get focused and perform the scroll.
I think this could work:
viewPager.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
scrollView.requestDisallowInterceptTouchEvent(true);
int action = event.getActionMasked();
switch(action){
case MotionEvent.ACTION_UP:
scrollView.requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});
But because of ViewPager does not recognize touch events when I perform vertical scroll this piece of code does nothing.
What I want to achieve is give focus to ViewPager when I touch it and give focus to ScrollView when I'm not touching the ViewPager. Thanks in advance.
This is some code I use for a scrollview that has problems with textviews focusing. It might help you. It might not.
ScrollView sv = (ScrollView) view.findViewById(R.id.svRecord);
sv.setOnTouchListener(this);
sv.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
sv.setFocusable(true);
sv.setFocusableInTouchMode(true);
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;
}
});
So I have an autoScrolling scrollView in my class and I want to intercept the user onTouch event so that it stops the scrolling when they click on the scrollView. How will I implement this functionality? I know it has something to do with Overriding the onTouchEvent function, but when I Override this it doesn't work.
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_POINTER_1_DOWN:
stopAutoScrolling();
case MotionEvent.ACTION_POINTER_1_UP:
startAutoScrolling();
}
return true;
}
I believe when you return true, you are saying you have handled the onTouchEvent. If you want the normal touch behavior to occur do something like:
return super.onTouchEvent(ev);
That way you call the native code which in your case would perform the scrolling as normal.
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()
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.