it-swarm.com.ru

Как изменить значок TabbedPage при повторном выборе вкладки в Android?

У меня есть приложение, использующее Xamarin FormsTabbedPage, которое имеет функцию, которая позволяет пользователю приостанавливать и воспроизводить страницу. Пожалуйста, смотрите код ниже.

Общий код

public partial class MainPage : TabbedPage
{
   public MainPage()
   {
      InitializeComponent();

      var homePage = new NavigationPage(new HomePage())
      { 
         Title = "Home",
         Icon = "ionicons_2_0_1_home_outline_25.png"
      };

      var phrasesPage = new NavigationPage(new PhrasesPage())
      {
         Title = "Play",
         Icon = "ionicons_2_0_1_play_outline_25.png"
       };

       Children.Add(homePage);
       Children.Add(phrasesPage);
   }
}

В iOS рендерер:

public class TabbedPageRenderer : TabbedRenderer
{
   private MainPage _page;
   protected override void OnElementChanged(VisualElementChangedEventArgs e)
  {
      base.OnElementChanged(e);
      var tabbarController = (UITabBarController)this.ViewController;
      if (null != tabbarController)
      {
         tabbarController.ViewControllerSelected += OnTabBarReselected;
       }
   }

   void OnTabBarReselected(object sender, UITabBarSelectionEventArgs e)
   {
      var tabs = Element as TabbedPage;
      var playTab = tabs.Children[4];

      if (TabBar.SelectedItem.Title == "Play") {
         if (tabs != null)
         {
            playTab.Title = "Pause";
            playTab.Icon = "ionicons_2_0_1_pause_outline_22.png";
         }
         App.pauseCard = false;
       }
       else {
        if (tabs != null) {
           playTab.Title = "Play";
           playTab.Icon = "ionicons_2_0_1_play_outline_25.png";
       }
       App.pauseCard = true;
    }
}

Android Renderer

public class MyTabbedPageRenderer: TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
    if (e.PropertyName == "Renderer")
    {
       viewPager = (ViewPager)ViewGroup.GetChildAt(0);
       tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
       setup = true;

       ColorStateList colors = null;
       if ((int)Build.VERSION.SdkInt >= 23)
       {
           colors = Resources.GetColorStateList(Resource.Color.icon_tab, Forms.Context.Theme);
       }
       else
       {
           colors = Resources.GetColorStateList(Resource.Color.icon_tab);
       }

       for (int i = 0; i < tabLayout.TabCount; i++)
       {
           var tab = tabLayout.GetTabAt(i);
           var icon = tab.Icon;
           if (icon != null)
           {
               icon = Android.Support.V4.Graphics.Drawable.DrawableCompat.Wrap(icon);
               Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTintList(icon, colors);
           }
       }
   }

   void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
   {
      var tabs = Element as TabbedPage;
      var playTab = tabs.Children[4];
      var selectedPosition = tab.Position;

      if(selectedPosition == 4) 
      {
         if (playTab.Title == "Play")
         {
            if (tabs != null)
            {
               playTab.Title = "Pause";
               playTab.Icon = "ionicons_2_0_1_pause_outline_22.png";
            }
            App.pauseCard = false;
          }
          else
          {
             if (tabs != null)
             {
                playTab.Title = "Play";
                playTab.Icon = "ionicons_2_0_1_play_outline_25.png";
             }
             App.pauseCard = true;
           }
         }
    }
}

Это прекрасно работает в iOS. Но каким-то образом в Android изменится только Title, но не Icon. Кто-нибудь знает, чего мне не хватает или как это сделать? Кроме того, возможно ли это сделать в общем коде вместо того, чтобы повторять почти одинаковые строки в коде на каждой платформе?

8
Samantha J T Star

Вы можете сделать это, используя вкладку, которая передается вам в параметрах OnTabReselected в TabRenderer

Вы можете переместить всю свою логику с этим объектом.

Это весь мой файл рендерера (Android):

[Assembly: ExportRenderer(typeof(SWTabSelection.MainPage), typeof(SWTabSelection.Droid.MyTabbedPageRenderer))]
namespace SWTabSelection.Droid
{
    public class MyTabbedPageRenderer : TabbedPageRenderer, TabLayout.IOnTabSelectedListener
    {
        private ViewPager viewPager;
        private TabLayout tabLayout;
        private bool setup;

        public MyTabbedPageRenderer() { }

        public MyTabbedPageRenderer(Context context) : base(context)
        {
            //Use this constructor for newest versions of XF saving the context parameter 
            // in a field so it can be used later replacing the Xamarin.Forms.Forms.Context which is deprecated.
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == "Renderer")
            {
                viewPager = (ViewPager)ViewGroup.GetChildAt(0);
                tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
                setup = true;

                ColorStateList colors = GetTabColor();

                for (int i = 0; i < tabLayout.TabCount; i++)
                {
                    var tab = tabLayout.GetTabAt(i);

                    SetTintColor(tab, colors);
                }
            }
        }


    void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
    {
        // To have the logic only on he tab on position 1
        if(tab == null || tab.Position != 1)
        {
            return;
        }

        if(tab.Text == "Play")
        {
            tab.SetText("Pause");
            tab.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_25);
            App.pauseCard = false;
        }
        else
        {
            tab.SetText("Play");
            tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
            App.pauseCard = true;
        }

        SetTintColor(tab, GetTabColor());

    }

        void SetTintColor(TabLayout.Tab tab, ColorStateList colors)
        {
            var icon = tab?.Icon;
            if(icon != null)
            {
                icon = Android.Support.V4.Graphics.Drawable.DrawableCompat.Wrap(icon);
                Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTintList(icon, colors);            
            }
        }

        ColorStateList GetTabColor()
        {
            return ((int)Build.VERSION.SdkInt >= 23) 
                ? Resources.GetColorStateList(Resource.Color.icon_tab, Forms.Context.Theme)
                               : Resources.GetColorStateList(Resource.Color.icon_tab);
        }

    }
}

Единственное, что у меня было с приведенным выше кодом, - это то, что значок не принимает цвет Оттенок, поэтому создал функцию с той же логикой, что и для настройки Оттенка, и я использую ее на повторном выборе вкладки. Если у вас есть только одна вкладка в вашем приложении, вы можете установить глобальный оттенок в Android Theme/Style xml.

Надеюсь это поможет.

4
apineda

Custom Renderer не требуется, вы можете изменить Title и Icon непосредственно в общем коде.

Просто внедрите событие CurrentPageChanged в TabbedPage

Полный код

public partial class TabbedPage1 : TabbedPage
{
    NavigationPage homePage;
    NavigationPage phrasesPage;

    public TabbedPage1 ()
    {
        InitializeComponent();

        var homePage = new NavigationPage(new Page1())
        {
            Title = "Home",
            Icon = "1.png"
        };

        var phrasesPage = new NavigationPage (new Page2())
        {
            Title = "Play",
            Icon = "1.png"
        };

        Children.Add(homePage);
        Children.Add(phrasesPage);

        this.CurrentPageChanged += (object sender, EventArgs e) => {

            var i = this.Children.IndexOf(this.CurrentPage);

            if (i == 0)
            {
                homePage.Title = "HomeChanged";
                homePage.Icon = "2.png";
            }
            else {
                phrasesPage.Title = "PlayChanged";
                phrasesPage.Icon = "2.png";
            }
        };
    }
}

Результат

 enter image description here

PS: сделать доступ к файлам изображений с другой платформы.

iOS - Resources 

Android - Resources->drawable

4
Cole Xia - MSFT

Нет способа определить, когда вкладка повторно выбирается в Xamarin.Forms, поэтому для определения логики нам придется использовать пользовательские обработчики.

Для Android нам нужно обработать два случая: страница текущей вкладки изменена и страница текущей вкладки изменена.

Мы подпишемся на CurrentPageChanged и в его EventHandler, мы проверим, является ли выбранная вкладка PhrasesPage. Если это так, мы обновим значок/текст.

В OnTabReselected мы можем проверить, какая страница выбрана в настоящий момент, и, если это PhrasesPage, мы можем обновить PhrasesPage.Icon и PhrasesPage.Text.

Образец приложения

https://github.com/brminnick/ChangeTabbedPageIconSample/tree/master

Android Custom Renderer

[Assembly: ExportRenderer(typeof(MainPage), typeof(TabbedPageRenderer))]
namespace YourNameSpace
{
    public class TabbedPageRenderer : TabbedRenderer, TabLayout.IOnTabSelectedListener
    {
        //Overloaded Constructor required for Xamarin.Forms v2.5+
        public TabbedPageRenderer(Android.Content.Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
        {
            base.OnElementChanged(e);

            Element.CurrentPageChanged += HandleCurrentPageChanged;
        }

        void HandleCurrentPageChanged(object sender, EventArgs e)
        {
            var currentNavigationPage = Element.CurrentPage as NavigationPage;
            if (!(currentNavigationPage.RootPage is PhrasesPage))
                return;

            var tabLayout = (TabLayout)ViewGroup.GetChildAt(1);

            for (int i = 0; i < tabLayout.TabCount; i++)
            {
                var currentTab = tabLayout.GetTabAt(i);
                var currentTabText = currentTab.Text;

                if (currentTabText.Equals("Play") || currentTabText.Equals("Pause"))
                {
                    Device.BeginInvokeOnMainThread(() => UpdateTab(currentTabText, currentTab, currentNavigationPage));
                    break;
                }
            }
        }

        void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
        {
            System.Diagnostics.Debug.WriteLine("Tab Reselected");

            var mainPage = Application.Current.MainPage as MainPage;

            var currentNavigationPage = mainPage.CurrentPage as NavigationPage;

            if(currentNavigationPage.RootPage is PhrasesPage)
                Device.BeginInvokeOnMainThread(() => UpdateTab(currentNavigationPage.Title, tab, currentNavigationPage));
        }

        void UpdateTab(string currentTabText, TabLayout.Tab tab, NavigationPage currentNavigationPage)
        {
            if (currentTabText.Equals("Puzzle"))
            {
                tab.SetIcon(IdFromTitle("Settings", ResourceManager.DrawableClass));
                currentNavigationPage.Title = "Settings";
            }
            else
            {
                tab.SetIcon(IdFromTitle("Puzzle", ResourceManager.DrawableClass));
                currentNavigationPage.Title = "Puzzle";
            }
        }

        int IdFromTitle(string title, Type type)
        {
            string name = System.IO.Path.GetFileNameWithoutExtension(title);
            int id = GetId(type, name);
            return id;
        }

        int GetId(Type type, string memberName)
        {
            object value = type.GetFields().FirstOrDefault(p => p.Name == memberName)?.GetValue(type)
                ?? type.GetProperties().FirstOrDefault(p => p.Name == memberName)?.GetValue(type);
            if (value is int)
                return (int)value;
            return 0;
        }
    }
}

 enter image description here

3
Brandon Minnick

Я думаю, что вы используете пользовательский рендер для настройки страницы с вкладками. Для Android вы должны ссылаться на значок из Resource.Drawable. Пожалуйста, попробуйте следующий фрагмент кода в Android рендерере.

public class CustomTabRenderer: TabbedRenderer 
{
     private Activity _act;

     protected override void OnModelChanged(VisualElement oldModel, VisualElement newModel)
     {
         base.OnModelChanged(oldModel, newModel);

         _act = this.Context as Activity;
     }

     // You can do the below function anywhere.
     public override void OnWindowFocusChanged(bool hasWindowFocus)
     {   

         ActionBar actionBar = _act.ActionBar;

         if (actionBar.TabCount > 0)
         {
             Android.App.ActionBar.Tab tabOne = actionBar.GetTabAt(0);

            tabOne.SetIcon(Resource.Drawable.Shell);
         }
         base.OnWindowFocusChanged(hasWindowFocus);
     }
 }

См. Также: https://forums.xamarin.com/discussion/17654/tabbedpage-icons-not-visible-Android

0
Rajesh

Попробуйте добавить этот код в OnElementChanged в TabbedPageRenderer 

if (e.PropertyName == "Renderer")
{
    ViewPager pager = (ViewPager)ViewGroup.GetChildAt(0);
    TabLayout layout = (TabLayout)ViewGroup.GetChildAt(1);

    for (int i = 0; i < layout.TabCount; i++)
    {
        var tab = layout.GetTabAt(i);
        var icon = tab.Icon;
        if (icon != null)
        {
            icon = Android.Support.V4.Graphics.Drawable.DrawableCompat.Wrap(icon);
            Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTintList(icon, colors);
        }
    }
}

Более подробная информация здесь: https://montemagno.com/xamarin-forms-Android-selected-and-unselected-tab-colors/

0
Maverick