代码之家  ›  专栏  ›  技术社区  ›  Morten J Petersen

Xamarin窗体滑动按钮

  •  3
  • Morten J Petersen  · 技术社区  · 9 年前

    我希望在我的应用程序中添加一个滑动功能,该功能与(旧?)iPhone(见图)。

    enter image description here enter image description here

    我正在努力研究如何在跨平台解决方案上实现这一点。我的直接想法是使用一个滑块和一个自定义渲染器,但不确定如果用户在完成幻灯片之前放手,如何创建捕捉启动的功能。如果任何人能够协助该功能,或者他们对如何实现这一功能有更好的建议,我们将不胜感激。

    3 回复  |  直到 9 年前
        1
  •  15
  •   Sharada    9 年前

    除非和直到-你真的需要每个平台都有一个特别的本地外观;您几乎可以使用编写自己的自定义滑块控件 PanGestureRecognizer 和 AbsoluteLayout 无需任何自定义渲染器 Translation animation 具有 Cubic easing 效应

    AbsoluteLayout 同时允许您定义自己的代表拇指和轨迹栏的控件。它还创建了一个几乎看不见的最顶层,用作平移手势侦听器。手势完成后,它会检查滑动是否完成(即轨迹栏的整个宽度),然后抬起 SlideCompleted 事件

    public class SlideToActView : AbsoluteLayout
    {
        public static readonly BindableProperty ThumbProperty =
            BindableProperty.Create(
                "Thumb", typeof(View), typeof(SlideToActView),
                defaultValue: default(View), propertyChanged: OnThumbChanged);
    
        public View Thumb
        {
            get { return (View)GetValue(ThumbProperty); }
            set { SetValue(ThumbProperty, value); }
        }
    
        private static void OnThumbChanged(BindableObject bindable, object oldValue, object newValue)
        {
            ((SlideToActView)bindable).OnThumbChangedImpl((View)oldValue, (View)newValue);
        }
    
        protected virtual void OnThumbChangedImpl(View oldValue, View newValue)
        {
            OnSizeChanged(this, EventArgs.Empty);
        }
    
        public static readonly BindableProperty TrackBarProperty =
            BindableProperty.Create(
                "TrackBar", typeof(View), typeof(SlideToActView),
                defaultValue: default(View), propertyChanged: OnTrackBarChanged);
    
        public View TrackBar
        {
            get { return (View)GetValue(TrackBarProperty); }
            set { SetValue(TrackBarProperty, value); }
        }
    
        private static void OnTrackBarChanged(BindableObject bindable, object oldValue, object newValue)
        {
            ((SlideToActView)bindable).OnTrackBarChangedImpl((View)oldValue, (View)newValue);
        }
    
        protected virtual void OnTrackBarChangedImpl(View oldValue, View newValue)
        {
            OnSizeChanged(this, EventArgs.Empty);
        }
    
        private PanGestureRecognizer _panGesture = new PanGestureRecognizer();
        private View _gestureListener;
        public SlideToActView()
        {
            _panGesture.PanUpdated += OnPanGestureUpdated;
            SizeChanged += OnSizeChanged;
    
            _gestureListener = new ContentView { BackgroundColor = Color.White, Opacity = 0.05 };
            _gestureListener.GestureRecognizers.Add(_panGesture);
        }
    
        public event EventHandler SlideCompleted;
    
        private const double _fadeEffect = 0.5;
        private const uint _animLength = 50;
        async void OnPanGestureUpdated(object sender, PanUpdatedEventArgs e)
        {
            if (Thumb == null | TrackBar == null)
                return;
    
            switch (e.StatusType)
            {
                case GestureStatus.Started:
                    await TrackBar.FadeTo(_fadeEffect, _animLength);
                    break;
    
                case GestureStatus.Running:
                    // Translate and ensure we don't pan beyond the wrapped user interface element bounds.
                    var x = Math.Max(0, e.TotalX);
                    if (x > (Width - Thumb.Width))
                        x = (Width - Thumb.Width);
    
                    if (e.TotalX < Thumb.TranslationX)
                        return;
                    Thumb.TranslationX = x;
                    break;
    
                case GestureStatus.Completed:
                    var posX = Thumb.TranslationX;
    
                    // Reset translation applied during the pan (snap effect)
                    await TrackBar.FadeTo(1, _animLength);
                    await Thumb.TranslateTo(0, 0, _animLength * 2, Easing.CubicIn);
    
                    if (posX >= (Width - Thumb.Width - 10/* keep some margin for error*/))
                        SlideCompleted?.Invoke(this, EventArgs.Empty);
                    break;
            }
        }
    
        void OnSizeChanged(object sender, EventArgs e)
        {
            if (Width == 0 || Height == 0)
                return;
            if (Thumb == null || TrackBar == null)
                return;
    
    
            Children.Clear();
    
            SetLayoutFlags(TrackBar, AbsoluteLayoutFlags.SizeProportional);
            SetLayoutBounds(TrackBar, new Rectangle(0, 0, 1, 1));
            Children.Add(TrackBar);
    
            SetLayoutFlags(Thumb, AbsoluteLayoutFlags.None);
            SetLayoutBounds(Thumb, new Rectangle(0, 0, this.Width/5, this.Height));
            Children.Add(Thumb);
    
            SetLayoutFlags(_gestureListener, AbsoluteLayoutFlags.SizeProportional);
            SetLayoutBounds(_gestureListener, new Rectangle(0, 0, 1, 1));
            Children.Add(_gestureListener);
        }
    }
    

    <StackLayout Margin="40">
        <local:SlideToActView HeightRequest="50" SlideCompleted="Handle_SlideCompleted">
            <local:SlideToActView.Thumb>
                <Frame CornerRadius="10" HasShadow="false" BackgroundColor="Silver" Padding="0">
                    <Image Source="icon.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" WidthRequest="40" />
                </Frame>
            </local:SlideToActView.Thumb>
    
            <local:SlideToActView.TrackBar>
                <Frame CornerRadius="10" HasShadow="false" BackgroundColor="Gray" Padding="0">
                    <Label Text="Slide 'x' to cancel" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" />
                </Frame>
            </local:SlideToActView.TrackBar>
        </local:SlideToActView>
        <Label x:Name="MessageLbl" FontAttributes="Bold" TextColor="Green" />
    </StackLayout>
    

    代码隐藏

    void Handle_SlideCompleted(object sender, System.EventArgs e)
    {
        MessageLbl.Text = "Success!!";
    }
    

    ios simulator android emulator


    更新:08/30

    更新的控制代码

    public class SlideToActView : AbsoluteLayout
    {
        public static readonly BindableProperty ThumbProperty =
            BindableProperty.Create(
                "Thumb", typeof(View), typeof(SlideToActView),
                defaultValue: default(View));
    
        public View Thumb
        {
            get { return (View)GetValue(ThumbProperty); }
            set { SetValue(ThumbProperty, value); }
        }
    
        public static readonly BindableProperty TrackBarProperty =
            BindableProperty.Create(
                "TrackBar", typeof(View), typeof(SlideToActView),
                defaultValue: default(View));
    
        public View TrackBar
        {
            get { return (View)GetValue(TrackBarProperty); }
            set { SetValue(TrackBarProperty, value); }
        }
    
        public static readonly BindableProperty FillBarProperty =
            BindableProperty.Create(
                "FillBar", typeof(View), typeof(SlideToActView),
                defaultValue: default(View));
    
        public View FillBar
        {
            get { return (View)GetValue(FillBarProperty); }
            set { SetValue(FillBarProperty, value); }
        }
    
        private PanGestureRecognizer _panGesture = new PanGestureRecognizer();
        private View _gestureListener;
        public SlideToActView()
        {
            _panGesture.PanUpdated += OnPanGestureUpdated;
            SizeChanged += OnSizeChanged;
    
            _gestureListener = new ContentView { BackgroundColor = Color.White, Opacity = 0.05 };
            _gestureListener.GestureRecognizers.Add(_panGesture);
        }
    
        public event EventHandler SlideCompleted;
    
        private const double _fadeEffect = 0.5;
        private const uint _animLength = 50;
        async void OnPanGestureUpdated(object sender, PanUpdatedEventArgs e)
        {
            if (Thumb == null || TrackBar == null || FillBar == null)
                return;
    
            switch (e.StatusType)
            {
                case GestureStatus.Started:
                    await TrackBar.FadeTo(_fadeEffect, _animLength);
                    break;
    
                case GestureStatus.Running:
                    // Translate and ensure we don't pan beyond the wrapped user interface element bounds.
                    var x = Math.Max(0, e.TotalX);
                    if (x > (Width - Thumb.Width))
                        x = (Width - Thumb.Width);
    
                    //Uncomment this if you want only forward dragging.
                    //if (e.TotalX < Thumb.TranslationX)
                    //    return;
                    Thumb.TranslationX = x;
                    SetLayoutBounds(FillBar, new Rectangle(0, 0, x + Thumb.Width / 2, this.Height));
                    break;
    
                case GestureStatus.Completed:
                    var posX = Thumb.TranslationX;
                    SetLayoutBounds(FillBar, new Rectangle(0, 0, 0, this.Height));
    
                    // Reset translation applied during the pan
                    await Task.WhenAll(new Task[]{
                        TrackBar.FadeTo(1, _animLength),
                        Thumb.TranslateTo(0, 0, _animLength * 2, Easing.CubicIn),
                    });
    
                    if (posX >= (Width - Thumb.Width - 10/* keep some margin for error*/))
                        SlideCompleted?.Invoke(this, EventArgs.Empty);
                    break;
            }
        }
    
        void OnSizeChanged(object sender, EventArgs e)
        {
            if (Width == 0 || Height == 0)
                return;
            if (Thumb == null || TrackBar == null || FillBar == null)
                return;
    
    
            Children.Clear();
    
            SetLayoutFlags(TrackBar, AbsoluteLayoutFlags.SizeProportional);
            SetLayoutBounds(TrackBar, new Rectangle(0, 0, 1, 1));
            Children.Add(TrackBar);
    
            SetLayoutFlags(FillBar, AbsoluteLayoutFlags.None);
            SetLayoutBounds(FillBar, new Rectangle(0, 0, 0, this.Height));
            Children.Add(FillBar);
    
            SetLayoutFlags(Thumb, AbsoluteLayoutFlags.None);
            SetLayoutBounds(Thumb, new Rectangle(0, 0, this.Width/5, this.Height));
            Children.Add(Thumb);
    
            SetLayoutFlags(_gestureListener, AbsoluteLayoutFlags.SizeProportional);
            SetLayoutBounds(_gestureListener, new Rectangle(0, 0, 1, 1));
            Children.Add(_gestureListener);
    
    
        }
    }
    

    XAML使用

    <StackLayout Margin="40">
        <local:SlideToActView HeightRequest="50" SlideCompleted="Handle_SlideCompleted">
            <local:SlideToActView.Thumb>
                <Frame CornerRadius="10" HasShadow="false" BackgroundColor="Silver" Padding="0">
                    <Image Source="icon.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" WidthRequest="40" />
                </Frame>
            </local:SlideToActView.Thumb>
    
            <local:SlideToActView.TrackBar>
                <Frame CornerRadius="10" HasShadow="false" BackgroundColor="Gray" Padding="0">
                    <Label Text="Slide 'x' to cancel" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" />
                </Frame>
            </local:SlideToActView.TrackBar>
    
            <local:SlideToActView.FillBar>
                <Frame CornerRadius="10" HasShadow="false" BackgroundColor="Red" Padding="0" />
            </local:SlideToActView.FillBar>
        </local:SlideToActView>
        <Label x:Name="MessageLbl" FontAttributes="Bold" TextColor="Green" />
    </StackLayout>
    

    enter image description here

        2
  •  0
  •   Sajidh Zahir    9 年前

    使用xamarin表单的自定义渲染,以便您可以定义滑块在每个平台中的外观,在android中,Seekbar通常用于滑块和iOS UiSlider

    https://blog.xamarin.com/customizing-xamarin-forms-controls-with-effects/

    https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/

    http://www.viralandroid.com/2015/11/android-custom-seekbar-example.html

    您可以在可移植类中保留泛型方法,正如您所解释的那样,这种行为只有两种状态,这也可以通过使用自定义开关小部件来实现

        3
  •  0
  •   Daspuru    8 年前

    Android中存在一个错误,即手势识别器不会触发启动或完成的事件!此处链接: https://bugzilla.xamarin.com/show_bug.cgi?id=39768

    public class SlideToOpenView : AbsoluteLayout
    {
    
        public static readonly BindableProperty ThumbProperty =
        BindableProperty.Create(
                "Thumb", typeof(View), typeof(SlideToOpenView),
            defaultValue: default(View));
    
        public View Thumb
        {
            get { return (View)GetValue(ThumbProperty); }
            set { SetValue(ThumbProperty, value); }
        }
    
        public static readonly BindableProperty TrackBarProperty =
            BindableProperty.Create(
                "TrackBar", typeof(View), typeof(SlideToOpenView),
                defaultValue: default(View));
    
        public View TrackBar
        {
            get { return (View)GetValue(TrackBarProperty); }
            set { SetValue(TrackBarProperty, value); }
        }
    
        public static readonly BindableProperty FillBarProperty =
            BindableProperty.Create(
                "FillBar", typeof(View), typeof(SlideToOpenView),
                defaultValue: default(View));
    
        public View FillBar
        {
            get { return (View)GetValue(FillBarProperty); }
            set { SetValue(FillBarProperty, value); }
        }
    
        private PanGestureRecognizer _panGesture = new PanGestureRecognizer();
        private View _gestureListener;
    
        private bool _android = false;
    
        public SlideToOpenView()
        {
            _panGesture.PanUpdated += OnPanGestureUpdated;
    
            SizeChanged += OnSizeChanged;
    
            _gestureListener = new ContentView { BackgroundColor = Color.White, Opacity = 0.05 };
            _gestureListener.GestureRecognizers.Add(_panGesture);
    
            if (Device.RuntimePlatform == Device.Android) {
                _android = true;
            }
        }
    
    
        public event EventHandler SlideCompleted;
    
        private const double _fadeEffect = 0.5;
        private const uint _animLength = 50;
    
        //Variable that stores the last state in axis X
        private double _lastX = -1;
        private bool _panRunning = false;
    
        async void OnPanGestureUpdated(object sender, PanUpdatedEventArgs e)
        {
    
            if (Thumb == null || TrackBar == null || FillBar == null)
                return;
    
            switch (e.StatusType)
            {
                case GestureStatus.Started:
                    Debug.WriteLine("GestureStatus.Started");
                    await TrackBar.FadeTo(_fadeEffect, _animLength);
    
                    break;
    
                case GestureStatus.Running:
    
                    // Translate and ensure we don't pan beyond the wrapped user interface element bounds.
    
                        var x = Math.Max(0, e.TotalX);
                        if (x > (Width - Thumb.Width))
                            x = (Width - Thumb.Width);
    
                        //Uncomment this if you want only forward dragging.
                        //if (e.TotalX < Thumb.TranslationX)
                        //    return;
    
                        Thumb.TranslationX = x;
                        SetLayoutBounds(FillBar, new Rectangle(0, 0, x + Thumb.Width / 2, this.Height));
    
                    if (_panRunning == false && _android == true)
                    {
                        Device.StartTimer(TimeSpan.FromMilliseconds(2000), TimerHandle);
                        _panRunning = true;
                    }
                    break;
    
                case GestureStatus.Completed:
                    _panRunning = false;
                    var posX = Thumb.TranslationX;
                    SetLayoutBounds(FillBar, new Rectangle(0, 0, 0, this.Height));
    
                    // Reset translation applied during the pan
                    await Task.WhenAll(new Task[]{
                    TrackBar.FadeTo(1, _animLength),
                    Thumb.TranslateTo(0, 0, _animLength * 2, Easing.CubicIn),
                    });
    
                    //await TrackBar.FadeTo(1, _animLength);
                    //await Thumb.TranslateTo(0, 0, _animLength * 2, Easing.CubicIn);
    
    
                    if (posX >= (Width - Thumb.Width - 10/* keep some margin for error*/))
                        SlideCompleted?.Invoke(this, EventArgs.Empty);
    
    
                    break;
            }
        }
    
        //Timer handle for Android Xamarin.Forms Gesture Bug
        bool TimerHandle()
        {
    
            if (_lastX == 0) {
                _lastX = -1;
                return false;
            }
    
            if (Thumb.TranslationX == _lastX && _lastX != -1) {
                _panRunning = false;
                var posX = Thumb.TranslationX;
                SetLayoutBounds(FillBar, new Rectangle(0, 0, 0, this.Height));
    
                // Reset translation applied during the pan
    
                TrackBar.FadeTo(1, _animLength);
                Thumb.TranslateTo(0, 0, _animLength * 2, Easing.CubicIn);
    
                if (posX >= (Width - Thumb.Width - 10/* keep some margin for error*/))
                    SlideCompleted?.Invoke(this, EventArgs.Empty);
                _lastX = -1;
                return false;
            } 
                _lastX = Thumb.TranslationX;
                return true;
    
        }
    
        void OnSizeChanged(object sender, EventArgs e)
        {
            Debug.WriteLine("OnSizeChanged");
            if (Width == 0 || Height == 0)
                return;
            if (Thumb == null || TrackBar == null || FillBar == null)
                return;
    
    
            Children.Clear();
    
            SetLayoutFlags(TrackBar, AbsoluteLayoutFlags.SizeProportional);
            SetLayoutBounds(TrackBar, new Rectangle(0, 0, 1, 1));
            Children.Add(TrackBar);
    
            SetLayoutFlags(FillBar, AbsoluteLayoutFlags.None);
            SetLayoutBounds(FillBar, new Rectangle(0, 0, 0, this.Height));
            Children.Add(FillBar);
    
            SetLayoutFlags(Thumb, AbsoluteLayoutFlags.None);
            SetLayoutBounds(Thumb, new Rectangle(0, 0, this.Width / 5, this.Height));
            Children.Add(Thumb);
    
            SetLayoutFlags(_gestureListener, AbsoluteLayoutFlags.SizeProportional);
            SetLayoutBounds(_gestureListener, new Rectangle(0, 0, 1, 1));
            Children.Add(_gestureListener);
    
    
        }
    }
    
    推荐文章