具有多个 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
类。您只是将View
s 添加到HomeActivity
,这就是为什么您必须在那里设置OnClickListener
,而在MainActivity
中设置它没有任何效果。布局只是View
层次结构的“蓝图”。它们与Activity
类没有内在联系。
您可以使用startActivity()
来启动相应的Activity
类,而不是那些inflate()
调用。但是,如果您的启动“页面”是MainActivity
,您需要将清单中的LAUNCHER
<activity>
更改为MainActivity
而不是HomeActivity
,并删除该...inflate(R.layout.activity_main, ...)
调用来自HomeActivity
的onCreate()
。添加上面提到的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总结篇系列:Activity启动模式(lauchMode)