如何在“选项卡式/轮播”页面上添加滑动点?

时间:2018-12-12 10:35:25

标签: xamarin.forms

我想知道如何像下面的图片一样在TabbedPageCarouselPage中添加指示滑动屏幕的点吗?

enter image description here

我尝试为此添加图像,但是它们看起来不自然,所以有真正的方法吗?

我的上述变通方法说明以3页为例:

我创建3张图像,每张图像都有3个点,其中一个突出显示:

第一个突出显示图像的点是第一个。

第二个图像突出显示的点是第二个。

1 个答案:

答案 0 :(得分:1)

您可以使用Xamarin.Forms.CarouselView并为页面指示器编写一个用户控件。请按照以下步骤操作,

在所有3个项目(PCL,iOS和Android)中,使用来自NuGet的Xamarin.Forms.CarouselView版本2.3.0-pre2(Xamarin.Forms.CarouselView)软件包安装软件包控制台。

在页面指令中添加对Carousel视图的引用,

xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView" 

和下面的Xaml代码,

 <StackLayout  Padding="0,0,0,5" BackgroundColor="#d8d8d8" >
     <cv:CarouselView x:Name="cview" ItemsSource="{Binding DataSource}" Position="{Binding Position, Mode=TwoWay}">
        <cv:CarouselView.ItemTemplate>
          <DataTemplate>       
            <Image  Aspect="AspectFill" HorizontalOptions="Center" VerticalOptions="Center" Source="{Binding PickedImage}" />
          </DataTemplate>
        </cv:CarouselView.ItemTemplate>
      </cv:CarouselView>
  <cutomControl:CarouselIndicators IndicatorHeight="16" IndicatorWidth="16" UnselectedIndicator="unselected_circle.png" SelectedIndicator="selected_circle.png" Position="{Binding Position}" ItemsSource="{Binding DataSource}" />
</StackLayout>

注意,Position和您的ViewModel应该具有

private int _position;
    public int Position
    {
        get { return _position; }
        set
        {
            _position = value;
            OnPropertyChanged();
        }
    }

注意,CarouselView下面的customControl。是的,您需要为其编写一个自定义控件。只需使用下面的自定义控制代码,然后在page指令中添加引用,

因此您的页面指令将如下所示,

 xmlns:cutomControl="clr-namespace:XXXX.CustomControls;assembly=XXXX"
 xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"

和自定义控制代码是

public class CarouselIndicators : Grid
{
    private ImageSource UnselectedImageSource = null;
    private ImageSource SelectedImageSource = null;
    private readonly StackLayout _indicators = new StackLayout() { Orientation = StackOrientation.Horizontal, HorizontalOptions = LayoutOptions.CenterAndExpand };

    public CarouselIndicators()
    {
        this.HorizontalOptions = LayoutOptions.CenterAndExpand;
        this.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
        this.Children.Add(_indicators);
    }

    public static readonly BindableProperty PositionProperty = BindableProperty.Create(nameof(Position), typeof(int), typeof(CarouselIndicators), 0, BindingMode.TwoWay, propertyChanging: PositionChanging);
    public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(CarouselIndicators), Enumerable.Empty<object>(), BindingMode.OneWay, propertyChanged: ItemsChanged);
    public static readonly BindableProperty SelectedIndicatorProperty = BindableProperty.Create(nameof(SelectedIndicator), typeof(string), typeof(CarouselIndicators), "", BindingMode.OneWay);
    public static readonly BindableProperty UnselectedIndicatorProperty = BindableProperty.Create(nameof(UnselectedIndicator), typeof(string), typeof(CarouselIndicators), "", BindingMode.OneWay);
    public static readonly BindableProperty IndicatorWidthProperty = BindableProperty.Create(nameof(IndicatorWidth), typeof(double), typeof(CarouselIndicators), 0.0, BindingMode.OneWay);
    public static readonly BindableProperty IndicatorHeightProperty = BindableProperty.Create(nameof(IndicatorHeight), typeof(double), typeof(CarouselIndicators), 0.0, BindingMode.OneWay);

    public string SelectedIndicator
    {
        get { return (string)this.GetValue(SelectedIndicatorProperty); }
        set { this.SetValue(SelectedIndicatorProperty, value); }
    }

    public string UnselectedIndicator
    {
        get { return (string)this.GetValue(UnselectedIndicatorProperty); }
        set { this.SetValue(UnselectedIndicatorProperty, value); }
    }

    public double IndicatorWidth
    {
        get { return (double)this.GetValue(IndicatorWidthProperty); }
        set { this.SetValue(IndicatorWidthProperty, value); }
    }

    public double IndicatorHeight
    {
        get { return (double)this.GetValue(IndicatorHeightProperty); }
        set { this.SetValue(IndicatorHeightProperty, value); }
    }

    public int Position
    {
        get { return (int)this.GetValue(PositionProperty); }
        set { this.SetValue(PositionProperty, value); }
    }

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)this.GetValue(ItemsSourceProperty); }
        set { this.SetValue(ItemsSourceProperty, (object)value); }
    }

    private void Clear()
    {
        _indicators.Children.Clear();
    }

    private void Init(int position)
    {

        if (UnselectedImageSource == null)
            UnselectedImageSource = ImageSource.FromFile(UnselectedIndicator);

        if (SelectedImageSource == null)
            SelectedImageSource = ImageSource.FromFile(SelectedIndicator);

        if (_indicators.Children.Count > 0)
        {
            for (int i = 0; i < _indicators.Children.Count; i++)
            {
                if (((Image)_indicators.Children[i]).ClassId == nameof(State.Selected) && i != position)
                    _indicators.Children[i] = BuildImage(State.Unselected, i);
                else if (((Image)_indicators.Children[i]).ClassId == nameof(State.Unselected) && i == position)
                    _indicators.Children[i] = BuildImage(State.Selected, i);
            }
        }
        else
        {
            var enumerator = ItemsSource.GetEnumerator();
            int count = 0;
            while (enumerator.MoveNext())
            {
                Image image = null;
                if (position == count)
                    image = BuildImage(State.Selected, count);
                else
                    image = BuildImage(State.Unselected, count);

                _indicators.Children.Add(image);

                count++;
            }
        }
    }

    private Image BuildImage(State state, int position)
    {
        var image = new Image()
        {
            WidthRequest = IndicatorWidth,
            HeightRequest = IndicatorHeight,
            ClassId = state.ToString()
        };

        switch (state)
        {
            case State.Selected:
                image.Source = SelectedImageSource;
                break;
            case State.Unselected:
                image.Source = UnselectedImageSource;
                break;
            default:
                throw new Exception("Invalid state selected");
        }

        image.GestureRecognizers.Add(new TapGestureRecognizer() { Command = new Command(() => { Position = position; }) });

        return image;
    }

    private static void PositionChanging(object bindable, object oldValue, object newValue)
    {
        var carouselIndicators = bindable as CarouselIndicators;

        carouselIndicators.Init(Convert.ToInt32(newValue));
    }

    private static void ItemsChanged(object bindable, object oldValue, object newValue)
    {
        var carouselIndicators = bindable as CarouselIndicators;

        carouselIndicators.Clear();
        carouselIndicators.Init(0);
    }

    public enum State
    {
        Selected,
        Unselected
    }
}