Xamarin Android中如何通过继承复用DrawerLayout与NavigationView逻辑
Great question—redundant code is a pain, and inheritance is perfect here to centralize all that drawer navigation logic. Here's a step-by-step approach to clean up your code:
Step 1: Create an Abstract Base Drawer Activity
First, we'll build a base class that handles all the common DrawerLayout and NavigationView setup, navigation clicks, and toggle behavior. This class will be abstract since it's meant to be inherited, not instantiated directly.
using Android.App; using Android.OS; using Android.Support.Design.Widget; using Android.Support.V4.Widget; using Android.Support.V7.App; using Android.Views; public abstract class BaseDrawerActivity : AppCompatActivity { private DrawerLayout _drawerLayout; private NavigationView _navigationView; private ActionBarDrawerToggle _drawerToggle; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // Child activities will set their own content view first } // Call this method in child activities AFTER setting the content view protected void SetupDrawer() { // Reference views from the child activity's layout (ensure IDs match across all layouts!) _drawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawer_layout); _navigationView = FindViewById<NavigationView>(Resource.Id.nav_view); // Setup the drawer toggle for the action bar _drawerToggle = new ActionBarDrawerToggle( this, _drawerLayout, Resource.String.drawer_open, // Add these strings to your Resources/values/strings.xml Resource.String.drawer_close ); _drawerLayout.AddDrawerListener(_drawerToggle); _drawerToggle.SyncState(); // Enable the hamburger menu icon in the action bar SupportActionBar.SetDisplayHomeAsUpEnabled(true); SupportActionBar.SetHomeButtonEnabled(true); // Attach the navigation item click listener _navigationView.NavigationItemSelected += OnNavigationItemSelected; // Let the child activity highlight the correct menu item HighlightCurrentMenuItem(); } private void OnNavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e) { // Handle navigation clicks centrally switch (e.MenuItem.ItemId) { case Resource.Id.nav_main: if (!(this is MainActivity)) { StartActivity(new Intent(this, typeof(MainActivity))); Finish(); // Prevent stacking multiple instances of the same activity } break; case Resource.Id.nav_settings: if (!(this is SettingsActivity)) { StartActivity(new Intent(this, typeof(SettingsActivity))); Finish(); } break; // Add more menu items here as your app grows } // Close the drawer after selection _drawerLayout.CloseDrawers(); } // Abstract method: child activities must implement this to highlight their corresponding menu item protected abstract void HighlightCurrentMenuItem(); // Handle the hamburger menu click public override bool OnOptionsItemSelected(IMenuItem item) { if (_drawerToggle.OnOptionsItemSelected(item)) { return true; } return base.OnOptionsItemSelected(item); } // Sync the toggle state after configuration changes (e.g., screen rotation) protected override void OnPostCreate(Bundle savedInstanceState) { base.OnPostCreate(savedInstanceState); _drawerToggle.SyncState(); } public override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig) { base.OnConfigurationChanged(newConfig); _drawerToggle.OnConfigurationChanged(newConfig); } }
Step 2: Update Your Child Activities
Now, modify MainActivity and SettingsActivity to inherit from BaseDrawerActivity instead of AppCompatActivity. They'll only need to handle their own layout and highlight the correct menu item:
MainActivity.cs
public class MainActivity : BaseDrawerActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.Main); // Your existing Main layout // Initialize the drawer using the base class method SetupDrawer(); // Add your MainActivity-specific logic here (e.g., binding data, setting up views) } protected override void HighlightCurrentMenuItem() { // Mark the "Main" menu item as selected _navigationView.Menu.FindItem(Resource.Id.nav_main).SetChecked(true); } }
SettingsActivity.cs
public class SettingsActivity : BaseDrawerActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.Settings); // Your existing Settings layout SetupDrawer(); // Add your SettingsActivity-specific logic here } protected override void HighlightCurrentMenuItem() { // Mark the "Settings" menu item as selected _navigationView.Menu.FindItem(Resource.Id.nav_settings).SetChecked(true); } }
Step 3: Ensure Consistent IDs Across Layouts
For this to work, make sure both your Main.axml and Settings.axml layouts use the same IDs for the DrawerLayout and NavigationView:
<!-- In both Main.axml and Settings.axml --> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Your main content layout here --> <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:menu="@menu/nav_menu" /> <!-- Make sure this menu has the correct item IDs --> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout>
Also, your nav_menu.xml (in Resources/menu) should have items with the IDs we referenced in the base class:
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/nav_main" android:title="Main" /> <item android:id="@+id/nav_settings" android:title="Settings" /> </menu>
Key Notes
- Abstract Method: The
HighlightCurrentMenuItemabstract method ensures each child activity handles its own menu selection state, keeping the base class clean. - Prevent Duplicate Activities: The
if (!(this is XActivity))check avoids starting a new instance of the activity the user is already in. - Configuration Changes: The base class handles syncing the drawer toggle state after screen rotations, so you don't have to repeat that code.
- Extensibility: If you add more activities later, you just add a new case to the base class's
OnNavigationItemSelectedmethod and create a new child activity inheriting fromBaseDrawerActivity.
This approach eliminates all redundant drawer logic and keeps your code DRY (Don't Repeat Yourself).
内容的提问来源于stack exchange,提问作者lostsky25

