我想創建一個使用導航抽屜的 Android 應用程式,該應用程式加載不同的片段,所有這些都包括一個工具列/應用程式欄和一個也有一個帶有ViewPager2的 TabView 的應用程式,如下所示:

因此,我使用 Android Studio 開始了一個新的 Java 專案,并選擇了創建 3 個不同片段的Navigation Drawer Activity模板。這是我的代碼:
activity_main.xml(從模板中洗掉了 ToolBar)
<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(僅帶有 ToolBar 的第一個片段)
<?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(將 ToolBar 移至此處以應用于其他片段)
<?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 代碼移動到片段中(使用
選項卡布局在第二個片段(圖庫)上正確顯示,并且 PageViewer2 成功滾動選項卡。但是,如果我單擊選項卡名稱,它不會切換當前選項卡。我怎樣才能做到這一點?

或者,如果您對如何更輕松地處理固定的 ToolBar(意味著它位于內部activity_main.xml)有其他建議,但其中一個片段附加 TabLayout 看起來與第一張圖片相同,我當然可以更改代碼。我也必須有導航抽屜。
我嘗試在另一個專案中堅持使用默認模板(在 中使用 ToolBar activity_main.xml),并在 Gallery 片段上將 TabLayout 和 ViewPager 放在相同的 XML 布局上。但是這樣做,選項卡看起來不一樣:TabLayout 和 ToolBar 之間出現一個水平分隔符(因為 TabLayout 不在<com.google.android.material.appbar.AppBarLayout>XML 節點內),并且 TabLayout 下方沒有陰影。下面的例子:

uj5u.com熱心網友回復:
在做了幾次測驗之后,我想用這種方法獲得的東西太難了。從頭開始(導航抽屜活動模板)和解決 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 已解決),但樣式并不完全相同:

不幸的是,如果我app:elevation="0dp"按照這里的幾個答案建議在 AppBarLayout 上進行設定,那么當TabLayout顯示其他沒有的片段時,陰影就會丟失!因此,此時,更簡單的處理方法是使用代碼禁用高程。
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;
}
}
}
這種方法似乎效果很好,NavigationUI 也可以:

當導航到其他沒有的片段時,陰影會保留TabLayout:

我仍然認為在代碼中這樣做不是“正確”的解決方案,但至少它可以作業并且沒有問題發布方法的麻煩(太多布局和包含!)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/430926.html
