1

I want to add a custom view above the tabbar in Xamarin Forms Shell (iOS and Android) to implement an audio player that is always visible.

For Android I looked into this blog post about customizing the tabbar on Android (https://www.andrewhoefling.com/Blog/Post/xamarin-forms-shell-customizing-the-tabbar-android), and added an extra layout with id '@+id/bottomtab.view' to my customized BottomTabLayout.axml.

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/bottomtab.navarea"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_gravity="fill"
            android:layout_weight="1" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <LinearLayout
                android:orientation="horizontal"
                android:id="@+id/bottomtab.view"
                android:layout_width="match_parent"
                android:background="@drawable/abc_action_bar_item_background_material"
                android:layout_height="70dp" />

            <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/bottomtab.tabbar"
                android:theme="@style/Widget.Design.BottomNavigationView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>

    <FrameLayout
        android:id="@+id/bottomtab.tabbar.container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal|bottom" />

</FrameLayout>

In my custom ShellItemRenderer of my TabBar subclass I want to add the xaml to the layout, but that doesn't work.

using Xamarin.Forms;

namespace TodosSample.Controls
{
    public class TodoTabBar : TabBar
    {
        public Tab LargeTab { get; set; }

        public View View { get; set; }
    }
}

XAML

<c:TodoTabBar.View>
    <Grid BackgroundColor="#232026" Padding="10">
    <Grid BackgroundColor="PaleVioletRed">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Grid BackgroundColor="#092C33" Grid.Row="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="60" />
        </Grid.ColumnDefinitions>

        <Button BackgroundColor="IndianRed" Text="Click me" Clicked="Button_Clicked"/>

            <Grid Grid.Column="1" BackgroundColor="Yellow">
                <Label Text="Guy en Marinda" />
            </Grid>

            <Grid Grid.Column="2" WidthRequest="50" HeightRequest="50" BackgroundColor="Aqua" />


    </Grid>

</c:TodoTabBar.View>

For some reason the background of the outer grid is applied, like you can see in the screenshot, but the contents are not shown. I don't know how to apply the correct dimensions to make the grid and the children take the full width of the screen.

Screenshot

// Rendering part in the custom TodoShellItemRenderer. This code is being called
private void SetupView()
{
    var todoTabBar = (TodoTabBar)ShellItem;

    var renderer = Platform.CreateRendererWithContext(todoTabBar.View, Context);
    renderer.Tracker.UpdateLayout();
    var nativeView = renderer.View;
    nativeView.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
    
    
    int width = ViewGroup.LayoutParams.MatchParent;
    int height = 70;
    todoTabBar.View.Layout(new Xamarin.Forms.Rectangle(0, 0, width, height));
    
    nativeView.Layout(0, 0, width, height);

    _view.RemoveAllViews();
    _view.AddView(nativeView);

    nativeView.ForceLayout();
}

I like the idea to define the contents of the view in xaml to be able to setup the bindings and such. How can I render the view correctly in the layout?

===============

Update 2020-11-12

I have changed my code to implement Leo's suggestion, but changed it to use the bottomtab.view layout in the android xml. (I use the framelayout bottomtab.tabbar.container for a kind of floating button, that works fine).

However, it looks like the width of the view above the tab bar is incorrect. In the screenshot you see that the Pink BoxView doesn't have a right side, it looks like the inner gridview (white background) is too wide. The bottom black border of the screenshot is the tabbar that I redacted.

Screenshot

This is my definition of the extra view in xaml:

<Grid BackgroundColor="HotPink">

    <BoxView BackgroundColor="Pink"  />
    <Grid BackgroundColor="White" Margin="5">

    </Grid>                
                
</Grid>

This is the adapted SetupView method

private void SetupTopView()
{
    var todoTabBar = (SearchTabBar)ShellItem;
    var height = 70;
    var width = (int)Resources.DisplayMetrics.WidthPixels;
    Rectangle size = new Rectangle(0, 0, width, height);
    todoTabBar.ExtraView.Layout(size);
    var renderer = Platform.CreateRendererWithContext(todoTabBar.ExtraView, Context);
    renderer.Tracker.UpdateLayout();
    var nativeView = renderer.View;

    var layout = new FrameLayout(Context);
    layout.AddView(nativeView);
    var lp = new FrameLayout.LayoutParams(width, height * (int)Resources.DisplayMetrics.Density);
    _bottomView.Measure((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);
    lp.BottomMargin = _bottomView.MeasuredHeight;

    layout.LayoutParameters = lp;
    _extraViewContainer.RemoveAllViews();
    _extraViewContainer.AddView(layout);
}

Any suggestion to let it use the correct width? Or is my xaml definition incorrect?

J_W
  • 11
  • 2
  • You may want to look to a different approach with a service https://stackoverflow.com/questions/64548271/how-can-i-do-this-floating-action-button-where-can-run-even-in-background-mode-i – Cfun Nov 10 '20 at 09:55

1 Answers1

0

You could try the below:

public override Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        var outerlayout = base.OnCreateView(inflater, container, savedInstanceState);
        _bottomView = outerlayout.FindViewById<BottomNavigationView>(Resource.Id.bottomtab_tabbar);
        _shellOverlay = outerlayout.FindViewById<FrameLayout>(Resource.Id.bottomtab_tabbar_container);

        if (ShellItem is TodoTabBar todoTabBar && todoTabBar.LargeTab != null)
            SetupView();

        return outerlayout;
    }

private void SetupView()
 {
        var todoTabBar = (TodoTabBar)ShellItem;
        var height = 70;
        var width = (int)Resources.DisplayMetrics.WidthPixels;
        Rectangle size = new Rectangle(0, 0, width, height);
        todoTabBar.View.Layout(size);
        var renderer = Platform.CreateRendererWithContext(todoTabBar.View, Context);
        renderer.Tracker.UpdateLayout();
        var nativeView = renderer.View;
 
        var layout = new FrameLayout(Context);
        layout.AddView(nativeView);
        var lp = new FrameLayout.LayoutParams(width, height* (int)Resources.DisplayMetrics.Density);
        _bottomView.Measure((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);
        lp.BottomMargin = _bottomView.MeasuredHeight;

        layout.LayoutParameters = lp;
        _shellOverlay.RemoveAllViews();
        _shellOverlay.AddView(layout);
 }
Leo Zhu
  • 15,726
  • 1
  • 7
  • 23
  • Thanks! I will try this and let you know. – J_W Nov 11 '20 at 10:26
  • Thanks for your quick responses! I added an update to my question. @LeonLu-MSFT – J_W Nov 12 '20 at 15:12
  • Perhaps another approach is to draw your views directly in your android project xaml , not in the shell. – Leo Zhu Nov 13 '20 at 01:12
  • Yes, that might be possible, but quite difficult to achieve, because I want to setup the view with binding for button, texts and progress indicators. – J_W Nov 13 '20 at 08:35
  • Do you have another suggestion, so I can keep using xaml? – J_W Nov 13 '20 at 20:40
  • I tried a lot but unfortunately I didn't find a good way to get the exact width and height. – Leo Zhu Nov 17 '20 at 07:31