如何在Android中重新选择选项卡时更改TabbedPage图标?

9

我可以帮助您进行翻译。这份代码是一个使用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;
           }
         }
    }
}

这在上完美运行。但是在上只有Title会改变,而Icon不会。有人知道我错过了什么或者应该如何做吗?此外,这是否可以在共享代码中完成,而不是在每个平台上重复几乎完全相同的代码行?

5个回答

5

自定义渲染器不是必需的,您可以直接在共享代码中更改标题图标

只需在TabbedPage中实现CurrentPageChanged事件即可。

完整代码

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

结果

输入图像描述

附注:从另一个平台访问图像文件。

iOS - 资源

Android - 资源->可绘制


谢谢Cole。抱歉回复晚了,最近很忙。下班后会尝试你的代码。 - Samantha J T Star
嗨,Cole。我又读了一遍你的评论。我想我在我的帖子中表述得不够清楚。但是我认为我说过“暂停和播放”。所以在同一页上重新选择选项卡。只有当页面更改时才会调用“CurrentPageChanged”。 - Samantha J T Star

5

您可以通过使用在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主题/样式xml中设置全局色调。

希望这能帮到您。


这太完美了!非常感谢你。 - Samantha J T Star

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


0
尝试将此代码添加到TabbedPageRenderer中的OnElementChanged中。
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/


嗨@Maverick,虽然我上面的代码中没有包含那些代码行,但它们已经存在了。请查看上面编辑过的代码。 - Samantha J T Star
if (e.PropertyName == "Renderer") 之前,您还缺少 base.OnElementChanged(e); - Maverick
谢谢。已添加。没有任何区别。 - Samantha J T Star

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接