TapGestureRecognizer outside of bounds doesn't

2019-06-08 22:47发布

问题:

I am trying to implement a little GridView with 5 labels:

  • "Setting 1"
  • "Setting 2"
  • "More settings"
  • "Setting 4"
  • "Setting 5"

Each of these labels are attached to a TapGestureRecognizer.

You cannot see Setting 4 and 5 at the first moment, they are beneath the screen.

If you press the "More settings" label/button the last two settings become visible by pushing the entire Grid upwards with "LayoutTo".

  • "Setting 1"
  • "Setting 2"
  • "Setting 4"
  • "Setting 5"

Now my problem is that the last two TapGestureRecognizers of the last two labels don't work. I found someone with a similar problem here: https://forums.xamarin.com/discussion/36094/offscreen-tapgesturerecognizer-not-firing.

I tried everything stated in this post, I changed my main layout to an AbsoluteLayout and used LayoutTo instead of TranslateTo but it still doesn't work.

If anyone has an idea how to get these TapGestureRecognizers to work or how to solve my problem of "dragging out" my GridView entries with a little animation but without having to rely on putting content out of screen bounds I would be very happy.

Here some of my xaml code:

<AbsoluteLayout x:Name="rellay">
<Grid x:Name="MainGrid" 
        AbsoluteLayout.LayoutBounds="0, 0, 1, .9" AbsoluteLayout.LayoutFlags="SizeProportional">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition x:Name="MapRow" Height="*" />
        <RowDefinition x:Name="DataRow" />
    </Grid.RowDefinitions>
    <maps:Map x:Name="map" Grid.Row="0" />
    <Grid RowSpacing="0" ColumnSpacing="0" x:Name="SecondGrid" Grid.Row="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="62" x:Name="StartRow" />
            <RowDefinition Height="62" x:Name="DestRow" />
            <RowDefinition Height="62" x:Name="OptRow" />
            <RowDefinition Height="62" x:Name="NameRow" />
            <RowDefinition Height="62" x:Name="PhoneRow" />
            <RowDefinition Height="62" x:Name="ZeitRow" />
            <RowDefinition Height="62" x:Name="DetailsRow" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="40" />
            <ColumnDefinition Width="1" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="40" />
        </Grid.ColumnDefinitions>

... (labels and TapGestureRecognizers)

Page-Constructor:

DataRow.Height = 3 * 62;

"Drag-Out-Button":

OptRow.Height = 0;
rellay.LayoutTo(new Rectangle(0, -3 * 62, rellay.Width, rellay.Height), 250, Easing.Linear);

I also tried to directly put stuff like an entry into the AbsoluteLayout (without Grids) outside of bounds and then push it up with LayoutTo but then it doesn't work either, the entry is "dead" in that case.

回答1:

Your last two labels are placed in MainGrid, but you set its LayoutBounds's height to .9. This may let these two labels outside of the MainGrid. Even though they can be seen when you LayoutTo() the AbsoluteLayout, the bottom area can't be controlled. This is why you change it to entry, it can't be touched neither.

You can try to expand the rellay's height: rellay.LayoutTo(new Rectangle(0, -3 * 62, rellay.Width, rellay.Height + 3 * 62), 250, Easing.Linear); to let these two labels touched. But I really recommend you to use ScrollView.

Create a custom scrollView:

public class MyScrollView : ScrollView
{
}

Then use this control wrapping the AbsoluteLayout

<local:MyScrollView x:Name="MyScroll">
    ...put your other controls here
</local:MyScrollView>

When you want to show the labels, push it using:

MyScroll.ScrollToAsync(0, 62 * 3, true);

At last, if you want to disable its scrolling when touching and dragging it, create a Custom Renderer to achieve this:

[assembly: ExportRenderer(typeof(MyScrollView), typeof(MyScrollRenderer))]
namespace ExpandLayoutDemo.iOS
{
    public class MyScrollRenderer : ScrollViewRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            ScrollEnabled = false;
        }
    }
}