How to change TabbedPage Icon when the tab is reselected in Android?

时间:2018-03-31 07:37:46

标签: c# xamarin xamarin.forms

I have an application using Xamarin Forms TabbedPage which has a feature that would allow the user to pause and play a page. Please see the code below.

Shared Code

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);
   }
}

In iOS renderer:

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;
           }
         }
    }
}

This is perfectly working in iOS. But somehow in Android only the Title would change but not the Icon. Anyone knows what Im missing or how it should be done? Also, is this possible to be done in the shared code instead of repeating almost exactly the same lines on code in each platform?

5 个答案:

答案 0 :(得分:5)

不需要

Custom Renderer,您可以直接在共享代码中更改TitleIcon

只需在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

答案 1 :(得分:4)

您可以使用OnTabReselectedTabRenderer参数中正在传递给您的标签来执行此操作。

您可以使用此对象移动整个逻辑。

这是我的整个渲染器文件(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);
        }

    }
}

我对上面的代码唯一的看法是图标没有采用Tint颜色,因此创建了一个函数,其逻辑与设置Tint相同,我在Tab Reselection上使用它。如果您的应用中只有一个标签,则可以在Android主题/样式xml中设置全局色调。

希望这有帮助。

答案 2 :(得分:3)

没有办法检测何时在Xamarin.Forms中重新选择制表符,因此我们必须使用自定义重新存档来检测逻辑。

对于Android,我们必须处理两种情况:当前标签页已更改,当前标签页已重新选择。

我们将订阅CurrentPageChanged,并在其EventHandler中,我们会检查所选的标签是否为PhrasesPage。如果是,我们将更新图标/文字。

OnTabReselected中,我们可以查看当前选择的页面,如果它是PhrasesPage,我们可以更新PhrasesPage.IconPhrasesPage.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

答案 3 :(得分:0)

我认为您使用自定义渲染进行选项卡式页面自定义。对于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

答案 4 :(得分:0)

尝试将此代码添加到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/

相关问题