Xamarin.forms動畫菜單案例(一)

關於如何通過自定義渲染器在Xamarin Forms中製作Satellite菜單的集成
首先我創建了一些適合我應用程序的動畫菜單,我認爲這對你們有用。

Xamarin動畫菜單效果圖:
這裏寫圖片描述
這裏寫圖片描述
它是完整的C#代碼,沒有XAML,Page級爲ContentPage,並且是完全可編輯的。代碼有些雜亂,如果你想修改它或使用它,只要修改你想要的那部分即可:

Xamarin Forms動畫菜單
代碼由 Julien Chateau提供
源碼開放歡迎大家進行優化

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace DynamicMenus
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class DynamicMenus : ContentPage
    {
        Button[] Buttons;
        Button menubutton;

        Label texte;
        Label titre;

        DateTime beginTime;
        AbsoluteLayout absoluteLayout;

        bool isCurrentPage;
        bool okMove = false;
        float each = 0.0f;
        float eachinv = 0.0f;
        double seconds = 0;
        double offset = 0;
        double RatioScreen;
        double delta = frac;
        static double frac = 0.25;      // Fraction de seconde to expand.

        string TypeMenu = "Dropdown";       // DropDown, Arc, Side ...
        public DynamicMenus()
        {
            InitializeComponent();
            NavigationPage.SetHasNavigationBar(this, false);

            Buttons = new Button[] {
            new Button{ Text = "a",   Image = "MenuDeroulant/icon1.png", },
            new Button{ Text = "b",   Image = "MenuDeroulant/icon2.png", },
            new Button{ Text = "c",   Image = "MenuDeroulant/icon3.png", },
            new Button{ Text = "d",   Image = "MenuDeroulant/icon4.png", },
            new Button{ Text = "e",   Image = "MenuDeroulant/icon5.png", }
        };
            //*  Each button click refers to OnButtonClicked *//s
            for (int i = 0; i < Buttons.Length; i++)
            {
                Buttons[i].Clicked += OnButtonClicked;
            }


            texte = new Label
            {
                Text = "Dropdown",
                Font = Font.SystemFontOfSize(NamedSize.Large),
                TextColor = Color.Maroon,
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.CenterAndExpand,
                HeightRequest = 200
            };

            titre = new Label
            {
                Text = "Animated Buttons",
                Font = Font.SystemFontOfSize(NamedSize.Large),
                HorizontalOptions = LayoutOptions.Center,
                TextColor = Color.Maroon,
                VerticalOptions = LayoutOptions.Start,
                HeightRequest = 30
            };

            menubutton = new Button
            {
                Text = "+",
                Font = Font.SystemFontOfSize(NamedSize.Medium),
                FontAttributes = FontAttributes.Bold,
                BackgroundColor = Color.Transparent,
                //BorderWidth = 2,
                //BorderColor = Color.White,
                TextColor = Color.White,
                Image = "MenuDeroulant/menu.png",
                HorizontalOptions = LayoutOptions.Start,
                VerticalOptions = LayoutOptions.Start,
                Opacity = 0.95,
            };
            menubutton.Clicked += OnButtonClicked;

            absoluteLayout = new AbsoluteLayout
            {
                BackgroundColor = Color.Blue.WithLuminosity(0.8),
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            foreach (Button but in Buttons)
            {
                absoluteLayout.Children.Add(but);
                AbsoluteLayout.SetLayoutFlags(but,
                    AbsoluteLayoutFlags.PositionProportional);
            }

            absoluteLayout.Children.Add(menubutton);
            AbsoluteLayout.SetLayoutFlags(menubutton,
                AbsoluteLayoutFlags.PositionProportional);

            absoluteLayout.Children.Add(texte);
            AbsoluteLayout.SetLayoutFlags(texte,
                AbsoluteLayoutFlags.PositionProportional);
            AbsoluteLayout.SetLayoutBounds(texte,
                new Rectangle(0.5, 0.5,
                    AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));


            this.Content = new StackLayout
            {
                Children = {
                titre,
                absoluteLayout
            }
            };
        }
        void OnButtonClicked(object sender, EventArgs e)
        {
            isCurrentPage = true;
            //* Button Menu *//

            if (sender == menubutton)
            {
                //* Sub buttons of the Menu *//
                //Just an example of action
            }
            else if (sender == Buttons[4])
            {
                delta = frac;
                menubutton.Text = "g";
                TypeMenu = "Dropdown";
                AbsoluteLayout.SetLayoutBounds(menubutton,
                    new Rectangle(1, 0,
                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            }
            else if (sender == Buttons[3])
            {
                delta = frac;
                menubutton.Text = "s";
                TypeMenu = "Side";
                AbsoluteLayout.SetLayoutBounds(menubutton,
                    new Rectangle(1, 0,
                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            }
            else if (sender == Buttons[2])
            {
                delta = frac;
                menubutton.Text = "i";
                TypeMenu = "Arc";
                AbsoluteLayout.SetLayoutBounds(menubutton,
                    new Rectangle(1, 0,
                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            }
            else if (sender == Buttons[1])
            {
                delta = frac;
                menubutton.Text = "d";
                TypeMenu = "DemiCircle";
                AbsoluteLayout.SetLayoutBounds(menubutton,
                    new Rectangle(0.5, 1,
                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            }
            else if (sender == Buttons[0])
            {
                delta = frac;
                menubutton.Text = "s";
                TypeMenu = "FlatBottom";
                AbsoluteLayout.SetLayoutBounds(menubutton,
                    new Rectangle(0.5, 1,
                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            }
            texte.Text = TypeMenu;
            ExpandContractMenu();
        }
        void ExpandContractMenu()
        {
            RatioScreen = absoluteLayout.Height / absoluteLayout.Width;
            Debug.WriteLine("\nH : " + absoluteLayout.Height + "\nW : " + absoluteLayout.Width + "\nRatio :  " + RatioScreen);
            if (!okMove)
            {
                okMove = true;
                //* delta start value (here 0,25) is half the time required to expand or contract *//
                if (delta == 0)
                {
                    delta = frac;
                }
                else
                {
                    delta = 0;
                }

                seconds = 0;
                offset = 0;
                beginTime = DateTime.Now;
                Device.StartTimer(TimeSpan.FromSeconds(1.0 / 60), () => {

                    if (okMove && seconds < frac)
                    {
                        if (TypeMenu == "Dropdown")
                        {
                            seconds = Math.Min((DateTime.Now - beginTime).TotalSeconds, frac);
                            offset = seconds + delta;
                            //* convert linear transition to sinusoid *//
                            //* Multiply PI by twice the time required to expand or contract *//
                            //* substract half a delta from offset *//
                            //* divide the whole thing by twice the screen height division you want (2 for the whole screen, 4 for half, 8 for a quarter...) *//
                            //  http://www.abhortsoft.hu/functionvisualizer/functionvisualizer.html
                            double offsetB = (Math.Sin(4 * Math.PI * (offset - 0.125)) + 1) / 4;

                            for (int i = 0; i < Buttons.Length; i++)
                            {
                                float div = 1 / (float)Buttons.Length; // Gives a fraction of distance.
                                each = (i * div); // Assign that fraction multiplied by each place in the collection
                                eachinv = Math.Abs(each - Buttons.Length); // reversed direction
                                AbsoluteLayout.SetLayoutBounds(Buttons[i],
                                    new Rectangle(1, offsetB - ((double)each * offsetB),
                                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
                            }
                        }
                        else if (TypeMenu == "Side")
                        {
                            seconds = Math.Min((DateTime.Now - beginTime).TotalSeconds, frac);
                            offset = seconds + delta;
                            //* convert linear transition to sinusoid *//
                            double offsetB = (Math.Sin(4 * Math.PI * (offset - 0.125)) + 1) / 4;

                            for (int i = 0; i < Buttons.Length; i++)
                            {
                                float div = 1 / (float)Buttons.Length;
                                each = (i * div);
                                eachinv = Math.Abs(each - Buttons.Length);
                                AbsoluteLayout.SetLayoutBounds(Buttons[i],
                                    new Rectangle(1 - (2 * (offsetB - ((double)each * offsetB))), 0,
                                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
                            }
                        }
                        else if (TypeMenu == "Arc")
                        {
                            seconds = Math.Min((DateTime.Now - beginTime).TotalSeconds, frac);
                            offset = seconds + delta;
                            double arkmult = 0.0;
                            if (delta == frac)
                                arkmult = Math.Abs(frac - seconds); // arkmult multiply the arker, so when 
                            if (delta == 0)
                                arkmult = seconds;
                            //* convert linear transition to sinusoid *//
                            double offsetB = (Math.Sin(4 * Math.PI * (offset - 0.125)) + 1) / 8;

                            for (int i = 0; i < Buttons.Length; i++)
                            {
                                float j = ((float)Math.Sin(0.75f * ((float)i - 2.0f)) + 1.0f) * 2.0f; // really fake radiality of the linear  0 1 2 3 4
                                double arker = (0.3 * (Math.Sin(Math.Acos((0.5 * j) - 1))));  // Draws an Arc for values 0 to 4 (adjust the 0,5 double to fit your collection size


                                float div = 1 / (float)Buttons.Length;
                                each = ((j + 2 * i) / 3 * div) + (1 / (float)Buttons.Length);
                                eachinv = Math.Abs(((j + 2 * i) / 3 * div) - 1);
                                AbsoluteLayout.SetLayoutBounds(Buttons[i],
                                    new Rectangle((1 - (RatioScreen * (offsetB - ((double)each * offsetB)))) - ((arker) * arkmult), (offsetB - ((double)eachinv * offsetB)) + ((arker / RatioScreen) * arkmult),
                                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
                            }
                        }
                        else if (TypeMenu == "DemiCircle")
                        {
                            seconds = Math.Min((DateTime.Now - beginTime).TotalSeconds, frac);
                            offset = seconds + delta;
                            double arkmult = 0.0;
                            if (delta == frac)
                                arkmult = Math.Abs(frac - seconds); // arkmult multiply the arker, so when 
                            if (delta == 0)
                                arkmult = seconds;
                            //* convert linear transition to sinusoid *//
                            double offsetA = (Math.Sin(4 * Math.PI * (offset - 0.125)) + 1) / 8;

                            for (int i = 0; i < Buttons.Length; i++)
                            {
                                float j = ((float)Math.Sin(0.75f * ((float)i - 2.0f)) + 1.0f) * 2.0f; // really fake radiality of the linear  0 1 2 3 4
                                double arkerY = 0.14 * Math.Sin(Math.Acos((j * 0.5 * (arkmult / frac)) - 1)); // Demi circle equation
                                float lifter = (float)Math.Min(0.0f, 5.0f * (float)Math.Pow((5.0f * (float)arkmult - 0.5f), 5.0f));
                                float div = 1.0f / (float)Buttons.Length;
                                each = ((j + i) / 2 * div);
                                eachinv = Math.Abs(((j + i) / 2 * div) - 1);
                                AbsoluteLayout.SetLayoutBounds(Buttons[i],
                                    new Rectangle(div + (3 * (offsetA - ((double)eachinv * offsetA))), 1 - (arkerY) - (double)lifter,    // the 0,5 in X is to center horizontally
                                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
                            }

                        }
                        else if (TypeMenu == "FlatBottom")
                        { // Button 3 stays behind the menu button
                            seconds = Math.Min((DateTime.Now - beginTime).TotalSeconds, frac);
                            offset = seconds + delta;
                            double arkmult = 0.0;
                            if (delta == frac)
                                arkmult = Math.Abs(frac - seconds); // arkmult multiply the arker, so when 
                            if (delta == 0)
                                arkmult = seconds;

                            for (int i = 0; i < Buttons.Length; i++)
                            {
                                float deltX = (Buttons.Length - 1.0f) / 5.0f;  // really not sure about that, it works at 5 items but would explode with any other number
                                double arkerX = (deltX * i) - (2 * deltX); // moves the buttons on x

                                Debug.WriteLine(i + " : " + arkerX);
                                float div = 0.5f / (float)Buttons.Length;
                                each = (i * div);
                                eachinv = Math.Abs((i * div) - 1);
                                AbsoluteLayout.SetLayoutBounds(Buttons[i],
                                    new Rectangle(0.5 + (arkerX * arkmult), 1,    // the 0,5 in X is to center horizontally
                                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
                            }
                        }

                        return isCurrentPage;
                    }
                    else
                    {
                        okMove = false;

                        isCurrentPage = false;
                        return isCurrentPage;
                    }



                });
            }
        }
        protected override void OnAppearing()
        {
            base.OnAppearing();
            isCurrentPage = true;
            beginTime = DateTime.Now;

            for (int i = 0; i < Buttons.Length; i++)
            {
                AbsoluteLayout.SetLayoutBounds(Buttons[i],
                    new Rectangle(1, -1,
                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            }
            AbsoluteLayout.SetLayoutBounds(menubutton,
                new Rectangle(1, 0,
                    AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            if (TypeMenu == "DemiCircle" || TypeMenu == "Flatbottom")
            {
                AbsoluteLayout.SetLayoutBounds(menubutton,
                    new Rectangle(0.5, 1,
                        AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            }


        }
        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            isCurrentPage = false;
        }
    }
}
發佈了7 篇原創文章 · 獲贊 4 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章