0

I'm developing a cross-platform app using Xamarin.Forms. As I want to set the background of my TabLayouts to a gradient, which isn't natively supported by XF, I've written a custom renderer for my TabbedPages:

using Android.App;
using Android.Graphics;
using Android.Support.Design.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;
using MyApp.Droid.Renderer;

[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedPageRenderer))]
namespace MyApp.Droid.Renderer
{
    public class CustomTabbedPageRenderer : TabbedPageRenderer
    {
        private Activity _activity;

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

            _activity = this.Context as FormsAppCompatActivity;
        }

        protected override void DispatchDraw(Canvas canvas)
        {
            base.DispatchDraw(canvas);

            TabLayout tabs = _activity.FindViewById<TabLayout>(Resource.Id.sliding_tabs);

            var gradient = new MyGradient();

            tabs.SetBackground(gradient);
        }
    }
}

Now as intended the renderer gets triggered every time the app loads a TabbedPage. However, _activity.FindViewById<TabLayout>(Resource.Id.sliding_tabs) always just returns the TabLayout of my MainPage (also properly setting MyGradient as background) and I can't figure out how to get the TabLayout on one of my subpages to modify it as well.

Not sure if this is a problem with Xamarin or Android...

Hope you guys can help me.

Thanks!

Jan

EDIT: here's the styles.xml (I hope that's what @Code-Apprentice was referring to):

<resources>
  <style name="MainTheme" parent="MainTheme.Base">
  </style>
  <!-- Base theme applied no matter what API -->
  <style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
    <!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->
    <item name="windowNoTitle">true</item>
    <!--We will be using the toolbar so no need to show ActionBar-->
    <item name="windowActionBar">false</item>
    <!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette -->
    <!-- colorPrimary is used for the default action bar background -->
    <item name="colorPrimary">#2196F3</item>
    <!-- colorPrimaryDark is used for the status bar -->
    <!--<item name="colorPrimaryDark">#1976D2</item>-->
    <item name="colorPrimaryDark">#FF8630</item>
    <!--<item name="android:windowTranslucentStatus">true</item>-->
    <!-- colorAccent is used as the default value for colorControlActivated
     which is used to tint widgets -->
    <item name="colorAccent">#FF4081</item>
    <!-- You can also set colorControlNormal, colorControlActivated
     colorControlHighlight and colorSwitchThumbNormal. -->
    <item name="windowActionModeOverlay">true</item>

    <item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
  </style>

  <style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
    <item name="colorAccent">#FF4081</item>
  </style>
</resources>

Also a screenshot of the MainPage's top: http://i.imgur.com/VyX3YX7.png

And it's subpage ( MainPage.Navigation.PushAsync(new SubPage()); ): http://i.imgur.com/pRmecjE.png

japhwil
  • 299
  • 4
  • 16
  • 1
    Please show the appropriate XML layout files. Also can you show some screenshots? – Code-Apprentice Jun 23 '17 at 18:59
  • Added the changes to my question – japhwil Jun 23 '17 at 19:23
  • I'm not quite understand, the layout in subpage should be defined in PCL? Why you want to find it in renderer? – Grace Feng Jun 26 '17 at 09:46
  • As far as I know (I am new to Xamarin.Forms though) that's how you're supposed to do it. So to put together a renderer for any kind of platform specific designing. – japhwil Jun 26 '17 at 12:27
  • @japhwil, yes, but for subpage of [Tabbed Page](https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/navigation/tabbed-page/), it is usually another page like `ContentPage`, why don't directly customize this page, why you try to find subpage in the renderer of `TabbedPage`? Do you understand it? – Grace Feng Jun 27 '17 at 07:39
  • Maybe I didn't phrase my question so well. I do get both TabbedPages in my App just fine (via the renderer), but what I need is the TabLayout (the bar on top that contains the tabs on Android). And as far as I know, the only way to get elements like that on Android is this `FindViewById` -system (which is kind of unintuitive imo). So the question is really: what do I have to put instead of `_application`? – japhwil Jun 27 '17 at 21:08

2 Answers2

0

I do get both TabbedPages in my App just fine (via the renderer), but what I need is the TabLayout (the bar on top that contains the tabs on Android).

If I correctly understand your question this time, you want to customize the tab layout in TabbedPage for each subpage.

Then you will need to override the SetTabIcon method in TabbedPageRenderer. For example:

[assembly: ExportRenderer(typeof(MyTabbedPage), typeof(MyTabbedPageRenderer))]

namespace YourNameSpace.Droid
{
    public class MyTabbedPageRenderer : TabbedPageRenderer
    {
        protected override void SetTabIcon(TabLayout.Tab tab, FileImageSource icon)
        {
            base.SetTabIcon(tab, icon);

            //set your custom layout for tab. The layout resource "mytablayout" is placed in the layout folder of android project.
            tab.SetCustomView(Resource.Layout.mytablayout);          
        }
    }
}

You can also check my answer in this question, there I placed a Button in the tab to remove the matching subpage.

Grace Feng
  • 16,564
  • 2
  • 22
  • 45
  • Thanks for the answer! This doesn't work for me though. For one thing it only affects the backgrounds of the separate tabs, so there are gaps in between. Also for some reason the method isn't even called on the second TabbedPage. – japhwil Jun 29 '17 at 09:12
0

For anyone else wondering, here's how I get the result I want now (even though I guess it's not the most elegant way of doing this):

using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Views;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;
using MyApp.Droid.Renderer;

[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedPageRenderer))]
namespace MyApp.Droid.Renderer
{
    public class CustomTabbedPageRenderer : TabbedPageRenderer
    {
        protected override void OnVisibilityChanged(Android.Views.View changedView, [GeneratedEnum] ViewStates visibility)
        {
            base.OnVisibilityChanged(changedView, visibility);

            if (visibility == ViewStates.Visible)
            {
                var tabs = changedView.FindViewById<TabLayout>(Resource.Id.sliding_tabs);
                var gradient = new MyGradient();
                tabs.SetBackground(gradient);
            }
        }
    }
}
japhwil
  • 299
  • 4
  • 16