具有多个 Activity 的 Android 导航抽屉

Posted

技术标签:

【中文标题】具有多个 Activity 的 Android 导航抽屉【英文标题】:Android Navigation Drawer with multiple Activity 【发布时间】:2019-09-10 09:11:38 【问题描述】:

我正在尝试使用抽屉式导航创建一个 BaseActivity 并将其扩展到其他活动中,以便我可以重用抽屉式导航代码。

到目前为止我的发现:

我可以使用片段来做到这一点(我可以做到)。 我可以使用 BaseActivity 并膨胀内容区域(在我的例子中,ID 为“main_container”的框架布局)来显示其他活动。在这种情况下,我已经弄清楚如何单击导航抽屉项目来更改活动。

但是,当我的一个活动(例如活动A)中有一个按钮并且我想通过单击该按钮加载另一个活动(活动B)、显示祝酒词等时,除非我写,否则单击侦听器不起作用BaseActivity 的 onCreate 方法中监听器的代码。

对我来说这没有意义,因为它迫使我在 BaseActivity 中编写所有代码,这不是编写代码的最有效方式(活动就像片段一样,所以我可以只使用片段代替)。

我想知道的是,如何在扩展 BaseActivity 的所有活动上加载导航抽屉,并且仍然允许活动保留它们的行为。

代码示例

我的 activity_home.xml (BaseActivity)

<?xml version="1.0" encoding="utf-8"?>
<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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
android:layout_
tools:context=".HomeActivity"
tools:openDrawer="start"
android:fitsSystemWindows="true">

<LinearLayout
    android:layout_
    android:layout_
    android:orientation="vertical">

    <include
        android:id="@+id/custom_tool"
        layout="@layout/custom_bar"/>


    <FrameLayout
        android:id="@+id/main_container"
        android:layout_
        android:layout_ />
</LinearLayout>

<android.support.design.widget.NavigationView
    android:layout_
    android:layout_
    android:id="@+id/nav_display"
    app:menu="@menu/nav_drawer_menu"
    android:layout_gravity="start"
    app:headerLayout="@layout/nav_header"/>

</android.support.v4.widget.DrawerLayout>

activity_main.xml (activityA)

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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_
    android:layout_
    tools:context=".MainActivity">

<ImageView
    android:id="@+id/imageView"
    android:layout_
    android:layout_
    android:layout_marginStart="105dp"
    android:layout_marginLeft="105dp"
    android:layout_marginTop="150dp"
    android:layout_marginEnd="105dp"
    android:layout_marginRight="105dp"
    android:layout_marginBottom="48dp"
    android:contentDescription="@string/app_logo"
    app:layout_constraintBottom_toTopOf="@+id/search_bar"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="1.0"
    app:srcCompat="@mipmap/ic_launcher" />


<SearchView
    android:id="@+id/search_bar"
    android:layout_
    android:layout_
    android:layout_marginStart="50dp"
    android:layout_marginLeft="50dp"
    android:layout_marginTop="48dp"
    android:layout_marginEnd="50dp"
    android:layout_marginRight="50dp"
    android:layout_marginBottom="50dp"
    android:background="@drawable/input_default"
    android:gravity="center"
    app:layout_constraintBottom_toTopOf="@+id/search_button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/imageView" />

<Button
    android:id="@+id/search_button"
    android:layout_
    android:layout_
    android:layout_marginStart="150dp"
    android:layout_marginLeft="150dp"
    android:layout_marginTop="50dp"
    android:layout_marginEnd="150dp"
    android:layout_marginRight="150dp"
    android:layout_marginBottom="228dp"
    android:background="@drawable/button_default"
    android:text="@string/search"
    android:textColor="@color/colorAccent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/search_bar" />

</android.support.constraint.ConstraintLayout>

当应用程序最初加载时,BaseActivity 的“main_content”会与 activity_main 布局一起膨胀。

当我单击activity_main.xml 中的search_button 时,我想加载另一个仅在我在基本活动中编写代码时才会发生的活动。

HomeActivity

package com.example.naisse;


import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

public class HomeActivity extends AppCompatActivity 

protected static int position;
private DrawerLayout drawer;
protected FrameLayout frames;

Button searchButton;
/**
 *  This flag is used just to check that launcher activity is called first time
 *  so that we can open appropriate Activity on launch and make list item position selected accordingly.
 * */
private static boolean isLaunch = true;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home);

    Toolbar toolbar = findViewById(R.id.custom_tool);
    setSupportActionBar(toolbar);

    drawer = findViewById(R.id.drawer_layout);

    frames = findViewById(R.id.main_container);

    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();


    /*inflating the layout with activity_main*/
    getLayoutInflater().inflate(R.layout.activity_main, frames);



    searchButton = findViewById(R.id.search_button);

    searchButton.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View v) 

            /*removing the activity_main and inflating they layout with activity_result*/
            frames.removeAllViews();
            getLayoutInflater().inflate(R.layout.activity_result, frames);
        
    );


@Override
public void onBackPressed() 
    if(drawer.isDrawerOpen(GravityCompat.START))
        drawer.closeDrawer(GravityCompat.START);
    else
        super.onBackPressed();
    


使用 HomeActivity 扩展 MainActivity

MainActivity

package com.example.naisse;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends HomeActivity 



@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);





如何解决这个问题?

【问题讨论】:

请向我们展示您如何将HomeActivity 扩展为MainActivity 我已经更新了问题。 哦,好的,那么我知道混乱在哪里了。您将布局与活动混为一谈。仅仅因为你在HomeActivity 中膨胀了一个名为activity_main 的布局并不意味着这是一个单独的Activity。您实际上并没有使用 MainActivity 类。您只是将Views 添加到HomeActivity,这就是为什么您必须在那里设置OnClickListener,而在MainActivity 中设置它没有任何效果。布局只是View 层次结构的“蓝图”。它们与Activity 类没有内在联系。 您可以使用startActivity() 来启动相应的Activity 类,而不是那些inflate() 调用。但是,如果您的启动“页面”是MainActivity,您需要将清单中的LAUNCHER &lt;activity&gt; 更改为MainActivity 而不是HomeActivity,并删除该...inflate(R.layout.activity_main, ...) 调用来自HomeActivityonCreate()。添加上面提到的setContentView() 覆盖将负责正确扩展扩展活动的布局。然后您的OnClickListener 应该在MainActivity 中工作。 不,您不必复制/粘贴任何内容。您现在的设置非常接近所需的设置。在HomeActivity 中设置和初始化基本布局将自动导致每个扩展它的Activity 发生这种情况。你的HomeActivity 应该是这样的:drive.google.com/file/d/1aBCOxDVAaTcNuvFnCwDKoeSwrW9u8lKF/…。请注意,现在所有内容都在 setContentView() 覆盖中。在该课程中,您真的不需要onCreate()MainActivity 看起来就像上面一样,但现在你可以把 OnClickListener 放在那里。 【参考方案1】:

我很惊讶这么多年来很少有人对此感兴趣。在所有活动的 xml 文件中维护 DrawerLayout 东西的重复代码是非常愚蠢的。

我尝试了@Mill3r 的自我回答,不幸的是,覆盖 setContentView 与数据绑定冲突并导致崩溃。

我在基本活动中创建了一个方法,而不是 setContentView:

public void setContent(View contentView) 
    // Find the content container
    frames = findViewById(R.id.main_container);

    frames.removeAllViews();
    frames.addView(contentView);

    // The rest of the base setup
    Toolbar toolbar = findViewById(R.id.custom_tool);
    setSupportActionBar(toolbar);

    drawer = findViewById(R.id.drawer_layout);

    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();

然后在所有从base中嘲讽的activity中,在设置databinding后调用这个函数,如:

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
setContent(binding.getRoot());

【讨论】:

【参考方案2】:

如需解答和理解,请阅读我和 Mike M 之间的讨论。

我最初做的是用布局膨胀“main_container”,因此 Activity 类没有被触发。为此,我必须重写 BaseActivity(在我的例子中为 HomeActivity)中的 setContentView() 方法,并将工具栏和导航抽屉等所有可共享代码放入其中。

我的新 HomeActivity.class

public class HomeActivity extends AppCompatActivity 

private DrawerLayout drawer;
protected FrameLayout frames;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home);


@Override
public void onBackPressed() 
    if(drawer.isDrawerOpen(GravityCompat.START))
        drawer.closeDrawer(GravityCompat.START);
    else
        super.onBackPressed();
    



@Override
public void setContentView(int layoutResID) 
    // Set the base layout
    super.setContentView(R.layout.activity_home);

    // Find the content container
    frames = findViewById(R.id.main_container);

    // Inflate the extending Activity's layout into it
    getLayoutInflater().inflate(layoutResID, frames);

    // The rest of the base setup
    Toolbar toolbar = findViewById(R.id.custom_tool);
    setSupportActionBar(toolbar);

    drawer = findViewById(R.id.drawer_layout);

    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();



现在工具栏和导航抽屉在所有活动之间共享。

【讨论】:

关于答案的几点: (a) 无需在标题中添加 [已解决] - 有一个功能齐全的答案接受系统,应该使用它来代替。此外,(b) 评论流经常被删除,因此不应从正确的答案中引用 - 如果您想记录答案,请在答案中写下对话的要点。

以上是关于具有多个 Activity 的 Android 导航抽屉的主要内容,如果未能解决你的问题,请参考以下文章

Android - 具有多个底部导航菜单的导航组件

Android四大组件的生命周期

Android总结篇系列:Activity启动模式(lauchMode)

Android MVVM:具有多个片段的活动 - 将共享 LiveData 放在哪里?

android 弹出多个Activity的问题

android开发源代码分析--多个activity调用多个jni库的方法