I have a ViewPager below a NestedScrollView width some top padding, and clipToPadding(false) and transparent background (like as image).
My ViewPager can't get touch event and doesn't work.
How can I solve this problem?
(I can't change my structure and can't move ViewPager to above of NestedScrollView or set TopMargin to NestedScrollView)
NestedScrollView
nestedScrollView = new NestedScrollView(getContext());
nestedScrollView.setFillViewport(true);
nestedScrollView.setLayoutParams(scrollParams);
nestedScrollView.setClipToPadding(false);
Solution:
This Problem solved With overwriting NestedScrollView and Override onTouchEvent.
(Thanks to #petrumo)
public class MyNestedScrollView extends NestedScrollView {
private boolean topZone = false;
public MyNestedScrollView(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN ){
topZone = (getPaddingTop() - getScrollY() > ev.getY());
}
if(topZone){
if(ev.getAction() == MotionEvent.ACTION_UP){
topZone = false;
}
return false;
}
return super.onTouchEvent(ev);
}
}
There is a workaround for this case, you can override onInterceptTouchEvent and onTouchEvent in the nestedscrollview. There are posts explaining how to do it, https://developer.android.com/training/gestures/viewgroup.html and http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html. When you intercept the event, based on the position and your custom logic you would decide to not use the touch to leave it for the viewpager or let the default scrollview logic handle it.
I am not in favor of this solution, but as you explained you need to have the NestedScrollview cover the viewPager, unless you can reconsider the restrictions
Related
I'm programming a custom ChipGroup class and in the layout I have a HorizontalScrollView and a Material ChipGroup inside. In my constructor I pass a boolean called "isScrollable" because I want it to be configurable the scrolling.
Is there a way to inactivate the scroll of the HorizontalScrollView if scrolling is configured as false?
You cannot disable the scrolling of a ScrollView. You would need to
extend to ScrollView and override the onTouchEvent method to return
false when some condition is matched.
The Gallery component scrolls
horizontally regardless of whether it is in a ScrollView or not - a
ScrollView provides only vertical scrolling (you need a
HorizontalScrollView for horizontal scrolling).
You seem to say you
have a problem with the image stretching itself -- this has nothing
to do with the ScrollView, you can change how an ImageView scales
with the android:scaleType property (XML) or the setScaleType method
for instance ScaleType.CENTER will not stretch your image and will center it at it's original size.
You could modify ScrollView as follows to disable scrolling:
class LockableScrollView extends ScrollView {
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private boolean mScrollable = true;
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
public boolean isScrollable() {
return mScrollable;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// if we can scroll pass the event to the superclass
return mScrollable && super.onTouchEvent(ev);
default:
return super.onTouchEvent(ev);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don't do anything with intercepted touch events if
// we are not scrollable
return mScrollable && super.onInterceptTouchEvent(ev);
}
}
then,
<com.mypackagename.LockableScrollView
android:id="#+id/QuranGalleryScrollView"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<Gallery android:id="#+id/Gallery"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="horizontal">
</Gallery>
</com.mypackagename.LockableScrollView>
in your XML file (just changed the ScrollView to your special LockableScrollView).
Then call
((LockableScrollView)findViewById(R.id.QuranGalleryScrollView)).setScrollingEnabled(false);
to disable scrolling of the view.
edited for clarity
I feel like this question already has an answer, but I can't find one.
I have a ScrollView in my layout, and it contains a variety of clickable views.
Under a specific condition I would like to disable clicks and events for the ScrollView and ALL of its children.
The following have not been helpful:
ScrollView.setEnabled(false)
ScrollView.setClickable(false)
ScrollView.setOnTouchListener(null)
As well as:
(parent view of the ScrollView).requestDisallowInterceptTouchEvent()
I have created a custom ScrollView with the following code:
public class StoppableScrollView extends ScrollView
{
private static boolean stopped = false;
public StoppableScrollView(Context context)
{
super(context);
}
public StoppableScrollView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
if(stopped)
{
return true;
}
else
{
super.onInterceptTouchEvent(ev);
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
if(stopped)
{
return true;
}
else
{
super.onTouchEvent(ev);
return false;
}
}
public static void setStopped(boolean inBool)
{
stopped = inBool;
}
public static boolean getStopped()
{
return stopped;
}
}
Using only onTouchEvent() will stop the scrolling, but not the clicking of child views.
Using only onInterceptTouchEvent() makes it such that when clicks work scrolling does not, and vice versa.
Using both onTouchEvent() and onInterceptTouchEvent() successfully stops unwanted clicks on child views when stopped is 'true' but it also disables scrolling regardless of the state of stopped.
Is there an easier way to get this behaviour, or is there a way to modify the StoppableScrollView class so that it will handle these touch events properly?
What probably should help is the following (because I had similar problems):
In the ScrollView you should do a RelativeLayout as Main Child (ScrollView does accept only 1 main child anyway). This RelativeLayout should of course of fill_parent in both directions.
At the really end of the RelativeLayout (after all other children), you could put now a LinearLayout with transparent background (#00FFFFFF) which has also fill_parent in both directions. This LinearLayout should have Visibility = View.GONE (by default)
Also you have to attach an empty OnClickListener to it. Now, because of zOrder if you make this LinearLayout Visibility = View.Visible it will catch all the events and avoid clicking the children above!
As scrollview allows immeditate one child say in my case i have linear layout.and in this linear layout i have other conreolls.
now our first task is to get this linear layout so what we can write is
LinearLayout l = (LinearLayout) scrollview.getChildAt(0);
now after getting this linear layour we can easily access other controlls placed inside it via this code and disable it.
for(int i =0; i<l.getChildCount(); i++)
{
Log.i(TAG,"child "+ l.getChildAt(i));
l.getChildAt(i).setEnabled(false);
}
I have a set of Viewpagers which i have implemented using the PageAdapters. In some of the Pages in the Pageviewer, i have webviews. The webviews has horizontal scrollers inside(Something similar to a Range selecter). When i try to drag some range, the page also gets swiped.
What exactly i need help is when i make any operation on a particular element/component in a webview, how can i avoid the pageviewer swiping and the operation inside the webview is accomplished.
You need to extend the Webview widget and override two methods as follows:
#Override
public boolean onInterceptTouchEvent(MotionEvent p_event)
{
return true;
}
#Override
public boolean onTouchEvent(MotionEvent p_event)
{
if (p_event.getAction() == MotionEvent.ACTION_MOVE && getParent() != null)
{
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.onTouchEvent(p_event);
}
I have a application that need event handling on a unusual way.
For my question, let me first explain a simple case that the current event handling system of Android don't fits for me.
Supposing that I have a FrameLayout (that I'll call ViewSwiper since now) that all Views added on it are MATCH_PARENT X MATCH_PARENT (that I'll call PageView), it's handles events by translating the actual View and replace it based on the direction of moving.
This component I already have done and work properly ( Using Animation to swipe views ).
But the problem is on that PageView I add on top of it, in case of ImageViews that return false on it's onTouchEvent, the ViewSwiper will handle the events and let another PageView enter the screen, but if I add a ScrollView on that, all the events will be consumed by the Scroll and the ViewSwiper will not have chance to replace the PageView.
So I figured out that returning false onTouchEvent of the ScrollView the parent can assume it's events, I wrote this sub-class of ScrollView to test it:
public class ScrollViewVertical extends ScrollView {
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
}
public boolean onTouchEvent(MotionEvent evt) {
super.onTouchEvent(evt);
return false;
}
}
But returning false make any further events to get dispatched to the parent, but I need these events for VERTICAL scrolling, so I have the idea to return falses only if the user are moving HORIZONTAL, that's what my code looks like:
public class ScrollViewVertical extends ScrollView {
private MovementTracker moveTracker;
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
moveTracker = new MovementTracker();
}
public boolean onTouchEvent(MotionEvent evt) {
if (moveTracker.track(evt))
if (moveTracker.getDirection() == Direction.HORIZONTAL)
return false;
return super.onTouchEvent(evt);
}
}
PS: MovementTracker will returns true on track() after some events and tell on which direction the user is moving.
But in that case, the ScrollView keep receiving events since it's returns true on the first events.
Any ideas on how can I handle the events on ViewSwiper when it's child returns false (even if some trues are returned).
PS: I can give more info about this if needed, and accept different solutions also.
Based on answers I tried the following:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return intercept;
}
public boolean onTouchEvent(MotionEvent evt) {
boolean x = super.onTouchEvent(evt);
if (moveTracker.track(evt)) {
intercept = moveTracker.getDirection() != Direction.VERTICAL;
if (!intercept)
getParent().requestDisallowInterceptTouchEvent(false);
}
return x;
}
Still nothing.
try this in onTouchEvent() of the scrollview
//if (evt.getAction() == MotionEvent.ACTION_MOVE) {
if (moveTracker.track(evt)){
if (moveTracker.getDirection() == Direction.VERTICAL){
//Or the direction you want the scrollview keep moving
getParent().requestDisallowInterceptTouchEvent(true);
}
}
return true;
Update
Please try the following to the custom Scrollview
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
super.dispatchTouchEvent(ev);
return false;
}
And nothing else
This way i assume the MotionEvent will perform on both views. And since they don't conflict (One is vertical the other one is Horizontal) this could work
Based on the answer from weakwire, I came to the following solution:
On ViewSwiper
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!super.dispatchTouchEvent(ev))
onTouchEvent(ev);
return true;
}
And on ScrollHorizontal I return false on dispatchTouchEvent when I don't need then anymore.
From a simplistic overview I have a custom View that contains some bitmaps the user can drag around and resize.
The way I do this is fairly standard as in I override onTouchEvent in my CustomView and check if the user is touching within an image, etc.
My problem comes when I want to place this CustomView in a ScrollView. This works, but the ScrollView and the CustomView seem to compete for MotionEvents, i.e. when I try to drag an image it either moves sluggishly or the view scrolls.
I'm thinking I may have to extend a ScrollView so I can override onInterceptTouchEvent and let it know if the user is within the bounds of an image not to try and scroll. But then because the ScrollView is higher up in the hierarchy how would I get access to the CustomView's current state?
Is there a better way?
Normally Android uses a long press to begin a drag in cases like these since it helps disambiguate when the user intends to drag an item vs. scroll the item's container. But if you have an unambiguous signal when the user begins dragging an item, try getParent().requestDisallowInterceptTouchEvent(true) from the custom view when you know the user is beginning a drag. (Docs for this method here.) This will prevent the ScrollView from intercepting touch events until the end of the current gesture.
None of the solutions found worked "out of the box" for me, probably because my custom view extends View, not ViewGroup, and thus I can't implement onInterceptTouchEvent.
Also calling getParent().requestDisallowInterceptTouchEvent(true) was throwing NPE, or doing nothing at all.
Finally this is how I solved the problem:
Inside your custom onTouchEvent call requestDisallow... when your view will take care of the event. For example:
#Override
public boolean onTouchEvent(MotionEvent event) {
Point pt = new Point( (int)event.getX(), (int)event.getY() );
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (/*this is an interesting event my View will handle*/) {
// here is the fix! now without NPE
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
clicked_on_image = true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (clicked_on_image) {
//do stuff, drag the image or whatever
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
clicked_on_image = false;
}
return true;
}
Now my custom view works fine, handling some events and letting scrollView catch the ones we don't care about. Found the solution here: http://android-devblog.blogspot.com.es/2011/01/scrolling-inside-scrollview.html
Hope it helps.
There is an Android event called MotionEvent.ACTION_CANCEL (value = 3). All I do is override my custom control's onTouchEvent method and capture this value. If I detect this condition then I respond accordingly.
Here is some code:
#Override
public boolean onTouchEvent(MotionEvent event) {
if(isTouchable) {
int maskedAction = event.getActionMasked();
if (maskedAction == MotionEvent.ACTION_DOWN) {
this.setTextColor(resources.getColor(R.color.octane_orange));
initialClick = event.getX();
} else if (maskedAction == MotionEvent.ACTION_UP) {
this.setTextColor(defaultTextColor);
endingClick = event.getX();
checkIfSwipeOrClick(initialClick, endingClick, range);
} else if(maskedAction == MotionEvent.ACTION_CANCEL)
this.setTextColor(defaultTextColor);
}
return true;
}