Android所有fragments中的NavigationDrawer和Toolbar,加上TabLayout合二为一
Android NavigationDrawer and Toolbar in all fragments, plus TabLayout into one
我想创建一个 Android 应用程序,它使用一个导航抽屉来加载不同的片段,所有片段都包括一个 toolbar/appbar 和一个还有一个 TabView 和 ViewPager2,像这样:
所以我用 Android Studio 开始了一个新的 Java 项目,并选择了 Navigation Drawer Activity 模板创建了 3 个不同的片段。这是我的代码:
activity_main.xml(从模板中删除了工具栏)
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<fragment
android:id="@+id/nav_host_fragment_content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.java(评论setupActionBarWithNavController
因为ToolBar不在了)
package com.testui2;
import android.os.Bundle;
import android.view.Menu;
import com.google.android.material.navigation.NavigationView;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AppCompatActivity;
import com.testui2.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setOpenableLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
//NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}
fragment_home.xml(只有工具栏的第一个片段)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
<include layout="@layout/app_bar_main"
android:id="@+id/appbar" />
<TextView
android:id="@+id/text_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
app_bar_main.xml(将工具栏移至此处也适用于其他片段)
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.TestUI2.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.TestUI2.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
app_bar_main_tabs.xml(与前一个相同,但需要它的第二个片段 TabLayout
)
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.TestUI2.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.TestUI2.PopupOverlay" />
<!-- This layout has the tabs -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.TabLayout.Colored" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
fragment_gallery.xml(第二个片段有 ToolBar 和 TabLayout 以及 ViewPager,就像顶部的图像一样)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.gallery.GalleryFragment">
<include layout="@layout/app_bar_main_tabs"
android:id="@+id/appbar" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.constraintlayout.widget.ConstraintLayout>
HomeFragment.java(第一个片段后面的代码,修改模板以在此处设置工具栏)
package com.testui2.ui.home;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.testui2.MainActivity;
import com.testui2.databinding.FragmentHomeBinding;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
HomeViewModel homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView textView = binding.textHome;
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
return root;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MainActivity currentActivity = (MainActivity) requireActivity();
currentActivity.setSupportActionBar(binding.appbar.toolbar);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
GalleryFragment.java(第二个片段后面的代码,带有标签和 viewpager2)
package com.testui2.ui.gallery;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.testui2.MainActivity;
import com.testui2.databinding.FragmentGalleryBinding;
public class GalleryFragment extends Fragment {
private FragmentGalleryBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentGalleryBinding.inflate(inflater, container, false);
View root = binding.getRoot();
// Code to handle tabs
GalleryPagerAdapter galleryPagerAdapter = new GalleryPagerAdapter(requireActivity());
ViewPager2 viewPager = binding.viewPager;
viewPager.setAdapter(galleryPagerAdapter);
TabLayout tabs = binding.appbar.tabs;
new TabLayoutMediator(tabs, viewPager,
(tab, position) -> tab.setText("TAB " + (position + 1))
).attach();
return root;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MainActivity currentActivity = (MainActivity) requireActivity();
currentActivity.setSupportActionBar(binding.appbar.toolbar);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
// Class to handle ViewPager2
private class GalleryPagerAdapter extends FragmentStateAdapter {
public GalleryPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public GalleryPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) { return GalleryPageFragment.newInstance(position); }
@Override
public int getItemCount() {
return 3;
}
}
}
GalleryPageFragment.java(处理ViewPager2
上页面的代码)
package com.testui2.ui.gallery;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.testui2.databinding.FragmentGalleryPageBinding;
public class GalleryPageFragment extends Fragment {
private FragmentGalleryPageBinding binding;
private static final String ARG_PARAM1 = "param1";
private int mParam1;
public GalleryPageFragment() {
// Required empty public constructor
}
public static GalleryPageFragment newInstance(int param1) {
GalleryPageFragment fragment = new GalleryPageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PARAM1, param1);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getInt(ARG_PARAM1);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentGalleryPageBinding.inflate(inflater, container, false);
View root = binding.getRoot();
binding.textGallery.setText(String.format("This is gallery page %d", mParam1 + 1));
return root;
}
}
基本上我采用了模板并对其进行了修改以将 ToolBar 代码移动到片段中(使用 and ),因为我希望选项卡直接停靠在 ToolBar 下方,并且在未来处理设备大屏幕同时显示片段。
不幸的是,这种方法存在问题,我想解决:
我无法将 NavigationUI 应用到此方法,因为我不知道如何从片段中正确调用 NavigationUI.setupActionBarWithNavController
.我必须从 Fragments 中调用它,因为工具栏在那里,实际上我同时缺少 AppBar 标题和汉堡包图标:
选项卡布局在第二个片段(图库)上正确显示,PageViewer2 成功滚动选项卡。但是,如果我单击选项卡名称,它不会切换当前选项卡。我该怎么做?
或者,如果您对如何更轻松地处理固定工具栏(意味着它位于 activity_main.xml
内)有其他建议,但其中一个片段附加 TabLayout 看起来与第一张图片相同,我当然可以更改代码。我也必须有导航抽屉。
我在另一个项目中尝试坚持使用默认模板(在 activity_main.xml
中使用工具栏),并且在 Gallery 片段上,将 TabLayout 和 ViewPager 放在相同的 XML 布局上。但是这样做,选项卡看起来不一样:TabLayout 和 ToolBar 之间出现水平分隔符(因为 TabLayout 不在 <com.google.android.material.appbar.AppBarLayout>
XML 节点内)并且 TabLayout 下方没有投影。示例如下:
经过几次测试,我想得到的东西,用那种方法太难了。 从头开始(导航抽屉Activity模板)并解决UI故障要容易得多。
app_bar_main.xml(从模板更改了 1 行,因为所有片段都已经有了工具栏)
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- added "app:elevation" line -->
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbarlayout"
app:elevation="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.TestUI3.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.TestUI3.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
fragment_gallery.xml(此片段也有 TabLayout
,因此它与显示其他“页面”的 ViewPager2
一起添加片段)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.gallery.GalleryFragment">
<!-- "android:elevation" should be the same than the previous
"app:elevation" on the AppBarLayout; the style is used
to copy the same colour of the ToolBar -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="8dp"
style="@style/Widget.MaterialComponents.TabLayout.Colored" >
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
使用此方法,选项卡可正确点击(问题 #1 已解决),但样式不完全相同:
不幸的是,如果我按照这里的几个答案的建议在 AppBarLayout 上设置 app:elevation="0dp"
,那么当显示其他没有 TabLayout
的片段时,投影会丢失!
因此,在这一点上,更简单的处理方法是使用代码禁用 elevation。
GalleryFragment.java(有TabLayout
的片段后面的代码)
package com.testui3.ui.gallery;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ViewModelProvider;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.testui3.R;
import com.testui3.databinding.FragmentGalleryBinding;
public class GalleryFragment extends Fragment {
private FragmentGalleryBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
GalleryViewModel galleryViewModel =
new ViewModelProvider(this).get(GalleryViewModel.class);
binding = FragmentGalleryBinding.inflate(inflater, container, false);
View root = binding.getRoot();
// ADDED: disable elevation on toolbar when this fragment is displayed
((AppCompatActivity) getActivity()).findViewById(R.id.appbarlayout).setElevation(0);
// Code to handle tabs
GalleryPagerAdapter galleryPagerAdapter = new GalleryPagerAdapter(requireActivity());
ViewPager2 viewPager = binding.viewPager2;
viewPager.setAdapter(galleryPagerAdapter);
TabLayout tabs = binding.tabLayout;
new TabLayoutMediator(tabs, viewPager,
(tab, position) -> tab.setText("TAB " + (position + 1))
).attach();
return root;
}
@Override
public void onDestroyView() {
// ADDED: Restore previous elevation when fragment disappears
((AppCompatActivity) getActivity()).findViewById(R.id.appbarlayout).setElevation(8);
super.onDestroyView();
binding = null;
}
// Class to handle ViewPager2
private class GalleryPagerAdapter extends FragmentStateAdapter {
public GalleryPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public GalleryPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) { return GalleryPageFragment.newInstance(position); }
@Override
public int getItemCount() {
return 3;
}
}
}
这个方法似乎很有效,导航UI也有效:
导航到其他没有 TabLayout
:
的片段时,投影会保留
我仍然认为在代码中这样做不是“正确”的解决方案,但至少它有效并且没有问题 post 方法的麻烦(太多的布局和包含!)。
我想创建一个 Android 应用程序,它使用一个导航抽屉来加载不同的片段,所有片段都包括一个 toolbar/appbar 和一个还有一个 TabView 和 ViewPager2,像这样:
所以我用 Android Studio 开始了一个新的 Java 项目,并选择了 Navigation Drawer Activity 模板创建了 3 个不同的片段。这是我的代码:
activity_main.xml(从模板中删除了工具栏)
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<fragment
android:id="@+id/nav_host_fragment_content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.java(评论setupActionBarWithNavController
因为ToolBar不在了)
package com.testui2;
import android.os.Bundle;
import android.view.Menu;
import com.google.android.material.navigation.NavigationView;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AppCompatActivity;
import com.testui2.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setOpenableLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
//NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}
fragment_home.xml(只有工具栏的第一个片段)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
<include layout="@layout/app_bar_main"
android:id="@+id/appbar" />
<TextView
android:id="@+id/text_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
app_bar_main.xml(将工具栏移至此处也适用于其他片段)
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.TestUI2.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.TestUI2.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
app_bar_main_tabs.xml(与前一个相同,但需要它的第二个片段 TabLayout
)
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.TestUI2.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.TestUI2.PopupOverlay" />
<!-- This layout has the tabs -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.TabLayout.Colored" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
fragment_gallery.xml(第二个片段有 ToolBar 和 TabLayout 以及 ViewPager,就像顶部的图像一样)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.gallery.GalleryFragment">
<include layout="@layout/app_bar_main_tabs"
android:id="@+id/appbar" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.constraintlayout.widget.ConstraintLayout>
HomeFragment.java(第一个片段后面的代码,修改模板以在此处设置工具栏)
package com.testui2.ui.home;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.testui2.MainActivity;
import com.testui2.databinding.FragmentHomeBinding;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
HomeViewModel homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView textView = binding.textHome;
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
return root;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MainActivity currentActivity = (MainActivity) requireActivity();
currentActivity.setSupportActionBar(binding.appbar.toolbar);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
GalleryFragment.java(第二个片段后面的代码,带有标签和 viewpager2)
package com.testui2.ui.gallery;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.testui2.MainActivity;
import com.testui2.databinding.FragmentGalleryBinding;
public class GalleryFragment extends Fragment {
private FragmentGalleryBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentGalleryBinding.inflate(inflater, container, false);
View root = binding.getRoot();
// Code to handle tabs
GalleryPagerAdapter galleryPagerAdapter = new GalleryPagerAdapter(requireActivity());
ViewPager2 viewPager = binding.viewPager;
viewPager.setAdapter(galleryPagerAdapter);
TabLayout tabs = binding.appbar.tabs;
new TabLayoutMediator(tabs, viewPager,
(tab, position) -> tab.setText("TAB " + (position + 1))
).attach();
return root;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MainActivity currentActivity = (MainActivity) requireActivity();
currentActivity.setSupportActionBar(binding.appbar.toolbar);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
// Class to handle ViewPager2
private class GalleryPagerAdapter extends FragmentStateAdapter {
public GalleryPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public GalleryPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) { return GalleryPageFragment.newInstance(position); }
@Override
public int getItemCount() {
return 3;
}
}
}
GalleryPageFragment.java(处理ViewPager2
上页面的代码)
package com.testui2.ui.gallery;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.testui2.databinding.FragmentGalleryPageBinding;
public class GalleryPageFragment extends Fragment {
private FragmentGalleryPageBinding binding;
private static final String ARG_PARAM1 = "param1";
private int mParam1;
public GalleryPageFragment() {
// Required empty public constructor
}
public static GalleryPageFragment newInstance(int param1) {
GalleryPageFragment fragment = new GalleryPageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PARAM1, param1);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getInt(ARG_PARAM1);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentGalleryPageBinding.inflate(inflater, container, false);
View root = binding.getRoot();
binding.textGallery.setText(String.format("This is gallery page %d", mParam1 + 1));
return root;
}
}
基本上我采用了模板并对其进行了修改以将 ToolBar 代码移动到片段中(使用
我无法将 NavigationUI 应用到此方法,因为我不知道如何从片段中正确调用
NavigationUI.setupActionBarWithNavController
.我必须从 Fragments 中调用它,因为工具栏在那里,实际上我同时缺少 AppBar 标题和汉堡包图标:选项卡布局在第二个片段(图库)上正确显示,PageViewer2 成功滚动选项卡。但是,如果我单击选项卡名称,它不会切换当前选项卡。我该怎么做?
或者,如果您对如何更轻松地处理固定工具栏(意味着它位于 activity_main.xml
内)有其他建议,但其中一个片段附加 TabLayout 看起来与第一张图片相同,我当然可以更改代码。我也必须有导航抽屉。
我在另一个项目中尝试坚持使用默认模板(在 activity_main.xml
中使用工具栏),并且在 Gallery 片段上,将 TabLayout 和 ViewPager 放在相同的 XML 布局上。但是这样做,选项卡看起来不一样:TabLayout 和 ToolBar 之间出现水平分隔符(因为 TabLayout 不在 <com.google.android.material.appbar.AppBarLayout>
XML 节点内)并且 TabLayout 下方没有投影。示例如下:
经过几次测试,我想得到的东西,用那种方法太难了。 从头开始(导航抽屉Activity模板)并解决UI故障要容易得多。
app_bar_main.xml(从模板更改了 1 行,因为所有片段都已经有了工具栏)
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- added "app:elevation" line -->
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbarlayout"
app:elevation="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.TestUI3.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.TestUI3.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
fragment_gallery.xml(此片段也有 TabLayout
,因此它与显示其他“页面”的 ViewPager2
一起添加片段)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.gallery.GalleryFragment">
<!-- "android:elevation" should be the same than the previous
"app:elevation" on the AppBarLayout; the style is used
to copy the same colour of the ToolBar -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="8dp"
style="@style/Widget.MaterialComponents.TabLayout.Colored" >
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
使用此方法,选项卡可正确点击(问题 #1 已解决),但样式不完全相同:
不幸的是,如果我按照这里的几个答案的建议在 AppBarLayout 上设置 app:elevation="0dp"
,那么当显示其他没有 TabLayout
的片段时,投影会丢失!
因此,在这一点上,更简单的处理方法是使用代码禁用 elevation。
GalleryFragment.java(有TabLayout
的片段后面的代码)
package com.testui3.ui.gallery;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ViewModelProvider;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.testui3.R;
import com.testui3.databinding.FragmentGalleryBinding;
public class GalleryFragment extends Fragment {
private FragmentGalleryBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
GalleryViewModel galleryViewModel =
new ViewModelProvider(this).get(GalleryViewModel.class);
binding = FragmentGalleryBinding.inflate(inflater, container, false);
View root = binding.getRoot();
// ADDED: disable elevation on toolbar when this fragment is displayed
((AppCompatActivity) getActivity()).findViewById(R.id.appbarlayout).setElevation(0);
// Code to handle tabs
GalleryPagerAdapter galleryPagerAdapter = new GalleryPagerAdapter(requireActivity());
ViewPager2 viewPager = binding.viewPager2;
viewPager.setAdapter(galleryPagerAdapter);
TabLayout tabs = binding.tabLayout;
new TabLayoutMediator(tabs, viewPager,
(tab, position) -> tab.setText("TAB " + (position + 1))
).attach();
return root;
}
@Override
public void onDestroyView() {
// ADDED: Restore previous elevation when fragment disappears
((AppCompatActivity) getActivity()).findViewById(R.id.appbarlayout).setElevation(8);
super.onDestroyView();
binding = null;
}
// Class to handle ViewPager2
private class GalleryPagerAdapter extends FragmentStateAdapter {
public GalleryPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public GalleryPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) { return GalleryPageFragment.newInstance(position); }
@Override
public int getItemCount() {
return 3;
}
}
}
这个方法似乎很有效,导航UI也有效:
导航到其他没有 TabLayout
:
我仍然认为在代码中这样做不是“正确”的解决方案,但至少它有效并且没有问题 post 方法的麻烦(太多的布局和包含!)。