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

у меня есть приложение, используя Xamarin Forms TabbedPage, который имеет функцию, которая позволит пользователю сделать паузу и сыграть странице. См. код ниже.

Общий Код

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 Визуализации

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. Кто знает, что им отсутствует или как это сделать? Кроме того, возможно ли это сделать в общем коде вместо повторения почти точно таких же строк в коде на каждой платформе?

5 ответов


вы можете сделать это, используя вкладку, которая является passe к вам в 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);
        }

    }
}

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

надеюсь, что это помогает.


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

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

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

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

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

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

Пользовательский Рендерер Android

[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


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


Я думаю, вы используете пользовательский рендер для настройки страницы с вкладками. Для Android вы должны ссылаться на значок из ресурса.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


попробуйте добавить этот код 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/