Reputation: 2422
I have a page which looks like this*:
<ScrollView>
<WebView/>
<WebView/>
<WebView/>
</Scrollview>
* not the actual layout, but that it enough for the question and makes the problem clearer.
I don't know how exactly a WebView
looks like because it can be very variable. A WebView
can contain a scrollable list.
I want to achieve this scrolling behaviour:
These are two WebViews (the one with the blue header and the one with the green header). I only want to scroll the list in the blue WebView
when I'm dragging in the list. If I touch anywhere else (for example on the blue or green header), I want to scroll in the ScrollView
.
My attempt to solve this is a CustomRenderer
for the WebView
, which I found in this thread in the Xamarin forums:
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.Touch -= Control_Touch;
}
if (e.NewElement != null)
{
Control.Touch += Control_Touch;
}
}
void Control_Touch(object sender, TouchEventArgs e)
{
// Executing this will prevent the Scrolling to be intercepted by parent views
switch (e.Event.Action)
{
case MotionEventActions.Down:
Control.Parent.RequestDisallowInterceptTouchEvent(true);
break;
case MotionEventActions.Up:
Control.Parent.RequestDisallowInterceptTouchEvent(false);
break;
}
// Calling this will allow the scrolling event to be executed in the WebView
Control.OnTouchEvent(e.Event);
}
This nearly does the job, I can scroll in the list inside of the WebView
now, but it won't scroll in the ScrollView
anymore if I tap anywhere else in the WebView
.
So I tried to check for the height of the inner list:
case MotionEventActions.Down:
if (ComputeVerticalScrollRange() > MeasuredHeight)
Control.Parent.RequestDisallowInterceptTouchEvent(true);
break;
case MotionEventActions.Up:
if (ComputeVerticalScrollRange() > MeasuredHeight)
Control.Parent.RequestDisallowInterceptTouchEvent(false);
break;
MeasuredHeight
is the height of the WebView
. ComputeVerticalScrollRange()
will always return the same height as MeasuredHeight
tho. So one possible solution would be to get the height of the inner list in the WebView
, but I don't know how I can get that (I'm new to Android). And maybe, there are even other solutions to this.
Upvotes: 0
Views: 437
Reputation: 16652
For the code Control.Parent.RequestDisallowInterceptTouchEvent(true);
, you will need to make sure this Control.Parent
is really the ScrollViewer
.
AFAIK, to implement the view for each platform, it uses renderers, the Control.Parent
here is the WebViewRenderer
, so I think what you need is possible this:
Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(true);
The real problem is when to set it to true or false, you may for example modify your Control_Touch
event like this:
private void Control_Touch(object sender, TouchEventArgs e)
{
var viewheight = Control.MeasuredHeight;
var height = (int)Math.Floor(Control.ContentHeight * Control.Scale);
switch (e.Event.Action)
{
case MotionEventActions.Down:
downY = e.Event.GetY();
mAction = ActionState.None;
Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(true);
break;
case MotionEventActions.Move:
upY = e.Event.GetY();
var delta = downY - upY;
if (Math.Abs(delta) > MIN_DISTANCE)
{
if (delta < 0)
{
mAction = ActionState.TB;
//top reached
if (Control.ScrollY == 0)
{
Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(false);
}
}
else if (delta > 0)
{
mAction = ActionState.BT;
//bottom reached
if (Control.ScrollY + viewheight + 5 >= height)
{
Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(false);
}
}
}
break;
case MotionEventActions.Up:
Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(true);
break;
}
Control.OnTouchEvent(e.Event);
}
I used two custom WebView
to test my demo, the idea is to disable scrolling of WebView
when it reached top and it is been scrolling from top to bottom, or vice versa. You may have a try.
Upvotes: 1