片段中的 ActionBar 和 Toolbar

ActionBar and Toolbar in a fragment

我正在努力让我的片段子视图在显示时考虑 ActionBar/Toolbar。

我遵循了 here 的示例,该示例将布局很好地划分为 activity 布局和包含 AppBar/Toolbar 标签的片段,在我的例子中,还有浮动操作按钮(很棒)。我重构了我已经工作的代码,使 AppBar/Toolbar 和 fab 在 activity 布局上,只有片段处于单独的布局中。但是在片段上包含 AppBar/Toolbar 和 fab 的方法使我能够拥有一个干净的 activity 可以容纳任何片段,有或没有 AppBar/Toolbar 或 fab(或任何其他 UI 个元素)。下面是我的基本布局设置。我遇到的问题是我的 RecyclerView 被 Appbar/Toolbar 遮住了。

activity_main.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <!-- Actual content of the screen -->
    <FrameLayout
        android:id="@+id/main_content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true" />

</android.support.design.widget.CoordinatorLayout>

fragment_list.axml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/list_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <include
        layout="@layout/include_toolbar_actionbar" />

    <MvvmCross.Droid.Support.V4.MvxSwipeRefreshLayout
        android:id="@+id/listsRefresher"
        android:layout_width="match_parent"
        android:layout_height="match_parent"    
        app:MvxBind="Refreshing IsLoading; RefreshCommand ReloadCommand">

        <MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView
            android:id="@+id/listsRecyclerView"
            android:layout_width="match_parent"   
            android:layout_height="match_parent"
            app:MvxItemTemplate="@layout/item_list"
            app:MvxBind="ItemsSource Lists; ItemClick ShowListItemsCommand" />

    </MvvmCross.Droid.Support.V4.MvxSwipeRefreshLayout>

    <include
        layout="@layout/include_floatingactionbutton" />

</FrameLayout>

include_toolbar_actionbar.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_app_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.ActionBar">

    <android.support.v7.widget.Toolbar
        android:id="@+id/main_tool_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.ToolBar" 
        app:layout_scrollFlags="scroll|enterAlways" />

</android.support.design.widget.AppBarLayout>

include_floatingactionbutton.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.FloatingActionButton
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right"
    android:layout_margin="@dimen/margin_medium"
    android:src="@drawable/ic_add_white_24dp"        
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    app:layout_anchor="@id/main_content_frame"
    app:layout_anchorGravity="bottom|right|end" />

MainActivity.cs

namespace List.Mobile.Droid.Activities
{
    [Activity(
        Label = "@string/applicationName",
        Icon = "@drawable/ic_icon", 
        Theme = "@style/AppTheme.Default",
        LaunchMode = LaunchMode.SingleTop,
        ScreenOrientation = ScreenOrientation.Portrait,
        Name = "list.droid.mobile.activities.MainActivity")]
    public class MainActivity : MvxAppCompatActivity<MainViewModel>
    {
        ...    
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            SetContentView(Resource.Layout.activity_main);

            ViewModel.ShowLists();
        }
    }
}

ListsFragment.cs

namespace List.Mobile.Droid.Views
{
    [MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame, true)]
    [Register("list.mobile.droid.views.ListsFragment")]
    public class ListsFragment : BaseFragment<ListsViewModel>, ActionMode.ICallback
    {
        ...    
        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            base.OnCreateView(inflater, container, savedInstanceState);

            var swipeToRefresh = FragmentView.FindViewById<MvxSwipeRefreshLayout>(Resource.Id.refresher);
            if (AppBar != null)
                AppBar.OffsetChanged += (sender, args) => swipeToRefresh.Enabled = args.VerticalOffset == 0;

            var listsRecyclerView = FragmentView.FindViewById<MvxRecyclerView>(Resource.Id.listsRecyclerView);
            ...    
            return FragmentView;
        }
   }
}

BaseFragment.cs

namespace List.Mobile.Droid.Views
{
    public abstract class BaseFragment<T> : MvxFragment<T> where T : MvxViewModel
    {    
        protected abstract int FragmentResourceId { get; }
        protected View FragmentView { get; set; }
        protected AppBarLayout AppBar { get; set; }
        protected FloatingActionButton Fab { get; set; }
        protected Toolbar Toolbar { get; set; } 

        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            var view = base.OnCreateView(inflater, container, savedInstanceState);

            FragmentView = this.BindingInflate(FragmentResourceId, null);

            AppBar = FragmentView.FindViewById<AppBarLayout>(Resource.Id.main_app_bar);
            Fab = FragmentView.FindViewById<FloatingActionButton>(Resource.Id.fab);
            Toolbar = FragmentView.FindViewById<Toolbar>(Resource.Id.main_tool_bar);
            AppCompatActivity parentActivity = ((AppCompatActivity)Activity);
            parentActivity.SetSupportActionBar(Toolbar);
            parentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(false);
            parentActivity.SupportActionBar.SetHomeButtonEnabled(false);

            return view;
        }
    }
}

如果你结合activity+fragment+include的布局结构你会看到这样的东西:

CoordinatorLayout
--FrameLayout -> id=main_content_frame
----FrameLayout -> id=list_frame
------AppBarLayout
--------Toolbar
------SwipeRefresh
--------Recycler
------Fab

这意味着,您在 FrameLayout 中有工具栏和 swipe/recycler,而这个位于另一个之上正是预期的行为。

要解决这个问题,您应该将 AppBar+Swipe+Fab 作为 CoordinatorLayout 的子项(这是正确处理 Toolbar/Fab/Scrolling 内容之间交互的布局。因此请更改您的 activity只是 FrameLayout 并将片段重新排序为:

CoordinatorLayout
--AppBarLayout
----Toolbar
--SwipeRefresh
----RecyclerView
--Fab

并且不要忘记将 app:layout_behavior="@string/appbar_scrolling_view_behavior" 添加到 SwipeRefreshLayout,以便协调器正确定位它。