导航抽屉用于切换活动而非片段

55
在Android中,是否可以使用导航抽屉而不是更新片段,我希望通过切换活动作为应用程序内的导航手段。

你到底想要什么? - SweetWisher ツ
我想要的是使用一个导航抽屉,使其对于所有活动都可用以切换活动。 - SleepNot
你能展示一下你的设计吗?为什么你只想要导航抽屉? - SweetWisher ツ
我的应用程序最初的设计是仪表板导航风格,但我现在要切换到抽屉式导航风格。 - SleepNot
还有其他的选择。首先定义您的需求,可能其他控件可以帮助您解决问题。 - SweetWisher ツ
你有检查过这个吗?https://dev59.com/CHPYa4cB1Zd3GeqPdwHQ - SweetWisher ツ
5个回答

52

是的,这是可能的——这就是我为我的应用所做的。我已经设置了许多活动,而不是将它们全部转换为片段,我想要定制导航抽屉以在它们所有的活动中使用。不幸的是,这不是一个快速的变通方法,所以如果您有使用片段的选项,我建议您使用片段。但无论如何,这就是我做的方式:

假设我有2个活动,我都希望有导航抽屉。在每个 layout.xml 中,我指定了一个 DrawerLayout, 以及相应的 ListView 来保存我的导航选项。实质上,每次我在活动之间切换时,都会创建导航抽屉,从而给人们留下持久的印象。为了让生活变得更加轻松,我将设置导航抽屉所需的公共方法放入了自己的类中:NavigationDrawerSetup.java。这样,我的活动可以使用相同的自定义适配器等。

在这个 NavigationDrawerSetup.java 类内部,我有以下内容:

  • configureDrawer() - 这设置了 ActionBarActionBarDrawerToggle 和所需的侦听器
  • 我的自定义数组适配器(用于填充列表中的导航选项)
  • selectOptions() 方法,用于处理抽屉项目的点击

当您在其中一个活动中设置导航抽屉时,只需创建一个新的 NavigationDrawerSetup 对象并传入所需的布局参数(如 DrawerLayoutListView 等),然后调用 configureDrawer()

        navigationDrawer = new NavigationDrawerSetup(mDrawerView, mDrawerLayout,
            mDrawerList, actionBar, mNavOptions, currentActivity);

    navigationDrawer.configureDrawer();

currentActivity 是因为导航抽屉与您所在的活动相关联,所以需要传入。在设置 ActionBarDrawerToggle 时必须使用它:

mDrawerToggle = new ActionBarDrawerToggle(currentActivity, // host Activity
        mDrawerLayout, /* DrawerLayout object */
        R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
        R.string.drawer_open, /* "open drawer" description for accessibility */
        R.string.drawer_close /* "close drawer" description for accessibility */
        )
你在设置自定义适配器时,还需要使用currentActivity。至于如何通过导航抽屉切换活动,则可以在selectItem()方法中设置新的意图。
private void selectItem(int position) {

    // Handle Navigation Options
    Intent intent;
    switch (position) {
        case 0:
            intent = new Intent(currentActivity, NewActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            currentActivity.startActivity(intent);
            break;
        case 1: 
            // etc.
    }

只要确保您的新Activity也已设置导航抽屉,它就应该显示。

有很多事情可以做来自定义此方法以满足您自己的需求,但这是我使用的一般结构。希望这能帮助到您!


1
稍后会检查这个建议,因为它似乎是一个很好的建议。也许使用导航抽屉与片段进行转场动画时很难模拟新片段的过渡动画,因为从一个活动到另一个活动的过渡与加载新片段看起来不同。 - SleepNot
@David Crozier,你在吗?我在创建导航抽屉时遇到了困难,如果你愿意,我可以向你展示我的代码。 - bipin
这个解决方案真的推荐吗?我做过类似的事情,但UI表现得不是很流畅。问题在于当你点击导航抽屉中的一个选项时,你必须隐藏抽屉并启动一个新的Activity。这个转换看起来有点奇怪,因为两种效果在屏幕上有点重叠。有什么建议吗? - narko
@AKh 我通过在启动新活动后finish()当前活动来“管理”堆栈。这对我有效,但如果您想在抽屉选择之间保留返回堆栈,则不适用于您。 - Alex Suzuki
这似乎是一种更简单的方法。https://dev59.com/OX3aa4cB1Zd3GeqPg7ax#22467742 - pavitran
随着活动数量的增长,这种方法不具有可扩展性。 - pcodex

18

您需要一个实现导航抽屉的BaseDrawerActivity,然后在需要导航抽屉的每个活动中扩展BaseDrawerActivity

首先创建BaseDrawerActivity.java

public class BaseDrawerActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{

    DrawerLayout drawerLayout;
    Toolbar toolbar;
    FrameLayout frameLayout;
    NavigationView navigationView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.activity_base_drawer);;

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

        frameLayout = (FrameLayout) findViewById(R.id.content_frame);

        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawerLayout.setDrawerListener(toggle);
        toggle.syncState();

        navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }

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

    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        int id = item.getItemId();

        //to prevent current item select over and over
        if (item.isChecked()){
            drawerLayout.closeDrawer(GravityCompat.START);
            return false;
        }

        if (id == R.id.nav_camera) {
            // Handle the camera action
            startActivity(new Intent(getApplicationContext(), CameraActivity.class));
        } else if (id == R.id.nav_gallery) {
            startActivity(new Intent(getApplicationContext(), GalleryActivity.class));
        } else if (id == R.id.nav_slideshow) {
            startActivity(new Intent(getApplicationContext(), SlideshowActivity.class));
        } else if (id == R.id.nav_manage) {
            startActivity(new Intent(getApplicationContext(), ManageActivity.class));
        } else if (id == R.id.nav_share) {
            startActivity(new Intent(getApplicationContext(), ShareActivity.class));
        } else if (id == R.id.nav_send) {
            startActivity(new Intent(getApplicationContext(), SendActivity.class));
        }
        drawerLayout.closeDrawer(GravityCompat.START);
        return true;
    }
}

然后在res/layout文件夹中创建activity_base_drawer.xml

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:openDrawer="start">

    <include layout="@layout/app_bar_home"/>

    <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:headerLayout="@layout/nav_header_home"
        app:menu="@menu/activity_home_drawer" />

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

其中@layout/app_bar_home代表:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.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"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <FrameLayout android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</android.support.design.widget.CoordinatorLayout>

接下来,您需要输入具有导航抽屉的活动,例如CameraActivity.java :

public class CameraActivity extends BaseDrawerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getLayoutInflater().inflate(R.layout.activity_camera, frameLayout);

        /**
        * Setting title
        */
        setTitle("Camera");

    }

    @Override
    protected void onResume() {
        super.onResume();
        // to check current activity in the navigation drawer
        navigationView.getMenu().getItem(0).setChecked(true);
    }
}

其中R.layout.activity_camera是您的CameraActivity.java的布局。

然后创建其他活动,如具有导航抽屉的GalleryActivity.java等:

public class GalleryActivity extends BaseDrawerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getLayoutInflater().inflate(R.layout.activity_gallery, frameLayout);

        // Setting title
        setTitle("Gallery");

    }

    @Override
    protected void onResume() {
        super.onResume();
        navigationView.getMenu().getItem(1).setChecked(true);
    }
}

1
你如何管理Activity堆栈...所有的Activity都将被保存在堆栈中。当你按下返回按钮时,所有的Activity将一个接一个地关闭。 - Rahul Hawge
1
你是如何初始化 frameLayout 的?(关于这行代码:getLayoutInflater().inflate(R.layout.activity_gallery, frameLayout);) - Yassir S
@YassirS,frameLayout 在 BaseDrawerActivity 中。 - Yashas
这个答案可以有一个改进。setContentView() 可以被重写。https://dev59.com/0n7aa4cB1Zd3GeqPkwVg#22652821 - h8pathak

3

作为对@David-Crozier提出的解决方案的一点改进,在关闭NavigationDrawer并启动新活动的两个动画重叠时,您可以在您的方法中包含一点延迟,就像在iosched应用程序v2014中所做的那样:

private void onNavDrawerItemClicked(final int itemId) {
    if (itemId == getSelfNavDrawerItem()) {
        mDrawerLayout.closeDrawer(GravityCompat.START);
        return;
    }

    if (isSpecialItem(itemId)) {
        goToNavDrawerItem(itemId);
    } else {
        // launch the target Activity after a short delay, to allow the close animation to play
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                goToNavDrawerItem(itemId);
            }
        }, NAVDRAWER_LAUNCH_DELAY);

        // change the active item on the list so the user can see the item changed
        setSelectedNavDrawerItem(itemId);
        // fade out the main content
        View mainContent = findViewById(R.id.main_content);
        if (mainContent != null) {
            mainContent.animate().alpha(0).setDuration(MAIN_CONTENT_FADEOUT_DURATION);
        }
    }

    mDrawerLayout.closeDrawer(GravityCompat.START);
}

这里提供参考链接:https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/BaseActivity.java


1

0
在你的mobile_navigation文件XML中,添加以下内容。
    <activity
    android:id="@+id/nav_custom"
    android:name="net.larntech.nav.CustomActivity"
    android:label="@string/menu_custom"
    tools:layout="@layout/activity_custom"
    />

navigation drawer

您可以通过下面的链接找到完整的教程。 Android Studio导航抽屉


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接